Chỗ An có hai đường mạng, Viettel với VNPT, cắm vô một con MikroTik hEX. Router tự chia tải, ngày thường xài không sao.
Vấn đề là có mấy việc cần đi đúng một nhà mạng. Có dịch vụ chỉ cho IP Viettel vô. Có cái lại chạy mượt trên VNPT mà qua Viettel thì rớt. Mỗi lần cần là phải vô Winbox sửa rule, sửa xong nhớ sửa lại, quên một cái là cả phòng ngồi ngó nhau.
Cái An muốn: trên máy có sẵn một cái nút. Bấm Viettel thì máy đó đi Viettel. Bấm VNPT thì đi VNPT. Máy nào không đụng tới thì vẫn chạy y như cũ, không biết gì hết.
Nghe thì dễ. Mò hai ngày mới ra, dính đủ bốn cái bẫy. Cái thứ năm thì mãi sau mới lòi ra, mà nó lại là cái đau nhất. Kể lại đây luôn cả mấy khúc đi sai, vì phần đó mới là phần không kiếm được ở đâu.

Ý tưởng: đổi gateway, để router nhìn địa chỉ MAC
Router không đọc được ý nghĩ của máy con. Nhưng nó thấy được máy con gửi gói tới địa chỉ MAC nào.
Nên thay vì một gateway 192.168.88.1, An dựng thêm hai gateway ảo nữa trên cùng con router, mỗi cái một MAC riêng:
192.168.88.2cho Viettel192.168.88.3cho VNPT
Cách dựng là dùng interface VRRP. VRRP sinh MAC theo công thức 00:00:5E:00:01:<vrid>, nên mỗi cái chắc chắn khác nhau. An lấy vrid 201 với 202.
Rồi bốn cái rule mangle: gói nào đi vô qua gw-viettel thì đánh dấu, gói mang dấu đó thì đẩy qua bảng định tuyến riêng, bảng đó có default route ra pppoe-out1. VNPT y chang, đổi số.
Máy con muốn đi Viettel thì chỉ cần đổi default gateway của nó thành .2. Không cài driver, không VPN, không đụng gì tới router nữa. Cấu hình cũ vẫn nằm nguyên đó.
Tới đây thấy đẹp quá. Rồi bắt đầu ăn hành.
Bẫy 1: cái /24 làm bảng ARP phình gấp ba
Lần đầu An đặt IP cho hai interface VRRP kiểu bình thường, 192.168.88.2/24 với 192.168.88.3/24.
Xong nhìn /ip route print: có ba cái connected route trùng nhau cho cùng lớp 192.168.88.0/24. Bảng ARP cũng nhân lên gấp ba, mỗi máy trong LAN hiện ba lần.
Router vẫn chạy, nhưng đó là kiểu chạy mà nửa đêm nó mới trở chứng.
Sửa: đặt /32, kèm network= chính nó.
/ip address add address=192.168.88.2/32 network=192.168.88.2 interface=gw-viettel/32 thì không sinh connected route cho cả lớp mạng, chỉ đúng một địa chỉ. Bảng route sạch lại liền.
Bẫy 2: bấm nút báo thành công mà không ra được mạng
Đây là chỗ mất nhiều thời gian nhất, và cũng là chỗ An quạu nhất.
Tool báo xanh lè "đã áp dụng thành công". Đổi gateway đúng, route đúng, mọi thứ nhìn đẹp. Mà máy đó không ra được internet luôn. Ping 1.1.1.1 không được. Ping 8.8.8.8 cũng không. Bấm về Mặc định thì chạy lại bình thường.
Ngồi soi gần một tiếng mới hiểu: connection-mark trong RouterOS là hai chiều. Cái dấu An đóng lên kết nối nằm luôn trên gói đi ra lẫn gói trả về.
Nên khi gói trả về từ internet chạy vô, cái rule mark-routing của An chộp luôn nó, rồi đẩy nó qua bảng định tuyến riêng. Gói trả về đáng lẽ phải quay ngược xuống LAN thì bị quăng ngược ra WAN. Banh chành hết trơn 😂
Sửa thì đúng một chữ. Rule mark-routing bắt buộc phải khoá theo hướng đi vô:
/ip firewall mangle set [find comment~"3/4"] in-interface=gw-viettel
/ip firewall mangle set [find comment~"4/4"] in-interface=gw-vnptCó in-interface, rule chỉ áp cho gói thật sự từ LAN đi lên. Gói trả về không dính. Sau đó test 9 lần bằng HTTPS thật, đủ 9 lần ra đúng nhà mạng.
Ai định làm policy routing trên RouterOS thì nhớ giùm cái này nha. mark-connection hai chiều, mark-routing phải có in-interface hoặc src-address-list. Không có là đứt.
Bẫy 3: Element not found mà PowerShell coi là lỗi chết người
Tool trên máy Windows viết bằng PowerShell. Nó gọi route.exe để gỡ route cũ trước khi thêm route mới.
Lần chạy đầu tiên thì làm gì có route cũ, nên route.exe in ra The route deletion failed: Element not found. Chuyện bình thường, không phải lỗi.
Vấn đề là Windows PowerShell 5.1 biến mỗi dòng stderr của chương trình ngoài thành một ErrorRecord thật sự. Mà script đang để $ErrorActionPreference = 'Stop'. Nên một câu thông báo vô hại giết luôn cả script, người dùng thấy đỏ lòm một dòng LỖI: rồi không biết làm gì.
Cái này khùi như đầu bờ, mà nó là hành vi mặc định của PowerShell 5.1 nên phải sống chung. Cách sống chung là bọc lại:
function Invoke-Native {
param([string]$Exe, [string[]]$Arguments)
$old = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try { $out = & $Exe @Arguments 2>&1 | Out-String
return @{ Code = $LASTEXITCODE; Output = $out.Trim() } }
catch { return @{ Code = -1; Output = $_.Exception.Message } }
finally { $ErrorActionPreference = $old }
}Hạ ErrorActionPreference đúng trong lúc gọi, trả về mã lỗi cho bên gọi tự quyết. Ai viết script PowerShell gọi netsh, route, arp, ipconfig thì nên xài kiểu này, khỏi bị mấy dòng cảnh báo vô hại giết oan.
Bẫy 4: bấm xong, có route khác giành mất
Cái này mới lòi ra sau cùng. Tool báo "xong" nhưng kèm một câu lơ lửng kiểu route khác đang chiếm, cần kiểm tra. Người dùng đọc xong đứng hình, không biết chiếm là chiếm cái gì.
Đây không phải lỗi router. Lệnh thêm route chạy sạch sẽ, nhưng Windows vẫn chọn một default route khác để đi.
Windows chọn đường theo metric hiệu dụng = RouteMetric của route + InterfaceMetric của card. Thấp hơn là thắng. Route tool thêm vô để metric 1, chỉ chắc chắn thắng route DHCP trên cùng một card. Nó không tự động thắng một card khác.
Mà máy nào cũng có thể có nhiều đường ra cùng lúc: VPN, card ảo Hyper-V hay VMware hay WSL, Wi-Fi đang bắt sang mạng khác, cục 4G USB cắm quên rút.
Nên An viết lại cho tool tự leo thang, và ghi rõ từng bước ra nhật ký:
Card đang thắng cũng nằm trong lớp mạng của router luôn? Vậy chỉ cần dời route sang đúng card đó, khỏi đụng metric.
Không phải? Vậy hạ InterfaceMetric của card LAN xuống đủ thấp để vượt qua. Giá trị gốc được cất lại, bấm Mặc định là trả về y như cũ.
Vẫn thua? Thì thôi, nói thẳng ra đứa nào đang chiếm:
Hồi đó nó ra vầy:
⚠ CHƯA ăn, hệ thống vẫn đi hướng khác.
Card 'OpenVPN TAP-Windows6' [VPN] đang chiếm default route qua 10.8.0.1, metric 1.
VPN toàn tuyến kéo hết traffic vô tunnel, chọn nhà mạng không còn tác dụng.
Ngắt VPN rồi bấm lại, hoặc bật split-tunnel cho VPN đó.
Bảng default route (metric thấp hơn thì thắng):
> OpenVPN TAP-Windows6 10.8.0.1 metric 1 (VPN)
Ethernet 192.168.88.2 metric 16 (vật lý)
Ethernet 192.168.88.1 metric 30 (vật lý)Loại card được đoán từ tên với mô tả driver: VPN, máy ảo, Wi-Fi, di động, vật lý. Mỗi loại một câu chỉ việc riêng, vì cách xử lý khác nhau.
Riêng VPN toàn tuyến, hồi đó An xếp vô ca cố tình không cứu. Giành được route cũng vô ích, vì traffic vẫn chui hết vô tunnel. Báo thành công lúc đó là báo láo.
Cái thang này chạy ngon được đúng mấy hôm.
Bẫy 5: metric 1 không phải là "ưu tiên cao"
Rồi máy An bấm nút, tool báo chuyển xong, kèm nguyên đoạn này:
⚠ CHƯA ăn, hệ thống vẫn đi hướng khác.
Card 'Ethernet' [vật lý] đang chiếm default route qua 192.168.88.1, metric 25.
Ngắt kết nối mạng phụ này rồi bấm lại.
Bảng default route (metric thấp hơn thì thắng):
> Ethernet 192.168.88.1 metric 25 (vật lý)
Ethernet 192.168.88.3 metric 26 (vật lý)Ngắt mạng phụ nào? Con máy đó có đúng một cái card mạng. Làm theo lời tool là tự cắt mạng của chính mình. 🤦
Nhìn kỹ hai dòng: cùng một card Ethernet. Đứa đang chiếm không phải mạng phụ nào hết, nó là route mặc định do DHCP của router cấp.
Tới đây mới lòi ra chỗ An hiểu sai từ đầu. route add ... metric 1 nghe như đang đặt ưu tiên cao. Không phải. Con số đó là RouteMetric, mà Windows đem cộng nó vào InterfaceMetric của card:
route DHCP cấp, ra .1 RouteMetric 0 + InterfaceMetric 25 = 25 <- thắng
route tool thêm, ra .3 RouteMetric 1 + InterfaceMetric 25 = 26Đặt metric 1 tức là tự cộng thêm một điểm vô người mình. Route DHCP để RouteMetric 0 nên nó luôn thấp hơn. Còn hạ InterfaceMetric thì trớt quớt, vì hai đứa nằm chung một card, cùng tụt đúng bằng nhau, khoảng cách y nguyên.
Nghĩa là bậc 2 của cái thang ở Bẫy 4 bó tay ở đúng ca phổ biến nhất. Còn bậc 3 thì đi khuyên người ta rút dây mạng đang xài. Cùi bắp.
Thắng bằng độ dài prefix, khỏi đua metric
Chỗ An bỏ sót: hệ điều hành chọn đường theo hai vòng, và metric là vòng thứ hai.
Prefix dài hơn thắng trước. Luôn luôn.
Cùng độ dài prefix rồi mới đem metric ra so.
Vậy đua metric làm chi. Cắt đôi không gian địa chỉ ra hai nửa:
route -p add 0.0.0.0 mask 128.0.0.0 192.168.88.3 metric 1 if 12
route -p add 128.0.0.0 mask 128.0.0.0 192.168.88.3 metric 1 if 120.0.0.0/1 cộng 128.0.0.0/1 phủ đúng bằng 0.0.0.0/0, không thiếu một địa chỉ nào, nhưng dài hơn đúng một bit. Thế là thắng mọi default route, khỏi cần biết metric nó bao nhiêu.
Không phải An nghĩ ra đâu. Mấy phần mềm VPN kéo toàn bộ traffic vô tunnel xài chiêu này từ đời nào rồi, OpenVPN kêu nó là redirect-gateway def1. An chỉ là đứa đi sau mà lâu hiểu.
Máy trong LAN không hề hấn gì: route on-link của lớp mạng là /24, dài hơn /1 nhiều, nên gói đi trong nhà vẫn đi thẳng.
Chạy thử trên máy thật, tái tạo đúng cảnh cũ:
Trước: > Ethernet 192.168.88.1 metric 15
Sau: > Ethernet 192.168.88.2 metric 16 [0.0.0.0/1]
Ethernet 192.168.88.1 metric 15Metric 16 thắng metric 15. Nhìn ngược đời mà đúng.
Mấy thứ phải sửa theo
Chiêu này làm hư luôn cách tool tự kiểm tra. Trước đó nó đọc bảng route rồi lấy dòng metric thấp nhất coi như đường đang đi. Có route /1 trong bảng thì đọc kiểu đó là đoán sai ngay. Giờ tool hỏi thẳng: Find-NetRoute trên Windows, ip route get trên Linux, route -n get trên macOS. Hỏi thẳng thì khỏi đoán.
Cái thang leo cũng xếp lại:
Card đang thắng cũng nằm trong lớp mạng của router? Dời route sang card đó.
Cắm cặp
/1. Ăn gần hết mọi ca.Vẫn thua thì mới hạ
InterfaceMetric. Chỗ này giờ chỉ còn cần khi có phần mềm khác cũng cắm route/1, thường là VPN.Hết cách thì chỉ đích danh đứa đang chiếm.
Còn câu "ngắt kết nối mạng phụ này" thì quăng đi. Đứa chiếm nằm chung card thì tool nói đúng bản chất: đó là route router cấp, đừng rút dây.
Chuyện VPN cũng phải đổi giọng so với Bẫy 4. Cặp /1 vượt được VPN thật, nhưng vượt xong thì traffic không còn đi trong tunnel nữa. Nên tool vẫn làm, mà ghi thêm một câu vô nhật ký cho bạn biết mình vừa bị kéo ra khỏi VPN, bấm Mặc định là trả lại.
Bên Linux với macOS An vá luôn, mà bên đó còn được lợi hơn. Linux có kiểu chết riêng: route DHCP để metric 0 thì không hạ xuống thấp hơn được nữa, mà thêm route trùng metric thì kernel quăng EEXIST vô mặt. macOS thì chỉ cho đúng một default route, trước giờ phải xoá nó đi rồi thêm lại, làm mà run tay. Có cặp /1 là hết phải phá gì của ai.
Tiện tay khoá luôn cái cổng SSH mở toang
Trong lúc mò, An mới để ý cổng SSH của con router đang mở ra internet. Ai gõ đúng IP là thấy màn hình đăng nhập.
Khoá hai lớp. Lớp một giới hạn ở tầng dịch vụ:
/ip service set ssh address=192.168.88.0/24Lớp này chưa đủ. /ip service address chỉ từ chối sau khi đã bắt tay xong, nghĩa là cổng vẫn lộ ra ngoài, vẫn ăn đủ mọi thứ quét. Nên thêm lớp hai, chặn thẳng ở tầng gói tin, trước khi tới dịch vụ:
/ip firewall filter add chain=input protocol=tcp dst-port=22 \
in-interface=pppoe-out1 action=dropBật xong ngồi ngó bộ đếm. 45 giây, bốn gói bị chặn. Bốn con bot đang gõ cửa liên tục mà mình không hay 😅
Rồi quăng luôn lên GitHub
Làm xong cho chỗ mình thì thấy tiếc. Chỗ nào có hai đường mạng cũng gặp đúng bài này.
Nên An gói lại thành mã nguồn mở. Có một app quản trị chạy ở localhost, kết nối vô router bằng SSH, tự đọc ra có mấy đường WAN, tự tra tên nhà mạng từ IP public bằng RDAP (vì comment trong router thường sai hoặc cũ, IP public thì không nói dối). Người quản trị đặt tên, chọn màu, bấm áp dụng. Xong nó đóng gói luôn cái app bấm nút cho máy người dùng, kèm cấu hình đúng của hệ thống đó.
Có sẵn mấy thứ An phải tự đau mới nghĩ ra:
Xem trước trước khi ghi. In ra đủ từng dòng lệnh sẽ chạy, đọc xong ưng thì mới bấm.
Backup tự động trước mỗi lần đụng vô router.
Dead-man switch. Cài một scheduler trên router, nếu người quản trị mất kết nối giữa chừng thì nó tự hoàn tác. Cấu hình mạng từ xa mà tự khoá mình ở ngoài là chuyện có thật.
Dò router phụ. Máy nằm sau một con router NAT khác thì không chọn được, vì MikroTik chỉ thấy đúng một địa chỉ nguồn. Tool dò ra và cảnh báo, kèm bốn cách xử lý.
Sinh khoá SSH ngay trong app, nạp thẳng vô router, thử đăng nhập lại bằng khoá mới, hư thì tự gỡ khoá ra trả router về nguyên trạng.
Chạy được cả RouterOS v6 lẫn v7. Bản v6 thì An mới test trên giả lập chứ chưa có phần cứng thật, nói trước cho khỏi bất ngờ.
Tự dựng ngay trên trình duyệt, khỏi cài gì
Lười cài Python thì có bản chạy thẳng trên web:
trinhleminhan.com/tools/mikrotik-wan-selector
Cách xài: trang đưa một lệnh chỉ đọc, bạn dán vô Winbox rồi copy kết quả dán ngược
lại. Trang tự đọc ra router có mấy đường WAN, LAN nào, VRID nào còn trống, rồi
xuất ra đoạn lệnh để dán vô router và file zip app đã nhúng đúng cấu hình của
bạn, tải về là xài.
Trang đó chạy hoàn toàn trong máy bạn. Không có máy chủ nào đứng giữa, không
gửi cấu hình router đi đâu hết, cũng không lưu gì lại. Bấm Ctrl+U coi được toàn bộ
mã. An cố tình làm vậy: cầm quyền SSH router của người lạ là chuyện An không muốn
đụng vô, mà bảo bạn mở cổng SSH ra internet thì càng không.
Đổi lại, bản web thiếu ba thứ mà bản chạy trên máy có: sao lưu tự động trước khi
ghi, kiểm chứng lại bằng SSH sau khi ghi, và dead-man switch. Cần chắc ăn thì tải
app quản trị.
Tải về
Mã nguồn với hướng dẫn đầy đủ:
github.com/trinhleminhan-vn/mikrotik-wan-selector
Bản dựng sẵn để xem thử giao diện, giải nén là bấm đúp chạy được luôn:
Hai file zip đó xài cấu hình mẫu, chỉ để coi cho biết mặt mũi. Cấu hình thật cho router của bạn thì app quản trị sinh ra ở bước đóng gói.
Cài app quản trị:
git clone https://github.com/trinhleminhan-vn/mikrotik-wan-selector
cd mikrotik-wan-selector
pip install -r requirements.txt
python admin.pyAi thích đọc mấy vụ mò tool kiểu này thì coi thêm bài An trị con Ezviz Studio cứng đầu: @Thuần hoá Ezviz Studio: kéo cửa sổ, tắt tiếng, tự bấm Continue
Có gì hư thì mở issue trên GitHub, hoặc nhắn cho An. Con router của bạn với con router của An chắc chắn không giống nhau, mà đó mới là chỗ vui.
Bình luận