计算机网络的课程设计要做防火墙,老师没有限制在什么系统上面做,所以决定在Linux上实现。找了一下相关的资料,发现其实Linux有提供Netfilter/Iptables,为用户提供防火墙的功能,稍微看了一下,使用Iptables能够很方便地配置用户想要的防火墙,但是好像只能做过滤、数据报修改以及网络地址转换,好像不能做获取其中信息的功能,而且看了一下网上其他人的提问或者博客,好像想做类似的功能还是需要直接使用Netfilter。而如果想要使用Netfiler的话,需要编写hook函数,这个过程中不得不避免要编写模块。所以这里记录一下我在这个过程中做的一些尝试以及遇到的问题。
使用的平台:Ubuntu 14.10
内核版本: 3.16.0-23-generic (这个很重要啊,不用的内核可能函数都是不一样的,网上的大部分教程用的内核版本都是2.6)
2015.4.23
第一次我是编写一个hello world,在加载模块的时候以及移除模块的时候各输出一次,这里做的都是跟着网上的教程写的。
代码如下:
1 #include <linux/module.h> 2 #include <linux/kernel.h> 3 #include <linux/init.h> 4 5 6 static int __init lkp_init(void); 7 static int __exit lkp_exit(void); 8 9 static int __init lkp_init(void){ 10 printk("<1>Hello,world!\n"); 11 return 0; 12 } 13 14 static int __exit lkp_exit(void){ 15 printk("<2>Hello,world!\n"); 16 return 0; 17 } 18 19 module_init(lkp_init); 20 module_exit(lkp_exit);
Makefile:
1 ifneq ($(KERNELRELEASE),) 2 mymodule-objs:=hello.c 3 obj-m += hello.o 4 5 else 6 PWD := $(shell pwd) 7 KVER := $(shell uname -r) 8 KDIR := /lib/modules/$(KVER)/build 9 10 all: 11 $(MAKE) -C $(KDIR) M=$(PWD) 12 clean: 13 rm -rf *.o *.mod.c *.ko *.symvers *order *.markers *- 14 endif
make一次以后然后加载模块: sudo insmod hello.ko
使用指令dmesg能够查看到加载的时候的输出。
移除模块: sudo rmmod hello.ko
再次使用dmesg能够查看到移除的时候的输出。
这里这个Makefile是怎么执行的,为什么需要使用dmesg来查看输出的问题我暂时先不写,因为这些在网上都能找到而且能够比较清楚地解释,我打算写的是一些我遇到的问题。
2015.4.26
开始编写与Netfilter有关的函数,首先写的这个也是按照别人的教程给的例子写的程序。写一个钩子挂载到 LOCAL_OUT上。然后每隔四个发出去的数据包就拦截下下一个数据包。
代码如下:
1 #ifndef __KERNEL__ 2 #define __KERNEL__ 3 #endif 4 #ifndef MODULE 5 #define MODULE 6 #endif 7 #include <linux/module.h> 8 #include <linux/kernel.h> 9 #include <linux/netfilter.h> 10 #include <linux/netfilter_ipv4.h> 11 12 static int count=0; 13 14 static unsigned int func(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)){ 15 count=(count+1)%5; 16 if(count==0){ 17 return NF_DROP; 18 } 19 return NF_ACCEPT; 20 } 21 22 static struct nf_hook_ops nfho; 23 24 static int __init myhook_init(void){ 25 nfho.hook = func; 26 nfho.owner = THIS_MODULE; 27 nfho.pf = PF_INET; 28 nfho.hooknum = NF_INET_LOCAL_OUT; 29 nfho.priority = NF_IP_PRI_FIRST; 30 return nf_register_hook(&nfho); 31 } 32 33 static void __exit myhook_fini(void){ 34 nf_unregister_hook(&nfho); 35 } 36 37 module_init(myhook_init); 38 module_exit(myhook_fini);
Makefile:
1 ifneq ($(KERNELRELEASE),) 2 mymodule-objs:=test0.c 3 obj-m += test0.o 4 5 else 6 PWD := $(shell pwd) 7 KVER := $(shell uname -r) 8 KDIR := /lib/modules/$(KVER)/build 9 10 all: 11 $(MAKE) -C $(KDIR) M=$(PWD) modules 12 clean: 13 rm -rf *.o *.mod.c *.ko *.symvers *order *.markers *- 14 endif
问题来了,如果是按照网上的其他例子来写的话,make的时候就会说NF_IP_LOCAL_OUT找不到。当然还有一个警告说nfho.hook = func有问题,这个可能要看看怎样写它才会不警告,这里不理这个警告没有问题。我们继续说NF_IP_LOCAL_OUT,打开保存所有头文件的目录,发现这个宏定义有啊,就在linux/netfilter_ipv4.h里面,是从uapi/linux/netfilter_ipv4.h包含进来的,但是这里又有个问题,它是被ifndef __KERNEL__ ``` endif 包住了,所以它编译的时候没有包含进去,如下面的代码:
1 #ifndef __KERNEL__ 2 3 #include <limits.h> /* for INT_MIN, INT_MAX */ 4 5 /* IP Cache bits. */ 6 /* Src IP address. */ 7 #define NFC_IP_SRC 0x0001 8 /* Dest IP address. */ 9 #define NFC_IP_DST 0x0002 10 /* Input device. */ 11 #define NFC_IP_IF_IN 0x0004 12 /* Output device. */ 13 #define NFC_IP_IF_OUT 0x0008 14 /* TOS. */ 15 #define NFC_IP_TOS 0x0010 16 /* Protocol. */ 17 #define NFC_IP_PROTO 0x0020 18 /* IP options. */ 19 #define NFC_IP_OPTIONS 0x0040 20 /* Frag & flags. */ 21 #define NFC_IP_FRAG 0x0080 22 23 /* Per-protocol information: only matters if proto match. */ 24 /* TCP flags. */ 25 #define NFC_IP_TCPFLAGS 0x0100 26 /* Source port. */ 27 #define NFC_IP_SRC_PT 0x0200 28 /* Dest port. */ 29 #define NFC_IP_DST_PT 0x0400 30 /* Something else about the proto */ 31 #define NFC_IP_PROTO_UNKNOWN 0x2000 32 33 /* IP Hooks */ 34 /* After promisc drops, checksum checks. */ 35 #define NF_IP_PRE_ROUTING 0 36 /* If the packet is destined for this box. */ 37 #define NF_IP_LOCAL_IN 1 38 /* If the packet is destined for another interface. */ 39 #define NF_IP_FORWARD 2 40 /* Packets coming from a local process. */ 41 #define NF_IP_LOCAL_OUT 3 42 /* Packets about to hit the wire. */ 43 #define NF_IP_POST_ROUTING 4 44 #define NF_IP_NUMHOOKS 5 45 #endif /* ! __KERNEL__ */
原因:在2.6.22以及以后的内核中,NF_IP_PRE_ROUTING以及NF_IP6_PRE_ROUTING都被放在了用户态,而在内核态编程必须统一使用NF_INET_PRE_ROUTING。
所以解决的办法就是使用NF_INET_XXXXXXX来代替相关的宏就行了。
这里坑了我比较长的时间。
修改了以后再编译一次,然后加载模块以后,ping一下,然后就出现效果了,每五个包就会有一个发不出去。
/******************************************************************************************************************************************************************************************/
持续更新...