NETLINK Как интерфейс ядра Linux Описание протокола Семейство сокетов Netlink - это интерфейс ядра Linux, используемый для межпроцессных коммуникаций (IPC) между ядром и процессами пользовательского пространства, а также пользовательскими процессами, примерно таким же способом, как при использовании доменных сокетов Unix (семейство AF_UNIX). Подобно им, сокеты Netlink не могут использоваться для взаимодействия процессов по сети, как сокеты AF_INET, но используют в качестве адресов не имена из пространства файловой системы, а идентификаторы процессов (PID). Описание протокола Для обращения к сокетам Netlink используется семейство AF_NETLINK. Хотя сокеты Netlink были разработаны Алексеем Кузнецовым еще для версии ядра 2.0, русскоязычные описания практически отсутствуют, за исключением перевода страницы man и перевода краткого описания протокола. Примеры кода, использующего сокеты Netlink, опубликованы в двух русскоязычных статьях, а также статьях в LinuxJournal и stackoverflow. Описание протокола Вначале рассмотрим описание протокола, затем пользовательский интерфейс сокета, а далее интерфейс ядра. Netlink обеспечивает для приложений сервис передачи дейтаграмм. Для netlink допустимо указывать тип сокета SOCK_RAW и SOCK_DGRAM и протокол не делает между ними различий. Отметим, что использовать функции протокола netlink можно через библиотеку libnetlink, нежели напрямую через интерфейс с ядром. Описание протокола Однако она имеет запутанный и плохо документированный API, который любит часто меняться (от ядра к ядру), что требует изменений и в приложениях, использующих эту библиотеку. Включаемый файл netlink.h содержит определения нескольких стандартных макросов для доступа или создания дейтаграмм netlink. Для доступа к буферам, передаваемым и принимаемым сокетом netlink, следует использовать только макросы из этого файла, которые кратко описаны в таблице 1. Описание протокола #include <asm/types.h> #include <linux/netlink.h> int NLMSG_ALIGN(size_t len); int NLMSG_LENGTH(size_t len); int NLMSG_SPACE(size_t len); void *NLMSG_DATA(struct nlmsghdr *nlh); struct nlmsghdr *NLMSG_NEXT(struct nlmsghdr *nlh, int len); int NLMSG_OK(struct nlmsghdr *nlh, int len); int NLMSG_PAYLOAD(struct nlmsghdr *nlh, int len); Описание протокола Имя Описание NLMSG_ALIGN Округляет размер сообщения netlink до ближайшего большего значения, выровненного по границе. NLMSG_LENGTH Принимает в качестве параметра размер поля данных (payload) и возвращает выровненное по границе значение размера для записи в поле nlmsg_len заголовка nlmsghdr. NLMSG_SPACE Возвращает размер, который займут данные указанной длины в пакете netlink. Описание протокола Имя Описание NLMSG_DATA Возвращает указатель на данные, связанные с переданным заголовком nlmsghdr. NLMSG_OK Возвращает значение TRUE (1), если сообщение не было усечено и его разборка прошла успешно. NLMSG_PAYLO Возвращает размер данных (payload), AD связанных с заголовком nlmsghdr. Описание протокола Имя Описание NLMSG_NEXT Возвращает следующую часть сообщения, состоящего из множества частей. Макрос принимает следующий заголовок nlmsghdr в сообщении, состоящем из множества частей. Вызывающее приложение должно проверить наличие в текущем заголовке nlmsghdr флага NLMSG_DONE – функция не возвращает значение NULL при завершении обработки сообщения. Второй параметр задает размер оставшейся части буфера сообщения. Макрос уменьшает это значение на размер заголовка сообщения. Описание протокола Семейство netlink_family выбирает модуль ядра или группу netlink для обмена информацией. Члены семейства перечислены в таблице 2. Сообщения netlink представляют собой поток байтов с одним или несколькими заголовками nlmsghdr и связанными с ними данными (payload). В сообщениях, состоящих из множества частей все заголовки, за исключением последнего содержат флаг NLM_F_MULTI, а в заголовке последнего сообщения установлен флаг NLMSG_DONE. Для доступа к байтовым потокам следует использовать только макросы NLMSG_*. Описание протокола Имя NETLINK_ ROUTE Описание Принимает обновления маршрутов и может использоваться для модификации маршрутной таблицы IPv4. NETLINK_ Зарезервирован для новых протоколов USERSOCK пользовательского пространства. NETLINK_ Принимает пакеты от межсетевых FIREWALL экранов IPv4. Описание протокола Имя Описание NETLINK_ TCPDIAG Мониторинг сокета TCP. NETLINK_ XFRM IPsec. NETLINK_ ARPD Используется таблицами arp пространстве. для управления в пользовательском Описание протокола Имя Описание NETLINK_ ROUTE6 Принимает и передает обновления таблицы маршрутов IPv6 (af_inet6). NETLINK_ IP6_FW Служит для приема сообщений о неудачном результате проверки правил на брандмауэре IPv6 (пока не реализован). Описание протокола Имя NETLINK_ TAPBASE ... NETLINK_ TAPBASE+15 Описание Экземпляры фиктивного устройства ethertap, позволяющее имитировать драйвер Ethernet из пользовательского пространства. Описание протокола Протокол netlink не обеспечивает гарантирован- ной доставки сообщений, но пытаясь приложить все усилия для доставки сообщения адресату. При нехватке памяти или возникновении иных ошибок протокол может отбрасывать пакеты. Для обеспечения гарантированной доставки отправитель может запрашивать у получателя подтверждение, устанавливая в заголовке флаг NLM_F_ACK. В качестве подтверждений используются пакеты NLMSG_ERROR с кодом ошибки 0. Функции генерации подтверждений должно обеспечивать пользовательское приложение. Описание протокола Ядро пытается передавать сообщения NLMSG_ERROR для каждого поврежденного пакета. Пользовательским программам следует придерживаться такой же практики. Каждое семейство netlink имеет свой набор из 32 multicast-групп. При вызове для сокета функции bind поле nl_groups в структуре sockaddr_nl должно содержать битовую маску групп, которым следует слышать сообщение. По умолчанию для этого поля установлено нулевое значение, которое отключает групповую передачу сообщений. Описание протокола Сокет может передавать групповые сообщения любым группам, установив в поле nl_groups битовую маску нужных групп перед вызовом функции sendmsg или connect. Возможность работы (приема или передачи) с групповыми сообщениями netlink имеют лишь приложения с флагом возможностей CAP_NET_ADMIN и программы, запущенные пользователем с эффективным идентификатором UID=0. Все отклики на групповые сообщения должны передаваться процессу-отправителю и членам группы. Описание протокола Структура заголовка сообщений netlink: struct nlmsghdr { __u32 nlmsg_len; /* размер сообщения с учетом заголовка */ __u16 nlmsg_type; /* тип сообщения (содержимое) */ __u16 nlmsg_flags;/* стандартные и дополнительные флаги */ __u32 nlmsg_seq; /* порядковый номер */ __u32 nlmsg_pid; /* Идентификатор процесса (PID), открывшего сокет */ }; Описание протокола Сообщения об ошибках имеют структуру: struct nlmsgerr { error; /* отрицательное значение кода ошибки или 0 для подтверждений */ struct nlmsghdr msg; /* заголовок сообщения, связанного с ошибкой */ }; После каждого заголовка nlmsghdr размещаются данные, указанного параметром nlmsg_type типа: int Описание протокола NLMSG_NOOP – пустое сообщение (игнорируется); NLMSG_ERROR – сообщение об ошибке, содержащее в поле данных структуру nlmsgerr; NLMSG_DONE – последняя часть сообщения. Члены семейства netlink могут поддерживать дополнительные типы сообщений, описанные с соответствующих страницах руководства и доступных с помощью команды man (например, man 7 rtnetlink для NETLINK_ROUTE). Основные флаги сообщений netlink, передаваемые в поле nlmsg_flags: Описание протокола Флаг Описание NLM_F_ Устанавливается для всех запросов. REQUEST NLM_F_ MULTI Сообщение является частью составного сообщения, завершающегося флагом NLMSG_DONE. NLM_F_ ACK Отклик с подтверждением успеха. NLM_F_ ECHO Запрос эхо-отклика. Описание протокола Адреса netlink для пользовательских программ и модулей ядра описываются структурой sockaddr_nl. Заданный такой структурой адрес может быть индивидуальным (unicast) или групповым. struct sockaddr_nl { sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* заполнение нулями */ pid_t nl_pid; /* идентификатор процесса */ __u32 nl_groups; /* маска групп */ }; Описание протокола Поле nl_pid содержит идентификатор процесса, владеющего сокетом-адресатом или 0, если сообщение адресовано ядру. Параметр nl_groups содержит маску, каждый бит которой представляет одну из групп netlink. При вызове bind() для сокета netlink следует указывать битовую маску группы, которую желает прослушивать приложение, в данном контексте. Различные группы могут быть объединены с помощью логического или (|). Основные группы определены в заголовочном файле netlink. Пример некоторых из них: Описание протокола – эта группа получает уведомления об изменениях в сетевых интерфейсах (интерфейс удалился, добавился, опустился, поднялся) RTMGRP_IPV4_IFADDR – эта группа получает уведомления об изменениях в IPv4 адресах интерфейсов (адрес был добавлен или удален) RTMGRP_IPV6_IFADDR – эта группа получает уведомления об изменениях в IPv6 адресах интерфейсов (адрес был добавлен или удален) RTMGRP_LINK Описание протокола RTMGRP_IPV4_ROUTE – эта группа получает уведомления об изменениях в таблице маршрутизации для IPv4 адресов RTMGRP_IPV6_ROUTE – эта группа получает уведомления об изменениях в таблице маршрутизации для IPv6 адресов После структуры заголовка nlmsghdr всегда расположен указатель на блок данных. Доступ к нему можно получить с помощью макросов, упомянутых выше. Пользовательское приложение Рассмотрим небольшое приложение, которое будет получать уведомления об изменениях в сетевых интерфейсах и таблице маршрутизации. В этой программе создается netlink сокет и проверяется успешность его создания. int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); // создаем нужный сокет if (fd < 0) { printf("Ошибка создания netlink сокета: %s", (char*)strerror(errno)); return 1; } Пользовательское приложение Далее происходит объявление необходимых переменных и заполнение структуры локального адреса. Тут мы указываем группы сообщений, на которые хотим подписаться: RTMGRP_LINK, RTMGRP_IPV4_IFADDR, RTMGRP_IPV4_ROUTE. Так же объявляем структуру сообщения iov и связываем с ней буфер данных. struct sockaddr_nl local; // локальный адрес char buf[8192]; // буфер сообщения struct iovec iov; // структура сообщения iov.iov_base = buf; // указываем buf в качестве буфера сообщения для iov Пользовательское приложение iov.iov_len = sizeof(buf); // указываем размер буфера memset(&local, 0, sizeof(local)); // очищаем структуру local.nl_family = AF_NETLINK; // указываем семейство протокола local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; // указываем необходимые группы local.nl_pid = getpid(); //указываем PID данного приложения Пользовательское приложение // структура сообщения netlink – инициализируем все поля struct msghdr msg; { msg.msg_name = &local; // задаем имя – структуру локального адреса msg.msg_namelen = sizeof(local); // указываем размер msg.msg_iov = &iov; // указываем вектор данных сообщения msg.msg_iovlen = 1; // задаем длину вектора } Пользовательское приложение После этого происходит связывание с сокетом, с помощью bind(). После этого мы становимся подписанными на сообщения для указанных групп. if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) { // связываемся с сокетом printf("Ошибка связывания с netlink сокетом: %s", (char*)strerror(errno)); close(fd); return 1; } Пользовательское приложение Далее следует бесконечный цикл приема сообщений из сокета. // читаем и разбираем сообщения из сокета while (1) { ssize_t status = recvmsg(fd, &msg, MSG_DONTWAIT); // прием сообщений для указанных групп if (status < 0) { if (errno == EINTR || errno == EAGAIN) { usleep(250000); continue; } Пользовательское приложение printf("Ошибка связывания приема сообщения netlink: %s", (char*)strerror(errno)); continue; } Так как принимаемый блок данных может иметь несколько заголовков и ассоциированных с ними данных – начинаем перебирать, с помощью netlink макросов все принятые данные. Каждое новое сообщение расположено по указателю struct nlmsghdr *h. struct nlmsghdr *h; // указатель на заголовок сообщения Пользовательское приложение for (h = (struct nlmsghdr*)buf; status >= (ssize_t)sizeof(*h); ) { // для всех заголовков сообщений int len = h–>nlmsg_len; // длина всего блока int l = len – sizeof(*h); // длина текущего сообщения char *ifName; // имя соединения if ((l < 0) || (len > status)) { printf("Некорректная длина сообщения: %i", len); continue; } Пользовательское приложение Теперь можно разбирать собственно сообщение. Смотрим на поле nlmsg_type и выясняем, что за сообщение. Если оно связано с таблицей маршрутизации – печатаем сообщение и идем к следующему сообщению. А если нет – начинаем детально разбираться. if ((h–>nlmsg_type == RTM_NEWROUTE) || (h– >nlmsg_type == RTM_DELROUTE)) { // если это изменения роутов – печатаем сообщение printf("Произошли изменения в таблице маршрутизации\n"); Пользовательское приложение } else { // в остальных случаях начинаем более детально разбираться char *ifUpp; // состояние устройства char *ifRunn; // состояние соединения struct ifinfomsg *ifi; // указатель на структуру, содержащую данные о сетевом подключении struct rtattr *tb[IFLA_MAX + 1]; // массив атрибутов соединения, IFLA_MAX определен в rtnetlink.h ifi = (struct ifinfomsg*) NLMSG_DATA(h); // получаем информацию о сетевом соединении, в котором произошли изменения Пользовательское приложение Рассмотрим кратко упомянутые структуры: struct ifinfomsg { unsigned char ifi_family; // семейство (AF_UNSPEC) unsigned short ifi_type; // тип устройства int ifi_index; // индекс интерфейса unsigned int ifi_flags; // флаги устройства unsigned int ifi_change; // маска смены, всегда должно быть равно 0xFFFFFFFF } Эта структура используется для представления сетевого устройства, его семейства, типа, индекса и флагов. Пользовательское приложение struct ifaddrmsg { unsigned char ifa_family; // Тип адреса (AF_INET или AF_INET6) unsigned char ifa_prefixlen; // Длина префикса адреса (длина сетевой маски) unsigned char ifa_flags; // Флаги адреса unsigned char ifa_scope; // Область адреса int ifa_index; // Индекс интерфейса, равен аналогичному полю в ifinfomsg } Эта структура служит для представления сетевого адреса, назначенного на сетевой интерфейс. Пользовательское приложение struct rtattr unsigned short rta_len; // Длина опции unsigned short rta_type; // Тип опции /* данные */ } Эта структура* служит для хранения, какого–либо параметра соединения или адреса. Объявляются массивы опций rtattr, куда будут складываться все необходимые данные. За получение этих данных отвечает вспомогательная функция parseRtattr. Пользовательское приложение Она использует макросы Netlink и заполняет указанный массив всеми атрибутами из блока данных структуры ifinfomsg или ifaddrmsg. parseRtattr(tba, IFA_MAX, IFA_RTA(ifa), h– >nlmsg_len); После того как мы получили массивы, заполненные атрибутами – можем работать с этим значениями, анализировать их, печатать. Доступ к каждому атрибуту осуществляется по его индексу. Все индексы определены в заголовочных файлах netlink и прокомментированы. В данном случае мы используем следующие индексы: Пользовательское приложение IFLA_IFNAME – индекс атрибута с именем интерфейса. IFA_LOCAL – индекс атрибута с локальным IP адресом. После всего этого мы обладаем полной информацией о том, что произошло и можем печатать информацию на экран. switch (h–>nlmsg_type) { //что конкретно произошло case RTM_DELADDR: printf("Был удален адрес интерфейса %s\n", ifName); break; Пользовательское приложение case RTM_DELLINK: printf("Был удален интерфейс %s\n", ifName); break; case RTM_NEWLINK: printf("Новый интерфейс %s, состояние интерфейса %s %s\n", ifName, ifUpp, ifRunn); break; case RTM_NEWADDR: printf("Был добавлен адрес для интерфейса %s: %s\n", ifName, ifAddress); break; } NETLINK API В ПРОСТРАНСТВЕ ЯДРА Для подключения API в модуле ядра потребуется подключение файлов из заголовков ядра, как минимум, <linux/netlink.h>. Затем необходимо выбрать СВОБОДНЫЙ (не занятый) тип протокола, одинаковый в ядре и прикладной программе, например: #define NETLINK_TEST 17 В пользовательском пространстве мы используем функцию socket() для создания сокета netlink, а в модуле ядра – иначе: NETLINK API В ПРОСТРАНСТВЕ ЯДРА struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)); Параметр unit – зто тип протокола netlink, например, NETLINK_TEST. Указатель на функцию input – это обратный вызов (callback), выполняемый, когда в сокет netlink приходит сообщение. Необходимо отметить, что в разных версиях ядра эта функция имеет разные прототипы, Например, в ядре 2.6.32 прототип (из файла /usr/src/linux/include/linux/netlink.h): NETLINK API В ПРОСТРАНСТВЕ ЯДРА struct sock *netlink_kernel_create(struct net *net, int unit,unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex, struct module *module); Здесь первый параметр определен в af_netlink.h как &init_net, затем указывается тип протокола, группа подписчиков multicast-сообщений (для unicast, то есть обмена сообщениями 1:1 необходимо указывать 0), затем функция обратного вызова, мьютекс для ее блокировки (при отсутствии – NULL), имя модуля, в котором находится callback-функция (если в этом же, то пишем THIS_MODULE). NETLINK API В ПРОСТРАНСТВЕ ЯДРА Прототип callback-функции определен заранее, если он не будет соответствовать, то при компиляции модуля возникнет предупреждение. Эта функция, как правило, вызывается при инициализации модуля, поэтому возвращаемое функцией netlink_kernel_create значение должно быть заранее объявлено глобально: static struct sock *nl_sk = NULL; В деструкторе модуля созданный сокет должен быть уничтожен: netlink_kernel_release(nl_sk); NETLINK API В ПРОСТРАНСТВЕ ЯДРА Из переданного в callback-функцию буфера сокета struct sk_buff *skb необходимо извлечь заголовок и данные пришедшего сообщения: struct nlmsghdr *nlh = NULL; nlh = (struct nlmsghdr *)skb->data; printk(KERN_INFO "received netlink message payload: %s\n", NLMSG_DATA(nlh)); Этого вполне достаточно для приема модулем ядра сообщений через сокет netlink сообщений пользователя. Приведем полный код простейшего модуля ядра для приема сообщений: NETLINK API В ПРОСТРАНСТВЕ ЯДРА #include <linux/module.h> #include <linux/kernel.h> # include <linux/netlink.h> #include <linux/skbuff.h> #define NETLINK_USER 31 struct sock *nl_sk = NULL; static void nl_data_ready (struct sk_buff *skb) { struct nlmsghdr *nlh = NULL; nlh = (struct nlmsghdr *)skb->data; printk(KERN_INFO "received netlink message payload: %s\n", NLMSG_DATA(nlh)); } NETLINK API В ПРОСТРАНСТВЕ ЯДРА static int __init my_module_init(void) { printk(KERN_INFO "Initializing Netlink Socket"); nl_sk = netlink_kernel_create(&init_net, NETLINK_USER,0, nl_data_ready,NULL, THIS_MODULE); return 0; } static void __exit my_module_exit(void) { printk(KERN_INFO "Goodbye"); netlink_kernel_release(nl_sk); } module_init(my_module_init); module_exit(my_module_exit); NETLINK API В ПРОСТРАНСТВЕ ЯДРА В пользовательском приложении происходит только отправка данных: #include <sys/types.h> #include <sys/socket.h> #include <linux/netlink.h> #define NETLINK_USER 31 #define MAX_PAYLOAD 2048 int main(int argc, char *argv[]) { struct sockaddr_nl s_nladdr, d_nladdr; struct msghdr msg; struct nlmsghdr *nlh=NULL ; struct iovec iov; NETLINK API В ПРОСТРАНСТВЕ ЯДРА int fd=socket(AF_NETLINK ,SOCK_DGRAM , NETLINK_USER); /* source address */ memset(&s_nladdr, 0 ,sizeof(s_nladdr)); s_nladdr.nl_family= AF_NETLINK ; s_nladdr.nl_pad=0; s_nladdr.nl_pid = getpid(); bind (fd, (struct sockaddr*) &s_nladdr, sizeof (s_nladdr)); /* destination address */ memset(&d_nladdr, 0 ,sizeof(d_nladdr)); d_nladdr.nl_family= AF_NETLINK; NETLINK API В ПРОСТРАНСТВЕ ЯДРА d_nladdr.nl_pad=0; d_nladdr.nl_pid = 0; /* destined to kernel */ /* Fill the netlink message header */ nlh = (struct nlmsghdr *)malloc(100); memset(nlh , 0 , 100); strcpy(NLMSG_DATA(nlh), " Mr. Kernel, Are you ready ?" ); nlh->nlmsg_len =100; nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; nlh->nlmsg_type = 0; NETLINK API В ПРОСТРАНСТВЕ ЯДРА /*iov structure */ iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; /* msg */ memset(&msg,0,sizeof(msg)); msg.msg_name = (void *) &d_nladdr ; msg.msg_namelen=sizeof(d_nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; /* send msg */ sendmsg(fd, &msg, 0); NETLINK API В ПРОСТРАНСТВЕ ЯДРА printf(" Send message payload: %s\n", (char *)NLMSG_DATA(nlh)); close(fd); return (EXIT_SUCCESS); } Для полноценного обмена данными модуль должен уметь отправлять ответные сообщения. В той же функции обратного вызова необходимо создать ответное сообщение, заполнить поля его заголовка и отправить ответ процессу, инициировавшему соединение: NETLINK API В ПРОСТРАНСТВЕ ЯДРА struct sk_buff *skb_out; pid = nlh->nlmsg_pid; /*pid of sending process */ skb_out = nlmsg_new(msg_size, 0); /* new msg filled zeros*/ nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */ res = nlmsg_unicast(nl_sk, skb_out, pid); /*snd msg*/ Здесь не очевидны параметры функции nlmsg_put: NETLINK API В ПРОСТРАНСТВЕ ЯДРА struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int payload, int flags) Первый из них – новый буфер сетевого пакета, затем указан pid отправителя (0 – это ядро), seq: порядковый номер сообщения, type: тип сообщения, payload: длина передаваемых данных (полезной нагрузки), flags: флаги сообщений. Возвращает NULL, если для skb пакета выделено меньше памяти, чем необходимо для размещения заголовка и данных netlink сообщения, иначе указатель на netlink сообщение. Что до функции отправки, то ее параметры очевидны. NETLINK API В ПРОСТРАНСТВЕ ЯДРА В ядрах версий свыше 3.0 группа и pid получателя задаются дополнительно в свойствах буфера: NETLINK_CB(skb_out).dst_groups = dst_groups; NETLINK_CB(skb_out).dst_pid = dst_pid; Для получения сообщения из ядра пользовательское приложение использует стандартную функцию приема UDP-пакетов: recvmsg(fd, &msg, 0); NETLINK API В ПРОСТРАНСТВЕ ЯДРА Кроме соединения в формате 1 к 1 процесса с модулем ядра, возможна и рассылка групповых сообщений (1 к N): void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, int allocation); Первые 3 параметра очевидны, а группа является объединением по логическому ИЛИ масок всех мультикастовых групп. Поскольку netlink не использует порты, то группа – это их ближайший аналог. Номер группы должен не превышать числа 32. Последний параметр, allocation – это тип выделяемой ядром памяти. NETLINK API В ПРОСТРАНСТВЕ ЯДРА Обычно в контексте обработки прерываний используется GFP_ATOMIC и GFP_KERNEL в остальных случаях. В прикладной программе необходимо указать номер группы ДО связывания (bind) сокета с адресом: addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); addr.nl_groups = MYMGRP; Если в структуре адреса такое поле отсутствует (в зависимости от версии ядра), то приходится поступать иначе: NETLINK API В ПРОСТРАНСТВЕ ЯДРА #define SOL_NETLINK 270 setsockopt(sock, SOL_NETLINK, NETLINK_ ADD_MEMBERSHIP, &group, sizeof(group)); Проверку наличия получателей групповых сообщений можно проверить функцией netlink_has_listeners(struct sock *sk, unsigned int group); параметры которой достаточно очевидны. NETLINK API В ПРОСТРАНСТВЕ ЯДРА В новых версиях ядра (выше 3.0) для групповой рассылки сообщений используется функция nlmsg_multicast(nl_sk, skb, pid, group, GFP_KERNEL); В них же предлагается для снятия ограничения в 32 группы на одном протоколе использовать более продвинутую библиотеку Generic Netlink (GeNetLink), которая еще менее документированная и еще более запутанная…