Chinaunix首页 | 论坛 | 博客
  • 博客访问: 365659
  • 博文数量: 166
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-21 17:29
文章分类

全部博文(166)

文章存档

2015年(60)

2014年(99)

2013年(7)

我的朋友

分类: C/C++

2014-03-17 01:23:56

1、 要做什么
  • netfilter Hook点注册一个自己的Hook函数,截取每一个数据包
  • 读取文件,获取IP列表
  • Ip匹配
  • 是否进行重定向的工作,是否丢弃等

2、netfilter结构以及HOOK点的选择

 

Fig.1 Hook Point of netfilter

[选择]NF_IP_PRE_ROUTING(local process所发送的包不在限制之列)

3、NF_IP_PRE_ROUTING下优先级的选择

            Hook点的Hook函数依照优先级一次执行。

            2.6Kernel(2.4Kernel下自己查询)PREROUTINGHOOK操作主要有(依优先级大小)

(1)     .hook = ip_sabotage       .priority = NF_IP_PRI_FIRST

      丢弃skb结构中设置桥参数但没有相关桥标志的包

(2)     .hook = ip_conntrack_defrag  .priority = NF_IP_PRI_CONNTRACK_ DEFRAG //(-400)

      完成分片重组,之后处理过程中的包都是非分片包,直到包要送出本机

(3)     .hook = ipt_hook   .priority = NF_IP_PRI_RAW //(-300)

      raw表,提供对收到的数据包在连接跟踪前进行处理的手段

(4)     .hook = ip_conntrack_in  .priority = NF_IP_PRI_CONNTRACK //(-200)

  完成连接跟踪,为每个skb找到所属连接(ESTABLISHED, REPLY)或新建连接(NEW, RELATED)

(5)     .hook = ipt_route_hook  .priority = NF_IP_PRI_MANGLE //(-150)

      mangle表,提供对收到的数据包进行修改的处理

(6)     .hook = ip_nat_in   .priority = NF_IP_PRI_NAT_DST //(-100)

      对刚收到的本机skb包进行目的NAT操作,只对NEW包处理

(7)     .hook = ing_hook   .priority = NF_IP_PRI_FILTER + 1 //(1)

      对进入本机的skb包进行排队处理,Qos操作

            [选择] .priority = NF_IP_PRI_CONNTRACK + 1(暂定)

       经过ip_conntrack_in之后,就可以判断连接状态,这样在进行IP匹配之前,可以判断这个状态,进一步减少要进行IP匹配包的数量

、总体程序框架设计

数据结构与全局变量

struct ip_hash_data

{

       char       *ip_str;

};

 

typedef struct ip_hash_data *ip_hash_elem;

 

struct ip_hash_entry

{

       ip_hash_elem        elem;

       struct ip_hash_entry     *next;

 

};

 

typedef struct ip_hash_entry *ip_hash_table;

 

struct global_param

{

       ip_hash_table      *ipt_install;

       ip_hash_table      *ipt_update;

       int                  ip_num_install;

       int                  ip_num_update;

       struct ip_hash_data      data_install[4999];

       struct ip_hash_data      data_update[4999];

};

()(example, 2.6 kernel , 2.4 kernel改变不大)

调用ip_hash_create创建2个空hash链表ipt_install ipt_update

调用2fileread模块填充2hash链表

调用rising_ip_match模块进行过滤匹配以及后续处理

 

static struct nf_hook_ops ip_match_ops = {

.hook = ip_match,  //主函数

.owner =  THIS_MODULE,

.pf = PF_INET,

.hooknum = NF_IP_PRE_ROUTING,

.priority = NF_IP_PRE_CONNTRACK + 1,

           };

 

            static int __init  init(void)

            {

              int ret;

              ret = nf_register_hook(ip_match_ops);  //注册自己的Hook函数

              if(ret < 0)

                     goto cleanup_hook;

              return ret;

 

            cleanup_hook:

              nf_unregister_hook(ip_match_ops)

              return ret;

            }

 

            module_init(init);

            ...

file_read()

static int fileread(const char *filename, ip_hash_table **ipt)

根据filename读取文件获取ip列表

