Netlink: интерфейс ядра Linux - презентация

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),
которая
еще
менее
документированная и еще более запутанная…