Вбудовування в tcp

Вбудовування в TCP / IP стек в ОС GNU / Linux

Дослідження протоколів - досить цікаве заняття. Як і раніше багато дослідників займаються їх оцінкою і верифікацією в різних ситуаціях. Особисто я зіткнувся з необхідністю дослідження протоколу в перший рік роботи в ІММ УрО РАН [1]. Як би це не звучало банально, але це був TCP протокол. Причина, по якій я став цим займатися, наступна - необхідно було розібратися з мережею в одному з обчислювальних кластерів. При передачі даних по протоколу TCP через 2-е 100 Мбіт. мережеві карти одночасно (т.зв. зв'язування каналів або по англ. bonding) спостерігався приріст швидкості в два рази, і в той же час на гігабітних картках швидкість не тільки не зростала, але навіть падала до 600 Мбіт. в с. Сніфер нічого особливого не показав. Та й взагалі кажучи, сніфер не завжди може дати вичерпну інформацію про те, що відбувається в мережі. З його допомогою можна дізнатися про те, що сталося в мережі і на основі цього спробувати зрозуміти, що відбувалося з протоколом. Але для цього як мінімум потрібно точно собі уявляти реалізацію даного протоколу в цій ОС. Погодьтеся, це не легко. Та й чи треба це взагалі, коли сама ОС і всі протоколи прямо перед нами? До того ж, коли це open-source ОС, де немає нічого, що було б приховано від наших очей.

У зв'язку з тим, що зміни в ядро ​​ОС Linux вносяться досить часто, важко написати приклад коду, який би працював на багатьох ядрах відразу. З цієї причини, в цій статті описаний приклад вбудовування в TCP стек в ядро ​​ОС Linux версії 2.6.12. Також, в додатку можна знайти код для версії ядра 2.6.18 (спасибі VenROCK'у, змусив!).

Мережева підсистема ОС GNU / Linux

Як тільки у ОС з'являється необхідність працювати з мережевим пакетом - це sk_buff. Вона створюється в той момент, коли система отримала пакет з мережі, або коли їй необхідно сформувати новий для відправки. З цієї причини в мережевому стеку практично кожна функція отримує цю структуру як параметра. Основне її призначення - дати простий і ефективний спосіб роботи з пакетом на всіх мережевих рівнях мережевого стека (див. Рис. 1). Структура sk_buff є керуючу структуру з приєднаним блоком пам'яті, в якому знаходиться пакет [2]. Таким чином, змінюючи змінні в структурі, ми змінюємо вміст пакета або службову інформацію про нього.

Вбудовування в tcp

Мал. 1. структура sk_buff

Нижче представлена ​​основна частина цієї структури.

Більш докладний її опис можна знайти у вихідних текстах ядра Linux: include / linux / skbuff.h.

Її повний опис можна знайти в include / net / sock.h.

Маючи в своєму розпорядженні ці дві структури (struct sk_buff і struct sock), можна приймати і відправляти пакети, доставляти інформацію від мережевого інтерфейсу до призначеного для користувача додатком і навпаки. Але структура sock описує тільки загальні речі. Наприклад, вона не містить очікуваний номер послідовності TCP сегмента і багатьох інших корисних речей. Для цих цілей служать інші структури.

Кожен протокол має свою структуру для зберігання необхідної йому інформації. Наприклад, для TCP ця структура називається tcp_sock. для UDP - udp_sock. продовжите самі :). Всі вони успадковують структуру sock. правда, не завжди безпосередньо, як це у випадку з tcp_sock (див. рис 2). Все найцікавіше про роботу протоколу можна знайти в цих структурах - його стан, тайм-аути, номер послідовності очікуваного пакета, і багато іншого специфічної інформації. Таким чином, найкращий спосіб дізнатися, що відбувається з протокольної машиною TCP - це зчитувати дані зі структури tcp_sock.

Вбудовування в tcp

Мал. 2. Спадкування структури sock

Доступ до структури tcp_sock

А потім витягується сам сокет:

Функція, яка проводить розрахунок хешу, називається tcp_hashfn. починаючи з версії ядра 2.6.18 вона стала називатися inet_ehashfn () і її перенесли в заголовки, так що тепер немає потреби описувати її в своєму коді. Але ми пишемо код під 2.6.12, так що доведеться нам подивитися, де і як описується ця функція. Вихідний код її так само можна підглянути в net / ipv4 / tcp_ipv4.c.

Тут варто зробити одне важливе зауваження - локальний порт передається в звичайному вигляді (LittleEndian), а порт призначення в мережевому порядку байт (BigEndian), тобто необхідно виконати htons (dest). Після вилучення сокета доступ до структури tcp_sock проводиться дуже просто:

Ну а далі, що вашій душі завгодно.

Примітка

Крім відмінностей в самій гілці 2.6. існує кілька відмінностей при підключенні до TCP стека в 2.4. Але все відмінність полягає в іншому назві структур і трохи відмінний спосіб отримання сокета. Принцип залишається тим же.

Список літератури