花几天写了个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点如下:

[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验和等检测), 目的地址转换在此点进行;
[2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;
[3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWARD包过滤在此点进行;
[4]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的源地址转换功能(包括地址伪装)在此点进行;
[5]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。

(摘自百度百科)

更多关注点在怎样使用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"
 9
10 #define _USER_SPACE_
11
12 struct nf_hook_ops hook_in;
13 struct nf_hook_ops hook_out;
14
15 static int __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;
21
22     nf_register_hook(&hook_in);
23     return 0;
24 }
25
26 static void __exit fire_exit()
27 {
28     nf_unregister_hook(&hook_in);
29 }
30
31 //有数据包到来调用
32
33 unsigned int fire_hook_entry(
34          unsigned int hooknum,
35          struct sk_buff *skb,
36          const struct net_device *in,
37          const struct net_device *out,
38          int (*okfn)(struct sk_buff*)
39         )
40 {
41
42 #ifdef _USER_SPACE_
43     return NF_QUEUE;
44 #endif
45
46     struct iphdr *ip = ip_hdr(skb);
47     struct tcphdr *tcp = tcp_hdr(skb);
48     struct udphdr *udp = udp_hdr(skb);
49
50     if(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)));
53
54         printk("源端口号 %6d 目的端口号 %6d",ntohs(tcp->source), ntohs(tcp->dest));
55
56         if(ntohs(tcp->dest) == 80)
57         {
58             printk("状态:队列\n");
59             return NF_QUEUE;
60         }
61         else
62             printk("状态:允许通过防火墙");
63         return NF_ACCEPT;
64     }
65     else if(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));
69         return NF_ACCEPT;
70     }
71     else if(ip->protocol ==1)
72     {
73         printk("icmp connect come\n");
74         return NF_QUEUE;
75     }
76     else if(ip->protocol == 2)
77     {
78         printk("igmp conect come\n");
79         return NF_ACCEPT;
80     }
81     return NF_ACCEPT;
82
83 //    printk("packet come\n");
84     return NF_ACCEPT;
85 }
86
87 module_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>
13
14 #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  * */
40
41 static int __init fire_init();
42
43 /*
44  * 防火墙退出函数,共module_init的参数使用
45  * 填充struct nf_hook_ops结构
46  *
47  * */
48
49 static void __exit fire_exit();
50
51 /*
52  * 防火墙钩子回调。供给nf_register_hook函数的参数
53  *
54  * struct nf_hook_ops结构的
55  * hook成员使用,用与注册回调函数
56  *
57  * */
58
59 unsigned int fire_hook_entry
60         (
61          unsigned int hooknum,
62          struct sk_buff *skb,
63          const struct net_device *in,
64          const struct net_device *out,
65          int (*okfn)(struct sk_buff*)
66         );
67 /*
68  *
69  *
70  * */
71
72 #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 %s\n", 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);

