262以上内核中netlink使用方法.docx
《262以上内核中netlink使用方法.docx》由会员分享,可在线阅读,更多相关《262以上内核中netlink使用方法.docx(12页珍藏版)》请在冰豆网上搜索。
262以上内核中netlink使用方法
2.6.24以上内核中netlink使用方法
2010-02-0414:
56 5119人阅读 评论
(1) 收藏 举报
structsocket数据结构null网络协议header
2.6.24以上内核中netlink使用方法
测试环境:
2.6.28
Netlink在2.6内核的不同版本中发生了很大变化,具体请参考(注意其中的版本号不一定确切):
0.综述
以下程序基本流程如下:
运行netlink内核模块;
运行用户态程序,向内核发送连接消息,通知内核自身进程id;
内核接收用户消息,记录其进程id;
内核向用户进程id发送netlink消息;
用户接收内核发送的netlink消息。
1.内核部分
1.1相关的数据结构变量:
[cpp] viewplaincopy
1.44 // --------- These are for netlink --------- //
2.45 #define NETLINK_REALNET 26
3.46 struct sock *g_nl_sk = NULL;
4.48 struct sockaddr_nl src_addr, dest_addr;
5.50 struct iovec iov;
6.52 int pid;
7.53 struct msghdr msg;
8.55 // ----------------------------------------- //
[cpp] viewplaincopy
1.45 #define NETLINK_REALNET 26
定义协议族。
该变量在netlink_kernel_create函数中使用。
在2.6.28内核中netlink定义了20个协议,每个协议使用唯一整数标识。
用户程序可以定义任意20个协议以外的协议,用唯一整数标识。
[cpp] viewplaincopy
1.46 struct sock *g_nl_sk = NULL;
sock数据结构,唯一标识netlink使用的sock,与普通socket编程中sock类似。
[cpp] viewplaincopy
1.48 struct sockaddr_nl src_addr, dest_addr;
标识netlinksock的源地址和目的地址。
[cpp] viewplaincopy
1.50 struct iovec iov;
接收发送netlink数据使用的数据结构。
[cpp] viewplaincopy
1.53 struct msghdr msg;
netlink消息头。
1.2调用过程
1.2.1创建netlinksocket
[cpp] viewplaincopy
1.g_nl_sk = netlink_kernel_create(&init_net, NETLINK_REALNET, 0, nl_data_r eady, NULL, THIS_MODULE);
1.2.2实现回调函数nl_data_ready
以下回调函数在netlink接收到完整的NETLINK_REALNET协议的数据包时由系统调用。
该函数接收并判断netlink消息,如果第一个字符为H,则保存该消息发出者的进程号,用以向该进程发送数据包;相同,如果为E,则清除与该进程的联系。
[cpp] viewplaincopy
1.185 void nl_data_ready(struct sk_buff *__skb)
2.186 {
3.187 struct sk_buff *skb;
4.188 struct nlmsghdr *nlh;
5.189 char str[100];
6.190
7.191 skb = skb_get (__skb);
8.192
9.193 if(skb->len >= NLMSG_SPACE(0))
10.194 {
11.195 nlh = nlmsg_hdr(skb);
12.196
13.197 memcpy(str, NLMSG_DATA(nlh), sizeof(str));
14.198 //DbgPrint("Message received:
%s/n", str);
15.199
16.200 // H stands for Hello message.
17.201 if(str[0] == 'H')
18.202 {
19.203 pid = nlh->nlmsg_pid;
20.204 u_connected = 1;
21.205 //sendnlmsg("Hello reply.");
22.206 }
23.207 // E stands for Exit message
24.208 else if(str[0] == 'E')
25.209 {
26.210 u_connected = 0;
27.211 pid = 0;
28.212 }
29.213 kfree_skb(skb);
30.214 }
31.215 }
[cpp] viewplaincopy
1.191 skb = skb_get (__skb);
获取实际数据包。
该函数的参数为netlink数据包的首地址,而sk_buff为网络协议栈使用的数据结构,两者存在细微差别。
[cpp] viewplaincopy
1.195 nlh = nlmsg_hdr(skb);
获取netlink数据包中netlinkheader的起始地址。
[cpp] viewplaincopy
1.197 memcpy(str, NLMSG_DATA(nlh), sizeof(str));
将netlink数据包的数据区拷贝到str中。
NLMSG_DATA(nlh)返回数据区地址。
相关宏定义参考:
[cpp] viewplaincopy
1.213 kfree_skb(skb);
释放接收到的消息。
1.2.3向用户进程发送netlink消息
以下函数的参数为netfilter捕捉到的sk_buff结构的数据包,目的是将该包通过netlink发送到用户态进程。
[cpp] viewplaincopy
1.247 void send_to_user(struct sk_buff *skb)
2.248 {
3.249 struct iphdr *iph;
4.250 struct ethhdr *ehdr;
5.251
6.252 struct nlmsghdr *nlh;
7.253
8.254 struct sk_buff *nl_skb;
9.255
10.256 //DbgPrint("Send packages to user/n");
11.257
12.258 if(skb == NULL)
13.259 {
14.260 return ;
15.261 }
16.262 if(!
g_nl_sk)
17.263 {
18.264 return ;
19.265 }
20.266 if(pid == 0)
21.267 {
22.268 return;
23.269 }
24.270
25.271 nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC);
26.272 //nl_skb = alloc_skb(NLMSG_SPACE(0), GFP_ATOMIC);
27.273 if(nl_skb == NULL)
28.274 {
29.275 // allocate failed.
30.276 return;
31.277 }
32.278
33.279 ehdr = eth_hdr(skb);
34.280 iph = ip_hdr(skb);
35.281
36.282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0);
37.283 NETLINK_CB(nl_skb).pid = 0;
38.284
39.285 //DbgPrint("Data length:
%d, len=%d/n", htons(iph->tot_len) + ETH_HLEN, NLMSG_SPACE(1514));
40.286
41.287 // Copy data to nlh
42.288 memcpy(NLMSG_DATA(nlh), (char *)ehdr, htons(iph->tot_len) + ETH_HLEN);
43.289
44.290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT);
45.291 }
[cpp] viewplaincopy
1.247 void send_to_user(struct sk_buff *skb)
参数skb为netfilter捕捉到的数据包,不是netlink数据包。
这里作为netlink的数据传输。
[cpp] viewplaincopy
1.271 nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC);
为发送数据包申请空间。
空间数据区大小为1514,即最大ethernet数据包长度。
NLMSG_SPACE(1514)返回数据区大小为1514的netlink数据包的大小。
详细参考:
[cpp] viewplaincopy
1.282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0);
填充netlink数据包头。
[cpp] viewplaincopy
1.283 NETLINK_CB(nl_skb).pid = 0;
确定发送数据包的进程号,0表示内核进程。
该处宏定义同样参考:
[cpp] viewplaincopy
1.290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT);
通过非阻塞方式发送数据包。
注意:
在发送完数据包之后,nl_skb指向的数据空间将被清空,下一次发送数据包必须重新调用alloc_skb分配空间,否则将会造成内核崩溃,必须重新启动。
1.2.4释放netlinksocket
使用完成netlink之后,必须要调用sock_release,否则
[cpp] viewplaincopy
1.45 #define NETLINK_REALNET 26
指定的协议编号将不再可用。
代码如下:
[cpp] viewplaincopy
1.239 void destory_netlink(void)
2.240 {
3.241 if(g_nl_sk !
= NULL)
4.242 {
5.243 sock_release(g_nl_sk->sk_socket);
6.244 }
7.245 }
2.用户态程序
2.1相关数据结构
[cpp] viewplaincopy
1.28 // ---------------- For Netlink -------------- //
2.29 struct sockaddr_nl nl_src_addr, nl_dest_addr;
3.30 struct nlmsghdr *nlh = NULL;
4.31 struct iovec iov;
5.32 int nl_fd;
6.33 struct msghdr nl_msg;
7.34 // ------------------------------------------- //
与内核态数据结构类似,不再赘述。
2.2运行过程
2.2.1初始化netlink
[cpp] viewplaincopy
1.114 void init_nl()
2.115 {
3.116 nl_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_REALNET);
4.117 memset(&nl_msg, 0, sizeof(nl_msg));
5.118 memset(&nl_src_addr, 0, sizeof(nl_src_addr));
6.119 nl_src_addr.nl_family = AF_NETLINK;
7.120 nl_src_addr.nl_pid = getpid();
8.121 nl_src_addr.nl_groups = 0;
9.122
10.123 bind(nl_fd, (struct sockaddr*)&nl_src_addr, sizeof(nl_src_addr));
11.124 memset(&nl_dest_addr, 0, sizeof(nl_dest_addr));
12.125 nl_dest_addr.nl_family = AF_NETLINK;
13.126 nl_dest_addr.nl_pid = 0; /* For Linux Kernel */
14.127 nl_dest_addr.nl_groups = 0; /* unicast */
15.128
16.129 sendnlmsg("H");
17.130
18.131 memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
19.132 }
[cpp] viewplaincopy
1.116 nl_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_REALNET);
创建用户netlinksocket,与普通socket创建方法相同,协议族为PF_NETLINK,协议类型为用户自定义的NETLINK_REALNET,与内核态定义相同。
[cpp] viewplaincopy
1.117 memset(&nl_msg, 0, sizeof(nl_msg));
清空netlink数据包。
[cpp] viewplaincopy
1.118 - 127行
与普通socket的初始化类似,如果不熟悉可以参考有关socket编程。
需要注意的是nl_src_addr的数据结构与普通socket有些不同。
[cpp] viewplaincopy
1.129 sendnlmsg("H");
向内核进程发送Hello消息,通知内核其进程id。
2.2.2向内核发送消息
以下函数创建并发送netlink消息到内核进程。
[cpp] viewplaincopy
1.36 void sendnlmsg(const char *message)
2.37 {
3.38 printf("Sending:
%s/n", message);
4.39 nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
5.40 nlh ->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
6.41 nlh -> nlmsg_pid = getpid();
7.42 nlh -> nlmsg_flags = 0;
8.43
9.44 strcpy((char *)NLMSG_DATA(nlh), message);
10.45
11.46 iov.iov_base = (void *)nlh;
12.47 iov.iov_len = nlh->nlmsg_len;
13.48 nl_msg.msg_name = (void *)&nl_dest_addr;
14.49 nl_msg.msg_namelen = sizeof(nl_dest_addr);
15.50 nl_msg.msg_iov = &iov;
16.51 nl_msg.msg_iovlen = 1;
17.52
18.53 printf("Start to send message.");
19.54 sendmsg(nl_fd, &nl_msg, 0);
20.55 printf("Sending finishes./n");
21.56 }
[cpp] viewplaincopy
1.39 nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
为netlinkheader分配存储空间,MAX_PAYLOAD由用户定义,为发送(用户)数据的最大长度。
[cpp] viewplaincopy
1.40 - 51行
指定netlink相关的参数,准备发送消息。
[cpp] viewplaincopy
1.54 sendmsg(nl_fd, &nl_msg, 0);
发送netlink数据包到内核进程,与普通socket中的sendmsg消息用法相同,最后一个参数0表示非阻塞模式。
详细参考socket编程。