Сервери з двома мережевими картами на Debian падають у дуже специфічний, дуже дратівливий спосіб: все «працює», доки не перестає.
SSH зависає на 20 секунд, health-контролі флапають, реплікація сховища зупиняється, а TCP-потоки скидаються без закономірності, окрім «гірше в пікові години».
Винуватцем часто є асиметрична маршрутизація: пакети приходять на інтерфейс A і виходять через інтерфейс B, тому станозалежні пристрої (або ваш власний ядро) вирішують, що щось не так.
Це випадок №53: два інтерфейси, два шлюзи, один сервер і купа випадкових втрат, які неможливо відтворити за командою.
Форма відмови: як асиметрія виглядає в продакшні
Дві мережеві карти мають бути нудними. Одна для «фронтенду», інша для «бекенду», або, можливо, одна для управління й одна для сховища.
У реальному світі межа стирається. Хтось додає другу маршрут за замовчуванням «на всякий випадок». Або демон маршрутизації вивчає щось, що ви не збиралися рекламувати.
Або ви підключаєте обидва інтерфейси до мереж, в яких у кожної своя думка про ваші пакети.
Асиметрична маршрутизація не є «поганою» сама по собі. Інтернет працює на асиметрії щодня. Проблема виникає при асиметрії через
станозалежні вузли: файрволи, NAT, балансувальники, conntrack, DSR‑пристрої і іноді саме ядро Linux,
якщо reverse-path filtering увімкнено агресивно.
Типові симптоми:
- Переривчасті TCP‑скидання, здебільшого на тривалих потоках (реплікація, база даних, iSCSI/NFS, стріми API).
- SSH іноді зависає одразу після входу або під час scp; повторні спроби «виправляють» ситуацію.
- Моніторинг показує втрати пакетів, але лише з певних підмереж джерела.
- Вхідний трафік приходить на один інтерфейс, а відповіді виходять іншим (побачено в tcpdump).
- У логах ядра з’являються повідомлення на кшталт
martian sourceабо відповіді тихо відкидаються, колиrp_filterстрогий.
Якщо на машині є два шлюзи за замовчуванням і немає політики маршрутизації, у вас немає отказоустойчивості.
У вас монета з наслідками.
Цікаві факти та контекст (так, у мереж є своя історія)
- Політика маршрутизації в Linux старіша за багато «сучасних» хмарних патернів. Фреймворк
ip ruleз’явився в кінці 1990‑х з Linux 2.2 і дозрів у 2.4/2.6. - Reverse path filtering (rp_filter) популяризували як засіб для зниження спуфінгу. Це функція безпеки, яка на багатодомних хостах може перетворитися на підступну пастку.
- Асиметрична маршрутизація не є за визначенням неправильною. Вона стає проблемою, коли проміжний пристрій очікує бачити обидві сторони потоку по одному й тому ж шляху.
- У Linux існує кілька механізмів вибору маршруту. Найдовший префікс обирається в межах таблиці, а потім правила маршрутизації вирішують, яка таблиця буде використана і коли.
- ARP flux — реальна річ. Якщо Linux відповідає на ARP «не тим» інтерфейсом, пірі відправляють трафік на неправильну MAC‑адресу і ви женетеся за привидами.
- ECMP (equal-cost multipath) може виглядати як «випадкові втрати». Воно детерміністичне для хешу потоку, але для додатка виглядає як хаос, коли проміжні пристрої не погоджуються між собою.
- Conntrack — це не лише деталь файрвола. Навіть «дозволити все» станозалежні правила залежать від conntrack; повернений трафік поза шляхом позначається як INVALID і відкидається.
- systemd-networkd змінив типовий досвід для багатьох адміністраторів. Debian 13 робить простим створення декількох маршрутів за замовчуванням, якщо ви не дієте свідомо.
Швидкий план діагностики
Коли пакети «випадково падають», на філософські мережеві дебати часу немає. Потрібен швидкий прохід:
підтвердити асиметрію, визначити, хто відкидає, а потім зробити маршрутизацію детерміністичною.
1) Доведіть (або спростуйте) асиметричну маршрутизацію за 5 хвилин
- Перевірте таблиці маршрутизації та правила:
ip route,ip rule. - Зробіть захоплення на вході/виході обох NIC під час одного збою:
tcpdumpна обох інтерфейсах. - Підтвердіть вибір IP‑джерела:
ip route get <dest> from <src>.
2) Знайдіть, хто відкидає: ядро, файрвол чи мережа
- Шукайте rp_filter/martians:
journalctl -k,sysctl net.ipv4.conf.*.rp_filter. - Шукайте відкидання через conntrack:
nft list rulesetі лічильники, абоiptables -L -v, якщо ви все ще живете небезпечно. - Перевірте помилки/втрати на NIC:
ip -s link,ethtool -S.
3) Зробіть маршрутизацію детерміністичною (не «налаштовуйте» спочатку)
- Виберіть один вихідний шлях на підмережу джерела за допомогою політики маршрутизації.
- Встановіть rp_filter у режим
loose(2) на багатодомних хостах, якщо ви не знаєте точно, що застосовуєте. - Припиніть рекламувати/використовувати два маршрути за замовчуванням без метрик і правил.
Жарт №1: Якщо ваша маршрутизація залежить від «який шлюз сьогодні вдарить удачу», вітаємо — ви винайшли балансування навантаження, але без переваг.
Ментальна модель: маршрутизація Linux, правила і чому Debian 13 вас дивує
Ви налагоджуєте проблему з двома NIC, розуміючи три шари прийняття рішень:
вибір адреси, пошук по таблиці маршрутизації і правила політики маршрутизації.
Більшість аварій виникають через припущення, що Linux «просто відповість тим інтерфейсом, на який прийшов пакет».
Це мило, але помилково.
Вибір маршруту: таблиці
Linux підтримує таблиці маршрутизації. Більшість систем використовують таблицю main за замовчуванням. Коли ви запускаєте ip route,
ви зазвичай бачите основну таблицю. Перемогу отримує найдовший префікс, а якщо маршрутів рівноцінних кілька — враховуються метрики і ECMP.
Проблеми з двома NIC починаються, коли обидва інтерфейси додають маршрут за замовчуванням у main. Linux тоді обирає вихід
на основі метрик (якщо вони різні) або ECMP (якщо рівні). Це може бути стабільним для потоку, але не збігатиметься з очікуваннями вашої мережі.
Політика маршрутизації: правила
ip rule дозволяє вибирати таблицю на основі властивостей пакета: адреси джерела, fwmark, вхідного інтерфейсу,
TOS, діапазону UID тощо. На практиці для серверів з двома NIC головним інструментом є маршрутизація за джерелом:
«Трафік з адреси 192.0.2.10 використовує таблицю 100, яка має маршрут за замовчуванням через шлюз A».
Reverse path filtering: rp_filter
rp_filter перевіряє, чи доступна адреса джерела вхідного пакета через інтерфейс, на якому він прийшов.
У строгому режимі (1) мультидомінність може зламатися, бо «кращий маршрут назад» може вести через інший NIC.
Режим loose (2) зазвичай підходить для багатодомних хостів: він перевіряє доступність адреси, але не обов’язково через той самий інтерфейс.
Conntrack і станозалежна фільтрація
Якщо ви використовуєте nftables/iptables зі станозалежними правилами (більшість робить так, інколи не усвідомлено), асиметрія може спричиняти,
що повернені пакети вважаються INVALID, бо conntrack не бачив первинний напрямок на цьому шляху. Тоді цілком валідний пакет відкидається.
Пакет не «поганий». Погана — топологія.
ARP і вибір сусідів
Ще один тихий лиходій — поведінка ARP на хостах з кількома інтерфейсами в одній L2‑домені або з перекритими префіксами.
Linux може відповідати на ARP‑запити одним інтерфейсом, використовуючи MAC іншого, або обирати адресу джерела, що плутає пірів.
Результат: трафік приходить не туди, куди ви очікували, а ваше «виправлення маршрутизації» перетворюється на гру «відбивай‑муху».
Практичні завдання (команди, виводи, рішення)
Наступні завдання написані так, ніби ви вже на чергуванні й пейджер ще гарячий. Кожне містить:
команду, реалістичний фрагмент виводу, що це означає, і рішення, яке потрібно прийняти.
Завдання 1 — Перелік інтерфейсів і адрес
cr0x@server:~$ ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
enp1s0 UP 192.0.2.10/24 fe80::a00:27ff:fe12:3456/64
enp2s0 UP 198.51.100.10/24 fe80::a00:27ff:fe65:4321/64
Значення: Два NIC, дві IPv4 підмережі. Це нормально. Стає ненормально, коли обидві видають себе за «інтернет» (два default).
Рішення: Підтвердіть, для чого кожна підмережа (frontend/backend/management). Запишіть це. Якщо ніхто не знає — у вас уже проблеми.
Завдання 2 — Перевірте основну таблицю на наявність кількох default
cr0x@server:~$ ip route show table main
default via 192.0.2.1 dev enp1s0 proto dhcp metric 100
default via 198.51.100.1 dev enp2s0 proto dhcp metric 100
192.0.2.0/24 dev enp1s0 proto kernel scope link src 192.0.2.10 metric 100
198.51.100.0/24 dev enp2s0 proto kernel scope link src 198.51.100.10 metric 100
Значення: Два default з однаковою метрикою. Linux може застосувати ECMP. Ваш файрвол, ймовірно, ні.
Рішення: Потрібна політика маршрутизації або один default. Не «просто міняйте метрики», якщо не готові до поведінки при відмові й протестували її.
Завдання 3 — Перевірте правила політики маршрутизації
cr0x@server:~$ ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
Значення: Немає правил політики маршрутизації. Все звертається до main, отже асиметрія у меню.
Рішення: Додайте правила за джерелом і таблиці per‑interface (або видаліть додатковий default).
Завдання 4 — Спробуйте запитати Linux «як би ти промаршрутував» для кожного джерела
cr0x@server:~$ ip route get 203.0.113.50 from 192.0.2.10
203.0.113.50 via 198.51.100.1 dev enp2s0 src 192.0.2.10 uid 0
cache
Значення: Трафік з 192.0.2.10 вийде через enp2s0. Класичне паливо для асиметрії.
Рішення: Реалізуйте «джерело 192.0.2.10 використовує шлюз 192.0.2.1» через політику маршрутизації.
Завдання 5 — Перевірте rp_filter (строгий режим часто мовчазний вбивця)
cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.enp1s0.rp_filter net.ipv4.conf.enp2s0.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.enp1s0.rp_filter = 1
net.ipv4.conf.enp2s0.rp_filter = 1
Значення: Увімкнено строгий reverse-path filtering. На багатодомних хостах він часто відкидає легітимні пакети.
Рішення: Встановіть у loose (2) або вимкніть (0) залежно від вашої моделі загроз і топології. Loose — розумний дефолт для двох NIC.
Завдання 6 — Шукайте підказки ядра: martians, rp_filter відкидання, дивна поведінка neighbor
cr0x@server:~$ journalctl -k --since "2 hours ago" | tail -n 8
Dec 30 09:11:04 server kernel: IPv4: martian source 203.0.113.50 from 203.0.113.50, on dev enp1s0
Dec 30 09:11:04 server kernel: ll header: 00000000: 00 1b 21 22 33 44 00 1b 21 aa bb cc 08 00
Dec 30 09:12:18 server kernel: nf_conntrack: table full, dropping packet
Значення: «Martian source» часто корелює з rp_filter або невідповідністю маршрутів/правил. Також: таблиця conntrack повна, що створює втрати, які виглядають «випадковими».
Рішення: Спочатку виправте маршрутизацію. Потім налаштуйте розмір conntrack, якщо потрібно. Якщо conntrack заповнений, ви не просто налагоджуєте маршрути — ви налагоджуєте перевантаження.
Завдання 7 — Перевірте заповнення conntrack
cr0x@server:~$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 262144
net.netfilter.nf_conntrack_max = 262144
Значення: Ви досягли межі. Нові потоки відкидаються або обходять трекінг залежно від правил. Усе боляче.
Рішення: Якщо хост обробляє багато з’єднань (проксі, NAT, завантажені API), збільшіть max і підтвердіть запас пам’яті. Також зменшіть таймаути бездіяльності, де доречно.
Завдання 8 — Перегляньте правила nftables і лічильники на предмет INVALID відкидань
cr0x@server:~$ nft list ruleset | sed -n '1,120p'
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
ct state invalid counter packets 1843 bytes 110580 drop
iif "lo" accept
tcp dport 22 accept
}
}
Значення: INVALID відкидається, і лічильник росте. Асиметричні шляхи — одна з головних причин.
Рішення: Не робіть «accept INVALID» як пластир. Виправте маршрути так, щоб пакети знову стали валідними.
Завдання 9 — Перевірте статистику інтерфейсу: реальні помилки vs відкидання через маршрутизацію
cr0x@server:~$ ip -s link show dev enp1s0
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:1b:21:aa:bb:cc brd ff:ff:ff:ff:ff:ff
RX: bytes packets errors dropped missed mcast
91433921 812334 0 0 0 1290
TX: bytes packets errors dropped carrier collsns
88122301 799221 0 0 0 0
Значення: Немає помилок на рівні NIC. Отже «випадкові втрати» ймовірно відбуваються на рівні політики/файрвола/conntrack або далі по мережі.
Рішення: Залишайтеся сфокусованими: правила маршрутизації, rp_filter, conntrack, стан файрвола.
Завдання 10 — Захопіть трафік на обох NIC, щоб довести асиметрію
cr0x@server:~$ sudo tcpdump -ni enp1s0 host 203.0.113.50 and tcp port 443 -c 6
09:14:22.110001 IP 203.0.113.50.443 > 192.0.2.10.53122: Flags [S.], seq 1200, ack 900, win 65160, options [mss 1460], length 0
09:14:22.110050 IP 203.0.113.50.443 > 192.0.2.10.53122: Flags [.], ack 901, win 65160, length 0
cr0x@server:~$ sudo tcpdump -ni enp2s0 host 203.0.113.50 and tcp port 443 -c 6
09:14:22.110120 IP 192.0.2.10.53122 > 203.0.113.50.443: Flags [.], ack 1201, win 501, length 0
09:14:22.110200 IP 192.0.2.10.53122 > 203.0.113.50.443: Flags [P.], seq 901:1041, ack 1201, win 501, length 140
Значення: SYN/ACK прибув на enp1s0, але ACK/дані вийшли через enp2s0. Це асиметрія, доведена квитанціями.
Рішення: Впровадьте політику маршрутизації так, щоб відповіді на трафік для 192.0.2.10 виходили через enp1s0 (або забезпечте симетрію в апстрімі, що складніше).
Завдання 11 — Перевірте поведінку neighbor/ARP на предмет flux
cr0x@server:~$ ip neigh show nud reachable,stale | head
192.0.2.1 dev enp1s0 lladdr 00:11:22:33:44:55 REACHABLE
198.51.100.1 dev enp2s0 lladdr 00:11:22:33:44:66 STALE
Значення: Тут очевидних проблем немає. Але якщо ви бачите ту саму IP‑адресу reachable через два інтерфейси або багато FAILED записів, підозрюйте ARP flux або проблеми L2‑дизайну.
Рішення: Якщо мережі перекриваються або ділять VLAN, застосуйте контролі ARP (розділ нижче) і виправте адресацію.
Завдання 12 — Підтвердіть маршрутизацію за джерелом після додавання правил
cr0x@server:~$ ip route get 203.0.113.50 from 192.0.2.10
203.0.113.50 via 192.0.2.1 dev enp1s0 src 192.0.2.10 uid 0
cache
Значення: Тепер вихід відповідає підмережі джерела. Це детерміністична поведінка, яка вам потрібна.
Рішення: Знову запустіть tcpdump і слідкуйте за лічильниками nft. Якщо INVALID перестали зростати, ви купили стабільність.
Завдання 13 — Переконайтеся, що випадкові ECMP defaults відсутні
cr0x@server:~$ ip route show default
default via 192.0.2.1 dev enp1s0 proto static metric 100
default via 198.51.100.1 dev enp2s0 proto static metric 200
Значення: Ви все ще можете тримати два default з різними метриками для фейловера, але ваші правила політики мусять бути узгоджені.
Рішення: Надавайте перевагу одному default у main, а інший тримайте лише в виділеній таблиці. Змішані моделі заплутують вас у майбутньому.
Завдання 14 — Перевірте стан systemd-networkd (реальність Debian 13)
cr0x@server:~$ networkctl status enp1s0 | sed -n '1,40p'
● 2: enp1s0
Link File: /usr/lib/systemd/network/99-default.link
Network File: /etc/systemd/network/10-enp1s0.network
State: routable (configured)
Online state: online
Address: 192.0.2.10/24
Gateway: 192.0.2.1
Значення: networkd керує вашою маршрутизацією. Це добре, якщо ви налаштовуєте усвідомлено, і хаотично, якщо дозволяєте DHCP «розбризкувати» default‑маршрути.
Рішення: Внесіть політику маршрутизації в конфігурацію networkd, щоб вона пережила перезавантаження і не залежала від героїчного paste на чергуванні.
Шаблони виправлень, які працюють
Шаблон A: Маршрутизація за джерелом (рекомендовано для більшості серверів з двома NIC)
Мета: трафік, що має адресу інтерфейсу A як джерело, використовує шлюз інтерфейсу A; трафік з адреси інтерфейсу B використовує шлюз інтерфейсу B.
Це зупиняє асиметричні відповіді без потреби змінювати апстрім.
Реалізується це через:
- Дві таблиці маршрутизації (по одній на NIC)
- Два записи
ip rule, що відповідають підмережам джерела - Маршрути в кожній таблиці: підключена підмережа + default через відповідний шлюз
Термінова (runtime) конфігурація з iproute2
cr0x@server:~$ sudo ip route add 192.0.2.0/24 dev enp1s0 src 192.0.2.10 table 100
cr0x@server:~$ sudo ip route add default via 192.0.2.1 dev enp1s0 table 100
cr0x@server:~$ sudo ip route add 198.51.100.0/24 dev enp2s0 src 198.51.100.10 table 200
cr0x@server:~$ sudo ip route add default via 198.51.100.1 dev enp2s0 table 200
cr0x@server:~$ sudo ip rule add from 192.0.2.10/32 table 100 priority 1000
cr0x@server:~$ sudo ip rule add from 198.51.100.10/32 table 200 priority 1001
Це працює, але зникає після перезавантаження, якщо не зберегти. Не будьте тією людиною.
Персистентна конфігурація через systemd-networkd (дружня для Debian 13)
Приклад /etc/systemd/network/10-enp1s0.network:
cr0x@server:~$ sudo sed -n '1,200p' /etc/systemd/network/10-enp1s0.network
[Match]
Name=enp1s0
[Network]
Address=192.0.2.10/24
Gateway=192.0.2.1
DNS=192.0.2.53
[RoutingPolicyRule]
From=192.0.2.10/32
Table=100
Priority=1000
[Route]
Destination=0.0.0.0/0
Gateway=192.0.2.1
Table=100
І /etc/systemd/network/20-enp2s0.network:
cr0x@server:~$ sudo sed -n '1,200p' /etc/systemd/network/20-enp2s0.network
[Match]
Name=enp2s0
[Network]
Address=198.51.100.10/24
Gateway=198.51.100.1
DNS=198.51.100.53
[RoutingPolicyRule]
From=198.51.100.10/32
Table=200
Priority=1001
[Route]
Destination=0.0.0.0/0
Gateway=198.51.100.1
Table=200
Потім перезапустіть networkd:
cr0x@server:~$ sudo systemctl restart systemd-networkd
cr0x@server:~$ ip rule show | sed -n '1,10p'
0: from all lookup local
1000: from 192.0.2.10 lookup 100
1001: from 198.51.100.10 lookup 200
32766: from all lookup main
32767: from all lookup default
Шаблон B: Один default, одна «спеціальна» мережа (найкраще, коли один NIC — справді приватний)
Якщо enp2s0 — суто мережа сховища і ніколи не має використовуватися для інтернету чи відповідей клієнтам, не давайте їй default взагалі.
Залиште лише підключену підмережу і, можливо, кілька явних маршрутів до пір сховища.
Це усуває цілий клас відмов. Інтерфейс стає «просто трубою до підмережі X».
Краща політика маршрутизації — та, яка вам не потрібна.
Шаблон C: Маршрутизація на основі fwmark (для складних додатків і VIP)
Якщо у вас є кілька вихідних адрес на одному інтерфейсі (VIP), контейнери або потрібно спрямовувати лише певний трафік,
ви можете помічати пакети в nftables і марширувати за fwmark.
Це потужніше й більш схильне до помилок. Використовуйте, коли маршрутизація за джерелом недостатня.
rp_filter: встановлюйте свідомо, а не за звичкою
На багатодомних хостах строгий rp_filter часто несумісний з політичною маршрутизацією і легітимною асиметрією.
Режим loose — зазвичай компроміс: він вимагає маршруту назад до джерела, але не через той самий інтерфейс.
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.all.rp_filter = 2
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.default.rp_filter=2
net.ipv4.conf.default.rp_filter = 2
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.enp1s0.rp_filter=2
net.ipv4.conf.enp1s0.rp_filter = 2
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.enp2s0.rp_filter=2
net.ipv4.conf.enp2s0.rp_filter = 2
Збережіть через /etc/sysctl.d/99-multihome.conf:
cr0x@server:~$ sudo tee /etc/sysctl.d/99-multihome.conf >/dev/null <<'EOF'
net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.default.rp_filter=2
EOF
cr0x@server:~$ sudo sysctl --system | tail -n 4
* Applying /etc/sysctl.d/99-multihome.conf ...
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
Контролі ARP: запобігайте «відповіді не тим NIC»
Якщо обидва NIC лежать в одному L2 або у вас перекриті маршрути, налаштуйте поведінку ARP, щоб зменшити flux:
arp_ignore і arp_announce.
Це не завжди потрібно, але коли потрібно — це різниця між здоровим станом і інтерпретативним танцем.
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.all.arp_ignore = 1
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.all.arp_announce = 2
Цитата про надійність (перефразована ідея)
Перефразована ідея (приписують Richard Cook): «Успіх — це не відсутність збоїв; це наявність адаптивної здатності.»
Три міні-історії з корпоративного світу
Міні-історія 1: Інцидент через хибне припущення
Середня SaaS‑компанія працювала на Debian‑серверах з двома NIC: один публічний VLAN для клієнтського трафіку, інший приватний VLAN для баз даних і бекапів.
Команда припускала, що відповіді виходитимуть тим самим NIC, на який прийшов запит. Вони «бачили, як це працює» в лабораторії.
Одного понеділка логіни користувачів почали флапати. Не повний падіння; просто повільно, спайково й дивно. Балансувальник показував SYN/ACK, що повертаються з затримкою,
і половина TLS‑хендшейків падала. Інженери ганялися за CPU, потім за сертифікатами, потім за балансувальником. Графіки всі були трохи неправильні по‑своєму.
Насправді все виявилося просто: оновлення DHCP на приватному NIC заново додало маршрут за замовчуванням з тією ж метрикою, що й публічний NIC.
Раптом частина відповідей на клієнтський трафік йшла через приватний шлюз. Периферійний файрвол відкидав їх, бо не мав стану для цього напрямку.
З погляду сервера — пакет відправлено. З погляду клієнта — сервер зник.
Виправлення не було героїчним: заблокувати один default у main, використати маршрутизацію за джерелом для вторинного інтерфейсу і припинити приймати default‑маршрути через DHCP на бекенді.
Постмортем мав рядок гідний рамки: «Ми припустили симетрію; ми налаштували випадковість.»
Міні-історія 2: Оптимізація, що обернулась проти
Інша організація вирішила «оптимізувати затримку», увімкнувши строгий rp_filter всюди.
Їхній security‑базовий набір вважав це безкоштовним захистом від спуфінгу. Це було розгорнуто автоматично по флоту, який включав багатодомні хости.
Через кілька тижнів реплікація сховища почала падати під навантаженням, але лише між певними стойками. TCP встановлювався, передавав дані,
потім зупинявся. Повторні спроби вдавалися. Усі звинувачували вендора сховища. Потім MTU. Потім хтось звинуватив ASIC комутатора, бо завжди ASIC, коли ви втомлені.
Виявилося, що політика маршрутизації вже була на місці, але строгий rp_filter не подобався lookup‑у шляху назад у певних випадках, де «найкращий маршрут» відрізнявся від ingress.
Ядро тихо відкидало легітимні пакети як martian. Реплікація зводилася до повторів і timeouts, перетворюючи невідповідність політики маршрутизації на колапс пропускної здатності.
«Оптимізація» зробила систему крихкою. Режим loose rp_filter відновив стабільність, а захист від спуфінгу залишили там, де йому місце: на краю мережі,
з явними ACL, а не в надії на чарівні sysctl.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Велика внутрішня платформа мала правило: кожен багатодомний хост мусить мати односторінкову «інструкцію наміру маршрутизації» в репозиторії.
Там описували, який інтерфейс які адреси має, який default, і які правила політики існують. Без поезії. Просто правда.
Під час міграції дата‑центру ввели новий кластер апстрім‑фаєрволів. Невеликий набір сервісів почав бачити переривчасті 502.
Команди аплікацій ескалували; мережники клялися, що нічого не змінювали «для тих VLAN». Це пахло як L7‑проблема, але втрата пакетів пахла L3.
SRE на чергуванні відкрив нотатку наміру маршрутизації і відразу помітив, що хост мав відповідати через конкретний NIC використовуючи таблицю 200.
Швидка перевірка ip rule показала, що ті правила відсутні в новому образі. Система завантажилася нормально. Але також неправильно запрацювала.
Вони відновили intended networkd конфіг, розгорнули оновлення, і проблема зникла. Без драми. Без war room. Без звинувачень на почуття файрвола.
Нудна документація плюс детерміністична конфігурація — не гламурно, але різниця між інцидентом і повідомленням у Slack.
Жарт №2: Єдине більш випадкове, ніж асиметрична маршрутизація — це нарада, де всі наполягають, що «це точно DNS».
Типові помилки: симптом → причина → виправлення
1) SSH зависає або переривається періодично
Симптоми: SSH підключається, потім пауза; scp зависає; повторні спроби допомагають.
Причина: Відповіді виходять через неправильний NIC; станозалежний файрвол або апстрім ACL відкидають трафік назад.
Виправлення: Маршрутизація за джерелом для кожного інтерфейсу; переконайтеся, що лише один default у main; валідуйте через tcpdump на обох NIC.
2) nftables показує зростання INVALID відкидань
Симптоми: Лічильник ct state invalid drop росте; користувачі бачать випадкові збої.
Причина: Conntrack бачить лише один напрямок через асиметрію (або таблиця conntrack заповнена).
Виправлення: Спочатку виправте маршрутизацію; потім налаштуйте розмір conntrack і переконайтесь, що не трекаєте трафік, який цього не потребує.
3) У логах з’являються «Martian source»
Симптоми: У логах ядра martians; пакети з’являються на «не тому» інтерфейсі.
Причина: Строгий rp_filter на багатодомному хості або неправильні маршрути/правила.
Виправлення: Встановіть rp_filter у loose (2) і впровадьте політику маршрутизації так, щоб lookup відповідав реальності.
4) Працювало, доки не обновився DHCP, потім зламалось
Симптоми: Раз на кілька годин/днів щось йде не так; перезавантаження «тимчасово допомагає».
Причина: DHCP встановлює або змінює default‑маршрути/метрики; маршрутизація знову стає недетермінованою.
Виправлення: Заблокуйте встановлення default через DHCP на вторинному NIC або ізолюйте DHCP до одного інтерфейсу; зберігайте маршрути через networkd.
5) Ламаються лише деякі віддалені підмережі
Симптоми: Більшість клієнтів в порядку; один регіон/провайдер флапає.
Причина: Маршрут назад від тих клієнтів потрапляє до іншого апстріма (інші правила NAT/ACL), що робить асиметрію помітною.
Виправлення: Забезпечте детерміністичний вихід за джерелом; гарантуйте, що апстрім бачить послідовні шляхи.
6) «Ми поставили метрики, тож має бути нормально»
Симптоми: Більшість часу стабільно, але при відмові або часткових проблемах — чорні діри.
Причина: Метрики визначають преференцію, але не гарантують, що відповіді слідуватимуть інтерфейсу ingress, особливо з кількома джерелами.
Виправлення: Використовуйте політику маршрутизації; метрики — для преференцій і фейловера, не для коректності.
7) Трафік сховища або реплікація падає під навантаженням
Симптоми: Пропускна здатність падає, retransmit‑и ростуть, timeouts.
Причина: Вичерпання таблиці conntrack, або станозалежна фільтрація плюс асиметрія, або одне й інше.
Виправлення: Уникайте трекінгу трафіку, який не потребує цього; збільшіть conntrack; виправте маршрутизацію, щоб потоки були стабільними.
8) ARP‑дивина: трафік приходить на «не той» NIC навіть при правильній маршрутизації
Симптоми: Піри відправляють пакети на «не ту» MAC; поведінка фейловера дивна.
Причина: ARP flux: хост відповідає на ARP з неправильного інтерфейсу; або перекриття L2‑доменів.
Виправлення: Розділіть L2 домени; налаштуйте arp_ignore/arp_announce; забезпечте унікальні підмережі та чистий дизайн VLAN.
Контрольні списки / покроковий план
Покроково: зупинити асиметричну маршрутизацію на Debian 13 (безпечний для продакшну порядок)
-
Зафіксуйте поточний стан (перш ніж щось чіпати):
ip -br addrip route show table mainip rule showsysctl net.ipv4.conf.all.rp_filter
Рішення: підтвердіть, що ви дійсно багатодомні (не просто аліаси) і чи існують два default.
-
Доведіть асиметрію через tcpdump на обох NIC під час одного збою.
Рішення: якщо ingress і egress різні для того самого потоку — переходьте до політики маршрутизації. Якщо ні, можливо MTU, перевантаження або апстрім‑фільтрація.
-
Визначте намір:
- Один NIC — default для всього, інший — суто приватний (найкраще).
- Обидва NIC обслуговують реальних клієнтів/пірів і повинні маршрутизувати правильно (потрібна політика маршрутизації).
Рішення: запишіть намір у коментарі в конфігу. «Ми запам’ятаємо» — це не план.
-
Впровадьте політику маршрутизації (таблиці + правила) або через networkd, або через ifupdown.
Рішення: на Debian 13, якщо networkd вже керує інтерфейсами, віддавайте перевагу його конфігурації для персистентності.
-
Встановіть rp_filter у loose (2) для багатодомних хостів.
Рішення: якщо політика безпеки наполягає на strict — нехай її автори підпишуть інцидент‑рев’ю, коли це зламає. Loose — реалістичний компроміс.
-
Перевірте через ip route get для репрезентативних напрямків від кожної адреси джерела.
Рішення: якщо будь-яке джерело обирає «неправильний» шлюз — таблиці/правила неповні.
-
Перевірте лічильники файрвола (nft INVALID відкидання тощо).
Рішення: якщо INVALID все ще зростає — перевірте, чи conntrack не переповнений і чи трафік не обминає очікуваний шлях.
-
Навантажувальне тестування або відтворення трафіку, схожого на продакшн.
Рішення: якщо збої з’являються лише під навантаженням — перевірте налаштування conntrack, перевантаження IRQ, qdisc і поліси апстріму, а не лише маршрутизацію.
-
Забезпечте довговічність:
- Завантажте файли networkd і sysctl.d в систему управління конфігурацією.
- Документуйте намір маршрутизації і «відомо‑добрий» вивід
ip ruleтаip route show table 100/200.
Рішення: якщо виправлення можна скасувати оновленням DHCP — ви не виправили проблему.
Оперативний чек‑лист: перевірка після змін (10 хвилин)
- Підтвердьте правила:
ip rule show - Підтвердьте таблиці:
ip route show table 100,ip route show table 200 - Підтвердьте rp_filter:
sysctl net.ipv4.conf.all.rp_filter - Підтвердьте стабільність лічильників nft:
nft list chain inet filter input(або еквівалент) - Підтвердьте симетрію трафіку через tcpdump під час тестової транзакції
- Переконайтесь, що після оновлення DHCP не з’являються несподівані default (або зачекайте проміжок оновлення)
Питання та відповіді
1) Чи можна просто виставити метрики і вважати справу зробленою?
Метрики визначають пріоритет, а не коректність. Вони не гарантують, що відповіді підуть тим самим інтерфейсом, що й адреса джерела,
і не виправляють очікувань conntrack/станозалежних пристроїв. Для коректності використовуйте політику маршрутизації; для пріоритетів/фейловера — метрики.
2) Чи справді потрібна політика маршрутизації, якщо підмережі різні?
Якщо є лише один маршрут за замовчуванням і інший інтерфейс не має default, ви часто можете обійтися без політики маршрутизації.
Якщо обидва інтерфейси мають шлюзи або ви надсилаєте трафік з обох адрес — політика маршрутизації безпечніша.
3) Який режим rp_filter варто використовувати на багатодомних хостах?
Зазвичай 2 (loose). Строгий (1) часто відкидає легітимний трафік у multi‑homed середовищах.
Вимикайте (0) лише якщо розумієте наслідки спуфінгу і маєте компенсуючі заходи.
4) Чому втрати виглядають випадковими?
Тому що рішення маршрутизації можуть відрізнятися на рівні потоку (ECMP хеш), на рівні кеша або після оновлення DHCP.
Додайте стан conntrack і очікування апстріму, і отримаєте збої, що залежать від часу та форми трафіку.
5) Чи стосується це IPv6 також?
Так, але механізми відрізняються (IPv6 rp_filter не той самий, а правила вибору джерела багатші).
Політика маршрутизації існує і для IPv6 через ip -6 rule і таблиці; тестуйте уважно, бо IPv6 за дизайном має кілька валідних адрес джерела.
6) Я бачу «nf_conntrack: table full». Це маршрутизація?
Безпосередньо — ні, але це створює втрати пакетів, що виглядають схоже. Спочатку виправте асиметрію маршрутів, потім налаштуйте conntrack.
Якщо ви трекаєте мільйони короткоживучих з’єднань, вам може знадобитись і більше ресурсів, і краща стратегія фільтрації.
7) Чи варто дозволяти INVALID пакети, щоб припинити втрати?
Ні. Це як відключити пожежну сигналізацію, бо вона голосна. INVALID часто означає асиметрію, невідповідність таймаутів або атаку.
Виправте шлях, щоб пакети знову стали валідними.
8) Як обробляти фейловер між шлюзами?
Якщо вам дійсно потрібен фейловер, зберігайте детерминістику по джерелу і використовуйте контрольовані механізми:
динамічну маршрутизацію (BFD/FRR), відстежувані маршрути або явні зміни метрик з автоматизацією. Уникайте «двох рівних default», якщо ви не розумієте end‑to‑end шлях.
9) А бондинг (LACP)? Чи не вирішить це проблему?
Bonding вирішує іншу задачу: відмовостійкість/агрегацію лінків на L2. Воно допомагає, якщо обидва лінки в один L2‑домен і ви хочете один логічний інтерфейс.
Воно не замінює коректну L3‑маршрутизацію, коли мережі/шлюзи різні.
10) Чому все ламається лише після додавання файрвола?
Бо станозалежні пристрої вимушують симетрію для потоків, які вони трекають. Без них інтернет може терпіти асиметрію.
Коли ви додаєте стан — шлях стає частиною контракту.
Наступні кроки, які варто виконати цього тижня
Виправлення випадку №53 — це не лише зупинити втрати сьогодні. Це гарантувати, що наступна зміна їх не воскресить.
Ось практичний список завдань, що переживе зміну персоналу і «тимчасові» мережеві експерименти.
- Визначте й задокументуйте намір маршрутизації для кожного хоста з двома NIC: який NIC за який трафік відповідає і чому.
- Усуньте випадкові дубль‑default: один default у main, або явна політика маршрутизації з окремими таблицями.
- Встановіть rp_filter свідомо: loose режим для мультихомінгу, збережіть через sysctl.d.
- Перевірте трьома інструментами:
ip route get,tcpdumpна обох NIC і лічильники файрвола (nft/conntrack). - Зробіть конфіг персистентною у systemd-networkd (або в обраному менеджері мереж) і зафіксуйте в автоматизації.
- Слідкуйте за conntrack, якщо ви станозалежні: ємність, таймаути й чи не трекаєте зайвий трафік.
- Проведіть репетицію після змін: симулюйте фейловер шлюзу, якщо ви претендуєте на фейловер, і перевірте реальну поведінку.
Маршрутизація з двома NIC не складна. Складно робити вигляд, що її немає, аж поки вона не нагадає про себе в найзавантаженіший день.
Зробіть її детерміністичною і повертайтеся вирішувати більш цікаві проблеми.