Linux - 模块编程初试

  计算机网络的课程设计要做防火墙,老师没有限制在什么系统上面做,所以决定在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一下,然后就出现效果了,每五个包就会有一个发不出去。

 

 

 

 

 

/******************************************************************************************************************************************************************************************/

持续更新...

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。