static int 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("%s\t", inet_ntoa(add));    add.s_addr = ip->daddr;    printf("%s\t", inet_ntoa(add));

    switch(ip->protocol)    {        //udp        case 17:            printf("UDP\t");            struct udphdr *udp = (struct udphdr*)(pdata + sizeof(struct iphdr));            printf("%d\t%d\t", ntohs(udp->source), ntohs(udp->dest));            printf("通过\n");            break;        case 6:            printf("TCP\t");            tcp = (struct tcphdr *)(pdata + sizeof(struct iphdr));            printf("%d\t%d\t", ntohs(tcp->source), ntohs(tcp->dest), block_port);            if( (ntohs(tcp->source) == block_port||ntohs(tcp->dest) == block_port) && ntohl(ip->saddr) >=filter.from_ip && ntohl(ip->saddr)<=filter.to_ip)            {                //char out[1024];                //strcpy(out,(char*)tcp + tcp->doff*4);                //out[strlen(out)-3] = '3';                //memcpy((char*)(tcp + tcp->doff*4), out, sizeof(out));                //printf("%s\n", (char*)tcp + tcp->doff*4);                printf("拦截\n");                return nfq_set_verdict(qh,ntohl(msg->packet_id), NF_DROP,n,pdata); 

            }            else                printf("通过\n");            break;

        case 1:            printf("ICMP\t");            printf("无\t无\t");            printf("通过\n");            break;        default:            printf("un\t");            printf("通过\n");            break;    }    return nfq_set_verdict(qh,ntohl(msg->packet_id), NF_ACCEPT,n,pdata);  }

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();                return 0;            case 'v':                fire_version();                return 0;            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 error\nyou should use \n\t-f begin ip you are going to block\n-t end ip you are going to block\n\t-p for the protocol or port you are going to block\n");        fprintf(stderr,"\tfor example:filter -f 192.168.15.* -t 192.16.120.* -p http\n");        fprintf(stderr,"more information use -h\n");        exit(-1);    }

    parse_cmd(protocol_type, from, to);

    printf("\nfirewall setup successfully\n\n");    printf("  you filter information:\n");    printf("\tfrom:%s     net byte order %ld\n", from, filter.from_ip);    printf("\tto  :%s     net byte order %ld\n", to, filter.to_ip);    printf("\tprotocol:%s\n\n", filter.protocol_type);    printf("now let's firework for firewall\n\n");

    printf("源ip\t\t目的ip\t\t协议\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);

    return 0;}