调用hash模块的ip_hash_insert插入hash元素

 

()

(1) int packet_judge(struct sk_buff **pskb)

在匹配IP列表匹配之前,先允许通过一些没必要IP匹配的包:

从外网卡进来的包,不是IP包,包状态不是NEW的包

(2) int is_in_ip_list(struct sk_buff **pskb, ip_hash_table *ipt,)

调用ip_hash_find查找源IP是否在hash链表中

(3) void ip_redirection(struct sk_buff **pskb, char *ip_str)

       针对NEW包完成重定向功能,包目的地址改成ip_str

 

static unsigned int ip_match(

unsigned int hook,

struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

int (*okfn)(struct sk_buff *))

            {

              int ret;

              ret = get_list_from_server();

              if(ret < 0) {

                     printk(KERN_NOTICE “cannot get list from server.”);

                     return NF_DROP;  //拒绝所有包通过

              }

              //让一些不用IP匹配的包通过,比如不是IP包,或者ESTABLISHED的包,或者从外网卡发送过来的包。

              ret = packet_judge();

              if (ret == 0)

                     return NF_ACCEPT;

             

              //进行IP匹配

              if(is_in_install_list) {

                     if(is_in_update_list)

                            return NF_ACCEPT;         //允许通过

                     else

                            ip_redirection();           //转向update页面

              } else

                     ip_redirection();                  //转向安装页面

              return NF_ACCEPT;

            }

有这么几种选择:

Ø       可以在应用层写一个程序,接受ip_list并写入文件ip_list_installip_list_update。而在内核里只要sys_read()进行简单的文件读取。

Ø       在内核中使用常用的socket函数完成ip_list的获取。比如调用bind(),可以用sys_socketcall(SYS_BIND, args)替代。

Ø       使用kernel socket lib完成ip_list的获取。常用的函数比如 sock_create_kern(), send_msg()

Ø       使用内核线程(没有mm域,会造成竞争以及并发的问题,不推荐使用)

Ø       自己构建TCP包,依葫芦画瓢构建SYN ACK的数据结构sk_buff以及TCP DATA,再用dev_queue_xmit()发送出去(该方法着眼于具体的包,有很多可以改进的地方,比如可以与服务器商定,在含有ip_list的包中,sk_buff的数据区域的最前面多加一些信息用于判断这个包是否位含有ip_list的包)

           

            问题:一个程序的kernel内核栈一般为8KB,而一个IP4byte,即使4000个也占有16KB

            static int packet_judge(struct sk_buff **pskb)

            {

              //sk_buff中包含有三个union,分别为传输层、网络层和链路层的头,可以通

              //过判断这些头部进行这些数据包的率选。

              //当然,还可以通过sk_buff的其他部分进行判断,比如成员struct nf_ct_info           //*nfct 判断IP包的连接状态是否为ESTABLISHED

              //再比如判断是哪个网卡接受过来的包,判断是内网卡还是外网卡

              // 等等

}

IP的匹配无论放在核还是核外都是一件很耗资源的事情(建议放在内核外面,不然会慢内核效率)

由于IP列表包含的IP数量多,一项一项地匹配是意见效率不高的事情,改进的一些想法有(留待进一步讨论)

Ø       hash表:一种用空间换取时间的方法,因为对于机器而言,用数组下标找寻数据是很快的方法,而hash表的作用就是把散列的数据用数组的形式进行存储。

Ø       其他方法(未及多想)

static int ip_redirection(struct sk_buff **pskb, u_int_32_t ip)

{

//修改sk_buff中ip头的daddr,指定目的地址

(*pskb)->nh.iph->daddr = ip; //?

}

注1:替代inet_addrinet_itoa

unsigned long inet_addr_my(char *ip_str);

char *inet_itoa_my(unsigned long ip_num);

       char *iota(unsigned long num);

 

注2:sk_buff理解的差异:在这个插入点上,即使时TCP包,我发现(*pskb)->h.th->dest并没有得到我想要的目的端口,而只能通过struct tcphdr *tcph = (struct tcphdr *)((*pskb)->nh.iph + (*pskb)->nh.iph->ihl)来获取tcph的数据

 

 

 

阅读(666) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~