Netlink 是一种特别的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功用强壮,现在在最新的 Linux 内核(2.6.14)中运用netlink 进行运用与内核通讯的运用许多,包含:
路由 daemon(NETLINK_ROUTE),
1-wire 子体系(NETLINK_W1),
用户态 socket 协议(NETLINK_USERSOCK),
防火墙(NETLINK_FIREWALL),
socket 监督(NETLINK_INET_DIAG),
netfilter 日志(NETLINK_NFLOG),
ipsec 安全策略(NETLINK_XFRM),
SELinux 事情告诉(NETLINK_SELINUX),
iSCSI 子体系(NETLINK_ISCSI),
进程审计(NETLINK_AUDIT),
转发信息表查询(NETLINK_FIB_LOOKUP),
netlink connector(NETLINK_CONNECTOR),
netfilter 子体系(NETLINK_NETFILTER),
IPv6 防火墙(NETLINK_IP6_FW),
DECnet 路由信息(NETLINK_DNRTMSG),
内核事情向用户态告诉(NETLINK_KOBJECT_UEVENT),
通用 netlink(NETLINK_GENERIC)。
Netlink 是一种在内核与用户运用间进行双向数据传输的非常好的方法,用户态运用运用规范的 socket API 就可以运用 netlink 供给的强壮功用,内核态需求运用专门的内核 API 来运用 netlink。
Netlink 相关于体系调用,ioctl 以及 /proc 文件体系而言具有以下长处:
1,为了运用 netlink,用户仅需求在 include/linux/netlink.h 中添加一个新类型的 netlink 协议界说即可,如 #define NETLINK_MYTEST 17 然后,内核和用户态运用就可以当即经过 socket API 运用该 netlink 协议类型进行数据交换。但体系调用需求添加新的体系调用,ioctl 则需求添加设备或文件, 那需求不少代码,proc 文件体系则需求在 /proc 下添加新的文件或目录,那将使本来就紊乱的 /proc 愈加紊乱。
2. netlink是一种异步通讯机制,在内核与用户态运用之间传递的音讯保存在socket缓存行列中,发送音讯仅仅把音讯保存在接纳者的socket的接 收行列,而不需求等候接纳者收到音讯,但体系调用与 ioctl 则是同步通讯机制,假如传递的数据太长,将影响调度粒度。
3.运用 netlink 的内核部分可以选用模块的方法完结,运用 netlink 的运用部分和内核部分没有编译时依靠,但体系调用就有依靠,并且新的体系调用的完结有必要静态地连接到内核中,它无法在模块中完结,运用新体系调用的运用在编译时需求依靠内核。
4.netlink 支撑多播,内核模块或运用可以把音讯多播给一个netlink组,归于该neilink 组的任何内核模块或运用都能接纳到该音讯,内核事情向用户态的告诉机制就运用了这一特性,任何对内核事情感兴趣的运用都能收到该子体系发送的内核事情,在后面的文章中将介绍这一机制的运用。
5.内核可以运用 netlink 首要建议会话,但体系调用和 ioctl 只能由用户运用建议调用。
6.netlink 运用规范的 socket API,因而很简单运用,但体系调用和 ioctl则需求专门的训练才干运用。
用户态运用 netlink
用户态运用运用规范的socket APIs, socket(), bind(), sendmsg(), recvmsg() 和 close() 就能很简单地运用 netlink socket,查询手册页可以了解这些函数的运用细节,本文仅仅解说运用 netlink 的用户应该怎么运用这些函数。留意,运用 netlink 的运用有必要包含头文件 linux/netlink.h。当然 socket 需求的头文件也必不可少,sys/socket.h。
为了创立一个 netlink socket,用户需求运用如下参数调用 socket():
socket(AF_NETLINK, SOCK_RAW, netlink_type)
第一个参数有必要是 AF_NETLINK 或 PF_NETLINK,在 Linux 中,它们俩实践为一个东西,它表明要运用netlink,第二个参数有必要是SOCK_RAW或SOCK_DGRAM, 第三个参数指定netlink协议类型,如前面讲的用户自界说协议类型NETLINK_MYTEST, NETLINK_GENERIC是一个通用的协议类型,它是专门为用户运用的,因而,用户可以直接运用它,而不用再添加新的协议类型。内核预界说的协议类型有:
#define NETLINK_ROUTE 0 /* Routing/device hook */
#define NETLINK_W1 1 /* 1-wire subsystem */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Firewalling hook */
#define NETLINK_INET_DIAG 4 /* INET socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event noTIficaTIons */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* audiTIng */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet rouTIng messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
关于每一个netlink协议类型,可以有多达 32多播组,每一个多播组用一个位表明,netlink 的多播特性使得发送音讯给同一个组仅需求一次体系调用,因而关于需求多拨音讯的运用而言,大大地降低了体系调用的次数。
函数 bind() 用于把一个翻开的 netlink socket 与 netlink 源 socket 地址绑定在一起。netlink socket 的地址结构如下:
struct sockaddr_nl
{
sa_family_t nl_family;
unsigned short nl_pad;
__u32 nl_pid;
__u32 nl_groups;
};
字段 nl_family 有必要设置为 AF_NETLINK 或着 PF_NETLINK,字段 nl_pad 当时没有运用,因而要总是设置为 0,字段 nl_pid 为接纳或发送音讯的进程的 ID,假如期望内核处理音讯或多播音讯,就把该字段设置为 0,不然设置为处理音讯的进程 ID。字段 nl_groups 用于指定多播组,bind 函数用于把调用进程参加到该字段指定的多播组,假如设置为 0,表明调用者不参加任何多播组。
传递给 bind 函数的地址的 nl_pid 字段应当设置为本进程的进程 ID,这相当于 netlink socket 的本地地址。可是,关于一个进程的多个线程运用 netlink socket 的状况,字段 nl_pid 则可以设置为其它的值,如:
pthread_self() 《《 16 | getpid();
因而字段 nl_pid 实践上未必是进程 ID,它仅仅用于区别不同的接纳者或发送者的一个标识,用户可以依据自己需求设置该字段。函数 bind 的调用方法如下:
bind(fd, (struct sockaddr*)&nladdr, sizeof(struct sockaddr_nl));
fd为前面的 socket 调用回来的文件描述符,参数 nladdr 为 struct sockaddr_nl 类型的地址。 为了发送一个 netlink 音讯给内核或其他用户态运用,需求填充方针 netlink socket 地址 ,此刻,字段 nl_pid 和 nl_groups 别离表明接纳音讯者的进程 ID 与多播组。假如字段 nl_pid 设置为 0,表明音讯接纳者为内核或多播组,假如 nl_groups为 0,表明该音讯为单播音讯,不然表明多播音讯。 运用函数 sendmsg 发送 netlink 音讯时还需求引证结构 struct msghdr、struct nlmsghdr 和 struct iovec,结构 struct msghdr 需如下设置:
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);
其间 nladdr 为音讯接纳者的 netlink 地址。
struct nlmsghdr 为 netlink socket 自己的音讯头,这用于多路复用和多路分化 netlink 界说的一切协议类型以及其它一些操控,netlink 的内核完结将运用这个音讯头来多路复用和多路分化现已其它的一些操控,因而它也被称为netlink 操控块。因而,运用在发送 netlink 音讯时有必要供给该音讯头。
struct nlmsghdr
{
__u32 nlmsg_len; /* Length of message */
__u16 nlmsg_type; /* Message type*/
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process PID */
};
字段 nlmsg_len 指定音讯的总长度,包含紧跟该结构的数据部分长度以及该结构的巨细,字段 nlmsg_type 用于运用内部界说音讯的类型,它对 netlink 内核完结是通明的,因而大部分状况下设置为 0,字段 nlmsg_flags 用于设置音讯标志,可用的标志包含:
/* Flags values */
#define NLM_F_REQUEST 1 /* It is request message. */
#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */
#define NLM_F_ECHO 8 /* Echo this request */
/* Modifiers to GET request */
#define NLM_F_ROOT 0x100 /* specify tree root */
#define NLM_F_MATCH 0x200 /* return all matching */
#define NLM_F_ATOMIC 0x400 /* atomic GET */
#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
/* Modifiers to NEW request */
#define NLM_F_REPLACE 0x100 /* Override existing */
#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
#define NLM_F_APPEND 0x800 /* Add to end of list */
标志NLM_F_REQUEST用于表明音讯是一个恳求,一切运用首要建议的音讯都应设置该标志。
标志NLM_F_MULTI 用于指示该音讯是一个多部分音讯的一部分,后续的音讯可以经过宏NLMSG_NEXT来获得。
宏NLM_F_ACK表明该音讯是前一个恳求音讯的呼应,顺序号与进程ID可以把恳求与呼应相关起来。
标志NLM_F_ECHO表明该音讯是相关的一个包的回传。
标志NLM_F_ROOT 被许多 netlink 协议的各种数据获取操作运用,该标志指示被恳求的数据表应当全体回来用户运用,而不是一个条目一个条目地回来。有该标志的恳求一般导致呼应音讯设置 NLM_F_MULTI标志。留意,当设置了该标志时,恳求是协议特定的,因而,需求在字段 nlmsg_type 中指定协议类型。
标志 NLM_F_MATCH 表明该协议特定的恳求只需求一个数据子集,数据子集由指定的协议特定的过滤器来匹配。
标志 NLM_F_ATOMIC 指示恳求回来的数据应当原子地搜集,这防备数据在获取期间被修正。
标志 NLM_F_DUMP 未完结。
标志 NLM_F_REPLACE 用于替代在数据表中的现有条目。
标志 NLM_F_EXCL_ 用于和 CREATE 和 APPEND 合作运用,假如条目现已存在,将失利。
标志 NLM_F_CREATE 指示应当在指定的表中创立一个条目。
标志 NLM_F_APPEND 指示在表结尾添加新的条目。
内核需求读取和修正这些标志,关于一般的运用,用户把它设置为 0 就可以,仅仅一些高档运用(如 netfilter 和路由 daemon 需求它进行一些杂乱的操作),字段 nlmsg_seq 和 nlmsg_pid 用于运用追寻音讯,前者表明顺序号,后者为音讯来源进程 ID。下面是一个示例:
#define MAX_MSGSIZE 1024
char buffer[] = “An example message”;
struct nlmsghdr nlhdr;
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
strcpy(NLMSG_DATA(nlhdr),buffer);
nlhdr-》nlmsg_len = NLMSG_LENGTH(strlen(buffer));
nlhdr-》nlmsg_pid = getpid(); /* self pid */
nlhdr-》nlmsg_flags = 0;
结构 struct iovec 用于把多个音讯经过一次体系调用来发送,下面是该结构运用示例:
struct iovec iov;
iov.iov_base = (void *)nlhdr;
iov.iov_len = nlh-》nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
在完结以上过程后,音讯就可以经过下面句子直接发送:
sendmsg(fd, &msg, 0);
运用接纳音讯时需求首要分配一个足够大的缓存来保存音讯头以及音讯的数据部分,然后填充音讯头,添完后就可以直接调用函数 recvmsg() 来接纳。
#define MAX_NL_MSG_LEN 1024
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;
struct nlmsghdr * nlhdr;
nlhdr = (struct nlmsghdr *)malloc(MAX_NL_MSG_LEN);
iov.iov_base = (void *)nlhdr;
iov.iov_len = MAX_NL_MSG_LEN;
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
recvmsg(fd, &msg, 0);
留意:fd为socket调用翻开的netlink socket描述符。
在音讯接纳后,nlhdr指向接纳到的音讯的音讯头,nladdr保存了接纳到的音讯的方针地址,宏NLMSG_DATA(nlhdr)回来指向音讯的数据部分的指针。
在linux/netlink.h中界说了一些便利对音讯进行处理的宏,这些宏包含:
#define NLMSG_ALIGNTO 4
/*宏NLMSG_ALIGN(len)用于得到不小于len且字节对齐的最小数值*/
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
/*宏NLMSG_LENGTH(len)用于核算数据部分长度为len时实践的音讯长度。它一般用于分配音讯缓存*/
#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
/*宏NLMSG_SPACE(len)回来不小于NLMSG_LENGTH(len)且字节对齐的最小数值,它也用于分配音讯缓存*/
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
/*宏NLMSG_DATA(nlh)用于获得音讯的数据部分的首地址,设置和读取音讯数据部分时需求运用该宏*/
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
/*宏NLMSG_NEXT(nlh,len)用于得到下一个音讯的首地址,一起len也削减为剩下音讯的总长度,该宏一般
在一个音讯被分红几个部分发送或接纳时运用*/
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)-》nlmsg_len), \
(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)-》nlmsg_len)))
/*宏NLMSG_OK(nlh,len)用于判别音讯是否有len这么长*/
#define NLMSG_OK(nlh,len) ((len) 》= (int)sizeof(struct nlmsghdr) && \
(nlh)-》nlmsg_len 》= sizeof(struct nlmsghdr) && \
(nlh)-》nlmsg_len 《= (len))
/*宏NLMSG_PAYLOAD(nlh,len)用于回来payload的长度*/
#define NLMSG_PAYLOAD(nlh,len) ((nlh)-》nlmsg_len – NLMSG_SPACE((len)))
函数close用于封闭翻开的netlink socket。
netlink内核API
netlink的内核完结在.c文件net/core/af_netlink.c中,内核模块要想运用netlink,也有必要包含头文件 linux/netlink.h。内核运用netlink需求专门的API,这彻底不同于用户态运用对netlink的运用。假如用户需求添加新的 netlink协议类型,有必要经过修正linux/netlink.h来完结,当然,现在的netlink完结现已包含了一个通用的协议类型 NETLINK_GENERIC以便利用户运用,用户可以直接运用它而不用添加新的协议类型。前面讲到,为了添加新的netlink协议类型,用户仅需增 加如下界说到linux/netlink.h就可以:
#define NETLINK_MYTEST 17
只需添加这个界说之后,用户就可以在内核的任何地方引证该协议。
在内核中,为了创立一个netlink socket用户需求调用如下函数:
struct sock *
netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));
参数unit表明netlink协议类型,如NETLINK_MYTEST,参数input则为内核模块界说的netlink音讯处理函数,当有消 息抵达这个netlink socket时,该input函数指针就会被引证。函数指针input的参数sk实践上便是函数netlink_kernel_create回来的 struct sock指针,sock实践是socket的一个内核表明数据结构,用户态运用创立的socket在内核中也会有一个struct sock结构来表明。下面是一个input函数的示例:
void input (struct sock *sk, int len)
{
struct sk_buff *skb;
struct nlmsghdr *nlh = NULL;
u8 *data = NULL;
while ((skb = skb_dequeue(&sk-》receive_queue)) != NULL)
{
/* process netlink message pointed by skb-》data */
nlh = (struct nlmsghdr *)skb-》data;
data = NLMSG_DATA(nlh);
/* process netlink message with header pointed by
* nlh and data pointed by data
*/
}
}
函数input()会在发送进程履行sendmsg()时被调用,这样处理音讯比较及时,可是,假如音讯特别长时,这样处理将添加体系调用 sendmsg()的履行时间,关于这种状况,可以界说一个内核线程专门担任音讯接纳,而函数input的作业仅仅唤醒该内核线程,这样sendmsg将 很快回来。
函数skb = skb_dequeue(&sk-》receive_queue)用于获得socket sk的接纳行列上的音讯,回来为一个struct sk_buff的结构,skb-》data指向实践的netlink音讯。
函数skb_recv_datagram(nl_sk)也用于在netlink socket nl_sk上接纳音讯,与skb_dequeue的不同指出是,假如socket的接纳行列上没有音讯,它将导致调用进程睡觉在等候行列nl_sk- 》sk_sleep,因而它有必要在进程上下文运用,方才讲的内核线程就可以选用这种方法来接纳音讯。
下面的函数input便是这种运用的示例:
void input (struct sock *sk, int len)
{
wake_up_interruptible(sk-》sk_sleep);
}
当内核中发送netlink音讯时,也需求设置方针地址与源地址,并且内核中音讯是经过struct sk_buff来办理的, linux/netlink.h中界说了一个宏:
#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)-》cb))
来便利音讯的地址设置。下面是一个音讯地址设置的比如:
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid = 0;
NETLINK_CB(skb).dst_group = 1;
字段pid表明音讯发送者进程ID,也即源地址,关于内核,它为 0, dst_pid 表明音讯接纳者进程 ID,也即方针地址,假如方针为组或内核,它设置为 0,不然 dst_group 表明方针组地址,假如它方针为某一进程或内核,dst_group 应当设置为 0。
在内核中,模块调用函数 netlink_unicast 来发送单播音讯:
int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);
参数sk为函数netlink_kernel_create()回来的socket,参数skb寄存音讯,它的data字段指向要发送的 netlink音讯结构,而skb的操控块保存了音讯的地址信息,前面的宏NETLINK_CB(skb)就用于便利设置该操控块, 参数pid为接纳音讯进程的pid,参数nonblock表明该函数是否为非堵塞,假如为1,该函数将在没有接纳缓存可运用时当即回来,而假如为0,该函 数在没有接纳缓存可运用时睡觉。
内核模块或子体系也可以运用函数netlink_broadcast来发送播送音讯:
void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation);
前面的三个参数与netlink_unicast相同,参数group为接纳音讯的多播组,该参数的每一个代表一个多播组,因而假如发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL, GFP_ATOMIC用于原子的上下文(即不可以睡觉),而GFP_KERNEL用于非原子上下文。
在内核中运用函数sock_release来开释函数netlink_kernel_create()创立的netlink socket:
void sock_release(struct socket * sock);
留意函数netlink_kernel_create()回来的类型为struct sock,因而函数sock_release应该这种调用:
sock_release(sk-》sk_socket);
sk为函数netlink_kernel_create()的回来值。
在源代码包中 给出了一个运用 netlink 的示例,它包含一个内核模块 netlink-exam-kern.c 和两个运用程序 netlink-exam-user-recv.c, netlink-exam-user-send.c。内核模块有必要先刺进到内核,然后在一个终端上运转用户态接纳程序,在另一个终端上运转用户态发送程序,发送程序读取参数指定的文本文件并把它作为 netlink 音讯的内容发送给内核模块,内核模块承受该音讯保存到内核缓存中,它也经过proc接口出口到 procfs,因而用户也可以经过 /proc/netlink_exam_buffer 看到悉数的内容,一起内核也把该音讯发送给用户态接纳程序,用户态接纳程序将把接纳到的内容输出到屏幕上。
示例:
内核模块 netlink-exam-kern.c:
//kernel module: netlink-exam-kern.c
#include 《linux/config.h》
#include 《linux/module.h》
#include 《linux/netlink.h》
#include 《linux/sched.h》
#include 《net/sock.h》
#include 《linux/proc_fs.h》
#define BUF_SIZE 16384
static struct sock *netlink_exam_sock;
static unsigned char buffer[BUF_SIZE];
static unsigned int buffer_tail = 0;
static int exit_flag = 0;
static DECLARE_COMPLETION(exit_completion);
static void recv_handler(struct sock * sk, int length)
{
wake_up(sk-》sk_sleep);
}
static int process_message_thread(void * data)
{
struct sk_buff * skb = NULL;
struct nlmsghdr * nlhdr = NULL;
int len;
DEFINE_WAIT(wait);
daemonize(“mynetlink”);
while (exit_flag == 0) {
prepare_to_wait(netlink_exam_sock-》sk_sleep, &wait, TASK_INTERRUPTIBLE);
schedule();
finish_wait(netlink_exam_sock-》sk_sleep, &wait);
while ((skb = skb_dequeue(&netlink_exam_sock-》sk_receive_queue))
!= NULL) {
nlhdr = (struct nlmsghdr *)skb-》data;
if (nlhdr-》nlmsg_len 《 sizeof(struct nlmsghdr)) {
printk(“Corrupt netlink message.\n”);
continue;
}
len = nlhdr-》nlmsg_len – NLMSG_LENGTH(0);
if (len + buffer_tail 》 BUF_SIZE) {
printk(“netlink buffer is full.\n”);
}
else {
memcpy(buffer + buffer_tail, NLMSG_DATA(nlhdr), len);
buffer_tail += len;
}
nlhdr-》nlmsg_pid = 0;
nlhdr-》nlmsg_flags = 0;
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid = 0;
NETLINK_CB(skb).dst_group = 1;
netlink_broadcast(netlink_exam_sock, skb, 0, 1, GFP_KERNEL);
}
}
complete(&exit_completion);
return 0;
}
static int netlink_exam_readproc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
if (off 》= buffer_tail) {
* eof = 1;
return 0;
}
else {
len = count;
if (count 》 PAGE_SIZE) {
len = PAGE_SIZE;
}
if (len 》 buffer_tail – off) {
len = buffer_tail – off;
}
memcpy(page, buffer + off, len);
*start = page;
return len;
}
}
static int __init netlink_exam_init(void)
{
netlink_exam_sock = netlink_kernel_create(NETLINK_GENERIC, 0, recv_handler, THIS_MODULE);
if (!netlink_exam_sock) {
printk(“Fail to create netlink socket.\n”);
return 1;
}
kernel_thread(process_message_thread, NULL, CLONE_KERNEL);
create_proc_read_entry(“netlink_exam_buffer”, 0444, NULL, netlink_exam_readproc, 0);
return 0;
}
static void __exit netlink_exam_exit(void)
{
exit_flag = 1;
wake_up(netlink_exam_sock-》sk_sleep);
wait_for_completion(&exit_completion);
sock_release(netlink_exam_sock-》sk_socket);
}
module_init(netlink_exam_init);
module_exit(netlink_exam_exit);
MODULE_LICENSE(“GPL”);
netlink-exam-user-send.c:
//application sender: netlink-exam-user-send.c
#include 《stdio.h》
#include 《sys/types.h》
#include 《sys/socket.h》
#include 《linux/netlink.h》
#define MAX_MSGSIZE 1024
int main(int argc, char * argv[])
{
FILE * fp;
struct sockaddr_nl saddr, daddr;
struct nlmsghdr *nlhdr = NULL;
struct msghdr msg;
struct iovec iov;
int sd;
char text_line[MAX_MSGSIZE];
int ret = -1;
if (argc 《 2) {
printf(“Usage: %s atextfilename\n”, argv[0]);
exit(1);
}
if ((fp = fopen(argv[1], “r”)) == NULL) {
printf(“File %s dosen‘t exist.\n”);
exit(1);
}
sd = socket(AF_NETLINK, SOCK_RAW,NETLINK_GENERIC);
memset(&saddr, 0, sizeof(saddr));
memset(&daddr, 0, sizeof(daddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 0;
bind(sd, (struct sockaddr*)&saddr, sizeof(saddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0;
daddr.nl_groups = 0;
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
while (fgets(text_line, MAX_MSGSIZE, fp)) {
memcpy(NLMSG_DATA(nlhdr), text_line, strlen(text_line));
memset(&msg, 0 ,sizeof(struct msghdr));
nlhdr-》nlmsg_len = NLMSG_LENGTH(strlen(text_line));
nlhdr-》nlmsg_pid = getpid(); /* self pid */
nlhdr-》nlmsg_flags = 0;
iov.iov_base = (void *)nlhdr;
iov.iov_len = nlhdr-》nlmsg_len;
msg.msg_name = (void *)&daddr;
msg.msg_namelen = sizeof(daddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ret = sendmsg(sd, &msg, 0);
if (ret == -1) {
perror(“sendmsg error:”);
}
}
close(sd);
}
netlink-exam-user-recv.c:
//application receiver: netlink-exam-user-recv.c
#include 《stdio.h》
#include 《sys/types.h》
#include 《sys/socket.h》
#include 《linux/netlink.h》
#define MAX_MSGSIZE 1024
int main(void)
{
struct sockaddr_nl saddr, daddr;
struct nlmsghdr *nlhdr = NULL;
struct msghdr msg;
struct iovec iov;
int sd;
int ret = 1;
sd = socket(AF_NETLINK, SOCK_RAW,NETLINK_GENERIC);
memset(&saddr, 0, sizeof(saddr));
memset(&daddr, 0, sizeof(daddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 1;
bind(sd, (struct sockaddr*)&saddr, sizeof(saddr));
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
while (1) {
memset(nlhdr, 0, NLMSG_SPACE(MAX_MSGSIZE));
iov.iov_base = (void *)nlhdr;
iov.iov_len = NLMSG_SPACE(MAX_MSGSIZE);
msg.msg_name = (void *)&daddr;
msg.msg_namelen = sizeof(daddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ret = recvmsg(sd, &msg, 0);
if (ret == 0) {
printf(“Exit.\n”);
exit(0);
}
else if (ret == -1) {
perror(“recvmsg:”);
exit(1);
}
printf(“%s”, NLMSG_DATA(nlhdr));
}
close(sd);
}