www.kernel.org
分类: LINUX
2012-03-20 20:44:24
[选择]NF_IP_PRE_ROUTING(local process所发送的包不在限制之列)
3、NF_IP_PRE_ROUTING下优先级的选择Hook点的Hook函数依照优先级一次执行。
2.6Kernel下(2.4Kernel下自己查询)PREROUTING的HOOK操作主要有(依优先级大小):
(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
调用2次fileread模块填充2个hash链表
调用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_install,ip_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,而一个IP占4个byte,即使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_addr和inet_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的数据