void fire_help(){    printf("welcome to use my network filter firework\n");    printf("how to set your own match to filter packets:\n\n");    printf("\t-p\tfilter protocol,like http,ftp...maybe you want to use port instead\n");    printf("\t-f\tfilter ip from argument\n");    printf("\t-h\tshow help information\n");    printf("\t-v\tshow sortware information and the author information\n\n");    printf("  author:%s\n", AUTHOR);    printf("  come form:CHINA\n");    printf("  email:18277973721@sina.cn\n");    printf("  version:%s\n\n",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";    else if(strcasecmp(protocol_type, "ftp") == 0)        filter.protocol_type = "ftp";    else if(strcasecmp(protocol_type, "smtp") == 0)        filter.protocol_type = "smtp";    else    {        fprintf(stderr, "not support protocol.\nversion %s only support http,ftp or smtp protocol\nmore information see -h option\n", 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 option\n");            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 %s\ni advice you to check your input\nmore information see -h option", from, to);        exit(-1);    }    return 1;}

int get_port_by_service(char *service){    if(strcasecmp(service, "HTTP") == 0)        return 80;    if(strcasecmp(service, "FTP") == 0)        return 21;    if(strcasecmp(service, "smtp") == 0)        return 25;    return 0;}

关于命令行选项和ip地址验证这块不多说。netfilter_queue库的使用参考链接:libnetfilter_quue,读者参考头文件和官方文档探索相信能够很快就能编写自己的包过滤防火墙来。

至于其他功能,读者可以发挥自己的想象力去搞。只要不违反法律,尽情的去玩吧。

转载于:https://www.cnblogs.com/ickelin/p/4340566.html

写了个Linux包过滤防火墙相关推荐

  1. Linux包过滤防火墙概述

    文章目录 一.Linux包过滤防火墙概述 1.1.netfilter 1.2.iptables 1.3 包过滤的工作层次 二.iptables的表.链结构 2.1 规则链 2.2五链 2.3 规则链之 ...

  2. Linux包过滤防火墙

    Linux包过滤防火墙 Netfilter 位于linux内核中的包过滤功能体系 称为linux防火墙的"内核态" Iptables 位于/sbin/iptables,用来管理防火 ...

  3. Linux包过滤防火墙(SNAT,DNAT)

    文章目录 一.Linux包过滤防火墙概述 二.iptables的表,链结构 三.数据包过滤的匹配流程 四.iptables的基本语法 五.设置默认策略 六.数据包的常见类型 七.规则的匹配条件 1.匹 ...

  4. linux包过滤防火墙之iptables23事--洪水猛兽

    目录 前言 一.iptables介绍 二.iptables-主机型防火墙 2.1 规则表 2.2 规则链 2.3 表链关系 2.3.1 结构关系 ​2.3.2 数据包过滤匹配流程 2.3.3 应用顺序 ...

  5. 第十一章 Linux包过滤防火墙-netfilter--基于Linux3.10

    11.1 netfilter框架 netfilter从Linux2.4引入linux内核,是现在3.10版本的防火墙框架,该框架可实现数据包过滤.数据包处理.地址伪装.透明代理.动态网络地址转换(Ne ...

  6. Linux内核--基于Netfilter的内核级包过滤防火墙实现

    知识基础:本防火墙的开发基于对Linux内核网络栈有个良好的概念,本人对网络栈的分析是基于早期版本(Linux 1.2.13),在明确了网络栈架构的前提下,上升一步分析高级版本内核中的Netfilte ...

  7. 基于Linux系统的包过滤防火墙

    第1 章.基于路由器的包过滤防火墙 1.1 包过滤防火墙的一般概念 1.1.1 什么是包过滤防火墙 包过滤防火墙是用一个软件查看所流经的数据包的包头(header),由此决定整个包的命运.它可能会决定 ...

  8. Linux下防火墙iptables用法规则详及其防火墙配置

    原博主文章更美丽: http://www.cnblogs.com/yi-meng/p/3213925.html iptables规则 规则--顾名思义就是规矩和原则,和现实生活中的事情是一样的,国有国 ...

  9. Linux iptables 防火墙 添加删除 端口

    Linux iptables 防火墙 添加删除 端口 ps:本人亲测,阿里云2核4G5M的服务器性价比很高,新用户一块多一天,老用户三块多一天,最高可以买三年,感兴趣的可以戳一下:阿里云折扣服务器 一 ...

最新文章

  1. 使用Iterator迭代器循环集合
  2. 目前的计算机还没有实现真正的智能
  3. 产品经理跳槽面试大揭秘……
  4. 浅谈python使用多态跟不用多态的区别_python 多态和 super 用法
  5. extend_gcd求解不定方程/膜线性方程/乘法(模)逆元
  6. Postgresql的一些命令
  7. winform布局、控件
  8. 前W3C顾问Klaus Birkenbihl谈HTML5与万维网未来
  9. Elasticsearch5.X Centos7安装过程
  10. vue 打印 某块内容成pdf
  11. ie以及ie内核浏览器连不上网,其他浏览器可以,部分软件连不上网的解决办法
  12. 主力吸筹猛攻指标源码_成功率90%以上【主力吸筹+买点提示+使用方法】通达信指标公式源码...
  13. 徐姗姗 20190912-2 命令行
  14. 使用Advanced Installer制作IIS安装包(一:配置IIS和Web.config)
  15. XOM版本1.2.5
  16. java csrf 跨域_Django跨域请求CSRF的实例方法
  17. FileDownload文件的下载
  18. 今天安利几个实用的APP给你
  19. dellr710服务器(DellR710服务器做完raid安装系统找不到磁盘)
  20. 微信小程序识别二维码功能

热门文章

  1. 【SQL Server学习笔记】SQL SERVER 视图
  2. 使用Excel 通过 ODBC 连接到 MySQL 数据库
  3. python列表的用法
  4. Django 用户登陆访问限制 @login_required
  5. CSS的单位及css3的calc()及line-height百分比
  6. Ajax的用法之JQuery
  7. C# 对象深拷贝、浅铐贝、直接拷贝(转)
  8. Python爬虫(九)_案例:使用正则表达式的爬虫
  9. python字典排序取最值总结
  10. Python 爬虫从入门到进阶之路(七)