Ви нічого «важливого» не змінювали, просто додали другий аплінк, VPN або бридж для контейнерів. І раптом половина вихідного трафіку зникла,
або відповіді повертаються інтерфейсом, через який їх не чекають, і тихо помирають. Логи мовчать. Моніторинг показує «втрати пакетів».
Колеги говорять: «маршрутизація Linux — чорночаївництво».
Це не магія. Це policy routing. Це детерміновано. І воно також безжальне так, як бувають лише зрілі підсистеми: воно зробить
точно те, що ви попросили, а не те, що ви мали на увазі. Це полеґід для продакшену на Debian 13 по налагодженню ip rule і ip route,
з командами, які можна виконати під тиском, і висновками, які можна пояснити в постмортемі.
Ментальна модель: що насправді відбувається з пакетом
Коли Debian 13 маршрутизує пакет, він не обирає «дефолтний маршрут» в одній глобальній таблиці й не закінчує на цьому.
Працює політичний двигун. Цей двигун оцінює список правил (ip rule) зверху донизу.
Кожне правило може співпасти з метаданими пакета (джерело, призначення, fwmark, вхідний інтерфейс, UID тощо) і вказати, яку таблицю маршрутів шукати.
Обрана таблиця потім переглядається для знаходження найкращого маршруту (ip route), і лише після цього ядро вирішує вихідний інтерфейс, шлюз і адресу джерела.
Ключовий момент: таблиці маршрутів — це не просто «маршрути». Це «можливий всесвіт маршрутів»,
і правила вирішують, який всесвіт застосувати для цього пакета.
Якщо ви налагоджуєте, не запитуйте «який у мене маршрут?». Запитайте: «яке правило перемогло, яка таблиця опитана, і який маршрут вибрано з тієї таблиці?»
Як на практиці відчувається «чорна магія»
Збої policy routing мають характерні ознаки: щось працює з однієї IP-адреси, але не з іншої; ping працює, а TCP — ні; вихідний трафік проходить, а відповіді — ні;
одна підмережа в порядку, інша — ні. Стандартні помилки маршрутизації зазвичай більш однорідні. Політика — вибіркова.
Ще одна ознака: ви виконуєте ip route, бачите ідеальний дефолтний маршрут, а трафік все одно виходить іншим інтерфейсом.
Це тому, що відповідний пакет ніколи не звертався до main. Він звернувся до іншої таблиці через правило, про яке ви забули.
Або через правило, вставлене network manager-ом. Або VPN-клієнтом. Або вашою «тимчасовою» заглушкою з минулого року.
Policy routing не є опцією, коли є складність
Якщо у вас є будь-що з переліченого нижче, ви вже використовуєте policy routing, хоч би визнавали це чи ні:
- Два аплінки (dual-WAN, LTE-резерв, міграція провайдера)
- VPN зі спліт тунелюванням
- Кілька source IP на одному хості
- Kubernetes/CNI з особливостями маршрутизації на хості
- Розподіл трафіку за застосунком (UID/cgroup marks)
- Будь-яка політика безпеки, що використовує fwmarks для спрямування трафіку
Трюк — зробити це спостережуваним і банальним. Банальність зберігає ваші вихідні дні.
Одна цитата, яку варто тримати на столі
Hope is not a strategy.
— General Gordon R. Sullivan
Надія у налаштуванні маршрутизації зазвичай виглядає як «воно працювало в staging» або «Linux розрулить». Воно розрулить.
Просто не так, як ви того хотіли.
Факти та історія, що пояснюють сучасні дивності
- Policy routing у Linux — не новинка. Воно у мейнлайні ще з ядра 2.2, і
iproute2виросло навколо нього. - «Main» таблиця не спеціальна за дизайном — лише за конвенцією. Правила вирішують, коли вона використовується; багато систем маршрутують більшість трафіку через main лише тому, що правила за замовчуванням це дозволяють.
ip ruleоцінюється за пріоритетом, а не за порядком вставлення. Пріоритети можуть перекриватися, і «менший номер перемагає» — справжній порядок.- Навіть на «простому» хості є кілька вбудованих правил. Зазвичай: local lookup, потім main, потім default. Цей локальний lookup — чому хост може досягати власні IP, навіть якщо інша маршрутизація зламана.
- Reverse path filtering (
rp_filter) створювався для здорового глузду, а не для мультигомінгу. Строгий режим відкидає пакети, що прийшли інтерфейсом, який ядро не використало б, щоб дістатися до джерела. fwmark-базована маршрутизація старіша за багато кар’єр. Маркування Netfilter та policy routing довго працюють разом, бо це чисте розділення: класифікуй через firewall, маршрутуй через правила.- «Дефолтний маршрут» у ядрі не є одиничним. Можна мати кілька дефолтів, метрики вирішують пріоритет у таблиці, а правила вирішують, чи буде дефолт ураховано взагалі.
- Network manager-и люблять додавати правила. systemd-networkd, ifupdown2, VPN-клієнти та стек контейнерів часто встановлюють свої правила і таблиці для ізоляції.
- Conntrack може зберегти рішення, які ви більше не хочете. Потік, що маршрутизувався в один бік, може продовжуватися цим шляхом до його закінчення, навіть після того, як ви «пофіксили» маршрутизацію.
Це не дрібниці. Це причини, чому policy routing здається мінливим.
Насправді воно не мінливе. Ви просто змінили правила, і правила змінили світ.
Швидкий план діагностики (перший/другий/третій)
Коли трафік «таємничо» обирає неправильний інтерфейс, не починайте з редагування конфігів. Почніть з того, щоб спитати ядро, що б воно зробило.
Ці перевірки впорядковані так, щоб швидко звузити простір проблем.
Перший: підтвердіть рішення маршруту для конкретного пакета
- Використайте
ip route getдля призначення, з явно вказаною source IP якщо потрібно. - Якщо задіяні fwmarks, тестуйте з mark через
ip route get ... mark(куди підтримується) або емуляцією через перевірку правил і таблиць. - Підтвердіть обрану таблицю через
ip ruleі вибраний маршрут черезip route show table X.
Другий: визначте, яке правило перемагає і чому
- Виведіть правила з пріоритетами. Шукайте збіги:
from,to,iif,fwmark,uidrange. - Перевірте правила, які ви не створювали: VPN-клієнти, cloud-init, networkd, стеки контейнерів.
- Підтвердіть, що маршрут існує в опитуваній таблиці; «blackhole» або «unreachable» маршрути можуть бути навмисними.
Третій: валідуйте зворотний шлях і фільтрацію
- Перевірте вибір адреси джерела та налаштування rp_filter.
- Шукайте асиметричну маршрутизацію: вихід через один інтерфейс, вхідні відповіді через інший.
- Перевірте поведінку NAT/conntrack; при потребі обережно очищайте записи.
Якщо виконувати ці кроки у вказаному порядку, зазвичай уникаєте ритуалу «перезапустити мережу, поки не перестане ламатися» о 2-й ранку.
Цей ритуал перетворює проблему маршрутизації на інцидент.
Практичні завдання: 12+ команд, очікуваний вивід і що робити далі
Кожне завдання нижче має три частини: команду, яку можна виконати, що означає вивід та рішення, яке потрібно прийняти.
Це різниця між налагодженням і інтерпретативним танцем у терміналі.
Завдання 1: Знімок правил (з пріоритетами) та пошук сюрпризів
cr0x@server:~$ ip -details -statistics rule show
0: from all lookup local
32764: from all fwmark 0x1 lookup vpn
32765: from 192.0.2.10 lookup isp2
32766: from all lookup main
32767: from all lookup default
Що це означає: Ядро спочатку перевірить local. Потім будь-який пакет з маркером 0x1 використовує таблицю vpn.
Будь-який пакет, джерело якого 192.0.2.10, використовує isp2. Решта йде в main.
Рішення: Якщо трафік «випадково» йде в VPN або до неправильного ISP, тепер ви знаєте умови. Якщо правило незнайоме, знайдіть інструмент, який його додав, перед видаленням.
Завдання 2: Перелік імен таблиць маршрутів і впевненість, що ви не гадаєте ID
cr0x@server:~$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
100 isp2
200 vpn
Що це означає: Імена таблиць відображаються у числові ID. Якщо скрипти посилаються на «table 200», а хтось її перейменував, ви створили загадку для Майбутнього Себе.
Рішення: Використовуйте імена в автоматизації де можливо (lookup vpn), тримайте цей файл під управлінням конфігурації і розглядайте ID як частину стабільного API.
Завдання 3: Запитайте ядро «як би ти це маршрутизував?»
cr0x@server:~$ ip route get 1.1.1.1
1.1.1.1 via 203.0.113.1 dev eth0 src 203.0.113.10 uid 0
cache
Що це означає: Для цього призначення ядро обрало шлюз 203.0.113.1 на eth0 і вказало джерело 203.0.113.10.
Рішення: Якщо ви очікували «використовувати eth1» або «джерело 192.0.2.10», це означає невідповідність правил/таблиць, а не проблему застосунку.
Завдання 4: Явно протестуйте політику на основі джерела (поширена пастка)
cr0x@server:~$ ip route get 1.1.1.1 from 192.0.2.10
1.1.1.1 via 192.0.2.1 dev eth1 src 192.0.2.10 uid 0
cache
Що це означає: З джерелом 192.0.2.10 ядро тепер використовує eth1. Це вказує на наявність правила from 192.0.2.10.
Рішення: Якщо відповіді падають, перевірте, що зворотний трафік також використовує той самий джерело і що rp_filter не відкидає асиметрію.
Завдання 5: Дамп main таблиці і перевірка дефолту та метрик
cr0x@server:~$ ip -statistics route show table main
default via 203.0.113.1 dev eth0 metric 100
default via 192.0.2.1 dev eth1 metric 200
203.0.113.0/24 dev eth0 proto kernel scope link src 203.0.113.10
192.0.2.0/24 dev eth1 proto kernel scope link src 192.0.2.10
Що це означає: Існує два дефолти; нижча метрика (100) перемагає, коли використовується main таблиця.
Рішення: Якщо ви планували failover, переконайтесь у наявності перевірки стану (а не лише метрик). Якщо планували розподіл трафіку, самі метрики не допоможуть; потрібні правила і окремі таблиці.
Завдання 6: Перевірте не-main таблицю (де зазвичай ховається «магія»)
cr0x@server:~$ ip route show table isp2
default via 192.0.2.1 dev eth1
192.0.2.0/24 dev eth1 scope link src 192.0.2.10
Що це означає: Таблиця isp2 має власний дефолт і підключений маршрут. Це мінімально адекватна policy-routing таблиця: вона може дістатися шлюзу і інтернету.
Рішення: Якщо таблиця має лише дефолт, але бракує підключеного маршруту, ARP/neighbor можуть працювати дивно. Додайте підключений маршрут або використайте proto kernel через призначення адреси.
Завдання 7: Перевірте blackhole/unreachable маршрути, що «працюють як задумано»
cr0x@server:~$ ip route show table vpn
blackhole 10.0.0.0/8
default via 10.8.0.1 dev tun0
10.8.0.0/24 dev tun0 scope link src 10.8.0.2
Що це означає: Хтось навмисно заборонив трафік RFC1918 у VPN таблиці. Це може запобігти випадковому hairpin у корпоративні мережі.
Рішення: Якщо ваш застосунок має доступ до сервісів 10.x і вони недоступні, це не «помилка маршрутизації», а політичне рішення. Виправте політику: видаліть або звужте blackhole, або налаштуйте правила, щоб лише певний трафік потрапляв у цю таблицю.
Завдання 8: Підтвердіть, чи rp_filter не відкидає ваші пакети
cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.eth0.rp_filter net.ipv4.conf.eth1.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1
Що це означає: Увімкнено строгий reverse path filtering. У мультигомінгових/політичних налаштуваннях строгий режим часто відкидає легітимний асиметричний трафік.
Рішення: Якщо у вас є навмисна асиметрія або кілька аплінків, перемкніться на loose режим (2) на відповідних інтерфейсах або глобально — після розуміння наслідків для безпеки.
Завдання 9: Переконайтеся, що правила дійсно співпадають (source, iif, fwmark)
cr0x@server:~$ ip rule show
0: from all lookup local
32764: from all fwmark 0x1 lookup vpn
32765: from 192.0.2.10 lookup isp2
32766: from all lookup main
32767: from all lookup default
Що це означає: Правила широкі. Правило по fwmark підхоплює будь-який помічений пакет будь-якого джерела. Правило по джерелу співпадає з точною IP-адресою.
Рішення: Якщо занадто багато трафіку йде в VPN, припиніть маркування всього підряд. Якщо занадто мало трафіку потрапляє в isp2, розширте from на підмережу або додайте додаткові from правила для інших адрес.
Завдання 10: Перевірте nftables/iptables маркування (прихований кермувальник)
cr0x@server:~$ nft list ruleset | sed -n '1,120p'
table inet filter {
chain output {
type filter hook output priority 0; policy accept;
meta skuid 1001 meta mark set 0x1
}
}
Що це означає: Трафік, згенерований UID 1001, маркується в OUTPUT hook як 0x1, що тригерить fwmark правило для маршрутизації.
Рішення: Якщо неправильний застосунок скеровується, виправте вибір UID, використайте cgroup marks або маркуйте лише конкретні призначення/порти. Уникайте широких маркерів, що випадково захоплюють оновлення пакетів, моніторинг або DNS.
Завдання 11: Використайте tcpdump з фокусом на інтерфейс, щоб довести асиметрію
cr0x@server:~$ sudo tcpdump -ni eth0 'host 1.1.1.1 and (tcp or icmp)'
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:10:55.120001 IP 203.0.113.10 > 1.1.1.1: ICMP echo request, id 511, seq 1, length 64
Що це означає: Вихідний ICMP йде через eth0. Якщо відповіді повертаються через eth1, ви знайшли асиметрію.
Рішення: Якщо ви бачите вихід через один інтерфейс і вхід через інший, перевірте rp_filter і переконайтесь, що зворотний шлях закріплений правильними правилами на основі джерела і маршрутами в таблицях.
Завдання 12: Перевірте neighbor/ARP на тім інтерфейсі, який має виходити
cr0x@server:~$ ip neigh show dev eth1
192.0.2.1 lladdr 52:54:00:12:34:56 REACHABLE
Що це означає: MAC шлюзу відомий і досяжний. Якби він був FAILED або відсутній, маршрутизація могла бути правильною, але L2 зв’язність — ні.
Рішення: Якщо neighbor resolution падає, не виніть ip rule. Перевірте VLAN, порти комутатора, security groups або налаштування ARP-фільтрації.
Завдання 13: Перевірте local таблицю і правило 0 (бо localhost брешуть)
cr0x@server:~$ ip route show table local | head
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 203.0.113.10 dev eth0 proto kernel scope host src 203.0.113.10
local 192.0.2.10 dev eth1 proto kernel scope host src 192.0.2.10
broadcast 203.0.113.0 dev eth0 proto kernel scope link src 203.0.113.10
Що це означає: Local таблиця — причина, чому ви завжди можете досягти своїх адрес, навіть якщо основна маршрутизація зламана.
Вона може маскувати проблеми під час тестування («curl на мій IP працює!»).
Рішення: Під час налагодження тестуйте з іншого хоста або використайте ip route get, щоб побачіти реальні рішення щодо виходу. Успіх у local таблиці не доводить зовнішню доступність.
Завдання 14: Перевірте conntrack на предмет потоків, що продовжують іти старим шляхом
cr0x@server:~$ sudo conntrack -L -p tcp 2>/dev/null | head
tcp 6 431999 ESTABLISHED src=203.0.113.10 dst=93.184.216.34 sport=42412 dport=443 src=93.184.216.34 dst=203.0.113.10 sport=443 dport=42412 [ASSURED] mark=1 use=1
Що це означає: Цей потік встановлений і має mark=1. Навіть якщо ви зміните правила маркування, існуючі з’єднання можуть зберігати оригінальний маршрут.
Рішення: Якщо ви тестуєте зміни маршрутизації, використовуйте нові з’єднання (інший source port) або вибірково видаляйте конкретні записи conntrack для тестового потоку. Уникайте очищення всього conntrack на продакшені, якщо вам не подобається пояснювати наслідки.
Завдання 15: Переконайтесь, що вибирається правильна адреса джерела (і виправте через ip route якщо потрібно)
cr0x@server:~$ ip route show table isp2 default
default via 192.0.2.1 dev eth1
Що це означає: Дефолтний маршрут не задає src. Ядро може обрати джерело, яке ви не очікували, якщо є декілька адрес.
Рішення: У середовищах з кількома адресами явно вказуйте src на ключових маршрутах в політичних таблицях, щоб поведінка була стабільною й легко діагностувалася.
Жарт №1: Policy routing — як офісна політика: офіційний оргшматок існує, але реальні рішення відбуваються в бічних правилах.
Три корпоративні історії (як дорослі ламають маршрутизацію)
Інцидент №1: Неправильне припущення («main table означає дефолтну поведінку»)
Середній SaaS зробив міграцію від одного ISP до двох аплінків: основний фібер і резервний LTE-роутер.
Фліт хостів мав кілька IP через застарілі allowlist-и. У швидкому тесті все виглядало добре:
ip route показував дефолт через фібер з нижчою метрикою. Чудово. Відправляємо в прод.
Наступного ранку частина вихідних API викликів почала таймаутитися. Не всі. Навіть не більшість.
Лише виклики від певного сервісу, що працював під виділеним системним користувачем, і лише до кількох партнерських мереж.
Команда ганялась за DNS. Потім за MTU. Потім за фаєрвол партнера. Партнер ганяв їх назад.
Насправді проблема була в одному ip rule, вставленому місяці тому «тимчасовим» VPN-клієнтом:
from all fwmark 0x1 lookup vpn. Трафік сервісного користувача маркувався старим nftables-правилом, якого ніхто не пам’ятав.
Той трафік ніколи не звертався до main. Він ішов у таблицю VPN, яка мала blackhole маршрут для одного з партнерських RFC1918 діапазонів, що використовувався за NAT.
Неправильне припущення було тонким: «якщо ip route виглядає правильно — маршрутизація правильна». Це не так.
ip route без вказаної таблиці чи get — це лише дефолтний всесвіт.
Фікс був нудним: видалити застаріле правило маркування, звузити VPN-стіринг лише до потрібних призначень і додати тест регресії маршрутизації в CI, що виконує ip route get з джерелами сервісів.
Постмортем-акція: «Жодне політичне правило не потрапляє в прод без власника і коментаря в системі управління конфігурацією». Це не бюрократія; це пам’ять.
Інцидент №2: Оптимізація, що обернулась проти нас («позначимо все для продуктивності»)
Велика корпорація мала проксі-шар на Debian з двома виходами: дешевий інтернет-лінк для масового трафіку і преміум-лінк для latency-sensitive API.
Хтось придумав розумну ідею: маркувати трафік за UID процесу і нехай policy routing розбереться. Не потрібно складних ACL-ів проксі.
Це працювало. Це також підштовхнуло до ще більш «хитрих» рішень.
Оптимізація полягала в тому, щоб маркувати весь вихідний трафік від групи сервісів для використання преміум-лінку, під приводом зниження хвостової затримки.
Правило nftables було широким: «якщо UID в цьому діапазоні, поставити mark 0x1».
Це включало не лише сервіс, але і агента оновлень, metrics shipper і TLS renewer, що працювали під тим же обліковим записом.
Довгий час нічого не ламалося. Потім на преміум-лінку стався флап під час вікна обслуговування.
Дизайн failover покладався на метрики в main таблиці, але маркований трафік обходив main цілком і йшов у політичну таблицю з одиничним дефолтом.
Коли преміум-шлюз став недоступний, маркований трафік не переключився на резерв. Він просто застопорився.
Моніторинг виглядав дивно, бо немаркований трафік був в порядку, а маркований помер мовчки.
Фікс не полягав у відмові від policy routing. Фікс — поважати режими відмов: додати резервний дефолт у політичній таблиці з вищою метрикою, або використовувати правило, що падає назад на main коли преміум недоступний.
І припинити використовувати UID-маркування як грубий інструмент; краще матчити за префіксами призначення або портами.
Жарт №2: Якщо ви «оптимізуєте» маршрути без плану на відмову, ви винайшли новий спосіб відправити пакети у довгу обідню перерву.
Інцидент №3: Нудна практика, що врятувала день («route get як стандартний тест»)
Фінтех компанія запускала Debian хости зі строгими вимогами: весь трафік бази даних має йти через приватну мережу, все інше — через інтернет.
Вони мали кілька NIC, VLAN-ів і VPN для адміністрування. Це багато рухомих частин.
Мережна команда не покладалася на племінні знання; вони кодифікували поведінку маршрутизації як тести.
Під час оновлення ОС зміна в керуванні мережевою конфігурацією випадково змінила ID таблиці: vpn перемістилася з 200 в 201 на деяких хостах.
Правила все ще посилались на lookup 200 у старому скрипті, тому ті машини мали правило, що вказувало в порожню таблицю.
Ядро відпало до main для адміністративного трафіку, що «було добре», поки команда безпеки не помітила з’єднання адміністраторів з невірного вихідного IP і не підняла питання.
Рятівним виявився нудний підхід: їх пайплайн розгортання запускав набір assert-тестів маршрутів на кожному хості після мережевих змін.
Він виконував ip route get перевірки для репрезентативних призначень з репрезентативними джерелами, включно з адмін-підмережами, партнерськими API і приватними базами.
Асерція для адмін-трафіку відпала відразу на уражених хостах. Жодного простою, ніякої містики — лише червоне попередження.
Вони зробили rollback, виправили скрипти щоб використовувати імена таблиць замість чисел, і залишили тести.
Система залишилась складною. Поведінка стала передбачуваною. Оце справжній успіх.
Типові помилки: симптом → корінна причина → виправлення
1) «Трафік виходить не тим інтерфейсом, але ip route виглядає правильно»
Симптом: ip route показує очікуваний дефолт, але пакети виходять іншим NIC.
Корінна причина: Політичне правило спрямовує цей трафік в іншу таблицю (fwmark, source-based, iif-based).
Виправлення: Використайте ip rule show і ip route get DEST from SRC. Виправте правило, що перемагає, або таблицю, що опитується. Не довіряйтесь лише вигляду main таблиці.
2) «Вихід працює, а вхідні відповіді відкидаються (або навпаки)»
Симптом: SYN-и відправляються, SYN-ACK-и не завершуються, або ICMP відповіді зникають.
Корінна причина: Асиметрична маршрутизація + rp_filter=1 (строгий) або upstream anti-spoofing.
Виправлення: Закріпіть шлях повернення правилами на основі джерела і підключеними маршрутами в таблицях; встановіть rp_filter=2 де доречно; переконайтесь у правильності src на політичних маршрутах.
3) «VPN split tunnel протікає трафік або захоплює все»
Симптом: Деякий трафік несподівано йде через VPN, або трафік, що мав йти через VPN, йде напряму.
Корінна причина: Занадто широке правило маркування, неправильний пріоритет правила, або відсутні специфічні маршрути в VPN таблиці.
Виправлення: Звузьте маркування до певних призначень/портів; переконайтесь, що VPN правило має пріоритет вище main; перевірте, що VPN таблиця містить явні маршрути для тунельованих префіксів і продуману стратегію дефолту.
4) «Існує дефолт політичної таблиці, але через нього нічого не працює»
Симптом: Пакети обирають правильну таблицю, але ніколи не досягають шлюзу.
Корінна причина: Відсутній підключений маршрут у тій таблиці, або шлюз недосяжний на тому інтерфейсі, або проблеми ARP/neighbor.
Виправлення: Додайте лінк-маршрут у цю таблицю; перевірте ip neigh; підтвердіть IP інтерфейсу і L2 зв’язність.
5) «Після виправлення правил, поведінка не змінюється для активних з’єднань»
Симптом: Нові потоки поводяться правильно, старі продовжують падати або використовують старий шлях.
Корінна причина: Conntrack зберігає стан і маркери для встановлених з’єднань.
Виправлення: Тестуйте з новими з’єднаннями; видаляйте конкретні conntrack записи; розгляньте зниження таймаутів для постраждалих потоків під час інциденту замість тотального очищення.
6) «Пакети правильно маршрутизуються, але джерело IP неправильне»
Симптом: Трафік виходить через потрібний інтерфейс, але використовує несподіваний source IP, через що upstream відкидає.
Корінна причина: Вибір адреси джерела обирає іншу адресу на тому інтерфейсі або з іншого інтерфейсу через scope і адресні лейбли.
Виправлення: Вкажіть src на критичних маршрутах в політичних таблицях; переконайтесь, що кожний інтерфейс має правильну primary адресу; перевірте через ip route get ... from ....
7) «Все працює до відмови лінку, потім тільки частина трафіку переключається»
Симптом: Немаркований трафік переключається добре; маркований або source-routed трафік застрягає.
Корінна причина: Політичні таблиці не мають вторинного дефолту або не мають failover-aware логіки; метрики main не застосовуються до політичних таблиць.
Виправлення: Побудуйте failover в межах політичних таблиць (резервний дефолт з вищою метрикою) або впровадьте перемикання залежно від стану лінку з явними змінами правил.
8) «Маршрутизація працює вручну, але падає у сервісі»
Симптом: Ваш ручний curl працює; демон падає.
Корінна причина: Маркування за UID, відмінності простору імен мережі, або сервіс прив’язується до конкретного source IP.
Виправлення: Перевірте nftables правила маркування в OUTPUT; перевірте користувача юніта сервісу та network namespace; валідуйте налаштування bind сервісу.
Чеклісти / покрокові плани для on-call
Чекліст A: «Трафік йде не тим виходом» (15 хвилин, без змін)
-
Виконайте знімок правил.
cr0x@server:~$ ip rule show 0: from all lookup local 32764: from all fwmark 0x1 lookup vpn 32765: from 192.0.2.10 lookup isp2 32766: from all lookup main 32767: from all lookup defaultРішення: Визначте ймовірне правило, що співпадає з класом трафіку (джерело IP? fwmark?).
-
Виконайте запит рішення маршруту для точного призначення, потім з підозрюваним джерелом.
cr0x@server:~$ ip route get 93.184.216.34 93.184.216.34 via 203.0.113.1 dev eth0 src 203.0.113.10 uid 0 cachecr0x@server:~$ ip route get 93.184.216.34 from 192.0.2.10 93.184.216.34 via 192.0.2.1 dev eth1 src 192.0.2.10 uid 0 cacheРішення: Підтвердіть, чи неправильна поведінка залежить від джерела. Якщо так — зосередьтесь на source-правилах і таблицях.
-
Дамп опитуваної таблиці.
cr0x@server:~$ ip route show table isp2 default via 192.0.2.1 dev eth1 192.0.2.0/24 dev eth1 scope link src 192.0.2.10Рішення: Якщо таблиця не має необхідного (підключеного маршруту, правильного шлюзу), виправте таблицю; не «виправляйте» змінюючи main.
-
Доведіть шлях пакета через tcpdump на обох інтерфейсах (короткий захват).
cr0x@server:~$ sudo tcpdump -ni eth0 'host 93.184.216.34 and tcp' tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytescr0x@server:~$ sudo tcpdump -ni eth1 'host 93.184.216.34 and tcp' tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytesРішення: Якщо бачите асиметрію, переходьте до rp_filter і правил зворотного шляху.
Чекліст B: «VPN split tunnel працює некоректно»
-
Визначте VPN таблицю і правило.
cr0x@server:~$ ip rule show | grep -i vpn 32764: from all fwmark 0x1 lookup vpnРішення: Якщо вибір VPN базується на mark, потрібно аудиту маркування. Якщо по джерелу — перевірте прив’язку застосунку до джерела/адреси.
-
Перевірте правила маркування.
cr0x@server:~$ nft list ruleset | grep -n 'mark set' | head 12: meta skuid 1001 meta mark set 0x1Рішення: Якщо матч занадто широкий — звузьте. Маркуйте лише те, що можете обґрунтувати письмово.
-
Переконайтеся, що маршрути в VPN таблиці включають призначення, які треба тунелювати.
cr0x@server:~$ ip route show table vpn blackhole 10.0.0.0/8 default via 10.8.0.1 dev tun0 10.8.0.0/24 dev tun0 scope link src 10.8.0.2Рішення: Якщо потрібно дістатися приватних префіксів, видаліть або звужте blackhole і додайте явні маршрути.
Чекліст C: «Failover не працює для частини трафіку»
-
Визначте, яку таблицю використовує цей трафік (яке правило співпало).
cr0x@server:~$ ip -details rule show 0: from all lookup local 32764: from all fwmark 0x1 lookup vpn 32765: from 192.0.2.10 lookup isp2 32766: from all lookup main 32767: from all lookup defaultРішення: Якщо трафік не використовує main, метрики main вам не допоможуть.
-
Переконайтеся, що політична таблиця має вторинний дефолт (якщо дизайн цього вимагає).
cr0x@server:~$ ip route show table isp2 | grep '^default' default via 192.0.2.1 dev eth1Рішення: Якщо є лише один дефолт і шлюз впав, цей трафік застопориться. Додайте резервний шлях у цю таблицю або реалізуйте зміну правил при падінні лінку.
FAQ
1) Чому ip route мене «обманює»?
Воно не обманює; воно показує main таблицю за замовчуванням. Policy routing може відправляти ваш пакет в іншу таблицю.
Використайте ip rule show і ip route get DEST from SRC, щоб побачити реальне рішення.
2) У чому різниця між ip route show і ip route get?
show перераховує маршрути в таблиці. get питає ядро, як воно вирішить конкретне призначення (і опціональне джерело),
повертаючи обраний шлюз, інтерфейс і адресу джерела. В інцидентах get — ваш детектор правди.
3) Чи можна мати два дефолтні маршрути в Debian 13?
Так. Ядро вибирає за метриками в таблиці. Але якщо правила спрямовують трафік в іншу таблицю, дефолт тієї таблиці застосовується.
Подвійні дефолти нормальні; незмодельовані подвійні дефолти — хаос.
4) Чому policy routing ламається лише для однієї source IP?
Бо багато політик базуються на джерелі: ip rule add from 192.0.2.10 lookup isp2.
Застосунки, що прив’язуються до конкретного джерела, або ядро, що обирає інший source, може змінити правило, яке співпаде.
5) Чи потрібно вимикати rp_filter для policy routing?
Не завжди, але строгий режим (1) часто конфліктує з навмисною асиметрією в мультигомінгових налаштуваннях.
Режим loose (2) — поширений компроміс. Пам’ятайте про безпеку: rp_filter допомагає проти спуфінгу, тому не змінюйте його легковажно.
6) Чому моє виправлення спрацювало для нових з’єднань, але не для старих?
Conntrack зберігає стан для встановлених потоків, включно з маркерами у багатьох налаштуваннях. Старі потоки можуть прив’язатися до старих рішень маршруту.
Тестуйте новими з’єднаннями або вибірково видаляйте записи conntrack.
7) Який найчистіший спосіб реалізувати split tunneling?
Помістіть у VPN таблицю лише ті префікси, які треба тунелювати, і збережіть явну політику дефолту (або відсутність дефолту, або дефолт лише для маркованого трафіку).
Тоді скеруйте лише потрібний трафік за допомогою специфічних правил (де можливо — по призначенню; коли треба — по mark).
8) Як дізнатися, який сервіс спричиняє fwmark-based routing?
Перегляньте nftables правила, що встановлюють mark-и (часто за UID/cgroup). Потім зіставте UID із сервісом.
Також перевірте conntrack записи на наявність mark=, щоб побачити, які потоки марковані.
9) Чи варто використовувати числові ID таблиць напряму у скриптах?
Уникайте цього, коли можете. Використовуйте імена таблиць і тримайте /etc/iproute2/rt_tables під управлінням конфігурації.
Числові ID підходять, але їх легко розсинхронізувати по флоту і важко аудиту в постмортемі.
10) Чи можна «просто почистити» правила і маршрути під час інциденту?
На продакшен-сервері, що несе реальний трафік: ні, не як перший крок.
Ви можете втратити SSH, зламати біндінги сервісів і стерти докази, потрібні для діагностики. Спершу зніміть снапшот, потім змінюйте вибірково.
Висновок: кроки, що зменшують подальший біль
Policy routing у Debian 13 — «чорна магія» лише тоді, коли ви ставитеся до нього як до фольклору.
Ставтеся до нього як до програми: правила — це керування потоком, таблиці — дані, а ip route get — ваш відлагоджувач.
Ядро послідовне. Ваша конфігурація може бути ні.
Практичні кроки, що окупаються:
- Стандартизуйте використання іменованих таблиць маршрутів і тримайте
/etc/iproute2/rt_tablesпід управлінням конфігу. - Додайте мінімальні регресійні тести маршрутизації: кілька
ip route getперевірок для критичних призначень і джерел. - Аудитуйте
ip ruleі nftables правила маркування щоквартально; видаляйте «тимчасові» правила, поки вони не стали політикою. - Визначайте явно, як відбувається failover у кожній політичній таблиці. Якщо ви не спланували відмови, ви спроектували сюрприз.
- Документуйте власника і призначення кожного не-дефолтного правила. Якщо немає власника — немає підстав існувати.
Зробіть це, і «маршрутизація — чорночаївництво» знову стане тим, чим завжди була: набором детермінованих пошуків, які можна опанувати,
навіть коли пейджер кричить.