花几天写了个so easy的Linux包过滤防火墙,估计实际意义不是很大。防火墙包括用户态执行程序和内核模块,内核模块完全可以用iptable代替。由于在编写的过程一开始写的是内核模块所以就直接用上来。
代码结构如下:
.
├── kernelspace
│ ├── Makefile
│ ├── Makefile_netlink
│ ├── modules.order
│ ├── Module.symvers
│ ├── netfilter.c
│ ├── netfilter.h
│ ├── netfilter.ko
│ ├── netfilter.mod.c
│ ├── netfilter.mod.o
│ ├── netfilter.o
│ ├── out.temp
│ └── tags
└── userspace
├── filter
├── filter_1
├── filter.c
├── filter.c~
├── load.sh
└── tags
由于在开发的过程中误删了filter源文件。后面再重新写过,过程也是挺艰辛的。后来想想自己写过一个rm命令吧,把原来的rm命令替换掉,或者还可以这样子,自己写一个命令,姑且叫delete命令吧。delete删除数据可以恢复的,再或者,用git吧,git管理想误删可不是这么容易的。
ok,闲话少扯,上代码上分析上开发过程。
先从内核模块看起,也就是文件树下的以kernelspace为根的文件。嗯~~文件挺多的,不过自己写的就三个,Makefile,netfilter.c and netfilter.h
内核模块采用linux的netfilter框架。
通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是 用户自定义的功能)。
IP层的五个HOOK点如下:
(摘自百度百科)
更多关注点在怎样使用netfilter上。两个函数
nf_register_hook ========> nf_unregister_hook。顾名思义,注册钩子,释放钩子。关键在于参数结构struct nf_hook_ops *reg的填充。函数和struct nf_hook_ops结构都可以在netfilter.h头文件中找到。作者netfilter.h目录为/usr/src/linux-head***/include/linux下找到。
struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
struct module *owner;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
关注更多的是这些成员的具体意义以及编程时候如何选择这些成员。
nf_hookfn *hook 是你自己定义的回调函数。当有符合条件的数据包到来时候会调用。
hooknum为前面提到的IP层的五个hook点的取值
prority根据uapi/linux/netfiler_ipv4.h的定义,可以取以下值
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
pf根socket编写时候类似,在此不多写。
现在献上内核模块的代码
1
/*
************************************************************************
2
> File Name: netfilter.c
3
> Author: ICKelin
4
> Mail: 18277973721@sina.cn
5
> Created Time: 2015年02月27日 星期五 02时39分09秒
6
***********************************************************************
*/
7
8 #include "netfilter.h" 910#define _USER_SPACE_
1112struct nf_hook_ops hook_in;
13struct nf_hook_ops hook_out;
1415staticint __init fire_init()
16{
17 hook_in.hook = fire_hook_entry;
18 hook_in.hooknum = NF_INET_LOCAL_IN;
19 hook_in.pf = PF_INET;
20 hook_in.priority = NF_IP_PRI_FIRST;
2122 nf_register_hook(&hook_in);
23return0;
24}
2526staticvoid __exit fire_exit()
27{
28 nf_unregister_hook(&hook_in);
29}
3031//有数据包到来调用3233 unsigned int fire_hook_entry(
34 unsigned int hooknum,
35struct sk_buff *skb,
36conststruct net_device *in,
37conststruct net_device *out,
38int (*okfn)(struct sk_buff*)
39 )
40{
4142#ifdef _USER_SPACE_
43return NF_QUEUE;
44#endif4546struct iphdr *ip = ip_hdr(skb);
47struct tcphdr *tcp = tcp_hdr(skb);
48struct udphdr *udp = udp_hdr(skb);
4950if(ip->protocol == 6)
51 {
52 printk("tcp连接:::: 源ip:%3d.%3d.%3d.%3d 目的ip %3d.%3d.%3d.%3d ", NET_TO_IP((ip->saddr)),NET_TO_IP((ip->daddr)));
5354 printk("源端口号 %6d 目的端口号 %6d",ntohs(tcp->source), ntohs(tcp->dest));
5556if(ntohs(tcp->dest) == 80)
57 {
58 printk("状态:队列n");
59return NF_QUEUE;
60 }
61else62 printk("状态:允许通过防火墙");
63return NF_ACCEPT;
64 }
65elseif(ip->protocol == 17)
66 {
67 printk("udp连接::: 源ip:%3d.%3d.%3d.%3d 目的ip %3d.%3d.%3d.%3d ", NET_TO_IP(ip->saddr), NET_TO_IP(ip->daddr));
68 printk("源端口号 %d 目的端口号 %d 状态:允许通过防火墙n", ntohs(udp->source), ntohs(udp->dest));
69return NF_ACCEPT;
70 }
71elseif(ip->protocol ==1)
72 {
73 printk("icmp connect comen");
74return NF_QUEUE;
75 }
76elseif(ip->protocol == 2)
77 {
78 printk("igmp conect comen");
79return NF_ACCEPT;
80 }
81return NF_ACCEPT;
8283// printk("packet comen");84return NF_ACCEPT;
85}
8687module_init(fire_init);
88 module_exit(fire_exit);
头文件netfilter.h包含基本文件linux头文件。在此也贴上,以便读者进行探索时候可以找到对应的头文件。
1
/*
************************************************************************
2
> File Name: netfilter.h
3
> Author: ICKelin
4
> Mail: 18277973721@sina.cn
5
> Created Time: 2015年02月27日 星期五 02时39分24秒
6
***********************************************************************
*/
7
8 #include <linux/in.h>
9 #include <linux/ip.h>
10 #include <linux/tcp.h>
11 #include <linux/udp.h>
12 #include <linux/icmp.h>
1314 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/netdevice.h>
17 #include <linux/init.h>
18 #include <linux/skbuff.h>
19 #include <linux/types.h>
20 #include <linux/inet.h>
21 #include <linux/netfilter_ipv4.h>
22/*23 * 防火墙初始化函数,供module_exit的参数使用
24 * 内部调用钩子注册函数nf_register_hook.填充
25 * struct nf_hook_ops结构
26 * struct nf_hook_ops
27 * {
28 * struct list_head list;
29 * nf_hookfn *hook;
30 * struct module *owner;
31 * u_int8_t pf;
32 * unsigned int hooknum;
33 * int priority;
34 * }
35 *
36 * 详细信息参考netfilter.h头文件
37 * nf_hookfd指定为fire_hook_entry作为回调函数
38 *
39 * */4041staticint __init fire_init();
4243/*44 * 防火墙退出函数,共module_init的参数使用
45 * 填充struct nf_hook_ops结构
46 *
47 * */4849staticvoid __exit fire_exit();
5051/*52 * 防火墙钩子回调。供给nf_register_hook函数的参数
53 *
54 * struct nf_hook_ops结构的
55 * hook成员使用,用与注册回调函数
56 *
57 * */5859 unsigned int fire_hook_entry
60 (
61 unsigned int hooknum,
62struct sk_buff *skb,
63conststruct net_device *in,
64conststruct net_device *out,
65int (*okfn)(struct sk_buff*)
66 );
67/*68 *
69 *
70 * */7172#define NET_TO_IP(addr) 73 ((unsigned char*)&addr)[0],74 ((unsigned char*)&addr)[1],75 ((unsigned char*)&addr)[2],76 ((unsigned char*)&addr)[3]
内核模块需要make
Makefile
obj-m := netfilter.o KERNELBUILD :=/lib/modules/$(shell uname -r)/build default: make -C $(KERNELBUILD) M=$(shell pwd) modules clean: rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions
内核模块其实还是挺简单的。如果编写用户层的包过滤防火墙的话没有必要在内核模块上花费太多功夫,以上内核模块实现的功能用iptable都可以实现。
至于协议解析部分,也不是三言两语能写的完。但是作者写过利用原始套接字进行抓包的程序,不过正在准备笔试就没有多大时间写博客总结。读者能看懂包解析部分的代码的是没什么问题的。
用户空间模块。用户空间模块采用的是netfilter_queue函数库。原本找资料的时候看到ipq这个库,不过后来到netfilter官网上找资料,ipq函数库被取代来。
libnetfilter_queue is a userspace library providing an API to packets that have been queued by the kernel packet filter. It is is part of a system that deprecates the old ip_queue / libipq mechanism.
libnetfilter_queue has been previously known as libnfnetlink_queue.
用户空间动起来也不难。关键是官网有api参考。看着官网的api再结合之前抓包的程序写起来就easy了。
这部分误删过一次,我那个泪奔啊,rm命令害死人,原本注释打得完美了,重写一次就没有打注释的欲望了。忘见谅。
/*
************************************************************************
> File Name: filter.c
> Author: ICKelin
> Mail: 18277973721@sina.cn
> Created Time: 2015年03月02日 星期一 01时04分38秒
***********************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <asm/byteorder.h>
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#define BUFF_SIZE 1024*10
#define IP_SIZE 50
#define AUTHOR "ICKelin"
#define VERSION "v1.1"
#define _DEBUG_
#define error(msg)
{fprintf(stderr, "%s error with %sn", msg, strerror(errno));exit(-1);}
struct filter_info
{
long from_ip;
long to_ip;
char *protocol_type;
}filter;
int parse_cmd(char *protocol_type, char *from, char *to);
void fire_help();
void fire_version();
int get_port_by_service(char *service);
staticint cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,struct nfq_data *nfa, void *data)
{
int is_block = 0;
struct nfqnl_msg_packet_hdr *msg = nfq_get_msg_packet_hdr(nfa);
if(msg == NULL)
error("nfqnl_msg_packet_hdr");
char *pdata;
int n = nfq_get_payload(nfa, (char**)&pdata);
struct iphdr *ip = (struct iphdr*)pdata;
struct tcphdr *tcp;
int block_port = get_port_by_service(filter.protocol_type);
struct in_addr add;
add.s_addr = ip->saddr;
printf("%st", inet_ntoa(add));
add.s_addr = ip->daddr;
printf("%st", inet_ntoa(add));
switch(ip->protocol)
{
//udpcase17:
printf("UDPt");
struct udphdr *udp = (struct udphdr*)(pdata + sizeof(struct iphdr));
printf("%dt%dt", ntohs(udp->source), ntohs(udp->dest));
printf("通过n");
break;
case6:
printf("TCPt");
tcp = (struct tcphdr *)(pdata + sizeof(struct iphdr));
printf("%dt%dt", ntohs(tcp->source), ntohs(tcp->dest));
if(ntohs(tcp->dest) == block_port && ntohl(ip->saddr) >=filter.from_ip && ntohl(ip->saddr)<=filter.to_ip)
printf("拦截n");
else
printf("通过n");
break;
case1:
printf("ICMPt");
printf("无t无t");
printf("通过n");
break;
default:
printf("unt");
printf("通过n");
break;
}
return0;
}
int main(int argc, char **argv)
{
char *protocol_type,*from, *to;
char opt;
int flag = 0;
while((opt = getopt(argc, argv, "hvf:t:p:")) != EOF)
{
switch(opt)
{
case‘h‘:
fire_help();
return0;
case‘v‘:
fire_version();
return0;
case‘f‘:
from = optarg;
flag=flag|1;
break;
case‘t‘:
to = optarg;
flag|=2;
break;
case‘p‘:
protocol_type = optarg;
flag|=4;
break;
default:
fire_help();
break;
}
}
if((flag^7) != 0)
{
fprintf(stderr, "command line options errornyou should use nt-f begin ip you are going to blockn-t end ip you are going to blocknt-p for the protocol or port you are going to blockn");
fprintf(stderr,"tfor example:filter -f 192.168.15.* -t 192.16.120.* -p httpn");
fprintf(stderr,"more information use -hn");
exit(-1);
}
parse_cmd(protocol_type, from, to);
printf("nfirewall setup successfullynn");
printf(" you filter information:n");
printf("tfrom:%s net byte order %ldn", from, filter.from_ip);
printf("tto :%s net byte order %ldn", to, filter.to_ip);
printf("tprotocol:%snn", protocol_type);
printf("now let‘s firework for firewallnn");
printf("源iptt目的iptt协议t源端口 目的端口 状态n");
struct nfq_handle *h;
struct nfq_q_handle *qh;
int fd;
int rv;
char buf[4096];
h = nfq_open();
if (!h)
error("nfq_open");
nfq_unbind_pf(h, AF_INET);
if (nfq_bind_pf(h, AF_INET) < 0)
error("nfq_bind_pf");
qh = nfq_create_queue(h, 0, &cb, NULL);
if (!qh)
error("nfq_create_queue");
if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0)
error("nfq_set_mode");
fd = nfq_fd(h);
while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0)
nfq_handle_packet(h, buf, rv);
nfq_destroy_queue(qh);
nfq_close(h);
return0;
}
void fire_help()
{
printf("welcome to use my network filter fireworkn");
printf("how to set your own match to filter packets:nn");
printf("t-ptfilter protocol,like http,ftp...maybe you want to use port insteadn");
printf("t-ftfilter ip from argumentn");
printf("t-htshow help informationn");
printf("t-vtshow sortware information and the author informationnn");
printf(" author:%sn", AUTHOR);
printf(" come form:CHINAn");
printf(" email:18277973721@sina.cnn");
printf(" version:%snn",VERSION);
}
void fire_version()
{
}
int parse_cmd(char *protocol_type, char *from, char *to)
{
char temp[IP_SIZE];
int index;
if(strcasecmp(protocol_type, "http") == 0)
filter.protocol_type = "http";
elseif(strcasecmp(protocol_type, "ftp") == 0)
filter.protocol_type = "ftp";
elseif(strcasecmp(protocol_type, "smtp") == 0)
filter.protocol_type = "smtp";
else
{
fprintf(stderr, "not support protocol.nversion %s only support http,ftp or smtp protocolnmore information see -h optionn", VERSION);
exit(-1);
}
while(*from)
{
if(*from != ‘.‘ && *from !=‘*‘ &&(*from<‘0‘||*from>‘9‘))
{
fprintf(stderr, "from ip address format error! format:###.###.##.#nexample:192.168.*.*nmore information use -h optionn");
exit(-1);
}
if(*from == ‘*‘)
temp[index++] = ‘0‘;
else
temp[index++] = *from;
from++;
}
temp[index] = 0;
filter.from_ip = ntohl(inet_addr(temp));
memset(temp, 0, sizeof(temp));
index = 0;
while(*to)
{
if(*to != ‘.‘ && *to !=‘*‘ &&(*to<‘0‘||*to>‘9‘))
{
fprintf(stderr, "to ip address format error! format:###.###.##.#nexample:192.168.*.*nmore information use -h option");
exit(-1);
}
if(*to == ‘*‘)
temp[index++] = ‘0‘;
else
temp[index++] = *to;
to++;
}
temp[index] = 0;
filter.to_ip = ntohl(inet_addr(temp));
if(filter.from_ip > filter.to_ip)
{
fprintf(stderr, "hello guys, there is no ip between %s to %sni advice you to check your inputnmore information see -h option", from, to);
exit(-1);
}
return1;
}
int get_port_by_service(char *service)
{
if(strcasecmp(service, "HTTP") == 0)
return80;
if(strcasecmp(service, "FTP") == 0)
return21;
if(strcasecmp(service, "smtp") == 0)
return25;
return0;
}
关于命令行选项和ip地址验证这块不多说。netfilter_queue库的使用参考链接:libnetfilter_quue,读者参考头文件和官方文档探索相信能够很快就能编写自己的包过滤防火墙来。
至于其他功能,读者可以发挥自己的想象力去搞。只要不违反法律,尽情的去玩吧。
原文:http://www.cnblogs.com/ickelin/p/4340566.html
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!