计算机网络的课程设计要做防火墙,老师没有限制在什么系统上面做,所以决定在Linux上实现。找了一下相关的资料,发现其实Linux有提供Netfilter/Iptables,为用户提供防火墙的功能,稍微看了一下,使用Iptables能够很方便地配置用户想要的防火墙,但是好像只能做过滤、数据报修改以及网络地址转换,好像不能做获取其中信息的功能,而且看了一下网上其他人的提问或者博客,好像想做类似的功能还是需要直接使用Netfilter。而如果想要使用Netfiler的话,需要编写hook函数,这个过程中不得不避免要编写模块。所以这里记录一下我在这个过程中做的一些尝试以及遇到的问题。

  使用的平台:Ubuntu 14.10

  内核版本: 3.16.0-23-generic  (这个很重要啊,不用的内核可能函数都是不一样的,网上的大部分教程用的内核版本都是2.6)

2015.4.23

  第一次我是编写一个hello world,在加载模块的时候以及移除模块的时候各输出一次,这里做的都是跟着网上的教程写的。

代码如下:

 #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h> static int __init lkp_init(void);
static int __exit lkp_exit(void); static int __init lkp_init(void){
printk("<1>Hello,world!\n");
return ;
} static int __exit lkp_exit(void){
printk("<2>Hello,world!\n");
return ;
} module_init(lkp_init);
module_exit(lkp_exit);

Makefile:

 ifneq ($(KERNELRELEASE),)
mymodule-objs:=hello.c
obj-m += hello.o else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf *.o *.mod.c *.ko *.symvers *order *.markers *-
endif

  make一次以后然后加载模块: sudo insmod hello.ko

  使用指令dmesg能够查看到加载的时候的输出。

  移除模块: sudo rmmod hello.ko

  再次使用dmesg能够查看到移除的时候的输出。

  这里这个Makefile是怎么执行的,为什么需要使用dmesg来查看输出的问题我暂时先不写,因为这些在网上都能找到而且能够比较清楚地解释,我打算写的是一些我遇到的问题。

2015.4.26

  开始编写与Netfilter有关的函数,首先写的这个也是按照别人的教程给的例子写的程序。写一个钩子挂载到 LOCAL_OUT上。然后每隔四个发出去的数据包就拦截下下一个数据包。

代码如下:

 #ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h> static int count=; 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 *)){
count=(count+)%;
if(count==){
return NF_DROP;
}
return NF_ACCEPT;
} static struct nf_hook_ops nfho; static int __init myhook_init(void){
nfho.hook = func;
nfho.owner = THIS_MODULE;
nfho.pf = PF_INET;
nfho.hooknum = NF_INET_LOCAL_OUT;
nfho.priority = NF_IP_PRI_FIRST;
return nf_register_hook(&nfho);
} static void __exit myhook_fini(void){
nf_unregister_hook(&nfho);
} module_init(myhook_init);
module_exit(myhook_fini);

Makefile:

 ifneq ($(KERNELRELEASE),)
mymodule-objs:=test0.c
obj-m += test0.o else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.ko *.symvers *order *.markers *-
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 包住了,所以它编译的时候没有包含进去,如下面的代码:

 #ifndef __KERNEL__

 #include <limits.h> /* for INT_MIN, INT_MAX */

 /* IP Cache bits. */
