Дослідіть світ протоколів TCP/IP за допомогою нашої статті, яка заглиблюється в теорію та практику, що стоять за цими основними елементами Інтернету. Зрозумійте структуру, функціональні можливості та застосування TCP/IP у мережах, що має вирішальне значення як для професіоналів, так і для ентузіастів. Стаття розбиває складні концепції на зрозумілі терміни, забезпечуючи розуміння того, як працює Інтернет у своїй основі. Ідеально підходить для ІТ-фахівців, студентів і всіх, хто цікавиться роботою мережевих комунікацій. Коли ми думаємо про комп’ютерні мережі, ми часто заглиблюємося у фізичну основу та структуру даних, що передаються мережею, а коли ми думаємо про мережеве програмування, основна увага приділяється інтернет-роз’ємам.
Однак, коли ви будете вчитися і досліджувати, можливо, вам захочеться зробити більше, наприклад, поекспериментувати з пакетами мережевих протоколів. Багато мережевих протоколів реалізовано в ядрі операційної системи, що може бути складним завданням, оскільки будь-яка зміна вимагає навичок написання драйверів для операційної системи. Однак, спеціалізовані бібліотеки дозволяють працювати з протоколами на низькому рівні з простору користувача. Під час написання цієї статті я написав невелику програму, яка надає відправну точку для розуміння комп’ютерних мереж і сімейства протоколів TCP/IP. За допомогою цієї програми ви можете експериментувати і отримувати подальші знання. Ця програма проста і зрозуміла і полегшить вам вивчення змісту цієї статті. Адже радість першої перемоги дає мотивацію, необхідну для того, щоб приділити більше часу вивченню теми. Ця стаття містить найважливіші поняття, які повинен знати кожен програміст, що займається комп’ютерними мережами.
Комп’ютерна мережа – це сукупність обчислювальних пристроїв, які взаємодіють і спільно використовують ресурси. Поняття мережі схоже на поняття графа. Мережа також складається з певної кількості вузлів і зв’язків. Різниця між мережею і графом полягає в тому, що вузли є значущими об’єктами, в даному випадку обчислювальними пристроями, а зв’язки представляють собою з’єднання між цими пристроями. У російській літературі комп’ютерні мережі іноді називають обчислювальними мережами.
Залежно від охоплення території комп’ютерні мережі бувають:
Персональні – Personal Area Network (PAN).
Локальні – Local Area Network (LAN).
Міські – Metropolitan Area Network (MAN).
Глобальні – Wide Area Network (WAN).
Різні датчики, підключені до смартфону, утворюють мережу PAN. Комп’ютерна мережа з пристроїв, підключених до вашого домашнього роутера, є LAN-мережею, мережа з абонентів провайдера в місті – це MAN-мережа, а весь інтернет, який вам надає провайдер – WAN-мережа.
Мережева модель – це концептуальна основа, яка стандартизує мережеву взаємодію. Вона включає базову термінологію, а також призначення та функції мережевих компонентів. Мережева модель поділяє компоненти мережі та їхні функції на рівні. Кожен рівень мережевої моделі має певну мету і функцію.
Зараз найбільш поширені дві мережеві моделі. Це семирівнева OSI-модель та чотирирівнева TCP/IP-модель. Наведемо схему моделей і як вони співвідносяться одна з одною.
У більшості випадків ви матимете справу з мережевою моделлю TCP/IP, проте так історично склалося, що номери шарів використовуються з мережевої моделі OSI. Наприклад, коли зустрічається термін Layer 2 або L2, мається на увазі 1-й рівень (канальний рівень) моделі TCP/IP.
Розбивка по шарах дозволяє технологіям кожного шару еволюціонувати незалежно від інших. Наприклад, завдяки повсюдному впровадженню оптоволокна швидкість інтернету зросла в десятки або навіть сотні разів, а інтернет базувався на протоколі IP, так і продовжує базуватися.
Більшість стандартів Інтернету та протоколів TCP/IP, що діють, регламентуються документами Request For Comments (RFC). Підручники з комп’ютерних мереж ставлять за мету пояснити модель TCP/IP, але за точним трактуванням понять краще звертатися до RFC.
Детально мережна модель TCP/IP розглянута в RFC 1122 (Requirements for Internet Hosts – Communication Layers) і RFC-1123 (Requirements for Internet Hosts – Application and Support) . Модель пояснюється та розширюється іншими RFC, але для розуміння основ.
хост (host);
повідомлення;
IP-датаграма;
пакет;
кадр;
IP-адреса;
MAC-адреса;
TCP-сегмент;
UDP-датаграма;
MTU.
IP-мережа – це набір з’єднаних між собою хостів. Комп’ютери з’єднані безпосередньо або опосередковано за допомогою релейних пристроїв (маршрутизаторів і комутаторів). Хости використовують інтерфейси для отримання повідомлень з мережі та надсилання повідомлень до мережі. Фізичні інтерфейси надсилають та отримують кадри, логічні інтерфейси надсилають та отримують IP-пакети.
Фізичний інтерфейс ідентифікується за MAC-адресою, а логічний – за IP-адресою. Повідомлення – це або UDP-дейтаграми, або TCP-сегменти. Повідомлення містить заголовок і корисні дані; щоб відправити повідомлення в IP-мережі, воно вкладається в IP-датаграму; якщо розмір IP-датаграми перевищує MTU, вона фрагментується і генерується кілька IP-пакетів, в іншому випадку – один на всю IP-датаграму.
Генерується лише один IP-пакет. IP-пакети пересилаються на вибраний логічний інтерфейс відповідно до таблиці маршрутизації хоста. Логічні інтерфейси не можуть самі пересилати IP-пакети, а використовують фізичні інтерфейси. Фізичний інтерфейс передає дані у вигляді кадрів. Кадри мають заголовок і корисне навантаження.
У заголовку кадру вказується MAC-адреса одержувача, MAC-адреса відправника і до якого протоколу належать дані у корисному вантажі (Ethertype). Адреса відправника відома і є MAC-адресою інтерфейсу хоста-відправника; для протоколів IPv4 Ethertype=0x0800. Адреса фізичного інтерфейсу визначається шляхом надсилання ARP-повідомлення до широкомовного домену; ARP-повідомлення інкапсулюється у кадр з EtherType=0x0806 (ARP).
Повідомлення містить MAC-адресу відправника, широкомовну MAC-адресу одержувача та IP-адресу цілі. Хост з фізичним інтерфейсом, якому призначено цю IP-адресу, надає MAC-адресу цього фізичного інтерфейсу у повідомленні-відповіді. Щоб уникнути надсилання ARP-повідомлення кожного разу, відповідність між IP-адресою та MAC-адресою зберігається в кеші хоста.
Після надсилання кадру іншому мережевому інтерфейсу вміст IP-пакета витягується з нього і збирається в IP-дейтаграму, якщо IP-адреса логічного інтерфейсу хоста збігається з IP-адресою одержувача. З IP-дейтаграм виділяються TCP-сегменти або UDP-дейтаграми. З них витягуються самі дані і передаються процесу операційної системи. В іншому випадку IP-пакет або відкидається, або пересилається відповідно до таблиці маршрутизації хоста.
Після передачі вони повертаються на логічний інтерфейс. Там вони групуються у кадри і передаються. Це спрощений опис, оскільки ми не заглиблювалися в те, як працюють віртуальні мережеві інтерфейси, віртуальні приватні мережі, PPP-з’єднання і мережеві транспортні протоколи TCP і UDP.
Адресація дозволяє вказати джерела та одержувача даних. Для шару L2 одержувач і відправник ідентифікуються MAC-адресами, L3 – IP-адресами, для L4 – портами.
Що стосується MAC-адресації, вам достатньо знати, що в більшості випадків фізичний інтерфейс має унікальну MAC-адресу, що складається з 6 байт. Що стосується IP-адресації, тут дещо цікавіше. Почнемо з того, що всього можливо 2^32 IP-адрес, але кількість допустимих IP-адрес хостів менше, а глобальних (IP-адрес, видимих в інтернеті) ще менше. Спробую пояснити чому так відбувається.
Інтернет проектувався як безліч з’єднаних комп’ютерних мереж, хости яких взаємодіють між собою. Для ідентифікації мереж використовується той самий адресний простір, що й ідентифікації інтерфейсів хостів. Як це реалізується?
Кожна IP-адреса – це послідовність з 32 біт. Перші n-біт в IP-адресі несуть інформацію про те, до якої мережі належить IP-адреса, біти, що залишилися, – це унікальна адреса всередині цієї мережі. Однак адреси, у яких усі біти 0 або всі біти 1 мають особливе значення. Якщо всі біти 0 це мережа, якщо 1 це широкомовна адреса.
Скільки перших біт в IP-адресі несуть інформацію про мережу, що визначається маскою мережі. Якщо виконати побітову IP-адресу з маскою мережі, то вийде ідентифікатор мережі. Якщо виконати побітове “І” з інвертованою маскою мережі, ми отримаємо унікальну адресу всередині мережі. Щоб можна було простіше подати інформацію про IP-адресу, і яка її частина використовується для ідентифікації мережі, використовується CIDR-нотація.
Наприклад, запис 192.168.0.0/24 означає: мережа має ідентифікатор 192.168.0.0, 24 перші біти використовуються для ідентифікації мережі. Для кодування хостів використовується останні 8 біт. Максимальна кількість хостів у мережі – 254 (0 – мережа, 255 – широкомовна адреса).
Але це ще не все. Не всі мережі можуть бути видимими в мережі, а деякі з них використовуються для спеціальних цілей.
Як ми бачимо, кількість IP-адрес хостів в інтернеті не така вже й велика, тому виконується перехід на IPv6-адресацію, де кількість IP-адрес набагато більше.
Коли розглядається мережа, потрібно завжди мати уявлення, на якому рівні це виконується. Якщо мережа розглядається на канальному рівні, вона складається з пристроїв, що мають MAC-адресу. Якщо вона розглядається на мережному рівні, вона складається з хостів, які мають IP-адресу.
З метою спрощення управління мережі на канальному рівні поділяються на сегменти, а на мережевому рівні на підмережі. І сегмент мережі та підмережа є широкомовним доменом. Іноді підмережі називають також сегментами, проте слід розуміти, що мається на увазі сегмент мережі лише на рівні L3.
індивідуальна адреса (unicast address);
широкомовна адреса (broadcast address);
групова адреса (multicast address).
Індивідуальна адреса – це унікальна адреса в сегменті мережі або локальної (глобальної) мережі.
Широкомовна адреса — загальна для всіх мережних пристроїв, що мають MAC-адресу або для всіх хостів підмережі. Повідомлення, які надсилаються на широкомовну адресу, будуть отримані всіма вузлами.
Можна зробити так, щоб повідомлення надсилалися тільки тим вузлам, які в них зацікавлені. Для цього використовуються групові адреси. Але це вже окрема тема, яка потребує окремого та ширшого пояснення.
Маршрутизація звичайного користувача мережі виглядає так. Якщо необхідно відправити IP-пакет за певною адресою, то щоразу проглядається таблиця маршрутизації хоста, і на підставі її визначається, потрібно його відправляти на хост усередині мережі, якій належить хост, що відправляє, або потрібно його перенаправити на особливий хост (маршрутизатор, шлюз), який вирішить, що з ним далі. У локальних домашніх мережах часто таким шлюзом є WiFi-роутер.
WiFi-роутер має низку функцій і фактично складається з декількох пристроїв. Однією з важливих функцій Wi-Fi-роутера є функція трансляції мережевих пристроїв (Network Address Translation – NAT), що дозволяє пристроям з локальної мережі отримати доступ в інтернет. Доступ цей трохи обмежений, але більшості користувачів цього достатньо.
Суть роботи NAT в тому, що хост, який знаходиться за NAT, видно для глобальної мережі як хост, у якого IP-адреса така сама, як і зовнішня адреса NAT, що дозволяє більш економно витрачати глобальні IP-адреси. Детальний опис роботи NAT вимагатиме окремої статті, а то й книги.
Під час передачі даних через мережу можуть виникати різні ситуації, наприклад, коли отримані дані відрізняються від переданих. Щоб виявити такі випадки, разом з даними надсилається контрольна сума, яка обчислюється на приймальному боці і порівнюється з отриманими даними, що дозволяє відповісти, чи були дані передані коректно, чи десь сталася помилка при передачі даних. Як правило, повідомлення з неправильними контрольними сумами відхиляються, і повідомлення вважається втраченим. Обчислення контрольних сум використовується на різних рівнях стеку. Наприклад, для Ethernet-кадрів використовується CRC-32; для IP-заголовків, ICMP-повідомлень, UDP-дейтаграм і TCP-сегментів – контрольна сума за методом доповнення. Щоб уникнути зайвих витрат процесорного часу на обчислення контрольних сум для Ethernet-кадрів і вихідних IP-пакетів, використовується механізм розвантаження, за допомогою якого контрольна сума обчислюється мережевим адаптером.
Комп’ютерні дані зберігаються та обробляються у двійковому коді. Двійковий код означає, що інформація кодується за допомогою двох значень, 0 і 1. Процесори зазвичай працюють не з окремими бітами, а з такими частинами, як 8, 16, 32, 64 і т.д. 8 бітів називаються байтами. Можна сказати, що пам’ять зберігається у вигляді послідовності байтів. Однак, байт може приймати лише 256 значень, що явно недостатньо для більшості математичних операцій, тому байти групуються у слова (2 байти), подвійні слова (4 байти) тощо. Однак такі групи байтів можуть зберігатися в пам’яті по-різному: візьмемо для прикладу 4-байтне подвійне слово 0x12345678, яке складається зі значень 0x12, 0x34, 0x56 і 0x78. Вони розміщуються у пам’яті у порядку 0x12, 0x34, 0x56, 0x78 або 0x78, 0x56, 0x34, 0x12. Спосіб їх розміщення в пам’яті залежить від архітектури процесора. Наприклад, у випадку архітектури x86, порядок, в якому байти з меншими дійсними бітами зберігаються перед байтами з більшими дійсними бітами.
Так історично склалося, що дані в пакетах, що передаються в IP-мережах, мають мережевий порядок байтів, який має на увазі, що байт, що містить старші біти, зберігається перший.
Що стосується порядку бітів, то якщо не заглиблюватися в питання кодування даних на рівні сигналів, порядок не важливий, так як мережевий адаптер зробить все без втручання програміста.
Існує ряд протоколів, на яких все ґрунтується:
Ethernet II;
IP – Internet Protocol;
ICMP – Internet Control Management Protocol;
UDP – User Datagram Protocol;
TCP – Transmission Control Protocol;
DHCP – Dynamic Host Configuration Protocol;
DNS – Domain Name Service.
Розглянемо їх детальніше. Дані передаються порціями, які називаються Protocol Data Unit (PDU). PDU складається з заголовка (header) та корисних даних (payload). PDU одного протоколу корисних даних можуть містити PDU іншого протоколу. Це називається інкапсуляцією. Залежно від рівня, на якому працює мережевий протокол, PDU можуть називатися по-різному:
на канальному рівні – кадр;
на мережевому рівні – пакет (IP, ICMP);
на транспортному рівні – сегмент або датаграма (TCP, UDP);
на прикладному рівні – повідомлення (DNS, DHCP).
Але в різній літературі можуть не дотримуватись правила, наприклад, часто можна зустріти IP-датаграму, TCP-пакети або UDP-пакети. А в програмах аналізу трафіку в мережі всі PDU називаються пакетами. У міру набуття досвіду, ви краще орієнтуватиметеся, що мало на увазі при вживанні того чи іншого терміну.
Зазвичай, описуючи протокол Ethernet, ми заглиблюємося мало не в біти, або навіть сигнали, що передаються мережею. Тут ми не будемо вдаватися в такі подробиці, оскільки дуже ймовірно, що ви використовуєте з’єднання WiFi, яке різні інструменти перехоплення мережевого трафіку зазвичай показують як IP-пакети, інкапсульовані в кадри Ethernet II. Ці кадри мають мало спільного з кадрами WiFi, але вони уніфікують роботу на канальному рівні. Взагалі, інформація про те, як працює WiFi, заслуговує на окрему статтю.
Ethernet-фрейм, який передається або приймається драйвером мережного адаптера, складається із заголовка та корисних даних.
Часто можна зустріти інформацію про мінімальний розмір Ethernet-фрейму, преамбулі та контрольну суму. Але це рівень реалізації драйвера мережного адаптера або апаратної реалізації самого адаптера, тому не розглядатиму його в статті. Для нас Ethernet-фрейм містить MAC-адресу одержувача, MAC-адресу відправника, тип кадру та самі дані.
Оригінальний опис протоколу знаходиться в RFC 791 Internet Protocol – DARPA Internet Program Protocol Specification .
Структура пакетів
Структура IP-пакету наведена нижче.
IP-пакети та IP-датаграми
IP-протоколи можуть передавати дані сегментами. У літературі зустрічаються два схожих терміни: IP-дейтаграми та IP-пакети. Іноді ці терміни вживаються неправильно. Пояснимо різницю: IP-датаграма – це дані, що передаються на мережевий рівень, тоді як IP-пакет – це дані, що передаються в IP-мережу; розмір IP-датаграми обмежений максимальним значенням поля Total Length (Загальна довжина. Максимальна одиниця передачі (англ. Maximum Transmission Unit), яка є максимальним обсягом даних, що може бути переданий в одному кадрі на канальному рівні. Фрагменти використовуються для передачі IP-датаграм, що містять більше корисних даних, ніж може вмістити один IP-пакет.
Фрагментація IP-датаграм
Протокол IP підтримує фрагментацію IP-датаграм. Суть фрагментації полягає в тому, що максимальний розмір даних однієї IP-датаграми становить 65535 байт (октетів), тоді як максимальний розмір даних, який може поміститися в PDU канального рівня (MTU), набагато менший (зазвичай близько 1500 байт) Якщо розмір IP-датаграми перевищує MTU, вона розбивається на кілька IP-пакетів, кожен з яких може бути переданий в PDU канального рівня; коли IP-пакети приймаються, вони групуються в IP-дейтаграму, яка аналізується на транспортному рівні.
Всі IP-пакети однієї IP-датаграми мають однакове значення в полі Identification, а поле Offset містить зміщення в payload IP-датаграми.
Хоча фрагментація дозволяє з’єднувати хости в різних мережах, її бажано уникати, оскільки вона ускладнює передачу даних і збільшує навантаження на мережу через створення нових IP-пакетів і перерахунку контрольних сум. Тому бажано, щоб TCP-сегмент або UDP-датаграма мала розмір не більше MTU шляхом прямування IP-пакетів.
Протокол ARP використовується визначення МАС-адреси фізичного інтерфейсу хоста з його IP-адресу. Опис протоколу наведено в RFC 826 – An Ethernet Address Resolution Protocol . MAC-адреса не обов’язково повинна бути адресою в мережі Ethernet, але нижче я наводжу структуру ARP-повідомлення для мережі Ethernet.
Опис протоколу наведено в RFC 792 (Internet Protocol DARPA Internet Program Protocol Specification) .
Структура повідомлень
В описі протоколу наведено багато різних повідомлень, але нам достатньо для початку розібратися з повідомленнями Echo і Reply
Хоча повідомлення, що використовуються протоколом, інкапсулюються в IP-датаграми, ICMP зараховують до того ж рівня, що і IP-мережевого.
Опис протоколу наведено в RFC 768 (User Datagram Protocol) .
Протокол дозволяє двом процесам обмінюватися UDP-датаграм. Кожна UDP-датаграма містить порт відправника (Source Port), порт одержувача (Destination Port), довжину дейтаграми (Length), контрольну суму (Checksum) і самі дані, що передаються.
При розрахунку контрольної суми додається псевдозаголовок, який передається, лише бере участь у розрахунку контрольної суми.
Протокол використовується як транспортний протокол там, де на транспортному рівні допускається дублювання одержуваних даних, пропуск даних або не важливий порядок, в якому дані будуть доставлені.
Як правило, обробка цих випадків покладається на протоколи рівня додатків або зовсім не здійснюється. Наприклад, потокове відео або аудіо дані пропускаються, так як повторна передача даних є в цьому випадку безглуздою. Але якщо ви хочете гарантовану доставку даних на транспортному рівні, вам необхідно використовувати протокол TCP.
Структури псевдозаголовка, що використовується при обчисленні контрольної суми та UDP-датаграми, наведено нижче.
Протокол TCP — найскладніший із усіх, наведених у статті. Призначення протоколу TCP – створити надійне віртуальне повнодуплексне з’єднання між процесами. На даний момент найсвіжіший опис протоколу наведено в RFC 9293 – Transmission Control Protocol (TCP) .
Структура повідомлень
Повідомлення, що використовуються у протоколі TCP, називаються TCP-сегментами. Прохання не плутати із сегментами мережі. Вони з ними не мають нічого спільного. При розрахунку контрольної суми для TCP-сегменту як і UPD використовується псевдозаголовок. Але якщо UDP розрахунок контрольної суми перестав бути обов’язковим, то TCP він обов’язковий.
Структура псевдозаголовка та TCP-сегменту наведена нижче.
Як видно з структури заголовка, протокол TCP, як і протокол IP, закладені можливості для розширення та еволюції протоколу за допомогою поля Options.
Ключові поняття, необхідні розуміння TCP:
Segment;
Sequence Number;
Acknowledge number;
TCP Window;
TCP Handshake;
MSS – Maximum Segment Size;
TCP Flags and TCP Options;
Window Scaling;
Selective Acknowledgement.
Для надійної передачі використовуються Sequence Number, Acknowledge Number і Window Size. Розглянемо як вони працюють разом. Дані розбиваються на TCP-сегменти.
За кожним TCP-сегментом закріплюється Sequence number.
Sequence Number може набувати значень від 0x00000000 до 0xffffffff. Якщо кожному байту, що передається, присвоїти номер і розбити на сегменти, то Sequence Number — це номер першого байта кожного сегмента.
Sequence Number служить для упорядкування сегментів, які прийшли не в порядку їх відсилання (out of order), підтвердження отриманих сегментів (acknowledgement), а також для повторного відсилання сегментів, що загубилися (retransmitting).
Кожен переданий по мережі TCP-сегмент містить поля Sequence Number та Acknowledge Number. Sequence Number ідентифікує сегмент, що відправляється, а Acknowledge Number вказує на те, який сегмент очікується.
Значення Sequence Number та Acknowledge Number дозволяють відстежувати прогрес передачі даних через з’єднання TCP. Кожна сторона генерує випадкове число діапазону від 0 до 2^32, яке називається ISN. Це число є початком для генерування Sequence Numbers сегментів, що відсилаються.
Сегменти надсилаються лише ті, які потрапляють у вікно. Розмір цього вікна повідомляється відправнику при встановленні з’єднання, але може бути змінений надалі.
У міру отримання підтверджень з боку, що приймає, вікно зсувається по колу. Перед передачею даних необхідно встановити з’єднання. Під час встановлення з’єднання сторони обмінюються параметрами майбутнього з’єднання. Будь-яка сторона може ініціювати розрив з’єднання.
Під час встановлення з’єднання сторони обмінюються параметрами Sequence Number, Acknowledge Number, Window Size, а також параметрами, які передаються в поле TCP Options (MSS, Window scale).
Прапори TCP
Для управління TCP-з’єднанням використовуються прапори в TCP-сегментах, що відсилаються. Найбільш важливими є:
SYN – використовується при встановленні TCP-з’єднання;
ACK – означає, що сегмент був отриманий стороною, що приймає;
FIN – використовується при нормальному (graceful) закритті TCP-з’єднання;
RST – використовується при аварійному закритті TCP-з’єднання.
Опції TCP
Протокол TCP розроблений таким чином, що його можна розширювати, використовуючи механізм опцій. Опції – це додаткові поля, які передаються в заголовку. Наприклад, під час встановлення з’єднання сторони обмінюються опціями, Window Scale, MSS. Залежно від налаштувань стека в TCP-сегменті може передаватися опція TCP timestamp. Якщо опція не підтримується, то вона ігнорується стеком.
Відкриття з’єднання, передача даних, закриття з’єднання
У RFC 9293 наведено докладний опис та представлена діаграма станів для TCP-з’єднання. Я не буду її тут наводити. За бажання ви її можете розібрати. Але хочу одразу сказати, її складно читати. Нижче наведено діаграму послідовностей для життєвого циклу TCP з’єднання.
Зверніть увагу, як змінюються значення в полях SequenceNumber і AcknowledgeNumber сегментів, що віддаються TCP.
Якщо при встановленні TCP-з’єднання використовується 3-way handshake, то із закриттям існує безліч різних варіантів. На діаграмі наведено 4-way handshake, який буде у випадку, якщо сервер не обробив усі дані від клієнта при отриманні TCP-сегменту FIN, ACK. Якщо дані всі оброблені, то виконується 3-way handshake (сервер відсилає тільки сегмент FIN,ACK).
Щоб вам було зрозуміліше у механізмі передачі та прийому TCP-сегментів, я намалював схематичний малюнок.
Налаштування стека TCP/IP
Роботу стека протоколів TCP/IP можна налаштовувати в операційній системі, тільки робити це краще в тому випадку, якщо ви усвідомлюєте, що саме ви змінюєте і навіщо.
У Windows стек можна налаштувати командою netsh, У Linux/MacOS – командою sysctl. Залежно від операційної системи перелік і значення за замовчуванням параметрів можуть відрізнятися. Наприклад, Windows tcp timestamps відключені за замовчуванням. Щоб увімкнути, потрібно виконати команду:
netsh int tcp set global timestamps=enabled
У Linux, навпаки, tcp timestamps включені, якщо ви хочете їх відключити потрібно виконати команду:
sysctl -w net.ipv4.tcp_timestamps=0
У MacOS увімкнути/вимкнути tcp timestamps у вас не вийде.
Для роботи в мережі TCP/IP хост необхідно налаштувати. Мінімально необхідно вказати його IP-адресу та маску підмережі. Також може знадобитися вказати адресу шлюзу та DNS-сервера. Протокол DHCP дозволяє хосту отримати ці дані автоматично з мережі.
Існують різні варіанти використання цього протоколу, але ми розглянемо основний успішний сценарій отримання IP-адреси хостом, що складається з обміну 4 повідомленнями.
Отримання конфігурації
1. Спочатку хост не має IP-адреси і не знає, де розташований DHCP-сервер, який йому цю інформацію може надати. Тому він надсилає широкомовне повідомлення DHCP Discover у свій сегмент мережі.
2. Якщо в мережі є DHCP-сервер, він відповідає unicast-повідомленням DHCP Offer, в якому міститься пропонована конфігурація для хоста.
3. Хост надсилає unicast-повідомлення DHCP Request, в якому вказує, призначена йому IP-адреса
4. Сервер відповідає unicast-повідомленням DHCP Acknowledge, яке говорить про те, що конфігурація хосту призначена.
Діаграма послідовностей наведена нижче.
Структура повідомлень
Опис протоколу наведено в RFC 2131 – Dynamic Host Configuration Protocol . DHCP-протокол є розширенням більш раннього протоколу BOOTP ( RFC 951 Bootstrap Protocol ). Тому заголовок DHCP-повідомлення майже повністю збігається з повідомленням BOOTP. Поле options завжди починається з магічного числа 0x62825363, за яким слідують опції DHCP, описані в RFC 2132 DHCP Options and BOOTP Vendor Extensions .
Кожна опція складається з коду, довжини та одного або кількох октетів повідомлення. Виняток становлять опції з кодами 0x00 (заповнювач) та 0xff (кінець опцій). Розмір DHCP-повідомлення в октетах повинен бути кратним чотирма, тому після опції з кодом 0xff може бути одна або кілька опцій з кодом 0x00.
Як виглядає DHCP-повідомлення, наведено нижче:
Протокол DNS регламентується RFC 1035 DOMAIN NAMES – IMPLEMENTATION AND SPECIFICATION .
Швидше за все, ви маєте уявлення про службу DNS, тому вона використовується для перетворення доменного імені хоста в його IP-адресу. Начебто все просто, і для програміста все ховається за простим API, але цей факт ускладнює розуміння суті DNS. IP-адреса хоста — це лише частина інформації, яку може зберігати DNS.
Програми типу nslookup та функції у Winsock або glibc заплутують у розумінні DNS. Я б порадив починати вивчення DNS з експериментів з утилітою dig і аналізу трафіку. DNS слід розглядати, не прив’язуючись до IP. DNS – це розподілена ієрархічна база даних доменів.
Щоб переконатися, що це дійсно база даних можете зайти на сайт і побачити підтвердження цього.
Домен
Що таке домен? Домен можна визначити як іменовану сутність, яка містить метаінформацію про себе і знаходиться у віданні організації чи приватної особи. До такої інформації відносяться IP-адреса, ім’я поштового сервера, що використовується, імена серверів імен, що обслуговують даний домен та ін. Для спрощення управління доменами вони організовуються в ієрархію.
Домени не існують власними силами, вони зберігаються серверами імен. Інформація, пов’язана з доменом, поєднується в зону, яка обслуговується конкретним сервером імен.
Зона
Зона — це інформація про домени, розміщені на DNS-сервері. Кореневу зону доменів інтернету можна переглянути тут .
Коренева зона доменів обслуговується 13 кореневими доменними серверами .
Ресурсний запис
Система доменних імен дозволяє структуровано зберігати інформацію. Доменні імена організуються в деревоподібну структуру, листям якої є ресурсні записи (Resource Record (RR)). Кожна RR має клас та тип. Як правило, класи можуть приймати такі значення:
IN (Internet);
CH (Chaos);
HS (Hesiod).
Хоча можна зустріти класи CH і HS, застосування їх специфічне, і з великою ймовірністю ви з ними не зіткнетеся.
А ось типів RR набагато більше, і частина з них обов’язково знати:
A (Address) – IP-адреса, закріплена за доменним ім’ям;
AAAA (IPv6 Address) — IPv6-адреса, закріплена за доменним ім’ям;
CNAME (Canonical Name);
MX (Mail Exchanger);
NS (Name Server);
PTR (Pointer);
SOA (Start of Authority);
TXT (Text);
SRV (Service).
Резолвер
Служби доменних імен дозволяють отримати IP-адресу хоста з його імені. Це реалізовано за допомогою розподіленої бази даних, що працює на декількох хостах. Хости зазвичай взаємодіють з локальним компонентом, який називається вирішувачем. Доступ до нього можна отримати через інтерфейси API операційної системи або бібліотеки мови програмування. При виконанні функцій API, обробник перевіряє свій локальний кеш на наявність IP-адреси імені хоста.
Якщо його не знайдено, він намагається звернутися до DNS-сервера за вказаною в конфігурації адресою. Для простоти пояснення те, що відбувається далі, опущено. Зрештою, DNS-сервер повертає IP-адресу імені хоста або, якщо такий хост недоступний, помилку.
Розв’язувач поміщає цю інформацію в свій кеш і повертає значення коду, який його викликав; в Linux API до розв’язувача знаходиться в бібліотеці glibc, в Windows – в бібліотеці Winsock. Зазвичай, приклади мережевого програмування показують, як використовувати API до вирішувача. У практичному розділі показано, як надсилати запити до DNS-сервера на низькому рівні, формуючи IP-пакети, що містять DNS-запити.
Структура пакетів
Простий DNS-запит виглядає так:
Приклад відповіді DNS наведено нижче:
Як правило, запити та відповіді надсилаються з використанням UDP як транспортний протокол. Однак, якщо відповідь занадто велика, сервер поверне прапор TC. Це означає, що для отримання повної відповіді потрібно використовувати TCP як транспорт.
Практично будь-яка операційна система має підтримку роботи з сімейством протоколів TCP/IP. Набір компонентів операційної системи, які забезпечують комунікацію у вигляді сімейства протоколів TCP/IP, називають стеком протоколів.- XPATH – /html/body/div[3]/div[3]/div/div[2]/div/div[14]/p[1]/text() Різні операційні системи надають доступ до стеку, використовуючи різні програмні інтерфейси. Найбільш поширеним є інтерфейс сокетів.
Хоча й існують відмінності в цьому інтерфейсі для різних операційних систем, більшість функцій схожі. Робота з сокетами передбачає програмування мовою С. Однак для різних мов написані обгортки, які дозволяють кроссплатформенно працювати з сокетами. Залежно від мови, обгортки можуть надавати більше або менше функцій. Наприклад, обгортка в Python більше залежить від платформи, на якій виконується, ніж обгортка в Java.
Зазвичай у літературі з мережевих технологій розглядаються основи комп’ютерних мереж, потім сокети. Але те, як саме можна створити та надіслати пакет, структура якого докладно розписана, не наводиться. Я хочу заповнити цю прогалину.
Інструкції процесора в операційній системі можуть виконуватися в режимі ядра або в режимі користувача. Більшість коду, який пише програміст, це інструкції процесору, які виконуються в режимі користувача. У режимі ядра виконується код драйверів та ядра операційної системи.
Стек TCP/IP виконується в режимі ядра, а з режиму користувача він зазвичай доступний тільки через виклик АPI-сокетів. Тому програміст тільки може виконати високорівневі операції, такі як відкрити TCP-з’єднання, передати дані TCP-з’єднання або передати дані як UDP-датаграму. Доступу до пакетів, що формуються, він не має.
Щоб отримати доступ до пакетів, що формуються, використовуються так звані Raw-сокети. Зауважу, що у Windows Raw-сокети мають обмежений функціонал. Наприклад, неможливо створити Raw-сокет, який дозволяв би працювати з Ethernet-фреймами. Тому для отримання доступу до формування Ethernet-фреймів використовують спеціальний NDIS-драйвер та бібліотеку npcap. У Linux досить просто створити AF_PACKET Raw-сокет.
Бібліотека надає високорівневий, якщо можна сказати, API для формування, фільтрації та перехоплення пакетів, який ховає деталі реалізації для різних платформ.
Існує бібліотека npcap для Windows і libpcap для Unix-подібних систем; бібліотека Scapy Python дозволяє ігнорувати цей факт і писати незалежні від платформи програми. Вона також підтримує багато мережевих протоколів. Вона включає в себе інструменти для аналізу, генерації та передачі пакетів з простору користувача. Наприклад, вона може обчислювати контрольну суму заголовків IP-пакетів і вказувати правильний Ethertype і протокол при інкапсуляції пакетів. Ця бібліотека корисна для експериментів з мережевими протоколами та досліджень.
Wireshark – це, мабуть, найпоширеніша програма для аналізу мережевого трафіку. За допомогою графічного інтерфейсу вона може записувати мережевий трафік на диск, фільтрувати пакети, досліджувати їх структуру, відстежувати TCP-сесії і т.д. Разом зі Scapy це, мабуть, найкраще програмне забезпечення для вивчення основ мережевих протоколів і проведення різноманітних експериментів.
Створення ARP-запиту
def create_arp_request(ip): return Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip)
Створення повідомлення Echo, що використовується в протоколі ICMP
def create_ping_request(ip): return IP(dst=ip)/ICMP()
Створення повідомлень DHCP Discover та DHCP Request
def create_dhcp_discover(mac): return (Ether(src=mac, dst='ff:ff:ff:ff:ff:ff') / IP(src='0.0.0.0', dst='255.255.255.255') / UDP(dport=67, sport=68) / BOOTP(op=1, chaddr=mac_to_bytes(mac)) / DHCP(options=[('message-type', 'discover'), 'end']))
def create_dhcp_request(mac, ip): return (Ether(src=mac, dst='ff:ff:ff:ff:ff:ff') / IP(src='0.0.0.0', dst='255.255.255.255') / UDP(dport=67, sport=68) / BOOTP(op=1, chaddr=mac_to_bytes(mac), ciaddr=ip) / DHCP(options=[('message-type', 'request'), 'end']))
Створення повідомлення DNS на отримання Resource Record типу AA
return IP(dst=dns_server)/UDP()/DNS(rd=1, qd=DNSQR(qname=host_name))
Організація TCP з’єднання
def create_tcp_syn(src, sport, dst, dport, seq): return IP(src=src, dst=dst)/TCP(seq=seq,sport=sport, dport=dport, flags="S") def create_tcp_ack(src, sport, dst, dport, ack, seq): return IP(src=src, dst=dst)/TCP(ack=ack, seq=seq, sport=sport, dport=dport, flags="A") def create_tcp_fin_ack(src, sport, dst, dport, ack, seq): return IP(src=src, dst=dst)/TCP(ack=ack, seq=seq, sport=sport, dport=dport, flags="F") def create_and_close_tcp_connection(host, dport): dst = socket.gethostbyname(host) src = get_default_interface_ip() sport = 12360 seq = 1000 syn = packets.create_tcp_syn(src=src, sport=sport, dst=dst, dport=dport, seq=seq) response = send_receive_l3(syn) ack = packets.create_tcp_ack(src=src, sport=sport, dst=dst, dport=dport, seq=response[TCP].ack, ack=response[TCP].seq+1) send_l3(ack) fin_ack = packets.create_tcp_fin_ack(src=src, sport=sport, dst=dst, dport=dport, seq=response[TCP].ack, ack=response[TCP].seq+1) response = send_receive_l3(fin_ack) ack = packets.create_tcp_ack(src=src, sport=sport, dst=dst, dport=dport, seq=response[TCP].ack, ack=response[TCP].seq+1) send_l3(ack)
Так як ми втручаємося в стандартну роботу стека протоколів TCP/IP, і стек не здогадується про наше втручання, він може відсилати RST-сегменти на сегменти, які він не очікує. Щоб такого не було, нам доведеться тимчасово заборонити відсилання RST-сегментів. У MacOS це виконується за допомогою pfctl. У Linux можна використовувати iptables.
echo "block drop out proto tcp from any to any flags R/R" | cat /etc/pf.conf - | sudo /sbin/pfctl -Ef -
Тема виявилася досить об’ємною і, на жаль, ми не змогли включити все через розмір статті. Однак ми надали достатньо матеріалу для розуміння основ і прикладів: за допомогою Scapy можна експериментувати з відправкою користувацьких пакетів з простору користувача з мінімальним написанням коду, без необхідності занурюватися в джунглі програмування драйверів. Wireshark також корисний для аналізу пакетів, що надсилаються мережею, і вивчення їхньої структури.
Приклади в цій статті не підходять для використання в реальних додатках і корисні лише для розуміння основ мережевих протоколів. У більшості випадків вам не знадобиться самостійно реалізовувати протокол ARP, писати DHCP або DNS-клієнти чи реалізовувати команди ping. Проте, експерименти з ними дадуть вам краще розуміння мережевих протоколів і того, як реалізація мережевого протоколу поводиться в різних непередбачуваних ситуаціях.