关于netlink中的NETLINK_NFLOG接口使用
使用的是内核提供的NETLINK_NFLOG接口来抓取特定数据包。但发现对数据的解析有错误,
源码如下:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <errno.h>
#include <linux/netfilter_ipv4/ipt_ULOG.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#define MAX_PAYLOAD 1024 // maximum payload size
#define MOD_ID 2
#define ERROR 0
//for print the information string
#if 1
#define DEBUG_INFO(...) \
do \
{ \
printf(__VA_ARGS__); \
}while (0)
#else
#define WIFI_DEBUG_INFO(...) ((void)0)
#endif
static int netlink_group_mask(int group)
{
return group ? 1 << (group - 1) : 0;
}
static void print_info(struct iphdr *iph)
{
if(NULL == iph)
return;
DEBUG_INFO("srcaddr:%d.%d.%d.%d\tdestaddr:%d.%d.%d.%d\n",
(iph->saddr >>24)&0xff,
(iph->saddr >>16)&0xff,
(iph->saddr >> 8)&0xff,
iph->saddr&0xff,
(iph->daddr >>24)&0xff,
(iph->daddr >>16)&0xff,
(iph->daddr >> 8)&0xff,
iph->daddr&0xff);
}
int main(int argc, char* argv[])
{
socklen_t len = 0;
int count = 0;
int state;
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct ulog_packet_msg *umsg = NULL;
int sock_fd, res;
struct iphdr *iph = NULL;
struct icmphdr *icp = NULL;
char buffer = {0};
len = sizeof(struct sockaddr_nl);
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NFLOG);
if(sock_fd == -1){
DEBUG_INFO("error getting socket: %s", strerror(errno));
return -1;
}
// To prepare binding
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); // self pid
src_addr.nl_groups = netlink_group_mask(MOD_ID); // multi cast
res = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
if(res < 0){
DEBUG_INFO("bind failed: %s", strerror(errno));
close(sock_fd);
return -1;
}
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
if(!nlh){
DEBUG_INFO("malloc nlmsghdr error!\n");
close(sock_fd);
return -1;
}
memset(&dest_addr,0,sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0;
dest_addr.nl_groups = 0;
memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
DEBUG_INFO("waiting received!\n");
// Read message from kernel
while(1)
{
++count;
state = recvfrom(sock_fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&dest_addr, &len);
if(state < 0)
DEBUG_INFO("recvfrom error !\n");
nlh = (struct nlmsghdr *)buffer;
if(!NLMSG_OK(nlh,state))
{
DEBUG_INFO("nlmsg error !\n");
return ERROR;
}
DEBUG_INFO("recved msg type: %d\t msg len: %d\n", nlh->nlmsg_type, nlh->nlmsg_len);
umsg =(struct ulog_packet_msg *)NLMSG_DATA(nlh);
DEBUG_INFO("mark:%ld\tindev_name: %s\toutdev_name:%s\tprefix:%s\tmac:%s\n", umsg->mark, umsg->indev_name, umsg->outdev_name, umsg->prefix, umsg->mac);
iph = (struct iphdr *)umsg->payload;
switch(iph->protocol)
{
case IPPROTO_ICMP:
{
DEBUG_INFO("this is icmp protocal !\n");
print_info(iph);
DEBUG_INFO("iph->ihl:%d\n", iph->ihl);
icp = (void *)iph + iph->ihl*4;
// icp = (void *)iph + 20;
DEBUG_INFO("icp->type:%d\t icp->code:%d\n", icp->type, icp->code);
if((icp->type == 0) && (icp->code == 0))
DEBUG_INFO("this is echo reply message !\n");
if((icp->type == 8) && (icp->code == 0))
DEBUG_INFO("this is echo request message !\n");
break;
}
case IPPROTO_TCP:
{
DEBUG_INFO("this is TCP protocal !\n");
print_info(iph);
break;
}
case IPPROTO_UDP:
{
DEBUG_INFO("this is UDP protocal !\n");
print_info(iph);
break;
}
default:
DEBUG_INFO("this is default protocal !\n");
print_info(iph);
break;
}
DEBUG_INFO("count = %d\n", count);
}
close(sock_fd);
return 0;
}
iph->ihl的长度应该是5,而这里是4?这是什么原因呀?
字节序问题?确切的说是bitfield序的问题?
先看看((char *)iph)是不是0x45。
顺便把iph->version也打印一下。 回复 2# nswcfd
嗯 嗯 是这个问题!这个该如何改是好呀?我查看了一下ntohl和ntohs是对unsigned long 和 unsigned short进行转换,这难道要我自己重写?
这怎么会出现这种情况呀,我这仅仅是内核和用户通讯呀!?
那prefix和mac的值为空一直没想明白
本帖最后由 nswcfd 于 2015-05-27 18:57 编辑
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ihl:4,
version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:4,
ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
}
ntohs和htons关注的是字节序,解决不了这个问题(bit序,应该跟编译器有关)。
应该是user space定义错了__LITTLE_ENDIAN_BITFIELD或者__BIG_ENDIAN_BITFIELD。
prefix是ULOG target的参数,默认没有。(当然也许你内核里直接调用了NFLOG的接口,而没有使用-j ULOG)
mac为空,跟在什么地方使用ULOG记录有关系,如果是OUTOUT路径,则mac还没有初始化,如果是INPUT路径,则应该有mac。
你的日志上显示,indev为空,outdev非空,则很有可能是OUTPUT路径。 用以下代码测试一下编译环境的bit endian
struct iph {
char ver:4;
char ihl:4;
};
int main()
{
char test = 0x45;
struct iph *p = (void *)&test;
if (p->ver == 4)
printf("__BIG_ENDIAN_BITFIELD\n");
else if (p->ver == 5)
printf("__LITTLE_ENDIAN_BITFIELD\n");
else
printf("sorry i don't know\n");
} 回复 5# nswcfd
我这运行的结果是BIG_ENDIAN_BITFIELD!
实在不行暴力一点,在#include <linux/ip.h>之前,#undef __LITTLE_ENDIAN_BITFIELD和__BIG_ENDIAN_BITFIELD,再#define __BIG_ENDIAN_BITFIELD。
或者不引用#include <linux/ip.h>,自己在.c里定义好struct iphdr,注意iph和version的顺序。
实在想不清楚为什么你的代码会选择了__LITTLE_ENDIAN_BITFIELD?被之前的#include间接定义了? 回复 7# nswcfd
这思路不错,我再好好检查下我的头文件包含的关系.
正是这个bit序的原因,导致了后面解析出来的icp->type 也出错。我自己以写死的方式:icp = (void *)iph +20; 那就正常了!不过这肯定是不行的!我再找找原因
关于mac和prefix为空的原因,正如你说的和在什么地方使用了ULOG有关系。我这里使用的是仅对OUTPUT链做了ULOG 处理(iptables -A OUTPUT -j ULOG --ulog-nlgroup 2)。所以才会出现为空的情况。
多谢你的回复呀! 学习了好多 {:qq11:}
页:
[1]