Модуль ядра Linux. Виртуальный сетевой интерфейс.

Вопросы программного кода и архитектуры Linux

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 24 янв 2013, 18:08

mcrandy писал(а):
Ох! Боюсь я таких "своих" программ-тестеров!
Открою секрет :) Эта программа основана на функциях _send(), ucnet_send(), _recv(), ucnet_recv(), которые являются базовыми для передачи трафика между машинами в создаваемой системе. Объем трафика там небольшой, не будет таких больших сообщений, которые я пытаюсь передавать.
Потому как по TCP нельзя передавать сообщения, да ещё и "заданной длины"(с) - TCP это stream, поток, "труба", в один конц которой можно "вливать", а из другой - "вытекает"...
Честно говоря я не понимаю... Разве я не так использую TCP?
Да. Вы принципиально используете TCP "не так".
В TCP нет, и вообще быть не может, никаких "сообщений".

А если это "являются базовыми для передачи трафика между машинами в создаваемой системе"(с) - то это значит, что вы закладываете в свою систему то, от чего ей ... прийдёт гоплык :lol:
И проявляться это будет, скорее, именно на коротких передаваемых сообщениях, чем на больших.

Но это предмет отдельного разговора ... для отдельной темы, да и не здесь, а где-то в Сети (там, кстати, есть темы, затрагивающие в примерах эти ваши вопросы).
mcrandy писал(а): Я должен быть уверен что сообщение доставлено, а UDP не подтверждает доставку...
Программа же не пытается отправить все громоздкое сообщение целиком, а отправляет его по частям.
Смотрите протокол SCTP (вместо TCP и UDP) - я ссылку выше показывал.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 00:23

Olej писал(а): - Такой пример с установкой фильтра протоколов описывается (именно в таком виде) давно, начиная с ядер 2.4 (а может быть и раньше). Возможно в поздних ядрах (2.6.29 и далее) что-то поменялось? ... Только чтобы никто ничего не заметил и не описал?
Пройдусь поиском что пишут по этому случаю полезного ... те публикации, которые могут даль пищу к размышлению:

Кодим в ядре Linux`а №6 - написано давно, очень поверхностно ... но может быть;
Следопыты
Экспериментальная реализация стека сетевых протоколов e6 в ядре ОС Linux
Инфицирование на лету - это просто пацанячьий визг ... но можно глянуть ;-)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 00:29

mcrandy писал(а): Решил помучать google на эту тему и нашел где то в комментарих соображения о том что после использования данного системного вызова следует выполнять kfree_skb(skb);. Решил попробовать:

Код: Выделить всё

int test_pack_rcv( struct sk_buff *skb, struct net_device *dev,
                   struct packet_type *pt, struct net_device *odev ) {
//   printk( KERN_INFO "packet received with length: %u\n", skb->len );
   kfree_skb(skb);
   return skb->len;
};
И, к моему удивлению, утечки памяти прекратились! Работа системы не нарушилась и сообщения приходят и отправляются... Беда в том что теперь я совсем не понимаю как работает системный вызов dev_add_pack()? какой буферный сокет я уничтожил?
Это одно из первых действий, которое я вам хотел посоветовать попробовать...
Но то, что при этом кажется, что проблема исчезла, вовсе не означает, что это так.
Хорошо бы понять природу происходящего.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 02:07

mcrandy писал(а):И, к моему удивлению, утечки памяти прекратились! Работа системы не нарушилась и сообщения приходят и отправляются... Беда в том что теперь я совсем не понимаю как работает системный вызов dev_add_pack()? какой буферный сокет я уничтожил?
Смотрим код реализации dev_add_pack() - http://lxr.free-electrons.com/source/ne ... dev.c#L410 :

Код: Выделить всё

410 void dev_add_pack(struct packet_type *pt)
411 {
412         struct list_head *head = ptype_head(pt);
413 
414         spin_lock(&ptype_lock);
415         list_add_rcu(&pt->list, head);
416         spin_unlock(&ptype_lock);
417 }
Структура struct packet_type добавляется в список обработчиков для данного типа протоколов, добавляется, но не замещает существующий обработчик. Если для сокетных буферов данного протокола существует N установленных обработчиков, то достаточно логично, чтобы для каждого из поступающих struct sk_buff создавалось N клонов структуры, чтобы каждый из N обработчиков мог независимо обработать свой экземпляр struct sk_buff.

Но "логично" не значит "так и есть".
Нужно проверять.
Кроме того, мне непонятна семантика целочисленного значения, возвращаемого обработчиком протокола.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 02:44

Olej писал(а):
mcrandy писал(а):
4. То что вы делаете "создаю трафик между двумя машинами"
У меня есть своя клиент-серверная программка организующая передачу рандомного сообщения заданной длины. В ней используются стандартные функции send() и recv() для передачи/приема соответственно. Используется протокол TCP. Она осложнена только тем что в начало сообщения записывает длину этого сообщения которое собирается передать, чтобы сервер знал сообщение какой длины ему ожидать. Программку прикрепляю к сообщению. Внутри есть небольшой README.
Ох! ;-) Боюсь я таких "своих" программ-тестеров!
В дополнение:
- UNIX - это не Windows, и изживать привычки Windows ой как не просто...
- в UNIX все консольные команды (и API) подчиняются стандартам POSIX, а поэтому первейшее и самое достоверное тестирование любого программного изделия - это тестирование штатными консольными командами...
- если это локальные программы (модули) - то командами: cat, echo, copy, ...
- если это сетевые компоненты, то командами: scp, sftp, nc, ...

В Windows подобное трудно представить (попробуйте командой направить поток в последовательный порт? ;-) ), а здесь это именно так. И только отработав на штатных командах POSIX, имеет смысл переходить на какие-то специально писанные тесты + только в том случае, если штатными тестами что-то проверить невозможно (но чаще такая ситуация - это недостаток изобретательности).

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 17:13

mcrandy писал(а): Беда в том что теперь я совсем не понимаю как работает системный вызов dev_add_pack()?
Никогда не называйте это "системный вызов" - это имя API ядра (или символ ядра ... вызов API ядра, на худой конец).
(дело не в названии, а в том, что за неправильным названием потянется и неправильность в понимании)
mcrandy писал(а): какой буферный сокет я уничтожил?
А здесь история в том, думаю, что вы неправильно толкуете понятие буфера сокетов struct sk_buff. И в этом причина и того, что в примерах в литературе показывают примеры без kfree_skb( skb ).

Я так понимаю это так:
- в прежних версиях версиях ядра 2.4 (и, похоже, ранних 2.6) struct sk_buff действительно представлял собой "пакет", содержащий все заголовки разных уровней (Ethernet, IP, TCP, ...) + сами данные сетевого пакета
- но сейчас struct sk_buff содержит только служебную информацию + указатели на заголовки разных уровней, расположенные в теле пакета, которое находится совсем отдельно
- см. http://www.ibm.com/developerworks/ru/li ... index.html

Код: Выделить всё

typedef unsigned char *sk_buff_data_t; 
struct sk_buff { 
   struct sk_buff *next; /* эти два элемента должны быть объявлены первыми. */ 
   struct sk_buff *prev; 
...
   sk_buff_data_t  transport_header; // заголовок TCP
   sk_buff_data_t  network_header;   // заголовок IP
   sk_buff_data_t  mac_header;       // заголовок MAC
...
   sk_buff_data_t  tail;                      
   sk_buff_data_t  end;              // конец области полного пакета
...
   unsigned char *head,              // начало области полного пакета
                 *data; 
...
}; 
- отсюда происходит и неверный смысл отладочного сообщения в примерах:

Код: Выделить всё

  printk( KERN_INFO "packet received with length: %u\n", skb->len );
- это не есть длина пакета!

Если это всё так, то можно производить сколько угодно клонов сокетного буфера и уничтожать их - при этом сами данные пакета не потеряются и не уничтожаются. На это указывает и то, что среди API ядра операция с сокетными буферами, наряду с alloc_skb, skb_copy, ... - есть и skb_clone.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 18:52

Olej писал(а):Но "логично" не значит "так и есть".
Нужно проверять.
Кроме того, мне непонятна семантика целочисленного значения, возвращаемого обработчиком протокола.
Есть там и ещё один вопрос, принципиально важный:
- если устанавливается несколько функций обработчиков последовательными вызовами dev_add_pack(), то в какой последовательности они будут вызываться при приёме пакета?
- и отсюда очень важное следствие: новый установленный dev_add_pack() обработчик будет вызываться раньше дефаултной обработки (так казалось бы должно быть).

Сделал я дополнительный пример, в котором устанавливаются 2 обработчика с похожими функциями фильтрации:

Код: Выделить всё

int test_pack_rcv_1( struct sk_buff *skb, struct net_device *dev,
                    struct packet_type *pt, struct net_device *odev ) {
   LOG( "filter function #1: %p\n", skb );
   kfree_skb( skb );
   return skb->len;
};


И вот что в результате:

Код: Выделить всё

[olej@fedora netproto]$ sudo insmod net_proto2.ko link=p7p1
[olej@fedora netproto]$ dmesg | tail -n1 | grep !
[ 9097.858345] ! module net_proto2 loaded for link p7p1

Код: Выделить всё

Теперь с другого хоста:
[olej@nvidia ~]$ ping 192.168.56.3
PING 192.168.56.3 (192.168.56.3) 56(84) bytes of data.
64 bytes from 192.168.56.3: icmp_req=1 ttl=64 time=0.508 ms
64 bytes from 192.168.56.3: icmp_req=2 ttl=64 time=0.304 ms
64 bytes from 192.168.56.3: icmp_req=3 ttl=64 time=0.290 ms
^C
--- 192.168.56.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.290/0.367/0.508/0.100 ms
И в итоге:

Код: Выделить всё

[olej@fedora netproto]$ sudo rmmod net_proto2
[olej@fedora netproto]$ dmesg | tail -n8 | grep !
[ 9097.858345] ! module net_proto2 loaded for link p7p1
[ 9142.121590] ! filter function #2: ed9bbcc0
[ 9142.121596] ! filter function #1: ed9bbcc0
[ 9143.121499] ! filter function #2: ed9bbd80
[ 9143.121504] ! filter function #1: ed9bbd80
[ 9144.121650] ! filter function #2: ed9bb0c0
[ 9144.121655] ! filter function #1: ed9bb0c0
[ 9257.459095] ! module net_proto2 unloaded
1. вызываются оба обработчика...
2. вызываются они в порядке обратном установке (позже установленный вызывается первым)...
3. т.е. все они будут вызваны раньше дефаултной обработки
4. но!!! функции обработчики получают одинаковый адрес struct sk_buff, т.е. указатель на одну копию :-o
5. а kfree_skb() выполненная дважды над одной структурой - не приводит к краху системы (как это было бы при kfree())
6. нужно смотреть код в ядре реализации kfree_skb().
Вложения
netproto.tgz
(6.11 КБ) 504 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 19:31

Olej писал(а):6. нужно смотреть код в ядре реализации kfree_skb().
http://lxr.free-electrons.com/source/ne ... uff.c#L624

Код: Выделить всё

610 void __kfree_skb(struct sk_buff *skb)
611 {
612         skb_release_all(skb);
613         kfree_skbmem(skb);
614 }
...
617 /**
618  *      kfree_skb - free an sk_buff
619  *      @skb: buffer to free
620  *
621  *      Drop a reference to the buffer and free it if the usage count has
622  *      hit zero.
623  */
624 void kfree_skb(struct sk_buff *skb)
625 {
626         if (unlikely(!skb))
627                 return;
628         if (likely(atomic_read(&skb->users) == 1))
629                 smp_rmb();
630         else if (likely(!atomic_dec_and_test(&skb->users)))
631                 return;
632         trace_kfree_skb(skb, __builtin_return_address(0));
633         __kfree_skb(skb);
634 }
635 EXPORT_SYMBOL(kfree_skb);
Даже не глядя в код, по комментариям ... - если число использования struct sk_buff становится нулевым, тогда буфер освобождается, а иначе число использования декрементируется (грубо).

Т.е. здесь объяснение необходимости kfree_skb():
- если вы установили новый обработчик, то skb->users увеличивается...
- если обработчик не делает kfree_skb(), то к дефаултному (последнему) обработчику struct sk_buff приходит с user > 1 ...
- а после этого дефаултного обработчика удалять буфер уже некому и уменьшить user уже некому.

P.S. Характерно, что среди API ядра операций с сокетными буферами есть alloc_skb, но нет ничего подобного destroy_skb ... потому, что в самом теле struct sk_buff определяется функция-деструктор:

Код: Выделить всё

...
void (*destructor)( struct sk_buff* );
...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 25 янв 2013, 20:23

Olej писал(а):И только отработав на штатных командах POSIX, имеет смысл переходить на какие-то специально писанные тесты + только в том случае, если штатными тестами что-то проверить невозможно
Один из лучших естественных тестов для обсуждаемой нами вашей задачи - это утилита nc - сетевой cat.

Вот как это может выглядеть:

- на удалённом хосте (на хостовой машине VM) делаем:

Код: Выделить всё

[olej@nvidia _TMP]$ echo 0123456789 > dg.txt
[olej@nvidia _TMP]$ cat dg.txt | nc -l 12345
- на отрабатываемой (модуль net_proto2.ko) машине (VM):

Код: Выделить всё

[olej@fedora netproto]$ nc 192.168.56.1 12345 > file.txt
[olej@fedora netproto]$ cat file.txt 
0123456789
- и сообщения протокольных обработчиков модуля:

Код: Выделить всё

[olej@fedora netproto]$ dmesg | tail -n20
[15252.925763] ! filter function #2: eb50d600
[15252.925764] ! filter function #1: eb50d600
[15346.592897] ! filter function #2: ed4f4000
[15346.592902] ! filter function #1: ed4f4000
[15404.210491] ! filter function #2: ed4f4000
[15404.210495] ! filter function #1: ed4f4000
[15404.210753] ! filter function #2: ed431900
[15404.210756] ! filter function #1: ed431900
[15404.210808] ! filter function #2: ed431f00
[15404.210810] ! filter function #1: ed431f00
[15404.211728] ! filter function #2: ed431f00
[15404.211732] ! filter function #1: ed431f00
[15505.423315] ! filter function #2: eb50d6c0
[15505.423319] ! filter function #1: eb50d6c0
[15505.423492] ! filter function #2: eb50d780
[15505.423493] ! filter function #1: eb50d780
[15505.423528] ! filter function #2: eb50d3c0
[15505.423529] ! filter function #1: eb50d3c0
[15505.423775] ! filter function #2: eb50d6c0
[15505.423777] ! filter function #1: eb50d6c0

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Модуль ядра Linux. Виртуальный сетевой интерфейс.

Непрочитанное сообщение Olej » 26 янв 2013, 18:03

Olej писал(а): Т.е. здесь объяснение необходимости kfree_skb():
- если вы установили новый обработчик, то skb->users увеличивается...
- если обработчик не делает kfree_skb(), то к дефаултному (последнему) обработчику struct sk_buff приходит с user > 1 ...
- а после этого дефаултного обработчика удалять буфер уже некому и уменьшить user уже некому.
В тему - http://www.osp.ru/os/2001/12/180759/:
При более внимательном изучении процедуры net_rx_action можно заметить еще два интересных момента. Один из них заключается в том, что можно зарегистрировать модуль «для всех протоколов» и пользоваться им для анализа всех пакетов. Второй, более тонкий, заключается в том, что пакет не отдается немедленно тому протоколу, тип которого соответствует нужному. Сначала делается попытка найти еще одно соответствие и только потом принимается решение о передаче пакета протокольной процедуре. Зачем? Для того чтобы оптимизировать передачу данных. Если имеется более одной записи для данного пакета (т.е. пакет придется отдать более чем одной процедуре/протоколу), то перед передачей выполняется действие atomic_inc(&skb->users), чтобы установить режим работы copy on write («копирование при записи»). Если одна из процедур решит, что это не ее пакет, то она освободит его, передав в полное распоряжение другой; если же нет, произойдет клонирование пакетов.

Ответить

Вернуться в «Linux изнутри»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 5 гостей