A example of NF_IP_PRE_ROUTING module(转载)

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匹配包的数量

4、总体程序框架设计

数据结构与全局变量

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];

};

init()(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元素

 

 

 

 

ip_match()

(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;

            }

get_list_from_server()

有这么几种选择:

Ø       可以在应用层写一个程序,接受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

packet_judge()

            static int packet_judge(struct sk_buff **pskb)

            {

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

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

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

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

              // 等等

}

is_in_install_list(),  is_in_update_list()

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

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

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

Ø       其他方法(未及多想)

ip_redirecton()

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的数据

 

 

 

``` #include <linux/module.h> #include <linux/kernel.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/ip.h> #include <linux/icmp.h> #include <linux/netlink.h> #include <net/sock.h> #include <linux/skbuff.h> #include <linux/string.h> #include <linux/time.h> #define NETLINK_USER 31 static struct sock *nl_sock = NULL; static unsigned int n = 100; // 传入的参数n,范围为[100~1000],默认100 // 统计信息结构体 struct ping_stat { u32 src_ip; struct timespec timestamp; }; static struct ping_stat stats[1000]; // 假设最多统计1000个包 static int stat_count = 0; // Netfilter钩子函数 static unsigned int ping_stat_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { struct iphdr *ip_header; struct icmphdr *icmp_header; unsigned int data_len; ip_header = ip_hdr(skb); if (ip_header->protocol != IPPROTO_ICMP) return NF_ACCEPT; icmp_header = (struct icmphdr *)(skb->data + (ip_header->ihl * 4)); data_len = skb->len - (ip_header->ihl * 4) - sizeof(struct icmphdr); if (data_len == n) { if (stat_count < 1000) { stats[stat_count].src_ip = ip_header->saddr; getnstimeofday(&stats[stat_count].timestamp); stat_count++; } } return NF_ACCEPT; } // Netfilter钩子结构体 static struct nf_hook_ops nfho = { .hook = ping_stat_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_FIRST, }; // Netlink消息处理函数 static void netlink_recv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; int pid; struct sk_buff *skb_out; int msg_size; int res; nlh = (struct nlmsghdr *)skb->data; pid = nlh->nlmsg_pid; // 发送方的PID // 准备响应消息 msg_size = stat_count * sizeof(struct ping_stat); skb_out = nlmsg_new(msg_size, 0); if (!skb_out) { printk(KERN_ERR "Failed to allocate new skb\n"); return; } nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); NETLINK_CB(skb_out).dst_group = 0; // 单播 memcpy(nlmsg_data(nlh), stats, msg_size); // 发送消息 res = nlmsg_unicast(nl_sock, skb_out, pid); if (res < 0) printk(KERN_ERR "Error while sending back to user\n"); } // 模块初始化函数 static int __init ping_stat_init(void) { struct netlink_kernel_cfg cfg = { .input = netlink_recv_msg, }; printk(KERN_INFO "Initializing Ping Stat Module\n"); // 注册Netfilter钩子 nf_register_hook(&nfho); // 创建Netlink socket nl_sock = netlink_kernel_create(&init_net, NETLINK_USER, &cfg); if (!nl_sock) { printk(KERN_ALERT "Error creating netlink socket\n"); return -1; } return 0; } // 模块退出函数 static void __exit ping_stat_exit(void) { printk(KERN_INFO "Exiting Ping Stat Module\n"); // 注销Netfilter钩子 nf_unregister_hook(&nfho); // 释放Netlink socket if (nl_sock) netlink_kernel_release(nl_sock); } module_init(ping_stat_init); module_exit(ping_stat_exit);```windows怎么ping指定200字节长度的报文
03-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值