在内核注册自定义的网卡驱动,并通过打印用户空间和内核的交互数据,可以更深层次的理解网络协议。
驱动代码:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/string.h>
#include <linux/etherdevice.h>
#include <linux/mm_types.h>
#include <linux/slab.h>
struct net_device *mydev;
static int test_start_xmit(struct sk_buff *skb, struct net_device *dev) {
print_hex_dump(KERN_NOTICE,"",0,16,1,(void*)skb->data,skb->len,false);
kfree_skb(skb);
return NETDEV_TX_OK;
}
static int start(struct net_device *dev) {
return 0;
}
static int stop (struct net_device *dev) {
return 0;
}
static void stats(struct net_device *dev, struct rtnl_link_stats64 *storage) {
printk(KERN_ALERT "get stats\n");
u64 packets, bytes;
dev_lstats_read(dev, &packets, &bytes);
storage->rx_packets = packets;
storage->tx_packets = packets;
storage->rx_bytes = bytes;
storage->tx_bytes = bytes;
}
static struct net_device_ops net_dev_ops = {
.ndo_start_xmit = test_start_xmit,
.ndo_open = start,
.ndo_stop = stop,
.ndo_get_stats64 = stats,
};
void add_myself_netdev(void) {
int ret;
mydev = alloc_netdev(0, "pan%d", NET_NAME_UNKNOWN, ether_setup);
if (!mydev) {
goto out;
}
mydev->netdev_ops = &net_dev_ops;
ret = register_netdev(mydev);
if (ret) {
printk(KERN_ALERT "register failed\n");
free_netdev(mydev);
}
out:
printk(KERN_ALERT "add myself net dev failed\n");
}
EXPORT_SYMBOL(add_myself_netdev);
static int hello_init(void) {
printk(KERN_ALERT "init fishing\n");
add_myself_netdev();
//dump_stack();
return 0;
}
static void hello_exit(void) {
unregister_netdev(mydev);
free_netdev(mydev);
printk(KERN_ALERT "exit fishing\n");
}
//subsys_initcall(hello_init);
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("shakespeare");
Makefile文件:
ifneq ($(KERNELRELEASE),)
$(info "2nd")
obj-m := fishing.o
else
#kdir := /lib/modules/$(shell uname -r)/build
kdir := /usr/src/linux-headers-$(shell uname -r)
pwd := $(shell pwd)
all:
$(info "1st")
make -C $(kdir) M=$(pwd) modules
clean:
rm *.ko *.o *.order *.mod.c *.symvers *.mod
endif
安装系统头文件,即可进行编译,然后加载到内核
其中,需要配置ip地址
sudo ip link set pan0 up
sudo ip addr add 192.168.90.1/24 dev pan0
使用该网卡发送数据包
nc -u 192.168.90.12 8080
使用tcpdump抓包
sudo tcpdum -i pan0 -nneX
因为192.168.90.12地址的mac不在arp缓存,所以内核会发送arp广播请求。
通过对比内核打印的skb->data内容和tcpdump抓取的报文可知,skb->data的前12个字节是mac地址,接着的0806是arp协议。