Содержание


Разработка модулей ядра Linux

Часть 34. Протоколы сетевого и транспортного уровней

Comments

Серия контента:

Этот контент является частью # из серии # статей: Разработка модулей ядра Linux

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Разработка модулей ядра Linux

Следите за выходом новых статей этой серии.

В данной статье рассматриваются возможности для работы с протоколами сетевого и транспортного уровней при создании собственных сетевых средств на основе программной техники модулей ядра.

Протокол сетевого уровня

  • На этом уровне обеспечивается обработка таких протоколов, как: IP/IPv4/IPv6, IPX, ICMP, RIP, OSPF, ARP, или добавление оригинальных пользовательских протоколов. Для установки обработчиков сетевого уровня предоставляется API сетевого уровня, объявленный в файле <linux/netdevice.h>.
  • struct packet_type { 
       __be16 type; /* This is really htons(ether_type). */ 
       struct net_device *dev; /* NULL is wildcarded here    */ 
       int (*func) ( struct sk_buff *, struct net_device *, 
                     struct packet_type *, struct net_device *); 
    ...
       struct list_head list; 
    }; 
    extern void dev_add_pack( struct packet_type *pt ); 
    extern void dev_remove_pack( struct packet_type *pt );

Фактически, в модули, работающие с сетевыми или транспортными протоколами, необходимо добавлять фильтр, через который будут проходить буфера сокетов из входящего потока интерфейса (пример реализации исходящего потока был представлен в предыдущих статьях). В нашем случае в подобную функцию будут направляться буфера сокетов, удовлетворяющие критериям, заложенным в структуре struct packet_type.

В архиве network.tgz можно найти примеры добавления собственных обработчиков сетевых протоколов. В листинге 1 представлен обработчик для нового протокола сетевого уровня.

Листинг 1. Пример реализации протокола сетевого уровня (файл net_proto.c)
#include <linux/module.h> 
#include <linux/init.h> 
#include <linux/netdevice.h> 

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 ); 
   return skb->len; 
}; 

#define TEST_PROTO_ID 0x1234 
static struct packet_type test_proto = { 
   __constant_htons( ETH_P_ALL ),  // может использоваться другое значение TEST_PROTO_ID
   NULL, 
   test_pack_rcv, 
   (void*)1, 
   NULL 
}; 

static int __init my_init( void ) { 
   dev_add_pack( &test_proto ); 
   printk( KERN_INFO "module loaded\n" ); 
   return 0; 
} 

static void __exit my_exit( void ) { 
   dev_remove_pack( &test_proto ); 
   printk( KERN_INFO "module unloaded\n" ); 
} 

module_init( my_init ); 
module_exit( my_exit ); 

MODULE_AUTHOR( "Oleg Tsiliuric" ); 
MODULE_LICENSE( "GPL v2" );

Основную сложность при работе с подобными модулями представляет процесс их тестирования, так как операционная система может не знать такого сетевого протокола и, следовательно, не иметь утилит для обмена данными в формате тестируемого протокола. Установим созданный нами модуль и проверим его работу, как показано ниже.

$ sudo insmod net_proto.ko 
$ dmesg | tail -n6 
module loaded 
packet received with length: 74 
packet received with length: 60 
packet received with length: 66 
packet received with length: 241 
packet received with length: 52 
$ sudo rmmod net_proto