/* Src IP address. */
#define NFC_IP_SRC 0x0001
/* Dest IP address. */
#define NFC_IP_DST 0x0002
/* Input device. */
#define NFC_IP_IF_IN 0x0004
/* Output device. */
#define NFC_IP_IF_OUT 0x0008
/* TOS. */
#define NFC_IP_TOS 0x0010
/* Protocol. */
#define NFC_IP_PROTO 0x0020
/* IP options. */
#define NFC_IP_OPTIONS 0x0040
/* Frag & flags. */
#define NFC_IP_FRAG 0x0080 /* Per-protocol information: only matters if proto match. */
/* TCP flags. */
#define NFC_IP_TCPFLAGS 0x0100
/* Source port. */
#define NFC_IP_SRC_PT 0x0200
/* Dest port. */
#define NFC_IP_DST_PT 0x0400
/* Something else about the proto */
#define NFC_IP_PROTO_UNKNOWN 0x2000 /* IP Hooks */
/* After promisc drops, checksum checks. */
#define NF_IP_PRE_ROUTING 0
/* If the packet is destined for this box. */
#define NF_IP_LOCAL_IN 1
/* If the packet is destined for another interface. */
#define NF_IP_FORWARD 2
/* Packets coming from a local process. */
#define NF_IP_LOCAL_OUT 3
/* Packets about to hit the wire. */
#define NF_IP_POST_ROUTING 4
#define NF_IP_NUMHOOKS 5
#endif /* ! __KERNEL__ */

  原因:在2.6.22以及以后的内核中,NF_IP_PRE_ROUTING以及NF_IP6_PRE_ROUTING都被放在了用户态,而在内核态编程必须统一使用NF_INET_PRE_ROUTING。

  所以解决的办法就是使用NF_INET_XXXXXXX来代替相关的宏就行了。

  这里坑了我比较长的时间。

  修改了以后再编译一次,然后加载模块以后,ping一下,然后就出现效果了,每五个包就会有一个发不出去。

2015.4.27

  先说一下在linux下用什么编辑环境完成内核、模块的开发。其实用一个vim来写也是没有问题,但是对于刚入门而且使用vim不熟练的新手来说,还是使用IDE比较好,毕竟用IDE有提示。这里推荐一篇介绍怎样用Eclipse来做内核开发的文章。 http://blog.chinaunix.net/uid-24512513-id-3183457.html

  这一次看了一下内核与用户空间通信的问题,在网上看了一下别人的博客,内核与用户空间通信的方法有很多种,这里我尝试的方法是使用socket来完成两者之间的通信。因为我对socket还不算太了解,所以这一次只能算是尝试一下。

  这一次的实验内容是写一个模块接受用户空间的程序发出来的信息,然后在系统记录里面输出语句,然后在用户空间的程序需要模块的信息时向用户空间的程序发出信息。先上代码:

模块的代码:

modules.c

 #ifndef __KERNEL__
#define __KERNEL__
#endif #ifndef MODULE
#define MODULE
#endif #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/uaccess.h>
#define SOCKET_OPS_BASE 128
#define SOCKET_OPS_SET (SOCKET_OPS_BASE)
#define SOCKET_OPS_GET (SOCKET_OPS_BASE)
#define SOCKET_OPS_MAX (SOCKET_OPS_BASE+1) #define KMSG "a message from kernel"
#define KMSG_LEN sizeof("a message from kernel") MODULE_LICENSE("GPL"); static int recv_msg(struct sock *sk,int cmd,void __user* user,unsigned int len){
int ret = ;
printk(KERN_INFO "sockopt: recv_msg()\n");
if(cmd == SOCKET_OPS_SET){
char umsg[];
int len = sizeof(char)*;
memset(umsg,,len);
ret = copy_from_user(umsg,user,len);
printk("recv_msg:umsg=%s. ret=%d\n",umsg,ret);
}
return ;
} static int send_msg(struct sock *sk,int cmd,void __user *user,int *len){
int ret = ;
printk(KERN_INFO "sockopt:send_msg()\n");
if(cmd == SOCKET_OPS_GET){
ret = copy_to_user(user,KMSG,KMSG_LEN);
printk("send_msg:umsg=%s. ret=%d.success\n",KMSG,ret);
}
return ;
} static struct nf_sockopt_ops test_sockops; static int __init init_sockopt(void){
test_sockops.pf = PF_INET;
test_sockops.set_optmin = SOCKET_OPS_SET;
test_sockops.set_optmax = SOCKET_OPS_MAX;
test_sockops.set = recv_msg;
test_sockops.get_optmin = SOCKET_OPS_GET;
test_sockops.get_optmax = SOCKET_OPS_MAX;
test_sockops.get = send_msg;
test_sockops.owner = THIS_MODULE; printk(KERN_INFO "sockopt: init_sockopt()\n");
return nf_register_sockopt(&test_sockops);
} static void __exit fini_sockopt(void){
printk(KERN_INFO "sockopt:fini_sockopt()\n");
nf_unregister_sockopt(&test_sockops);
} module_init(init_sockopt);
module_exit(fini_sockopt);

