一、前言
本文主要是介绍如何对arp请求包进行拦截,并代替系统进行响应。对arp请求进行拦截,需要在驱动中进行,具体代码如下文所示。(本文仅供参考)
二、环境
OS Ubuntu 20.04.6 LTS
Linux ubuntu 5.15.0-71-generic
三、源码
驱动代码如下(arp_hook.c):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/netfilter_arp.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/if_packet.h>
#include <linux/netdevice.h>
#include <net/arp.h>
#define MAC_arg(x) x[0],x[1],x[2],x[3],x[4],x[5]
void print_arp_packet(struct sk_buff *skb) {
struct arphdr *arp;
unsigned char *arp_ptr;
__be32 sip, tip;
unsigned char *sha, *tha;
arp = (struct arphdr *)skb->data;
arp_ptr = (unsigned char *)(arp+1);
sha = arp_ptr;
memcpy(&sip, arp_ptr+ETH_ALEN, 4);
tha = arp_ptr+ETH_ALEN + 4;
memcpy(&tip, arp_ptr+ETH_ALEN+4+ETH_ALEN, 4);
printk(KERN_INFO "ARP ar_op=%s sha=%02x:%02x:%02x:%02x:%02x:%02x sip=%pI4 tha=%02x:%02x:%02x:%02x:%02x:%02x tip=%pI4\n",
(ntohs(arp->ar_op)==ARPOP_REQUEST ? "Request" : "Response") , MAC_arg(sha),&sip, MAC_arg(tha), &tip);
}
//构建arp响应包
void arp_response(struct sk_buff *skb, const struct nf_hook_state *state){
struct arphdr *arp = NULL;
unsigned char *data = NULL;
unsigned char src_hw[ETH_ALEN] = {0}, dest_hw[ETH_ALEN] = {0};
__be32 src_ip, dest_ip;
arp = (struct arphdr *)skb->data;
data = (unsigned char *)(arp+1);
//从包中拷贝mac地址等信息
memcpy(&src_ip, data + ETH_ALEN + 4 + ETH_ALEN, 4); //新包源ip=旧包目的ip
memcpy(dest_hw, data, ETH_ALEN); //新包目的MAC=旧包源MAC
memcpy(&dest_ip, data + ETH_ALEN, 4); //新包目的ip=旧包源ip
//获取本机mac
memcpy(src_hw, state->in->dev_addr, ETH_ALEN);
arp_send(ARPOP_REPLY, ETH_P_ARP, dest_ip, state->in, src_ip, dest_hw, src_hw, dest_hw);
/*arp_send(int type, int ptype, __be32 dest_ip,
struct net_device *dev, __be32 src_ip,
const unsigned char *dest_hw, const unsigned char *src_hw,
const unsigned char *target_hw)
*/
}
// 定义钩子函数
static unsigned int arp_hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct ethhdr *eth_header = NULL;
struct arphdr *arp_header = NULL;
//长度不对直接返回
if (skb->len < sizeof(struct ethhdr) + sizeof(struct arphdr)) {
return NF_ACCEPT;
}
//获取以太网头部指针
eth_header = eth_hdr(skb);
if (htons(ETH_P_ARP) != eth_header->h_proto){
return NF_ACCEPT;
}
//判断是否为ARP请求报文
//(需要注意的是邻居发的针对于网关IP的ARP请求的处理,但正常没有此情况发生,故此处暂时不处理)
arp_header = arp_hdr(skb);
if (htons(ARPOP_REQUEST) != arp_header->ar_op) {
return NF_ACCEPT;
}
//只处理固定接口名的arp报文
if (strcmp(skb->dev->name, "enp1s0") == 0)
{
print_arp_packet(skb);
arp_response(skb, state);
return NF_DROP;
}
return NF_ACCEPT;
}
//定义钩子结构体
struct nf_hook_ops arp_hook = {
.hook = arp_hook_func,
.pf = NFPROTO_ARP,
.hooknum = NF_ARP,
.priority = NF_IP_PRI_FIRST
};
static int __init arp_hook_init(void)
{
nf_register_net_hook(&init_net, &arp_hook);
printk(KERN_INFO "ARP hook module loaded\n");
return 0;
}
static void __exit arp_hook_exit(void)
{
nf_unregister_net_hook(&init_net, &arp_hook);
printk(KERN_INFO "ARP hook module unloaded\n");
}
module_init(arp_hook_init);
module_exit(arp_hook_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhazha");
MODULE_DESCRIPTION("ARP hook Module");
Makefile如下:
# Makefile for driver
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m += arp_hook.o
all: driver
driver:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
运行结果:
其它:
加载驱动命令:insmod arp_hook.ko
卸载驱动命令:remod arp_hook
查看驱动是否已经加载:lsmod | grep arp_hook
linux日志查看:tail -f /var/log/syslog
arp表查看命令:arp -a