Lxf78 драйвер мережевого пристрою - своїми руками

Не дивлячись на те, що по ходу викладу, я постараюся зробити необхідні роз'яснення та посилання, для успішного і правильного розуміння матеріалу статті, читачеві знадобляться деякі початкові знання. Перш за все - знайомство з мовою програмування C, крім того, я вважаю, що читач уявляє, як працюють комп'ютерні мережі, вже працював в ОС Linux і знає, що таке «модуль ядра».

Всі приклади, наведені в статті - робочі, і являють собою необхідні початкові етапи того самого шляху, який потрібно пройти, щоб навчитися створювати драйвери різних мережевих пристроїв. Я використовував дистрибутив Mandrake 10.1 з ядром 2.6.8.1-12mdk. Для інших версій ядра, можливо, знадобиться внести деякі незначні зміни в вихідні тексти.

Щоб мати можливість компілювати модулі ядра, після звичайної установки дистрибутива, потрібно додатково встановити вихідні тексти вашої версії ядра в каталог usr / src. У моєму дистрибутиві це довелося зробити вручну, тому, що навіть при виборі розширеного варіанту установки і є підозра на необхідність включення в неї вихідних текстів, в потрібному каталозі у мене виявилися тільки дрімучі вихідні ядра 2.4. Потрібні початкові тексти знаходяться на третьому диску дистрибутива в папці / media / main3 / у вигляді rpm-пакету: kernel-source-2.6-2.6.8.1-12mdk.i586.rpm. Їх можна встановити за допомогою команди:

Установку слід проводити в командному режимі з каталогу, що містить пакет, в режимі суперкористувача.

Перед початком роботи раджу створити свій робочий каталог / home / user /<что-нибудь>. в якому будуть знаходитися ваші результат ні тексти і помістити туди Makefile, взятий мною з прикладу, пропонованого середовищем розробки KDevelop, злегка модифікований, для забезпечення його працездатності (ох уже ці особливості вільно розповсюджуваних програм!):

Щоб пристосувати цей файл під свої потреби, замініть myname на ім'я вашого модуля. Збірка здійснюється командою make.

Ласкаво просимо в світ хакерів Linux!

Збережіть цей модуль в якомусь файлі (наприклад, helloworld.c), внесіть відповідні зміни в Makefile (див. Вище), а потім дайте команду make. На екрані повинен з'явитися текст:

Після успішної компіляції в вашому каталозі утворюється безліч файлів, з яких для нас цікавий власне об'єктний модуль helloworld.ko. Для завантаження отриманого модуля в ядро ​​потрібно використовувати команду:

При цьому наш модуль буде занесений в список завантажених модулів в файлі / proc / modules (в цьому можна переконатися, використовуючи команду cat / proc / modules | grep helloworld). Крім того, буде запущена инициализирующая функція ssl_init_module (void). що викликає системну функцію printk (). яка призначена для реєстрації подій і попереджень з режиму ядра. Ми не будемо зараз детально розглядати роботу цієї функції, відзначимо лише, що передане їй як аргумент повідомлення «Hello World!» Після завантаження модуля слід шукати у файлі / var / log / messages. куди (при відповідній настройці демона syslog) відправляється більшість повідомлень, що приходять з ядра. Для зручності спостереження за з'являються новими повідомленнями в цьому файлі можна скористатися командою

Якщо ви проігнорували поради бувалих і все-таки розробляєте модулі ядра в графічному середовищі, її зручно запускати в окремому вікні терміналу, створивши своєрідну «консоль повідомлень».

Вивантажити модуль з ядра можна за допомогою команди:

При цьому в запущеній консолі повідомлень можна буде спостерігати, як модуль попрощається зі світом все тими ж «доступними йому засобами».

Матриця: підключення

Наступним кроком на нашому шляху буде створення найпростішого модуля мережевого драйвера. Для цього в нашому модулі потрібно визначити структуру даних мережевого драйвера struct net_device. визначення якої знаходиться в заголовки /usr/include/linux/netdevice.h. У структурі представлено безліч функцій і полів, з яких для нас найбільш важливими є наступні:
• char name [IFNAMSIZ] - містить ім'я інтерфейсу, яке буде відображатися при конфігуруванні мережевої підсистеми.
• int (* open) (struct net_device * dev) - функція, яка запускається кожного разу, коли утиліта ifconfig активує інтерфейс.
• int (* stop) (struct net_device * dev) - функція, яка запускається при зупинці інтерфейсу.
• int (* hard_start_xmit) (struct sk_buff * skb, struct net_device * dev) - функція, яка активізується мережевий підсистемою щоразу, коли потрібно передати пакет даних.
• struct net_device_stats * (* get_stats) (struct net_device * dev) - функція, яка викликається всякий раз, коли будь-яке додаток намагається отримати статистичні дані про роботу інтерфейсу.
• void * priv - додатковий покажчик, який можна використовувати довільним чином.