用户空间的代码 main.c:

 #include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>
#include <errno.h> #define SOCKET_OPS_BASE 128
#define SOCKET_OPS_SET (SOCKET_OPS_BASE)
#define SOCKET_OPS_GET (SOCKET_OPS_BASE)
#define SOCKET_OPS_MAX (SOCKET_OPS_BASE+1) #define UMSG "a message from userspace"
#define UMSG_LEN sizeof("a message from userspace") char kmsg[]; int main(void){
int sockfd;
int len;
int ret;
//if you want to create the socket success,you must use root right to run this pragramme
sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
if(sockfd < ){
printf("can not create a socket\n");
printf("create socket error : %s",strerror(errno));
return -;
} ret = setsockopt(sockfd,IPPROTO_IP,SOCKET_OPS_SET,UMSG,UMSG_LEN);
printf("setsockopt: ret = %d.msg=%s\n",ret,UMSG);
len = sizeof(char)*; ret = getsockopt(sockfd,IPPROTO_IP,SOCKET_OPS_GET,kmsg,&len);
printf("getsockopt: ret=%d.msg=%s\n",ret,kmsg);
if(ret!=){
printf("getsockopt error:errno=%d,errstr=%s\n",errno,strerror(errno));
}
close(sockfd);
return ;
}

Makefile:

 ifneq ($(KERNELRELEASE),)
mymodule-objs :=modules.c
obj-m += modules.o
else
PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
gcc -o main main.c
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf *.o *.mod.c *.symvers *order *.markers *-
endif

  这是实验的代码其实大部分都是按网上的教程写的,但是这里讲一下我在从编写到运行成功遇到的问题。

  首先,从网上找的样例代码基本没有问题,但是Makefile文件我没有使用样例提供的代码,一是因为他写得有点复杂,我刚入门看得不是很懂,然后我就按照之前hello world的Makefile写了一个这次用的Makefile,结构基本一样,就是多了一步编译用户空间的代码,这里使用gcc编译就可以了。

  其次,编译成功并把模块加载成功以后,我运行用户空间的程序,因为最近才开始对linux有一定的了解,这里所以下怎样在终端运行可运行的文件:直接输入名字好像是无法运行的,例如我运行编译好的main,如果直接输入main,是无法运行的,但是可以使用路径名运行,就是说使用 ./main来运行,当然使用绝对路径来运行应该也是没有问题的。

  运行main,发现有出问题了,无法创建socket,输出sockfd结果为-1,在网上找了一下解决办法,发现可以使用trerror(errno)来输出错误的提示,输出看一下,发现原来是权限不够,所以如果想要运行main,还是得使用sudo ./main 运行。

  输出结果发现没有问题,除了输出来的语句格式太恶心了(→_→不要吐槽我)。

  详细代码是怎样跑的,我暂时先不写了,一是最近时间真不够用,二是这段代码还是比较容易读懂的,当然,中途可能需要去看一下那些宏定义是什么意思。

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

持续更新...

