Linux内核IP Queue机制的分析(一)——用户态接收数据包(3)

2020-05-26 00:00:00 程序 模块 内核 报文 数据包

3. ipq_user.c
这个函数就是具体的测试函数。功能比较简单,通过调用libipq.c中提供的API实现获取IP Queue数据包的简单信息,包括数据包在本地机上的入口以及报文的长度等,
整个源码如下:

/*
* ipq_usr.c
*
* Testing program for receiving IP Queue packets from kernel 2.6.18.3
*
* Dec 1, 2008
* Godbach created.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "libipq.h"

struct ipq_handle *h = NULL;

static void sig_int(int signo)
{
ipq_destroy_handle(h);
printf("Exit: %%s\n", ipq_errstr());
exit();
}

int main(void)
{
unsigned char buf[1024];
/* creat handle*/
h = ipq_create_handle(, PF_INET);
if(h == NULL){
printf("%%s\n", ipq_errstr());
return ;
}
printf("ipq_creat_handle success!\n");
/*set mode*/
unsigned char mode = IPQ_COPY_PACKET;
int range = sizeof(buf);
int ret = ipq_set_mode(h, mode, range);
printf("ipq_set_mode: send bytes =%%d, range=%%d\n", ret, range);

/*register signal handler*/
signal(SIGINT, sig_int);

/*read packet from kernel*/
int status;
struct nlmsghdr *nlh;
ipq_packet_msg_t *ipq_packet;

while(1){
status = ipq_read(h, buf, sizeof(buf));
if(status > sizeof(struct nlmsghdr))
{
nlh = (struct nlmsghdr *)buf;
ipq_packet = ipq_get_packet(buf);
printf("recv bytes =%%d, nlmsg_len=%%d, indev=%%s, datalen=%%d, packet_id=%%x\n", status, nlh->nlmsg_len,
ipq_packet->indev_name, ipq_packet->data_len, ipq_packet->packet_id);
}
}
return ;
}

四、应用程序的测试
1. 测试环境的建立
(1)内核态:要求已编译的内核支持Netlink机制, 并进入内核源码目录net/ipv4/netfilter下,检查是否生成ip_queue.ko。如果有相应的文件,则确保该模块是否加载,没有加载的话,modprobe ip_queue进行加载
(2)用户态:要求有已经安装iptables,并加上一条如下规则:
iptables -I INPUT -p icmp -j QUEUE
这里我们在INPUT链上开始处添加了一条对所有ICMP报文进行IP Queue的规则。通过添加不同的iptables规则,可以对不同的报文进行IP Queue。

如果系统上没有安装iptables的话,那么可以用一个简单的内核模块来实现其功能。即在NF对应的Hook点上注册一个钩子函数,对于某种类型的数据包直接return NF_QUEUE即可。附件的源码中我提供了一个模块程序,在NF的PRE_ROUTING出注册了一个对所有ICMP报文return NF_QUEUE的模块。没有iptables的朋友可以使用这个小模块程序替代。

注意:我这里所使用的内核为2.6.18.3。其他内核版本没有进行测试。不过,个人觉得如果用户态的应用程序应该在2.6上没有问题,只是提供的内核模块程序不能保证。
2. 程序的测试
搭建好上面提示的环境之后,可以对应用程序的源码进行编译:

gcc libipq.c ipq_user.c -o ipq_user

执行ipq_user:

[root@localhost ipq_user]# ./ipq_user
ipq_creat_handle success!
ipq_set_mode: send bytes =44, range=1024

随后,程序处于等待接受内核数据包的状态。我们从另外一台主机发送ping包到本地主机,然后看到终端的输出为:

[root@localhost ipq_user]# ./ipq_user
ipq_creat_handle success!
ipq_set_mode: send bytes =44, range=1024
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c2f9b500
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=cb0c8c00
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c72aa920
recv bytes =148, nlmsg_len=148, indev=eth0, datalen=60, packet_id=c2f9b3c0
Exit: No error

从以上信息中可以看出:
(1)用户态发送的模式设置信息的包长度为44 bytes;
(2)接收到内核态发送的包长度为148bytes, 这和从收到的IP Queue包中保存的长度nlmsg_len一致。
(3)ping包进入本地主机的eth网口,报文的长度为60 bytes。这个实际的ping包的长度一致。

好了,到现在为止,我们已经成功的通过程序接收到内核态发送的IP Queue数据包。我将在下一篇文章中讲解用户态对接收到报文的简单处理以及发送给内核的整个过程。

附件
        附件中提供了此次测试进行的应用程序的源码,和一个简单的对icmp报文进行IP Queue的内核模块程序。  

文章来源CU社区:Linux内核IP Queue机制的分析(一)——用户态接收数据包


相关文章