В этом примере обработчик протокола перехватывает (фильтрует) все пакеты (см. константу ETH_P_ALL в листинге 1) на всех сетевых интерфейсах. При добавлении собственного протокола вместо значения ETH_P_ALL должно было бы использоваться значение TEST_PROTO_ID, но в таком случае у нас бы не нашлось инструментов для тестирования модуля. Большое количество идентификаторов протоколов (Ethernet Protocol ID's) можно найти в файле <linux/if_ether.h>, а наиболее интересные из них приведены ниже:

#define ETH_P_LOOP   0x0060  /* Ethernet Loopback packet  */
...
#define ETH_P_IP     0x0800  /* Internet Protocol packet  */
...
#define ETH_P_ARP    0x0806  /* Address Resolution packet */
...
#define ETH_P_PAE    0x888E  /* Port Access Entity (IEEE 802.1X) */
...
#define ETH_P_ALL    0x0003  /* Every packet (be careful!!!) */
...

Здесь же можно найти описание заголовка Ethernet-пакета, который используется при заполнении структуры struct packet_type:

struct ethhdr { 
   unsigned char h_dest[ETH_ALEN];   /* ether-адрес получателя */ 
   unsigned char h_source[ETH_ALEN]; /* ether-адрес отправителя    */ 
   __be16       h_proto;             /* идентфикатор типа пакета */ 
} __attribute__((packed));

Протокол транспортного уровня

На этом уровне обеспечивается обработка таких IP-протоколов, как: UDP, TCP, SCTP и т.д., которые описаны в файле <linux/in.h>.

/* Standard well-defined IP protocols.  */ 
enum { 
  IPPROTO_IP =     0,   /* Dummy protocol for TCP               */ 
  IPPROTO_ICMP =   1,   /* Internet Control Message Protocol    */ 
  IPPROTO_IGMP =   2,   /* Internet Group Management Protocol   */ 
...
  IPPROTO_TCP =    6,   /* Transmission Control Protocol        */ 
...
  IPPROTO_UDP =   17,   /* User Datagram Protocol               */ 
... 
  IPPROTO_SCTP = 132,   /* Stream Control Transport Protocol    */ 
... 
  IPPROTO_RAW  = 255,   /* Raw IP packets                       */ 
}

Для установки обработчика протоколов транспортного уровня существует специальный API, объявленный в <net/protocol.h>.

//данная структура используется для регистрации протоколов
struct net_protocol {
   int  (*handler)( struct sk_buff *skb ); 
   void (*err_handler)( struct sk_buff *skb, u32 info ); 
   int  (*gso_send_check)( struct sk_buff *skb ); 
   struct sk_buff *(*gso_segment)( struct sk_buff *skb, int features ); 
   struct sk_buff **(*gro_receive)( struct sk_buff **head, struct sk_buff *skb ); 
   int (*gro_complete)( struct sk_buff *skb ); 
   unsigned int no_policy:1, 
                netns_ok:1; 
};
// второй параметр функций inet_..._protocol – это константа из списка IPPROTO_*
int inet_add_protocol( const struct net_protocol *prot, unsigned char num ); 
int inet_del_protocol( const struct net_protocol *prot, unsigned char num );

В листинге 2 представлен пример модуля, устанавливающего обработчик для протокола транспортного уровня.

Листинг 2. Пример реализации протокола транспортного уровня (файл trn_proto.c)
#include <linux/module.h> 
#include <linux/init.h> 
#include <net/protocol.h> 

int test_proto_rcv( struct sk_buff *skb ) { 
   printk( KERN_INFO "Packet received with length: %u\n", skb->len ); 
   return skb->len; 
}; 

static struct net_protocol test_proto = { 
   .handler = test_proto_rcv, 
   .err_handler = 0, 
   .no_policy = 0, 
}; 

#define PROTO IPPROTO_RAW 
static int __init my_init( void ) { 
   int ret; 
   if( ( ret = inet_add_protocol( &test_proto, PROTO ) ) < 0 ) { 
      printk( KERN_INFO "proto init: can't add protocol\n"); 
      return ret; 
   }; 
   printk( KERN_INFO "proto module loaded\n" ); 
   return 0; 
} 

static void __exit my_exit( void ) { 
   inet_del_protocol( &test_proto, PROTO ); 
   printk( KERN_INFO "proto module unloaded\n" ); 
} 

module_init( my_init ); 
module_exit( my_exit ); 

MODULE_AUTHOR( "Oleg Tsiliuric" ); 
MODULE_LICENSE( "GPL v2" );

Установим и проверим функционирование нашего модуля для протокола IPPROTO_RAW:

$ sudo insmod trn_proto.ko 
$ lsmod | head  -n2 
Module                  Size  Used by 
trn_proto                780  0
$ cat /proc/modules | grep proto 
trn_proto 780 0 - Live 0xf9a26000 
$ ls -R /sys/module/trn_proto
/sys/module/trn_proto: 
holders  initstate  notes  refcnt  sections  srcversion 
...
$ sudo rmmod trn_proto 
$ dmesg | tail -n60 | grep -v ^audit 
proto module loaded 
proto module unloaded

Но если попытаться установить обработчик для уже обрабатываемого (т.е. установленного) протокола (например, IPPROTO_TPC), то возникнет следующая ошибка.

$ sudo insmod trn_proto.ko 
insmod: error inserting 'trn_proto.ko': -1 Operation not permitted 
$ dmesg | tail -n60 | grep -v ^audit 
proto init: can't add protocol 
$ lsmod | grep proto 
$

Как видно, здесь возникла уже упоминавшаяся ранее сложность.

Поэтому если вы планируете реализовать поддержку нового (или не использующегося в системе) протокола, то в системе может не оказаться инструментов для его тестирования, и предварительно необходимо будет создать тестовые приложения прикладного уровня.

  • Если же попытаться смоделировать работу нового протокола под видом уже существующего (например, IPPROTO_UDP), то вам сначала понадобится удалить существующий обработчик данного протокола, что может нарушить работоспособность системы (например, в случае с IPPROTO_UDP такое действие приведет к разрушению системы разрешения доменных имён DNS).

Заключение

В данной статье мы рассмотрели вопросы взаимодействия с протоколами сетевого и транспортного уровней из коду модулей ядра.

В следующей статье мы завершим обсуждение возможностей использования модулей ядра для разработки сетевых интерфейсов.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=844844
ArticleTitle=Разработка модулей ядра Linux: Часть 34. Протоколы сетевого и транспортного уровней
publish-date=11072012