测试环境:2.6.28
Netlink在2.6内核的不同版本中发生了很大变化,具体请参考(注意其中的版本号不一定确切):
http://blog.youkuaiyun.com/sealyao/archive/2009/10/02/4628141.aspx
0. 综述
以下程序基本流程如下:
运行netlink内核模块;
运行用户态程序,向内核发送连接消息,通知内核自身进程id;
内核接收用户消息,记录其进程id;
内核向用户进程id发送netlink消息;
用户接收内核发送的netlink消息。
1. 内核部分
1.1 相关的数据结构变量:
- 44//---------Thesearefornetlink---------//
- 45#defineNETLINK_REALNET26
- 46structsock*g_nl_sk=NULL;
- 48structsockaddr_nlsrc_addr,dest_addr;
- 50structioveciov;
- 52intpid;
- 53structmsghdrmsg;
- 55//-----------------------------------------//
- 45#defineNETLINK_REALNET26
定义协议族。该变量在netlink_kernel_create函数中使用。
在2.6.28内核中netlink定义了20个协议,每个协议使用唯一整数标识。用户程序可以定义任意20个协议以外的协议,用唯一整数标识。
- 46structsock*g_nl_sk=NULL;
sock数据结构,唯一标识netlink使用的sock,与普通socket编程中sock类似。
- 48structsockaddr_nlsrc_addr,dest_addr;
标识netlink sock的源地址和目的地址。
- 50structioveciov;
接收发送netlink数据使用的数据结构。
- 53structmsghdrmsg;
netlink消息头。
1.2 调用过程
1.2.1 创建netlink socket
- g_nl_sk=netlink_kernel_create(&init_net,NETLINK_REALNET,0,nl_data_ready,NULL,THIS_MODULE);
1.2.2 实现回调函数nl_data_ready
以下回调函数在netlink接收到完整的NETLINK_REALNET协议的数据包时由系统调用。该函数接收并判断netlink消息,如果第一个字符为H,则保存该消息发出者的进程号,用以向该进程发送数据包;相同,如果为E,则清除与该进程的联系。
- 185voidnl_data_ready(structsk_buff*__skb)
- 186{
- 187structsk_buff*skb;
- 188structnlmsghdr*nlh;
- 189charstr[100];
- 190
- 191skb=skb_get(__skb);
- 192
- 193if(skb->len>=NLMSG_SPACE(0))
- 194{
- 195nlh=nlmsg_hdr(skb);
- 196
- 197memcpy(str,NLMSG_DATA(nlh),sizeof(str));
- 198//DbgPrint("Messagereceived:%s/n",str);
- 199
- 200//HstandsforHellomessage.
- 201if(str[0]=='H')
- 202{
- 203pid=nlh->nlmsg_pid;
- 204u_connected=1;
- 205//sendnlmsg("Helloreply.");
- 206}
- 207//EstandsforExitmessage
- 208elseif(str[0]=='E')
- 209{
- 210u_connected=0;
- 211pid=0;
- 212}
- 213kfree_skb(skb);
- 214}
- 215}
- 191skb=skb_get(__skb);
获取实际数据包。
该函数的参数为netlink数据包的首地址,而sk_buff为网络协议栈使用的数据结构,两者存在细微差别。
- 195nlh=nlmsg_hdr(skb);
获取netlink数据包中netlink header的起始地址。
- 197memcpy(str,NLMSG_DATA(nlh),sizeof(str));
将netlink数据包的数据区拷贝到str中。NLMSG_DATA(nlh)返回数据区地址。相关宏定义参考:
http://blog.youkuaiyun.com/wangjingfei/archive/2010/02/04/5288263.aspx
- 213kfree_skb(skb);
释放接收到的消息。
1.2.3 向用户进程发送netlink消息
以下函数的参数为netfilter捕捉到的sk_buff结构的数据包,目的是将该包通过netlink发送到用户态进程。
- 247voidsend_to_user(structsk_buff*skb)
- 248{
- 249structiphdr*iph;
- 250structethhdr*ehdr;
- 251
- 252structnlmsghdr*nlh;
- 253
- 254structsk_buff*nl_skb;
- 255
- 256//DbgPrint("Sendpackagestouser/n");
- 257
- 258if(skb==NULL)
- 259{
- 260return;
- 261}
- 262if(!g_nl_sk)
- 263{
- 264return;
- 265}
- 266if(pid==0)
- 267{
- 268return;
- 269}
- 270
- 271nl_skb=alloc_skb(NLMSG_SPACE(1514),GFP_ATOMIC);
- 272//nl_skb=alloc_skb(NLMSG_SPACE(0),GFP_ATOMIC);
- 273if(nl_skb==NULL)
- 274{
- 275//allocatefailed.
- 276return;
- 277}
- 278
- 279ehdr=eth_hdr(skb);
- 280iph=ip_hdr(skb);
- 281
- 282nlh=nlmsg_put(nl_skb,0,0,0,NLMSG_SPACE(1514)-sizeof(structnlmsghdr),0);
- 283NETLINK_CB(nl_skb).pid=0;
- 284
- 285//DbgPrint("Datalength:%d,len=%d/n",htons(iph->tot_len)+ETH_HLEN,NLMSG_SPACE(1514));
- 286
- 287//Copydatatonlh
- 288memcpy(NLMSG_DATA(nlh),(char*)ehdr,htons(iph->tot_len)+ETH_HLEN);
- 289
- 290netlink_unicast(g_nl_sk,nl_skb,pid,MSG_DONTWAIT);
- 291}
- 247voidsend_to_user(structsk_buff*skb)
参数skb为netfilter捕捉到的数据包,不是netlink数据包。这里作为netlink的数据传输。
- 271nl_skb=alloc_skb(NLMSG_SPACE(1514),GFP_ATOMIC);
为发送数据包申请空间。空间数据区大小为1514,即最大ethernet数据包长度。NLMSG_SPACE(1514)返回数据区大小为1514的netlink数据包的大小。详细参考:
http://blog.youkuaiyun.com/wangjingfei/archive/2010/02/04/5288263.aspx
- 282nlh=nlmsg_put(nl_skb,0,0,0,NLMSG_SPACE(1514)-sizeof(structnlmsghdr),0);
填充netlink数据包头。
- 283NETLINK_CB(nl_skb).pid=0;
确定发送数据包的进程号,0表示内核进程。该处宏定义同样参考:
http://blog.youkuaiyun.com/wangjingfei/archive/2010/02/04/5288263.aspx
- 290netlink_unicast(g_nl_sk,nl_skb,pid,MSG_DONTWAIT);
通过非阻塞方式发送数据包。注意:在发送完数据包之后,nl_skb指向的数据空间将被清空,下一次发送数据包必须重新调用alloc_skb分配空间,否则将会造成内核崩溃,必须重新启动。
1.2.4 释放netlink socket
使用完成netlink之后,必须要调用sock_release,否则
- 45#defineNETLINK_REALNET26
指定的协议编号将不再可用。代码如下:
- 239voiddestory_netlink(void)
- 240{
- 241if(g_nl_sk!=NULL)
- 242{
- 243sock_release(g_nl_sk->sk_socket);
- 244}
- 245}
2. 用户态程序
2.1 相关数据结构
- 28//----------------ForNetlink--------------//
- 29structsockaddr_nlnl_src_addr,nl_dest_addr;
- 30structnlmsghdr*nlh=NULL;
- 31structioveciov;
- 32intnl_fd;
- 33structmsghdrnl_msg;
- 34//-------------------------------------------//
与内核态数据结构类似,不再赘述。
2.2 运行过程
2.2.1 初始化netlink
- 114voidinit_nl()
- 115{
- 116nl_fd=socket(PF_NETLINK,SOCK_RAW,NETLINK_REALNET);
- 117memset(&nl_msg,0,sizeof(nl_msg));
- 118memset(&nl_src_addr,0,sizeof(nl_src_addr));
- 119nl_src_addr.nl_family=AF_NETLINK;
- 120nl_src_addr.nl_pid=getpid();
- 121nl_src_addr.nl_groups=0;
- 122
- 123bind(nl_fd,(structsockaddr*)&nl_src_addr,sizeof(nl_src_addr));
- 124memset(&nl_dest_addr,0,sizeof(nl_dest_addr));
- 125nl_dest_addr.nl_family=AF_NETLINK;
- 126nl_dest_addr.nl_pid=0;/*ForLinuxKernel*/
- 127nl_dest_addr.nl_groups=0;/*unicast*/
- 128
- 129sendnlmsg("H");
- 130
- 131memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
- 132}
- 116nl_fd=socket(PF_NETLINK,SOCK_RAW,NETLINK_REALNET);
创建用户netlink socket,与普通socket创建方法相同,协议族为PF_NETLINK,协议类型为用户自定义的NETLINK_REALNET,与内核态定义相同。
- 117memset(&nl_msg,0,sizeof(nl_msg));
清空netlink数据包。
- 118-127行
与普通socket的初始化类似,如果不熟悉可以参考有关socket编程。需要注意的是nl_src_addr的数据结构与普通socket有些不同。
- 129sendnlmsg("H");
向内核进程发送Hello消息,通知内核其进程id。
2.2.2 向内核发送消息
以下函数创建并发送netlink消息到内核进程。
- 36voidsendnlmsg(constchar*message)
- 37{
- 38printf("Sending:%s/n",message);
- 39nlh=(structnlmsghdr*)malloc(NLMSG_SPACE(MAX_PAYLOAD));
- 40nlh->nlmsg_len=NLMSG_SPACE(MAX_PAYLOAD);
- 41nlh->nlmsg_pid=getpid();
- 42nlh->nlmsg_flags=0;
- 43
- 44strcpy((char*)NLMSG_DATA(nlh),message);
- 45
- 46iov.iov_base=(void*)nlh;
- 47iov.iov_len=nlh->nlmsg_len;
- 48nl_msg.msg_name=(void*)&nl_dest_addr;
- 49nl_msg.msg_namelen=sizeof(nl_dest_addr);
- 50nl_msg.msg_iov=&iov;
- 51nl_msg.msg_iovlen=1;
- 52
- 53printf("Starttosendmessage.");
- 54sendmsg(nl_fd,&nl_msg,0);
- 55printf("Sendingfinishes./n");
- 56}
- 39nlh=(structnlmsghdr*)malloc(NLMSG_SPACE(MAX_PAYLOAD));
为netlink header分配存储空间,MAX_PAYLOAD由用户定义,为发送(用户)数据的最大长度。
- 40-51行
指定netlink相关的参数,准备发送消息。
- 54sendmsg(nl_fd,&nl_msg,0);
发送netlink数据包到内核进程,与普通socket中的sendmsg消息用法相同,最后一个参数0表示非阻塞模式。详细参考socket编程。
本文详细介绍Linux 2.6.28内核版本中Netlink的使用方法,包括内核与用户态之间的消息传递流程,涉及的主要数据结构及函数,如netlink_kernel_create、sendmsg等,并解释了如何创建和发送Netlink消息。
3308

被折叠的 条评论
为什么被折叠?



