Интерфейс Netlink

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