A2. Власні ping і traceroute
ping і traceroute вважаються найпростішими мережевими інструментами,
хоча майже нічого не ховають: перший надсилає ICMP echo request і чекає
echo reply, другий виставляє пакетам малий TTL і збирає повідомлення
Time Exceeded від маршрутизаторів на шляху. Модуль 6 пояснює
заголовок IPv4, поле TTL і місце ICMP поруч із IP. Тут ви побачите
ці байти власними очима: самі збудуєте ICMP-пакет, порахуєте контрольну
суму й розберете заголовок відповіді.
Після цієї роботи ви зможете:
- відкрити raw-сокет і зібрати пакет ICMP без допомоги ядра;
- порахувати контрольну суму Інтернету й пояснити, чому пакет із хибною сумою ядро цілі мовчки викидає;
- виміряти RTT і частку втрат і відрізнити свою відповідь від чужої за ідентифікатором і номером послідовності;
- отримати шлях пакета через TTL і читати в
tracerouteрядки із зірочками; - прочитати
ttl=у виводіpingі за ним оцінити, скільки маршрутизаторів пакет пройшов.
Це корисно щоразу, коли «пінг є, а сайт не відкривається»: ви знатимете,
що саме ping довів, а чого не довів.
Завдання
Section titled “Завдання”Написати на C дві програми, myping і mytraceroute. Обидві беруть
IPv4-адресу в десятковій формі; імена й DNS не потрібні.
sudo ./myping [-c кількість] [-i інтервал_с] [-W таймаут_с] адресаsudo ./mytraceroute [-m макс_стрибків] [-w таймаут_с] [-q проб_на_стрибок] адресаЗначення за замовчуванням: для myping чотири пакети, інтервал 1 с,
таймаут 1 с; для mytraceroute тридцять стрибків, таймаут 1 с, три проби.
Інтервал і таймаут можуть бути дробовими.
myping має вміти:
-
надсилати ICMP echo request із 56 байтами даних і правильною контрольною сумою, з ідентифікатором процесу й номером, що зростає з одиниці;
-
для кожної відповіді друкувати рядок у форматі
64 bytes from 10.0.0.2: icmp_seq=1 ttl=62 time=0.321 ms; розмір — це довжина ICMP-повідомлення,ttlбереться з IP-заголовка відповіді, час вимірюється в мілісекундах; -
приймати лише свої відповіді: тип echo reply, відповідний
idіicmp_seq, правильна контрольна сума, відправник збігається з адресою цілі; інакше пакет мовчки пропускається; повторна відповідь на вже отриманий номер не рахується; -
наприкінці друкувати підсумок у такому вигляді:
--- 10.0.0.2 ping statistics ---4 packets transmitted, 4 received, 0% packet lossrtt min/avg/max = 0.069/0.080/0.090 msРядок
rttдрукується, лише якщо була хоча б одна відповідь; -
завершуватися з кодом 0, якщо прийшла хоч одна відповідь, з кодом 1, якщо жодної, і з кодом 2 при помилці аргументів чи відсутності прав на raw-сокет, пояснивши це в stderr.
mytraceroute має вміти:
- для кожного значення TTL від 1 до
-mнадсилати задану кількість проб і друкувати по одному рядку на стрибок: номер, адреса маршрутизатора й час кожної проби, для проби без відповіді*; приклад:2 10.201.2.2 0.010 ms 0.005 ms 0.005 ms; рядок без жодної відповіді має вигляд3 * * *; - способом проб можна вибрати UDP на великі порти або ICMP echo; перевірка приймає обидва;
- зупинятися, коли відповідь прийшла від самої цілі (для UDP це
Destination Unreachable з кодом «порт недосяжний»), і завершуватися
з кодом 0; якщо цілі не досягнуто за
-mстрибків, код 1; - не приймати чужих ICMP-повідомлень: у тілі помилки лежить початок вашої проби, за ним і треба впізнавати свою.
Чого робити не треба. IPv6, DNS-імена, підтримку довільного розміру
даних, прапор -f, звернення до ядра через SOCK_DGRAM з IPPROTO_ICMP
(для нього потрібна настройка ping_group_range, а ми вчимося писати заголовки
самі).
Обмеження. C, raw-сокет SOCK_RAW з IPPROTO_ICMP, контрольну суму
рахуєте самі. Запускати треба від root або з CAP_NET_RAW.
Готово, коли:
sudo ./check.sh ./myping ./mytracerouteпроходить усі перевірки;- ви можете пояснити, чому ядро, яке прийняло ваш echo request із нульовою
контрольною сумою, не відповіло б на нього (і що побачите в
tcpdump); - у звіті є відповідь на запитання з етапу 5.
Перед початком
Section titled “Перед початком”- Прочитайте в модулі 6 про заголовок IPv4, TTL і
повідомлення ICMP. Формат ICMP-заголовка візьміть із
man 7 icmp, а про raw-сокети подивітьсяman 7 raw. - Розпакуйте архів курсу: чекер лежить у
labs/a2-ping-traceroute/check.sh, поруч єMakefile. - Знадобляться
gcc,python3,nftables(для однієї з перевірок),tcpdump. Потрібні права root, а також ядро з просторами імен, тож підійде ВМ, WSL2 чи контейнер ізCAP_NET_ADMIN(див. огляд).
-
Топологія для дослідів.
Чекер будує ланцюжок із чотирьох просторів імен:
lab-a2-h, два маршрутизаториlab-a2-r1іlab-a2-r2та цільlab-a2-t:lab-a2-h 10.201.1.1 ─ 10.201.1.2 lab-a2-r1 10.201.2.1 ─ 10.201.2.2 lab-a2-r2 10.201.3.1 ─ 10.201.3.2 lab-a2-tЗбудувати його без перевірки можна командою
sudo ./check.sh --up, аsudo ./check.sh --downйого прибирає. Запустіть системнийpingіtracerouteяк еталон поведінки:Terminal window sudo ip netns exec lab-a2-h ping -c2 10.201.3.2sudo ip netns exec lab-a2-h traceroute -n 10.201.3.2Зверніть увагу на
ttl=62у відповідях: ціль відповіла з TTL 64, і два маршрутизатори відняли по одиниці. -
Одна проба ICMP echo.
Відкрийте
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), зберіть 8-байтовий заголовок (тип 8, код 0, контрольна сума,id,seq) і 56 байтів даних. Поки порахуйте суму наосліп, надішліть пакет і подивітьсяtcpdump -ni any icmpу просторі імен цілі: це корисно, щоб побачити, що відбувається до будь-якої перевірки. Потім порахуйте правильну суму.Контрольна сума Інтернету: складіть усі 16-бітні слова пакета, у якому поле суми дорівнює нулю, додайте перенос зі старших розрядів до молодших, а результат інвертуйте. Для перевірки прийнятого пакета складіть усі слова разом із полем суми: має вийти
0xffff, тобто нуль після інверсії. -
Відповідь.
Raw-сокет для ICMP віддає пакет разом з IP-заголовком, і довжина заголовка (поле IHL) змінна. Звідти ж береться TTL. Так само raw-сокет одержує всі ICMP-пакети хоста, а не лише відповіді на ваші запити, тож кожен треба перевірити: тип, код,
id,seq, суму й відправника. Виміряйте RTT заclock_gettime(CLOCK_MONOTONIC).Перевірте, що
sudo ./check.sh ./myping ./mytracerouteуже проходить групи про ping, а перевірки traceroute ще падають. -
Серія пакетів і підсумок.
Цикл по
-cіз інтервалом-i, таймаутом-W, підрахунком втрат, мінімуму, середнього й максимуму. Обробіть випадок, коли відповіді немає:mypingне має зависати і має завершитися з кодом 1. -
Traceroute.
UDP-сокет із
setsockopt(IP_TTL)і окремий raw-сокет для ICMP. Перша проба з TTL 1 дійде до першого маршрутизатора, який зменшить TTL до нуля, викине пакет і надішле Time Exceeded. Кожна нова проба збільшує TTL на одиницю. Ціль пізнається по Destination Unreachable з кодом 3 (порт недосяжний), бо на портах 33434 і вище зазвичай ніхто не слухає.Запишіть у звіт: чому адреса маршрутизатора у рядку
tracerouteзазвичай є адресою інтерфейсу, який дивиться назад до вас, і що станеться вtraceroute, якщо маршрутизатор на шляху не шле Time Exceeded (перевірте це: чекер робить саме так ізlab-a2-r2). -
Звірка із системними утилітами.
Порівняйте власний вивід із системним
pingіtraceroute -nна тому самому ланцюжку, а потім перевірте себе чекером.
Перевірка
Section titled “Перевірка”cd labs/a2-ping-traceroutemakesudo ./check.sh ./myping ./mytracerouteЧекер сам будує простори імен lab-a2-*, запускає ваші програми всередині
lab-a2-h і після себе все прибирає. Він перевіряє:
- ping проти ядра цілі: 4 з 4 відповідей,
ttl=62, 64 байти, зростанняicmp_seq, узгодженістьmin/avg/maxіз рядками відповідей; - ping проти відповідача, керованого чекером: затримка рівно 100 мс
вимірюється правильно; перед справжньою відповіддю приходять чужа
(інший
id) і пошкоджена (хибна сума), а після неї дублікат, і нічого з цього не має потрапити в статистику; на кожен другий запит відповіді немає (50% втрат); адреса, якої немає, не зависає; - traceroute: шлях
10.201.1.2 → 10.201.2.2 → 10.201.3.2, нумерація, час у мілісекундах, зупинка на цілі й на першому маршрутизаторі; маршрутизатор, що не відповідає, дає рядок із зірочками, а трасування триває;-m 2обмежує довжину.
Вигляд рядків між полями, кількість пробілів, заголовок перед виводом і порядок опцій значення не мають. Порядок проб в одному рядку теж: чекер дивиться лише на номер стрибка, адреси, час і зірочки.
Часті помилки
Section titled “Часті помилки”Контрольна сума при ненульовому полі. Суму рахують тоді, коли поле суми
вже заповнене чимось. Обнуліть його перед підрахунком. Якщо суми немає
або вона хибна, ядро цілі не відповість, а ping покаже 100% втрат, і
нічого в коді не підкаже, де помилка: подивіться tcpdump.
Порядок байтів. Поля id і seq лежать у пакеті в мережевому порядку
(htons). Якщо ви порівнюєте без ntohs, то відповіді є, але «не ваші».
Довжина IP-заголовка. Припущення, що вона завжди 20 байтів, працює майже завжди. Використайте поле IHL.
Чужі пакети. Raw-сокет віддає копію кожного ICMP, який прийшов на хост.
Якщо не перевіряти id, відповідь на чужий ping стане вашою.
ttl береться з власного запиту. У виводі ping це TTL відповіді,
і він лежить в IP-заголовку, який ви отримали від raw-сокета.
Перша відповідь у traceroute приходить від цілі. Ви не виставили IP_TTL
на сокеті, що відправляє, або виставили після sendto.
Час не в тих одиницях. tv_nsec лічить наносекунди, tv_usec
мікросекунди. Чекер одразу покаже помилку: відповідач затримав
відповідь на 100 мс, а ви надрукували 100000.
Далі, якщо цікаво
Section titled “Далі, якщо цікаво”Додати -s (розмір даних) і побачити, на яких розмірах ping починає
фрагментувати, а з прапором заборони фрагментації (IP_MTU_DISCOVER)
перетворити власний ping на інструмент для пошуку MTU шляху. Перейти
на SOCK_DGRAM з IPPROTO_ICMP і порівняти, що ядро вже робить за вас.
Спробувати ICMP-проби замість UDP і зрозуміти, чому traceroute -I інколи
проходить там, де UDP-проби відкидає файрвол.