Linux - 模块编程初试的更多相关文章

  1. Linux模块编程框架

    Linux是单内核系统,可通用计算平台的外围设备是频繁变化的,不可能将所有的(包括将来即将出现的)设备的驱动程序都一次性编译进内核,为了解决这个问题,Linux提出了可加载内核模块(Loadable ...

  2. 初探linux内核编程,参数传递以及模块间函数调用

    一.前言                                  我们一起从3个小例子来体验一下linux内核编程.如下: 1.内核编程之hello world 2.模块参数传递 3.模块间 ...

  3. Linux内核模块编程——Hello World模块

    Linux内核模块编程 编程环境 Ubuntu 16.04 LTS 什么是模块 内核模块的全称是动态可加载内核模块(Loadable Kernel Modul,KLM),可以动态载入内核,让它成为内核 ...

  4. linux网络编程 no route to host 解决方案

    linux网络编程 no route to host 解决方案 [整合资料] (2013-05-13 21:38:12) 转载▼ 标签: net iptables it 分类: Linux 参考资料h ...

  5. Linux网络编程入门 (转载)

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  6. [转] - Linux网络编程 -- 网络知识介绍

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  7. 【转】Linux网络编程入门

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  8. 《转》Linux网络编程入门

    原地址:http://www.cnblogs.com/duzouzhe/archive/2009/06/19/1506699.html (一)Linux网络编程--网络知识介绍 Linux网络编程-- ...

  9. Linux系统编程【转】

    转自:https://blog.csdn.net/majiakun1/article/details/8558308 一.Linux系统编程概论 1.1 系统编程基石 syscall: libc:标准 ...

随机推荐

  1. Hexo 添加Live2D看板娘

    title: Hexo 添加 Live2D看板娘 二次元什么的最喜欢了[大好きです] 准备 项目地址 live2d模型 部分模型预览 开始 首先进入Hexo博客根目录安装live2d插件 $ npm ...

  2. macbookpro安装Ubuntu16.04.1 LTS爬坑之旅。亲测有效(集众家之长)。安装时间为2017-11-19。

    1.格式化U盘 要求:(1)切换分区格式为Mac OS扩展 (日志型):(2)方案(scheme)设置为:GUID Partition Map:如图(使用mac自带磁盘工具) 2.给Ubuntu划分磁 ...

  3. [读书笔记1]《C语言嵌入式系统编程修炼》

      大学前两年一直搞的是单片机,写的是嵌入式C语言程序,走过了不少弯路,现在感觉仍然在走弯路.有幸偶尔看到了这篇文章,深感自己以前写程序的时候存在很多误区.现写篇博客做下总结. 作者:宋宝华出处:天极 ...

  4. 51nod1265判断四点共面

    1265 四点共面 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 给出三维空间上的四个点(点与点的位置均不相同),判断这4个点是否在同一个平面内(4点共 ...

  5. Android 性能优化(24)*性能工具之「Traceview,dmtracedump」Profiling with Traceview and dmtracedump :记录并查看函数调用栈*

    Profiling with Traceview and dmtracedump In this document Traceview Layout         Traceview工具界面介绍 T ...

  6. 2、ipconfig命令

    该命令能够显示出正在使用的计算机的IP信息情况.这些信息包括IP地址.子网掩码.默认网关(连接本地计算机与Internet的计算机).通过IP地址可以进行扫描.远程管理.入侵检测等.ipconfig命 ...

  7. [转]Git使用基础篇

    http://www.git-scm.com/ https://try.github.io/levels/1/challenges/1 本文转自:http://www.open-open.com/li ...

  8. Quartz.Net学习笔记(1)-完整的例子

    一.开发环境 系统:Win10 编译器:VS2013 .Net版本:4.5 Quartz版本:2.3.3 二.涉及程序集 Common.Logging.Core.dll Common.Logging. ...

  9. Listview多种布局的使用

    ListView中有两个可以用来让ListView可以在视图中显示多种布局的方法,分别是getItemType和getViewTypeCount 其中 getItemViewType返回的是有参数po ...

  10. python利用requests统计1个接口的响应时间

    参照 https://www.cnblogs.com/yoyoketang/p/8035428.html requests统计接口的响应时间有2种方式 r.elapsed.total_seconds( ...