Linux基于Netfilter对ip数据包的抓取、分析

因为近期工作需要分析终端发送过来的数据包,于是便在社区了解了Netfilter框架,经过总结,有了以下的文章。由于个人写代码时间不长,首先未考虑到代码的健壮性,其次也是给刚刚开始了解Netfilter框架的同学一些参考。

内容的不足之处请各位大佬多多指教。

1、什么是Netfilter?

Netfilter是Linux 2.4.x引入的一个子系统,它作为一个通用的、抽象的框架,提供一整套的hook函数的管理机制,使得诸如数据包过滤、网络地址转换(NAT)和基于协议类型的连接跟踪成为了可能,翻译成小白能听懂的话来说,就是Netfilter框架提供了主要的几个接口。当本机发送/接受数据包的时候,可以调用Netfilter上的接口(hook)对数据进行我们想要的处理。

1.1 Netfilter的hook函数是如何调用的本文基于内核版本号为:4.15,想知道自己的内核版本号,在终端下输入:uname -r

1.1.1Netfilter的5个hook点,分别为:

[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验和等检测), 目的地址转换在此点进行;

[2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;

[3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWARD包过滤在此点进行;

[4]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的源地址转换功能(包括地址伪装)在此点进行;

[5]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。

1.1.2了解完这些hook点我们就需要知道如何在hook点注册/解注我们自己的hook

**首先是hook函数的信息:

static tatic struct nf_test_ops[] nfho = {

.hook = my_func, //我们自己要在hook上做操作的函数的名字

.pf = PF_INET,//协议簇,PF_INET表示IPV4

.hooknum =NF_INET_LOCAL_IN , //hook挂载点,本次数据包解析就是在该hook点上进行

.priority = NF_IP_PRI_FIRST, //hook函数的优先级

};

**其次是hook函数的注册。

static int __init init_nf_test(void) {

int ret;

ret = nf_register_net_hooks(&init_net,nf_test_ops, ARRAY_SIZE(nf_test_ops));

if (ret < 0) {

printk("register hook fail\n"); //如果注册失败就在终端输出注册register hook fail

return ret;

}

return 0;

}

**最后是hook函数的解注。

static void __exit exit_nf_test(void) {

nf_unregister_net_hooks(&init_net,nf_test_ops, ARRAY_SIZE(nf_test_ops));

}

2、如何对IP数据进行处理

在本机接受或发送数据包的时候,所有的数据可以理解为全部打包在skb这个结构体内,我们对IP的分析就是操作函数在skb结构体内内进行指针偏移。本文不详解skb结构体,具体可参见:https://blog.csdn.net/Hbw_aini/article/details/125436745?spm=1001.2014.3001.5506

在iphdr结构中主要有以下几个参数:

struct iphdr {

#if defined(__LITTLE_ENDIAN_BITFIELD)

__u8ihl:4,//首部长度

version:4;//版本号

#else

#error"Please fix <asm/byteorder.h>"

#endif

__u8tos;//服务类型

__be16tot_len;//总长度

__be16id;//标识

__be16frag_off;//分片偏移

__u8ttl;//生存时间

__u8protocol;//协议

__sum16check;//校验和

__be32saddr;//源ip地址

__be32daddr;//目的ip地址

/*The options start here. */

};

3、核心代码

unsigned int ipt_hook(unsigned int hooknum, struct sk_buff *skb,

const struct net_device *in, const struct net_device *out,

int (*okfn)(struct sk_buff *)) {

struct iphdr *iph = ip_hdr(skb);//用ip_hdr()函数来将skb结构体内的ip数据交给iphdr

struct ethhdr *eth_header;

char *data;

eth_header = (struct ethhdr *)(skb_mac_header(skb));

iph = (struct iphdr *)(skb_network_header(skb));

hdr_dump(eth_header);

printk("src IP:'"NIPQUAD_FMT"', dst IP:'"NIPQUAD_FMT"' \n",

NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));

//以下是对ip的各个参数进行的分析并打印

printk("version:0x%02x ihl:0x%02x (%u) tos:0x%02x tot_len:0x%02x (%u) id:0x%02x (%u) ",iph->version,iph->ihl,iph->tos,iph->tot_len,iph->id);

printk("frag_off:0x%02x (%u) ttl:0x%02x (%u) protocol:0x%02x (%u) check:0x%04x ",iph->frag_off,iph->ttl,iph->protocol,iph->check);

return NF_ACCEPT;

}

4、全部代码

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/types.h>

#include <linux/skbuff.h>

#include <linux/ip.h>

#include <linux/udp.h>

#include <linux/tcp.h>

#include <linux/netfilter.h>

#include <linux/netfilter_ipv4.h>

MODULE_LICENSE("GPLv3");

unsigned int ipt_hook(unsigned int hooknum, struct sk_buff *skb,

const struct net_device *in, const struct net_device *out,

int (*okfn)(struct sk_buff *));

//hook函数定义

static struct nf_hook_ops nf_test_ops[] = {

{

.hook = (nf_hookfn *)ipt_hook,

.pf = NFPROTO_IPV4,

.hooknum = NF_IP_INET_LOCAL_IN,

.priority = NF_IP_PRI_FIRST,

},

};

//输出网络头

void hdr_dump(struct ethhdr *ehdr) {

printk("[MAC_DES:%x,%x,%x,%x,%x,%x "

"MAC_SRC: %x,%x,%x,%x,%x,%x Prot:%x]\n",

ehdr->h_dest[0],ehdr->h_dest[1],ehdr->h_dest[2],ehdr->h_dest[3],

ehdr->h_dest[4],ehdr->h_dest[5],ehdr->h_source[0],ehdr->h_source[1],

ehdr->h_source[2],ehdr->h_source[3],ehdr->h_source[4],

ehdr->h_source[5],ehdr->h_proto);

}

//将ip地址转换为点分十进制,即:0.0.0.0的格式

#define NIPQUAD(addr) \

((unsigned char *)&addr)[0], \

((unsigned char *)&addr)[1], \

((unsigned char *)&addr)[2], \

((unsigned char *)&addr)[3]

#define NIPQUAD_FMT "%u.%u.%u.%u"

unsigned int ipt_hook(unsigned int hooknum, struct sk_buff *skb,

const struct net_device *in, const struct net_device *out,

int (*okfn)(struct sk_buff *)) {

struct iphdr *iph = ip_hdr(skb);

struct ethhdr *eth_header;

char *data;

eth_header = (struct ethhdr *)(skb_mac_header(skb));

iph = (struct iphdr *)(skb_network_header(skb));

hdr_dump(eth_header);

printk("src IP:'"NIPQUAD_FMT"', dst IP:'"NIPQUAD_FMT"' \n",

NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));

printk("version:0x%02x ihl:0x%02x (%u) tos:0x%02x tot_len:0x%02x (%u) id:0x%02x (%u) ",iph->version,iph->ihl,iph->tos,iph->tot_len,iph->id);

printk("frag_off:0x%02x (%u) ttl:0x%02x (%u) protocol:0x%02x (%u) check:0x%04x ",iph->frag_off,iph->ttl,iph->protocol,iph->check);

return NF_ACCEPT;

}

static int __init init_nf_test(void) {

int ret;

ret = nf_register_net_hooks(&init_net,nf_test_ops, ARRAY_SIZE(nf_test_ops));

if (ret < 0) {

printk("register nf hook fail\n");

return ret;

}

return 0;

}

static void __exit exit_nf_test(void) {

nf_unregister_net_hooks(&init_net,nf_test_ops, ARRAY_SIZE(nf_test_ops));

}

module_init(init_nf_test);

module_exit(exit_nf_test);

Makefile

obj-m += nf_test.o

all:

make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:

make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

test:

sudo dmesg -C

sudo insmod example.ko

sudo rmmod example.ko

dmesg

猜你喜欢

转载自blog.csdn.net/qq_46230392/article/details/129138452