利用netfilter的框架实现对arp报文的处理,这里只是打印arp报文信息,更多的处理可以在此基础上实现。

arp 首部封装格式:

使用netfilter框架处理ARP报文_linux network

内核版本 :使用netfilter框架处理ARP报文_netfilter_02

不同版本内核头文件可能不一样带来编译出错问题,可以参考这篇博客javascript:void(0) 更新一下内核。

源码如下:

/*
 *  Description : print arp packet info
 *  Date        : 20180331
 *  Author      : fuyuande
 *  Note        : Kernel version 3.4.39
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_arp.h>

#define LOG(fmt,arg...) printk("[%s %d] "fmt,__FUNCTION__,__LINE__,##arg)

/* arp内容 */
#pragma pack(push,1)    /* 字节对齐 */
struct arp_info {
    unsigned char src[ETH_ALEN];
    __be32 srcip;
    unsigned char dst[ETH_ALEN];
    __be32 dstip;    
};
#pragma pack(pop)

#define IP1(addr) ((unsigned char *)&addr)[0]
#define IP2(addr) ((unsigned char *)&addr)[1]
#define IP3(addr) ((unsigned char *)&addr)[2]
#define IP4(addr) ((unsigned char *)&addr)[3]

static unsigned int arp_input_hook(unsigned int hooknum,
			       struct sk_buff *skb,
			       const struct net_device *in,
			       const struct net_device *out,
			       int (*okfn)(struct sk_buff *))
{
    struct ethhdr * ethh = NULL;
    /* 获取L2层首部 */
    ethh = eth_hdr(skb);
    if(ethh == NULL)
    {
        return NF_ACCEPT;    
    }    
    /* 打印网络层协议类型 */
    LOG(" L3 type :%x \r\n",ethh->h_proto);
    return NF_ACCEPT;    
}
static unsigned int arp_output_hook(unsigned int hooknum,
			       struct sk_buff *skb,
			       const struct net_device *in,
			       const struct net_device *out,
			       int (*okfn)(struct sk_buff *))
{
    struct arphdr *arph = NULL;    /* arp首部 */
    struct arp_info *arpinfo = NULL;
    arph = arp_hdr(skb);           /* 获取arp首部 */ 
    
    if(arph == NULL)
    {
        LOG("Weird! arp header null \r\n");
        return NF_ACCEPT;
    }
    /* 打印arp首部信息 */
    LOG(" arp info :\r\n"
        "-------------\r\n"    
        "arp hw type :%x \r\n"
        "arp pro type :%x \r\n"
        "arp hln :%d\r\n"
        "arp plen:%d\r\n"
        "arp ops :%d\r\n"
        "-------------\r\n",        
        ntohs(arph->ar_hrd),ntohs(arph->ar_pro),arph->ar_hln,arph->ar_pln,ntohs(arph->ar_op));
    /* 打印mac地址信息 */    
    arpinfo = (unsigned char *)(arph + 1);
    LOG("\n-------------\r\n"
        "mac : %x:%x:%x:%x:%x:%x \r\n"
        "sip : %d:%d:%d:%d \r\n"
        "dmac : %x:%x:%x:%x:%x:%x \r\n"
        "dip : %d:%d:%d:%d \r\n"
        "-------------\r\n",
        arpinfo->src[0],arpinfo->src[1],arpinfo->src[2],arpinfo->src[3],arpinfo->src[4],arpinfo->src[5],
        IP1(arpinfo->srcip),IP2(arpinfo->srcip),IP3(arpinfo->srcip),IP4(arpinfo->srcip),
        arpinfo->dst[0],arpinfo->dst[1],arpinfo->dst[2],arpinfo->dst[3],arpinfo->dst[4],arpinfo->dst[5],
        IP1(arpinfo->dstip),IP2(arpinfo->dstip),IP3(arpinfo->dstip),IP4(arpinfo->dstip));    
    return NF_ACCEPT;    
}


struct nf_hook_ops arp_hook_ops[] ={
    {
        .hook = arp_input_hook,    /* 输入arp钩子函数*/
        .pf = NFPROTO_ARP,         /* 协议类型 */   
        .hooknum = NF_ARP_IN,      /* arp input 链*/  
        .priority = 0,             /* 优先级 */   
    },
    {
        .hook = arp_output_hook,   /* 输出arp钩子函数 */
        .pf = NFPROTO_ARP,         /* 协议类型 */
        .hooknum = NF_ARP_OUT,     /* arp output 链 */   
        .priority = 0,             /* 优先级 */
    },
    {}
};

static int __init arp_hook_init(void)
{
    /* 注册netfilter钩子 */
    nf_register_hooks(arp_hook_ops,ARRAY_SIZE(arp_hook_ops));
    return 0;
}

static void __exit arp_hook_exit(void)
{
    /* 注销netfilter钩子 */    
    nf_unregister_hooks(arp_hook_ops,ARRAY_SIZE(arp_hook_ops));
    return ;
}

module_init(arp_hook_init)
module_exit(arp_hook_exit)
MODULE_LICENSE("GPL");

Makefile:

obj-m := arphook.o
PWD :=$(shell pwd)
KERNEL_DIR :="/usr/src/linux-headers-3.4.39-030439-generic/"

modules:
	$(MAKE) -C $(KERNEL_DIR) M=${PWD} modules
clean:
	@rm *.ko *.mod.c  *.o  Modu*  modu* 

在内核3.4.39版本上可以直接执行如下命令编译、加载

#编译模块
sudo make
#加载模块
sudo insmod arphook.ko
#卸载模块
sudo rmmod arphook.ko

效果如下:

使用netfilter框架处理ARP报文_linux_03

一开始钩子函数注册在PF_INET上,没有抓到ARP报文,后来查了查,发现协议注册错了,更改成NFPROTO_ARP就可以。