NAT і DHCP
Навіщо це
Section titled “Навіщо це”Ви підключаєте ноутбук до Wi-Fi, і за секунду в нього є адреса, шлюз і DNS-сервер. Ви нічого не вводили. Пристрій, якого ще хвилину тому не існувало в мережі, став її повноправним учасником. Звідки він узяв адресу, якщо для відправлення чогось у мережу вона потрібна, а без мережі адресу нікому спитати?
Або інше. У вашій квартирі десяток пристроїв, а провайдер видав одну адресу. Усі десять відкривають сайти одночасно, і відповіді приходять кожному на свій. Як маршрутизатор розбирає, яка відповідь чия, якщо зовні всі десять виглядають однаково?
Буває й так: ви запустили сервер на ноутбуці, а друг із іншого міста не може зайти, хоч усе працює. Або навпаки: у вас усе налаштовано, а для деяких користувачів дзвінок у відеочаті ніяк не з’єднується, хоча в кожного окремо інтернет чудовий.
Усе це теми одного модуля. DHCP видає адреси. NAT дозволяє багатьом пристроям ділити одну публічну адресу. Разом вони складають те, що на побутовому рівні називають «роутером».
Передумови. Адресація, приватні діапазони й широкомовлення (модуль 6), таблиця маршрутів і пересилання (модуль 7), ARP (модуль 5). Про порти досить уявлення з модуля 1; стани TCP, які тут згадуються, докладно розібрано в модулі 11. Про файрвол докладно в модулі 16, тут лише те, що потрібно для розмови про NAT.
DHCP: адреса з повітря
Section titled “DHCP: адреса з повітря”Проблема курки і яйця
Section titled “Проблема курки і яйця”Щоб надіслати пакет, потрібна адреса відправника. Щоб отримати адресу від сервера, потрібно з ним поговорити. DHCP (Dynamic Host Configuration Protocol) розв’язує це так: клієнт шле повідомлення, у якому адреса відправника нульова (0.0.0.0), а адреса призначення широкомовна (255.255.255.255). Кадр піде на ff:ff:ff:ff:ff:ff, усі пристрої сегмента його отримають, а DHCP-сервер, якщо він є, відповість. Працює це поверх UDP: клієнт слухає порт 68, сервер порт 67.
Обмін із сервером
Section titled “Обмін із сервером”-
Discover. Клієнт шукає сервер: «хто тут видає адреси?» Разом із цим він перелічує, які параметри хотів би отримати (маску, шлюз, DNS тощо).
-
Offer. Сервер відповідає: «можу дати 192.168.1.116 на 12 годин, ось решта параметрів». Якщо в сегменті кілька DHCP-серверів, клієнт може отримати кілька пропозицій.
-
Request. Клієнт обирає одну пропозицію й повідомляє про це, вказуючи адресу сервера. Це повідомлення теж широкомовне, щоб інші сервери дізналися про відмову і звільнили зарезервовані для нього адреси.
-
Acknowledgement. Сервер підтверджує. Лише тепер клієнт має право налаштувати адресу на інтерфейсі. Якщо сервер передумав (наприклад, адресу вже віддали комусь іншому), він надсилає відмову (NAK), і клієнт починає спочатку.
Оренда
Section titled “Оренда”DHCP видає адресу в оренду (lease) на певний термін, інакше адреси кінчалися б на тому, що колись було підключено й забуто. Щоб зберегти адресу, клієнт мусить оренду продовжувати.
У пакеті Ack разом із терміном оренди приходять два таймери. Після T1, за замовчуванням половини терміну, клієнт намагається продовжити оренду, звертаючись до свого сервера напряму. Якщо сервер мовчить, то після T2, за замовчуванням 87,5% терміну, клієнт шле запит широкомовно, сподіваючись, що відповість будь-який сервер. Якщо оренда спливла, а відповіді немає, клієнт зобов’язаний покинути адресу й почати Discover заново.
Якщо DHCP-сервер упав на годину, зв’язок одразу не зникне. Пристрої продовжують користуватися тим, що мають, і проблеми почнуться лише для нових пристроїв і для тих, чия оренда закінчиться в цей час.
Що ще віддає DHCP
Section titled “Що ще віддає DHCP”Адреса найменша частина відповіді. Решта параметрів зветься опціями (options), і кожна має номер. Ось ті, що трапляються постійно:
| Опція | Номер | Що задає |
|---|---|---|
| Маска підмережі | 1 | розмір мережі |
| Маршрутизатор | 3 | шлюз за замовчуванням |
| DNS-сервери | 6 | куди слати DNS-запити |
| Доменне ім’я | 15 | суфікс для коротких імен |
| Час оренди | 51 | на скільки видана адреса |
| Тип повідомлення | 53 | Discover, Offer тощо |
| Ідентифікатор сервера | 54 | хто видав |
| Безкласові статичні маршрути | 121 | додаткові маршрути клієнта |
Опції роблять DHCP чимось більшим за видавача адрес. Через нього можна розіслати маршрути (121), дати мережевому завантажувачеві ім’я файлу (опції для PXE), вказати сервер часу. Фактично DHCP-сервер є «сервером налаштувань» для клієнтів.
Relay: коли сервер не в тому сегменті
Section titled “Relay: коли сервер не в тому сегменті”Широкомовний Discover не проходить маршрутизатор, тож сервер у сусідній підмережі його не отримає. Тримати по DHCP-серверу в кожному сегменті незручно. Для цього є relay agent: маршрутизатор слухає широкомовні DHCP-запити, переписує їх у звичайні (unicast) пакети до сервера й записує в поле giaddr власну адресу на цьому інтерфейсі. Сервер по giaddr бачить, з якої підмережі прийшов запит, і видає адресу саме з неї. Відповідь іде назад до relay, який доставляє її клієнтові. Один сервер може обслуговувати десятки підмереж.
NAT: одна адреса на всіх
Section titled “NAT: одна адреса на всіх”Навіщо
Section titled “Навіщо”Адрес IPv4 близько чотирьох мільярдів, а пристроїв набагато більше. Для внутрішніх мереж виділено приватні діапазони, які ніколи не маршрутизуються в публічному інтернеті: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (RFC 1918). У двох різних компаніях можуть бути однакові адреси 10.0.0.5, і нікому це не заважає, бо назовні вони не виходять.
Але якщо пристрій зі своєю приватною адресою захоче відкрити сайт, то пакет піде з src=192.168.1.116. Сервер відповість на цю адресу, а вона в інтернеті нічия: жоден маршрутизатор не знає, куди її везти. Потрібна підміна: на межі мережі переписувати адресу відправника на публічну. Така підміна називається NAT (Network Address Translation).
PAT: коли хостів більше, ніж адрес
Section titled “PAT: коли хостів більше, ніж адрес”Проста заміна адреса на адресу розв’язала б задачу, якби публічних адрес було стільки ж, скільки внутрішніх. Але їх стільки немає, і на практиці NAT переписує ще й порт відправника. Цей варіант називають PAT (Port Address Translation), або NAT із перевантаженням. Слово «NAT» у розмові майже завжди означає саме його.
Хост 192.168.1.116 відкриває з’єднання з вихідного порту 40000 до 203.0.113.10:80. Шлюз бачить вихідний пакет, записує в таблицю «приватна адреса й порт ↔ публічна адреса й порт», переписує адресу відправника на свою публічну, а порт залишає, якщо він вільний. Коли інший хост спробує використати той самий порт до того самого сервера, шлюз змінить порт на інший. Сервер бачить два різні з’єднання з однієї адреси: від 203.0.113.1:40000 і від 203.0.113.1:40001. Відповіді прийдуть на ці адреси, а шлюз за таблицею переписує призначення назад і відправляє потрібному хосту.
NAT має стан. Запис у таблиці з’являється, коли зсередини надійшов перший пакет потоку, і зникає за тайм-аутом. Без запису відповідь не має куди повертатися: шлюз не знає, кому її віддати.
Ззовні не можна просто так зайти досередини. Якщо записів у таблиці немає, вхідний пакет на публічну адресу шлюзу звернений до самого шлюзу, а не до жодного хоста за ним. Ефект корисний, але побічний; про нього нижче.
SNAT, masquerade і DNAT
Section titled “SNAT, masquerade і DNAT”У Linux NAT виконує підсистема netfilter, керована через nftables (детальніше в модулі 16).
SNAT (source NAT) змінює адресу відправника: так роблять вихідні з’єднання. Виконується вона у хуку postrouting, коли рішення про маршрут уже ухвалено. Правило пишуть або як snat to 203.0.113.1 з явною адресою, або як masquerade, і відрізняються вони тим, звідки береться нова адреса. masquerade бере адресу інтерфейсу, через який пакет виходить, щоразу наново, а коли інтерфейс падає, забуває пов’язані з ним записи. Для домашнього маршрутизатора, де публічна адреса приходить із DHCP і міняється, саме це й потрібно. Якщо адреса стала, snat to трохи ефективніший.
DNAT (destination NAT) змінює адресу призначення: так роблять проброс портів (port forwarding), коли пакет, що прийшов на публічну адресу, треба відправити всередину. Виконується в хуку prerouting, до маршрутизації, тож після заміни пакет маршрутизується вже за новою адресою.
Обидва напрямки використовують одну й ту саму таблицю. Коли відповідь повертається, netfilter автоматично виконує зворотну підміну: окремих правил для відповіді писати не треба.
Conntrack: пам’ять шлюзу
Section titled “Conntrack: пам’ять шлюзу”Таблицю, про яку йшлося вище, веде модуль conntrack (connection tracking) у ядрі Linux. Він відстежує кожен потік, що проходить через машину, а не лише NAT-овані. Без цього NAT був би неможливий, а stateful-файрвол (модуль 16) теж.
Кожен запис містить два кортежі: як пакет виглядає в одному напрямку й як у зворотному. Різниця між ними і є трансляція. Якщо кортежі однакові з точністю до розвороту, NAT для потоку немає.
Для TCP conntrack відстежує стани, схожі на стани TCP (модуль 11): SYN_SENT, SYN_RECV, ESTABLISHED, FIN_WAIT, LAST_ACK, TIME_WAIT, CLOSE. Для UDP і ICMP, де з’єднання немає, стан приблизний: перший пакет створює запис, відповідь підтверджує його, а тайм-аут обрізає тишу. У правилах nftables стан виражається спрощеніше, через ct state:
| Стан | Що означає |
|---|---|
new |
перший пакет потоку, який ще не бачили |
established |
потік, у якому пакети вже йшли в обидва боки |
related |
новий потік, породжений наявним (наприклад, ICMP-помилка до з’єднання, яке ми вели) |
invalid |
пакет, що не вписується в жоден потік |
Тайм-аути
Section titled “Тайм-аути”Записи живуть обмежений час. Типові значення в Linux (перевіряйте на своїй системі у /proc/sys/net/netfilter/):
| Параметр | Значення в нашому прогоні |
|---|---|
nf_conntrack_tcp_timeout_established |
432000 с (п’ять діб) |
nf_conntrack_tcp_timeout_time_wait |
120 с |
nf_conntrack_udp_timeout |
30 с |
nf_conntrack_udp_timeout_stream |
120 с (для UDP-потоку, що вже чув відповідь) |
nf_conntrack_max |
262144 записи (залежить від пам’яті) |
П’ять діб для ESTABLISHED виглядають дивно, але TCP-з’єднання може мовчати годинами, залишаючись живим, і якщо шлюз забуде запис, то перший же наступний пакет опиниться без пари. Для UDP, де жодного з’єднання немає, тайм-аут навмисно короткий: 30 секунд тиші, і запис зникає. Тому застосунки, яким треба тримати UDP-«з’єднання» за NAT відкритим (VoIP, ігри, VPN), надсилають keepalive кожні 15–25 секунд.
Максимум записів має практичні наслідки. Коли таблиця повна, ядро відкидає нові потоки й пише про це в журнал (nf_conntrack: table full, dropping packet). Зазвичай це видно на навантажених шлюзах, серверах із тисячами короткоживучих з’єднань і у відповідь на сканування або SYN flood.
Що з портами
Section titled “Що з портами”Кількість записів на одного клієнта обмежена не лише максимумом таблиці. Вихідних портів 65535, але унікальним має бути весь кортеж: адреси, порти й протокол. Тому ті самі публічна адреса й порт можуть одночасно служити для з’єднань до різних серверів. Вичерпуються порти лише тоді, коли багато клієнтів тримають тисячі одночасних з’єднань до одного й того самого сервера та порту. Для провайдерських NAT ця межа реальна.
NAT не є файрволом
Section titled “NAT не є файрволом”Найпоширеніша помилка: «мене захищає NAT, бо ззовні мене не видно». Так здається, бо з побутового роутера прямо з інтернету справді не зайти. Та причина тут інша.
Вхідний пакет до приватної адреси не доходить не тому, що NAT його перехоплює, а тому, що в публічному інтернеті немає маршруту до 192.168.1.116. Його відкинули б маршрутизатори провайдера. Але якщо зловмисник сидить по той бік шлюзу (у тому самому сегменті провайдера, або провайдер сам погодився маршрутизувати приватну мережу до вас), то нічого, крім налаштувань пересилання, між ним і вашим хостом не стоїть. Далі в розділі про Linux це показано руками.
Захист виконує правило фільтрації з відстеженням стану: приймати пакети з зовнішнього боку, лише якщо вони належать з’єднанню, яке почалося зсередини. Побутові роутери вмикають обидва механізми разом, тому їх легко сприйняти за один, а от на налаштованому вручну шлюзі їх треба вмикати окремо.
Hairpin NAT
Section titled “Hairpin NAT”Ви пробросили порт 8080 на сервер у локальній мережі. Ззовні 203.0.113.1:8080 працює. Та коли з ноутбука в тій самій мережі ви відкриваєте ту саму адресу, з’єднання зависає.
Причина в шляху пакета. Ноутбук (192.168.1.50) шле SYN на публічну адресу шлюзу. Шлюз робить DNAT і пересилає пакет серверу (192.168.1.116) в тій самій мережі, з якої він прийшов, не змінюючи адресу відправника. Сервер бачить запит від 192.168.1.50, який знаходиться в нього в локальній мережі, і відповідає напряму, минаючи шлюз. Для ноутбука ж відповідь приходить не від 203.0.113.1:8080, куди він слав SYN, а від 192.168.1.116:8000. Це не те з’єднання, яке він відкривав, і він відповідає на неї скиданням (RST).
Лікується це тим, що шлюз робить для таких потоків ще й SNAT: підміняє відправника на власну адресу, і тоді сервер відповідає шлюзу, а той повертає відповідь ноутбуку з правильною адресою. Це називається hairpin NAT (NAT-шпилька, бо пакет іде до шлюзу й повертається тією самою дорогою) або NAT loopback. Інший і часто кращий вихід: щоб у локальній мережі DNS повертав для цього імені внутрішню адресу, і пакет взагалі не ходив до шлюзу.
CGNAT і наслідки
Section titled “CGNAT і наслідки”Адрес IPv4 на всіх не вистачило, тож провайдери самі почали ставити NAT між своїми клієнтами й інтернетом. Це CGNAT (carrier-grade NAT): ваш домашній роутер отримує від провайдера приватну адресу, і на стороні провайдера трафік транслюється ще раз. NAT у NAT. Для проміжку між абонентом і провайдером виділено окремий діапазон 100.64.0.0/10 (RFC 6598). Він не належить ні до приватних діапазонів з RFC 1918, ні до публічних адрес, тож не зіткнеться з вашою домашньою мережею. Якщо ip addr на вашому роутері показує WAN-адресу з цього діапазону, ви за CGNAT.
Наслідки відчутні:
- Проброс портів неможливий. Порт, який ви відкриваєте на домашньому роутері, нічого не змінює: назовні сидить ще один NAT, яким ви не керуєте.
- Одна публічна адреса на сотні абонентів. Якщо хтось із них потрапив у чорний список за спам чи DDoS, постраждають усі. Сайти, що обмежують запити за адресою, бачать усіх цих людей як одного користувача.
- Порти розподілені між абонентами. Кожному дістається обмежений діапазон портів, і програма, що відкриває багато з’єднань, впирається в нього.
NAT і прямі з’єднання
Section titled “NAT і прямі з’єднання”Два пристрої за різними NAT хочуть з’єднатися напряму (дзвінок, гра, передача файлу). Але жодному з них не можна просто надіслати пакет ззовні: записів у таблицях ще немає.
Спершу кожен пристрій дізнається свою зовнішню адресу й порт через публічний сервер (протокол STUN: запит вгору, у відповіді «я бачу тебе як 203.0.113.1:40000»). Потім обидва обмінюються цією інформацією через посередника, який має публічну адресу (сигнальний сервер). Нарешті обидва одночасно надсилають один одному UDP-пакет на зовнішню адресу партнера. Перший пакет кожного в першій мить відкидається другим NAT, бо запису ще немає, але він створює запис у таблиці свого NAT, через який зустрічний пакет уже пройде. Це називається UDP hole punching, а весь набір прийомів разом зветься ICE.
Працює не завжди. Деякі NAT, серед них багато CGNAT, для кожного нового призначення виділяють новий зовнішній порт (симетричний NAT), і тоді порт, який бачив STUN-сервер, не збігається з портом, яким користуватиметься партнер. Тоді лишається єдиний вихід: пересилати весь трафік через публічний ретранслятор (TURN-сервер), що коштує грошей і додає затримку. Частина дзвінків у відеочатах з’єднується саме через ретранслятор, і помилки конфігурації тут немає.
Як це насправді в Linux
Section titled “Як це насправді в Linux”Усе нижче запущено в просторах імен: m9cli і m9cl2 (клієнти в локальній мережі 192.168.1.0/24), m9gw (шлюз) і m9srv (сервер з адресою 203.0.113.10). Назовні в шлюзу інтерфейс m9-gw з адресою 203.0.113.1. Спочатку клієнт m9cli з’єднаний зі шлюзом прямою парою veth (на боці шлюзу m9-gl), а для досліду з hairpin додано міст m9br у m9gw і другого клієнта m9cl2. Вивід скорочено. Так само побудована лабораторна B2.
DHCP через dnsmasq
Section titled “DHCP через dnsmasq”dnsmasq, крім DNS, уміє роздавати адреси. Мінімальний конфіг лише для DHCP:
interface=m9-glbind-interfacesexcept-interface=loport=0dhcp-range=192.168.1.100,192.168.1.150,255.255.255.0,12hdhcp-option=option:router,192.168.1.1dhcp-option=option:dns-server,192.168.1.1dhcp-leasefile=/tmp/m9/leaseslog-dhcpno-pingport=0 вимикає вбудований DNS: тут потрібен лише DHCP. dhcp-range задає пул і термін оренди. no-ping потрібен, бо за замовчуванням dnsmasq перед Offer пінгує адресу, щоб перевірити, чи вона вільна. У нашому спостереженні це коштувало трьох секунд між Discover і Offer.
sudo ip netns exec m9gw dnsmasq -C dnsmasq.confsudo ip netns exec m9cli dhclient -v -d -1 m9-cКлієнт друкує чотири кроки:
DHCPDISCOVER on m9-c to 255.255.255.255 port 67 interval 3DHCPOFFER of 192.168.1.116 from 192.168.1.1DHCPREQUEST for 192.168.1.116 on m9-c to 255.255.255.255 port 67DHCPACK of 192.168.1.116 from 192.168.1.1bound to 192.168.1.116 -- renewal in 20855 seconds.Те саме з боку проводу дає tcpdump -i m9-gl -nn -vv 'udp port 67 or udp port 68'. Ось Offer, скорочено:
192.168.1.1.67 > 192.168.1.116.68: BOOTP/DHCP, Reply, xid 0x7a4ea138 Your-IP 192.168.1.116 Server-IP 192.168.1.1 DHCP-Message (53), length 1: Offer Server-ID (54), length 4: 192.168.1.1 Lease-Time (51), length 4: 43200 RN (58), length 4: 21600 RB (59), length 4: 37800 Subnet-Mask (1), length 4: 255.255.255.0 Domain-Name-Server (6), length 4: 192.168.1.1 Default-Gateway (3), length 4: 192.168.1.1Тут видно: xid ідентифікує транзакцію (той самий у всіх чотирьох пакетах, так клієнт відрізняє свою відповідь від чужих), термін оренди 43200 секунд (дванадцять годин), RN і RB це T1 і T2: 21600 с (половина) і 37800 с (87,5%). Поле Your-IP це запропонована адреса. У tcpdump на veth може зустрітися повідомлення bad udp cksum: суму драйвер обчислює відкладено, бо пакет ніколи не виходить на справжній дріт, і помилкою це не є.
Сервер веде власний реєстр оренд. Формат файлу leases у dnsmasq: час спливу в секундах від епохи, MAC, адреса, ім’я хоста, ідентифікатор клієнта.
1790796347 42:21:ed:5b:03:eb 192.168.1.116 vm *А клієнт показує, що адреса отримана динамічно:
$ ip -4 addr show dev m9-c inet 192.168.1.116/24 brd 192.168.1.255 scope global dynamic m9-cСлово dynamic означає, що адреса тимчасова, з орендою; у повному виводі поруч є valid_lft (скільки секунд вона ще дійсна).
NAT: masquerade
Section titled “NAT: masquerade”Спершу переконаємося, що проблема існує. Сервер не має маршруту до приватної мережі, і без NAT відповідь не повернеться:
$ ip netns exec m9cli curl -sS -m 2 http://203.0.113.10/curl: (28) Connection timed out after 2002 millisecondsДодаємо правило на шлюзі:
sudo ip netns exec m9gw sysctl -w net.ipv4.ip_forward=1sudo ip netns exec m9gw nft -f - <<'EOF'table ip nat { chain postrouting { type nat hook postrouting priority srcnat; policy accept; oifname "m9-gw" masquerade }}EOFТаблиця сімейства ip (тобто IPv4) із назвою nat; у ній ланцюжок postrouting, прикріплений до хука postrouting типу nat із пріоритетом srcnat. Саме правило: для пакетів, що виходять інтерфейсом m9-gw, застосувати masquerade. Тепер:
$ ip netns exec m9cli curl -sS -m 2 http://203.0.113.10/you are 203.0.113.1:60508Тестовий сервер повертає адресу, з якої до нього прийшли: це публічна адреса шлюзу, а не 192.168.1.116. Порт 60508 той самий, який обрав клієнт: NAT намагається його зберегти.
Замість masquerade можна написати явний snat to 203.0.113.1 (перевірено: працює так само). Різниця, як сказано вище, у тому, що masquerade щоразу дивиться на адресу інтерфейсу.
conntrack у дії
Section titled “conntrack у дії”$ ip netns exec m9gw conntrack -Ltcp 6 119 TIME_WAIT src=192.168.1.116 dst=203.0.113.10 sport=60508 dport=80 \ src=203.0.113.10 dst=203.0.113.1 sport=80 dport=60508 [ASSURED] mark=0 use=1Запис містить два кортежі. Перший, src=192.168.1.116 ... sport=60508, це пакет у напрямку від клієнта так, як він прийшов на шлюз. Другий, src=203.0.113.10 dst=203.0.113.1 ..., це відповідь, яку очікують. Зверніть увагу: у відповіді призначення 203.0.113.1, а не 192.168.1.116. Це і є трансляція. Число 119 це секунди до видалення запису; [ASSURED] означає, що потік бачили в обох напрямках.
Відкритий потік живе інакше:
tcp 6 431999 ESTABLISHED src=192.168.1.116 dst=203.0.113.10 sport=48448 dport=80 ...udp 17 29 src=192.168.1.116 dst=203.0.113.10 sport=36076 dport=5000 ...Для ESTABLISHED залишилось майже п’ять діб, для UDP усього 29 секунд, і це згадані вище тайм-аути. Послідовність станів одного TCP-з’єднання видно в потоці подій:
$ ip netns exec m9gw conntrack -E -p tcp --dport 80 [NEW] tcp 6 120 SYN_SENT src=192.168.1.116 dst=203.0.113.10 sport=53538 dport=80 [UNREPLIED] ... [UPDATE] tcp 6 60 SYN_RECV ... [UPDATE] tcp 6 432000 ESTABLISHED ... [ASSURED] [UPDATE] tcp 6 120 FIN_WAIT ... [UPDATE] tcp 6 29 LAST_ACK ... [UPDATE] tcp 6 120 TIME_WAIT ...Корисні ще conntrack -C (скільки записів зараз), conntrack -F (скинути таблицю) і conntrack -S (лічильники помилок, серед них записи, що не вдалося вставити).
Проброс порту
Section titled “Проброс порту”На клієнті m9cli запущено веб-сервер на порту 8000. Шлюз має переслати на нього порт 8080 публічної адреси:
sudo ip netns exec m9gw nft add chain ip nat prerouting \ '{ type nat hook prerouting priority dstnat; policy accept; }'sudo ip netns exec m9gw nft add rule ip nat prerouting \ ip daddr 203.0.113.1 tcp dport 8080 dnat to 192.168.1.116:8000Ланцюжок прикріплено до хука prerouting із пріоритетом dstnat: підміна повинна статися до маршрутизації. Перевірка із зовнішнього боку:
$ ip netns exec m9srv curl -s -o /dev/null -w '%{http_code}\n' http://203.0.113.1:8080/200$ ip netns exec m9gw conntrack -L -p tcp | grep 8080tcp 6 119 TIME_WAIT src=203.0.113.10 dst=203.0.113.1 sport=54796 dport=8080 \ src=192.168.1.116 dst=203.0.113.10 sport=8000 dport=54796 ...Кортежі тут виглядають інакше, ніж у попередньому прикладі. Перший каже, що клієнт стукав у 203.0.113.1:8080. Другий, що відповідь очікують від 192.168.1.116:8000. Змінилась адреса призначення, а адреса джерела лишилася справжньою, тож внутрішній сервер бачить справжню адресу клієнта.
Hairpin
Section titled “Hairpin”Правило dnat вище не обмежене інтерфейсом, а тому спрацьовує й для клієнтів у мережі. Запит із m9cl2 (192.168.1.50) до 203.0.113.1:8080 зависає:
$ ip netns exec m9cl2 curl -sS -m 3 http://203.0.113.1:8080/curl: (28) Connection timed out after 3002 milliseconds192.168.1.50.54468 > 203.0.113.1.8080: Flags [S]192.168.1.116.8000 > 192.168.1.50.54468: Flags [S.], ...192.168.1.50.54468 > 192.168.1.116.8000: Flags [R], ...SYN пішов на публічну адресу, SYN-ACK прийшов від приватної, і клієнт відповів RST: рівно той збій, який описано вище. Виправляємо SNAT-ом для потоків, що йдуть із локальної мережі назад у неї:
sudo ip netns exec m9gw nft add rule ip nat postrouting \ oifname "m9br" ip saddr 192.168.1.0/24 ip daddr 192.168.1.116 tcp dport 8000 masqueradeПісля цього curl повертає 200.
NAT не файрвол: експеримент
Section titled “NAT не файрвол: експеримент”Дамо серверу маршрут до приватної мережі через шлюз, наче ми провайдер, що маршрутизує її:
sudo ip netns exec m9srv ip route add 192.168.1.0/24 via 203.0.113.1$ ip netns exec m9srv curl -s -o /dev/null -w '%{http_code}\n' http://192.168.1.116:8000/200Сервер дістався до приватної адреси напряму, хоч masquerade налаштований. Правила NAT діють лише на ті пакети, які вони вміють переписати. Ті, що прийшли зовні на приватну адресу, NAT не чіпає, і шлюз їх просто пересилає за таблицею маршрутів.
Закриває це фільтр:
sudo ip netns exec m9gw nft -f - <<'EOF'table inet filter { chain forward { type filter hook forward priority filter; policy drop; ct state established,related accept ct status dnat accept iifname "m9br" accept }}EOFПолітика drop відкидає все пересилання за замовчуванням. Пропускаються лише відповіді на потоки, що вже йдуть (established,related), потоки, які пройшли DNAT, тобто проброшені порти (ct status dnat), і все, що починається з боку локальної мережі (iifname "m9br"). Результат:
$ ip netns exec m9srv curl -sS -m 2 http://192.168.1.116:8000/curl: (28) Connection timed out after 2002 milliseconds$ ip netns exec m9srv curl -s -o /dev/null -w '%{http_code}\n' http://203.0.113.1:8080/200$ ip netns exec m9cli curl -s http://203.0.113.10/you are 203.0.113.1:36108Пряма спроба не проходить, проброс працює, вихідні з’єднання клієнтів теж. Захист дало правило фільтрації, яке так само спирається на conntrack, як і NAT, але є окремим кроком. Докладніше про це в модулі 16 і в лабораторній B4.
Типові помилки розуміння
Section titled “Типові помилки розуміння”NAT захищає від атак ззовні. Вхідний пакет не доходить до приватної адреси лише тому, що до неї немає маршруту в публічній мережі. Тільки правила фільтрації з відстеженням стану відкидають те, чого внутрішні хости не запрошували. Побутовий роутер має обидва механізми, тож здається, що це один.
NAT змінює лише адресу. У PAT переписується ще й порт, і саме це дозволяє багатьом хостам ділити одну адресу. Тому NAT має зберігати стан: без таблиці відповіді нікуди повертати.
Записи в conntrack існують, доки триває з’єднання. Вони живуть за тайм-аутами: для UDP це десятки секунд, для TIME_WAIT двохвилинний період. Запис може зникнути, поки програма ще вважає, що з’єднання відкрито, і тоді перший же наступний пакет загубиться. Через це VPN і VoIP надсилають keepalive.
Проброс портів на роутері розв’язує проблему зовнішнього доступу. Лише якщо роутер має публічну адресу. За CGNAT адреса на вашому роутері приватна (часто з діапазону 100.64.0.0/10), і проброс не діє, бо назовні сидить ще один NAT.
Якщо роутер роздає адреси, він і є DHCP-сервером мережі. Сервер це лише програма, яка слухає порт 67. Вона може працювати на роутері, на окремій машині або на маршрутизаторі в іншому сегменті, з’єднаному через relay. А в мережі, де дві такі програми відповідають одночасно, клієнт візьме ту, що швидша.
DHCP і DNS це одна служба. Це два окремі протоколи: DHCP лише повідомляє клієнтові, який DNS-сервер використовувати, а більше нічого про нього не знає. Плутають їх через те, що dnsmasq уміє обидва.
Перевір себе
Лабораторна
Section titled “Лабораторна”B2. Linux як маршрутизатор. Три підмережі, статичні маршрути, NAT і DHCP через dnsmasq: усе, що в цьому модулі, ви зберете самі й перевірите чекером.
B4. Файрвол і WireGuard. Політика «за замовчуванням заборонено» в nftables, stateful-правила й проброс портів. Відмінність між NAT і фільтрацією, показана вище, там стає практичною вимогою.
Джерела
Section titled “Джерела”- Kurose, Ross, Computer Networking: A Top-Down Approach, розділ про мережевий рівень (DHCP і NAT)
- Stevens, TCP/IP Illustrated, Vol. 1, розділи про DHCP і NAT
- Peterson, Davie, Computer Networks: A Systems Approach
- RFC 2131 і RFC 2132 (DHCP та його опції), RFC 1918 (приватні адреси), RFC 6598 (адресний простір для CGNAT)
- Документація nftables: wiki.nftables.org, розділи про NAT і stateful-фільтрацію
man nft(8),man dnsmasq(8),man conntrack(8),man dhclient(8)