xlhl3 发表于 2015-05-27 12:07

关于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?这是什么原因呀?

nswcfd 发表于 2015-05-27 16:45

字节序问题?确切的说是bitfield序的问题?
先看看((char *)iph)是不是0x45。
顺便把iph->version也打印一下。

xlhl3 发表于 2015-05-27 18:30

回复 2# nswcfd



嗯 嗯 是这个问题!这个该如何改是好呀?我查看了一下ntohl和ntohs是对unsigned long 和 unsigned short进行转换,这难道要我自己重写?
这怎么会出现这种情况呀,我这仅仅是内核和用户通讯呀!?
那prefix和mac的值为空一直没想明白


   

nswcfd 发表于 2015-05-27 18:54

本帖最后由 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路径。

nswcfd 发表于 2015-05-27 19:01

用以下代码测试一下编译环境的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");
}

xlhl3 发表于 2015-05-27 19:34

回复 5# nswcfd

我这运行的结果是BIG_ENDIAN_BITFIELD!


   

nswcfd 发表于 2015-05-27 20:03

实在不行暴力一点,在#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间接定义了?

xlhl3 发表于 2015-05-27 21:17

回复 7# nswcfd
这思路不错,我再好好检查下我的头文件包含的关系.
正是这个bit序的原因,导致了后面解析出来的icp->type 也出错。我自己以写死的方式:icp = (void *)iph +20; 那就正常了!不过这肯定是不行的!我再找找原因
关于mac和prefix为空的原因,正如你说的和在什么地方使用了ULOG有关系。我这里使用的是仅对OUTPUT链做了ULOG 处理(iptables -A OUTPUT -j ULOG --ulog-nlgroup 2)。所以才会出现为空的情况。
多谢你的回复呀! 学习了好多 {:qq11:}


   
页: [1]
查看完整版本: 关于netlink中的NETLINK_NFLOG接口使用