Решта поля і функції забезпечують драйверу додаткові можливості, які ми зараз розглядати не будемо. Їх призначення можна дізнатися в довідковій літературі. Слід відразу зазначити, що прийом даних від мережевого пристрою зазвичай відбувається по перериванню, його оброблювачем, і явної вказівки на цей обробник в структурі інтерфейсу не міститься.

Нам потрібно заповнити необхідні для роботи драйвера поля. Якщо поле містить покажчик на функцію - необхідно реалізувати цю функцію. У нашому драйвер ми визначимо функції ініціалізації (open). зупинки (stop) і передачі даних (hard_start_xmit). крім того визначимо структуру net_device_stats. вміст якої ми будемо отримувати через додатковий покажчик (priv).

Для того, щоб система дізналася, що завантажується модуль є драйвером мережевого пристрою, при завантаженні модуль дожен повідомити їй про це спеціальний пристрій register_netdev (). Про свою вивантаженні модуль повідомляє функцією unregister_netdev (). Як аргумент для цих функцій передається покажчик на структуру даних драйвера.

Для ініціалізації інтерфейсу в мережевий підсистемі драйвер повинен скористатися спеціальною функцією netif_start_queue (). при зупинці - функцією netif_stop_queue (). аргументом яких також є покажчик на структуру драйвера.

При необхідності передачі даних через мережевий інтерфейс ОС викликає функцію hard_start_xmit () якої в якості аргументу передається покажчик на буфер (sk_buff). що містить готову IP-датаграмму. Викликана функція повинна зробити все необхідне для відправки датаграми мережевому пристрою для передачі на фізичному рівні, а потім повідомити системі про те, що пакет переданий, викликавши функцію dev_kfree_skb (). аргументом якої служить покажчик на буфер, отриманий від ОС. На цьому цикл передачі для драйвера завершується, система може заповнювати звільнений буфер новими даними, а драйвер очікує наступної передачі і покажчика на готовий буфер.

Оскільки наш драйвер поки не пов'язаний з будь-яким пристроєм, функція передачі буде переносити дані, що передаються в свій власний буфер (my_buf). а потім реєструвати ці дані у вигляді повідомлень за допомогою функції printk (). Для уявлення реєстрованих даних в текстовому форматі ми введемо в програму допоміжні функції tpdumpk () і printAddr ().

Наведемо текст нашого, нехай найпростішого, але вже мережевого драйвера:

Для завантаження і ініціалізації скомпільованої драйвера зручно користуватися невеликим сценарієм, текст якого наведено нижче:

Затримка (sleep 1) потрібно для того, щоб система встигла зареєструвати модуль і була готова до ініціалізації його інтерфейсу до моменту подачі команди ifconfig. Після завантаження та ініціалізації нашого драйвера на екрані повинен з'явитися приблизно такий текст (інформація про інші мережеві інтерфейси опущена для економії місця):

Якщо ваш комп'ютер підключений до Інтернету, то перед тим, як відправляти на наш інтерфейс дані, переконайтеся, що цьому не перешкоджають настройки комп'ютера. Налаштуйте міжмережевий екран (firewall) з урахуванням появи нової підмережі, або навіть взагалі вимкніть його на час налагодження (особливо обережні можуть одночасно відключитися і від Інтернету).

Для перевірки працездатності нашого інтерфейсу потрібно запустити в окремій консолі команду tail -f / var / log / messages для відображення поточних повідомлень з ядра, а у вашій робочої консолі скористатися командою:

Таким чином, ми намагаємося відправляти ICMP-пакети на неіснуючий інтерфейс, який доступний через наш драйвер. Оскільки такого інтерфейсу, як і функцій прийому даних, у нашого драйвера поки немає, для ОС всі відправлені пакети будуть втрачені. Але не для нас! Всі передані пакети будуть документуватися в файлі / var / log / messages. і це можна спостерігати в консолі повідомлень приблизно в такому вигляді:

Якщо ваш комп'ютер налаштований на роботу в мережі, то ви напевно побачите, що періодично наш інтерфейс надходять і широкомовні пакети, які нескладно дізнатися на одноманітному тлі керуючих.

Наше наступне завдання - навчити драйвер приймати дані і відправляти їх в мережеву підсистему ОС Linux. Для цього ми введемо в нього необхідні функції, а потім зв'яжемо з апаратним пристроєм, керуючим передачею даних по кабелю. В якості такого пристрою ми використовуємо СОМ-порт комп'ютера. Таким чином ми отримаємо програму, схожу на відомий драйвер SLIP. Звичайно, наш драйвер не буде підтримувати всіх функцій свого відомого прототипу, але зате він буде набагато простіше і не буде вимагати для роботи додаткових програм-демонів. Однак, все це буде вже в наступній частині. НЕ пропустіть!

. Корисні посилання

Схожі статті