netfilter 分析
DESCRIPTION
NETFILTER 分析. [email protected] 2006/2. 目录. introduction netfilter conntrack nat alg extension iptables match target programming netfilter. 防火墙是什么. 网络报文的选择,监控,过滤系统 部署在网络边界,关键路径上 网络报文的表示 策略的表示. 防火墙的类型. 包过滤 应用代理 状态检测. LINUX 防火墙的发展历史. linux2.0.x:ipfw linux2.2.x:ipchains - PowerPoint PPT PresentationTRANSCRIPT
www.kernelchina.org
目录 introduction netfilter conntrack nat alg extension iptables match target programming netfilter
www.kernelchina.org
防火墙是什么 网络报文的选择,监控,过滤系统 部署在网络边界,关键路径上 网络报文的表示 策略的表示
www.kernelchina.org
防火墙的类型 包过滤 应用代理 状态检测
www.kernelchina.org
LINUX 防火墙的发展历史 linux2.0.x : ipfw linux2.2.x : ipchains linux2.4.x : iptables linux2.6.x : iptables
www.kernelchina.org
nf_hooks[NPROTO][NF_MAX_HOOKS]
• NPROTO = 32 ,支持协议族的数量• NF_MAX_HOOKS = 8 ,支持检查点的数量• 检查点上的 nf_hook_ops 结构,按 priority 的值,从小到大排列
nf_hook_ops
检查点
www.kernelchina.org
struct nf_hook_ops{
struct list_head list;nf_hookfn *hook;int pf;int hooknum; int priority;
};
unsigned int nf_hookfn(unsigned int hooknum,struct sk_buff **skb,const struct net_device *in,const struct net_device *out, int (*okfn)(struct sk_buff *));
• list :链表结构; hook :检查点上调用的函数; pf :协议族;hooknum :检查点的编号; priority :此结构的优先级• nf_register_hook :注册 nf_hook_ops 结构• nf_unregister_hook :卸载 nf_hoop_ops 结构
检查点上的数据结构
www.kernelchina.org
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \(list_empty(&nf_hooks[(pf)][(hook)])\ ? (okfn)(skb)\ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))• NF_HOOK :定义检查点• (okfn)(skb) :如果检查点上没有钩子函数,直接调用这个函数;如果有钩子函数,则先遍历检查点上的钩子函数,并根据钩子函数的返回值来确定下一步的动作;如果钩子函数完全遍历,同样需要调用这个函数• nf_hook_slow :遍历检查点上的 nf_hook_ops 结构,并调用nf_hookfn
检查点的定义
www.kernelchina.org
NF_DROP : 禁止包通过NF_ACCEPT : 允许包通过NF_STOLEN : 包被本机缓存NF_QUEUE : 把包发送到用户空间NF_REPEAT : 重复上一次遍历过程
NF_HOOK
nf_hook_slow
nf_hookfn
(okfn)返回值
检查点的调用流程
www.kernelchina.org
检查点在 PF_INET 中的位置NF_IP_PRE_ROUTING
NF_IP_LOCAL_IN NF_IP_LOCAL_OUT
NF_IP_FORWARD
NF_IP_POST_ROUTING
ROUTE
ROUTE
链路层
传输层
网络层
www.kernelchina.org
NF_IP_PRE_ROUTINGHOOK FUNCTION FILE
NF_IP_PRE_ROUTING ip_rcv ip_input.c
www.kernelchina.org
NF_IP_LOCAL_INHOOK FUNCTION FILE
NF_IP_LOCAL_IN ip_local_deliver ip_input.c
www.kernelchina.org
NF_IP_FORWARDHOOK FUNCTION FILE
NF_IP_FORWARDip_forward ip_forward.c
ipmr_queue_xmit ipmr.c
www.kernelchina.org
NF_IP_LOCAL_OUTHOOK FUNCTION FILE
NF_IP_LOCAL_OUT
ip_build_and_send_pkt
ip_output.c
ip_queue_xmit ip_output.cip_build_xmit_slow ip_output.c
ip_build_xmit ip_output.csend_reset ipt_REJECT.c
send_unreach ipt_REJECT.cigmpv3_sendpack igmp.cigmp_send_report igmp.c
www.kernelchina.org
NF_IP_POST_ROUTINGHOOK FUNCTION FILE
NF_IP_POST_ROUTING
__ip_finish_output ip_output.cip_mc_output ip_output.cip_mc_output ip_output.c
www.kernelchina.org
conntrack 的检查点NF_IP_PRE_ROUTING
NF_IP_LOCAL_IN NF_IP_LOCAL_OUT
NF_IP_FORWARD
NF_IP_POST_ROUTING
ROUTE
ROUTE
链路层
传输层
网络层
www.kernelchina.org
ip_conntrack_in• if ((*pskb)->nfct)
return NF_ACCEPT;• *pskb = ip_ct_gather_frags(*pskb);• icmp_error_track(*pskb, &ctinfo,
hooknum)• if (!(ct = resolve_normal_ct(*pskb,
proto,&set_reply,hooknum,&ctinfo)))• ret = proto->packet(ct, (*pskb)-
>nh.iph, (*pskb)->len, ctinfo);• ret = ct->helper->help((*pskb)-
>nh.iph, (*pskb)->len, ct, ctinfo);
•每个包与一个状态结构关联,如果关联已存在,就不需要重复检查•在连接跟踪前要先把分片包组装成完整的包•如果是 icmp 协议的包,需要特殊处理•查找是否有与此包相关的状态结构,如果有,则根据包的信息进行状态转换,如果没有,则创建新的状态结构•不同协议对此协议包的特殊处理•此状态结构是否有相关的 helper ,如果有,就执行相应的 helper 函数
www.kernelchina.org
ip_confirm• !is_confirmed((struct ip_conntrack
*)skb->nfct->master)• ct = __ip_conntrack_get(nfct, &ctinfo);• if (CTINFO2DIR(ctinfo) !=
IP_CT_DIR_ORIGINAL)return
NF_ACCEPT;• hash = hash_conntrack(&ct-
>tuplehash[IP_CT_DIR_ORIGINAL].tuple);repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
• list_prepend(&ip_conntrack_hash[hash], &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
list_prepend(&ip_conntrack_hash[repl_hash], &ct->tuplehash[IP_CT_DIR_REPLY]);
• set_bit(IPS_CONFIRMED_BIT, &ct->status);
•如果是已确认过的包就不需要再次确认•从 skbuff 的信息中找到与之相关的状态结构•只有 ORIGINAL 方向的包可以创建状态结构, REPLY 方向的包只需匹配状态结构•计算 ORIGINAL 方向和 REPLY 方向的哈希值•如果此状态结构不在哈希表中,就把它加入哈希表•设置状态结构的已确认标记
www.kernelchina.org
ip_conntrack 状态结构......
tuplehash[O]tuplehash[R]
ip_conntrack......
......
......
sk_buff
......
......
......
sk_buff
struct ip_conntrack_tuple{
src.ipsrc.u // 不同协议,这个值不同dst.ipdst.u // 不同协议,这个值不同dst. protonum
}
ORIGINAL 方向的包REPLY 方向的包
www.kernelchina.org
ct_general............
......nfct......nf_ct_info
ip_conntracksk_buff
• 每个 sk_buff 都将与一个 ip_conntrack 关联,所以从 sk_buff 中可以得到与之关联的 ip_conntrack• 每个 sk_buff 都将与 ip_conntrack 的一个状态关联,所以从 sk_buff可以得到相应 ip_conntrack 的状态
ip_conntrack 与 sk_buff 的关系
www.kernelchina.org
......tuplehash[O]tuplehash[R]
bysource......
byipsproto
ip_conntrack_hash
bysource (for nat)
byipsproto (for nat)
ip_conntrack
ip_conntrack 与全局表的关系ip_conntrack 与全局表的关系
www.kernelchina.org
ip_conntrack 状态转换 [1]IP_CT_NEW IP_CT_RELATED
IP_CT_ESTABLISHED + IP_CT_IS_REPLY
IP_CT_ESTABLISHED
ORIGINAL
REPLY
ORIGINAL
0 4321nf_ct_info
www.kernelchina.org
ip_conntrack 状态转换 [2]任意有效状态
IP_CT_RELATED + IP_CT_IS_REPLY
0 4321nf_ct_info
IP_CT_RELATED
ORIGINAL icmp error
REPLY icmp error
www.kernelchina.org
ip_conntrack 与 tcp 协议struct ip_conntrack_protocol_tcp{
tcp_pkt_to_tuple // 从 tcp 包中得到 tupletcp_invert_tuple // 从 tcp 包中得到反向的 tupletcp_packet //tcp 协议私有状态处理tcp_new // 创建 tcp 协议的状态结构时调用tcp_exp_matches_pkt // 这个包是否符合 expect 的要求......
}struct ip_ct_tcp 状态结构中与 tcp 协议相关的私有状态
www.kernelchina.org
ip_conntrack 与 udp 协议struct ip_conntrack_protocol_udp{
udp_pkt_to_tuple // 从 udp 包中得到 tupleudp_invert_tuple // 从 udp 包中得到反向的 tupleudp_packet //udp 协议对状态结构的特殊处理udp_new // 创建 udp 协议的状态结构时调用.......
}
www.kernelchina.org
ip_conntrack 与 icmp 协议struct ip_conntrack_protocol_icmp{
icmp_pkt_to_tuple // 从 icmp 包中得到 tupleicmp_invert_tuple // 从 icmp 包中得到反向的 tupleicmp_packet //icmp 协议对状态结构的特殊处理icmp_new // 创建 icmp 协议的状态结构时调用......
}struct ip_ct_icmp 状态结构中,与 icmp 协议相关的私有信息
www.kernelchina.org
nat 的检查点NF_IP_PRE_ROUTING
NF_IP_LOCAL_IN NF_IP_LOCAL_OUT
NF_IP_FORWARD
NF_IP_POST_ROUTING
ROUTE
ROUTE
链路层
传输层
网络层
www.kernelchina.org
num_manips
nat 与 ip_conntrack
......initialized
bysource......
byipsproto
bysource
byipsproto
ip_conntrack:ip_nat_info
struct ip_nat_info_manipdirection 包的方向hooknum 检查点编号maniptype 转换类型manip 替换地址和端口 ( 或 id)
www.kernelchina.org
ip_nat_fn• ct = ip_conntrack_get(*pskb, &ctinfo);• ret = call_expect(master_ct(ct), pskb,
hooknum, ct, info);
• ret = ip_nat_rule_find(pskb, hooknum, in, out, ct, info);
• do_bindings(ct, ctinfo, info, hooknum, pskb);
•首先找到与包关联的状态结构•如果这个状态结构是从 expect 创建出来的,就调用它的父连接的 nat helper的 expect 函数来创建本连接的地址转换结构•如果状态结构的状态是IP_CT_RELATED 、 IP_CT_RELATED+IP_CT_IS_REPLY 、 IP_CT_NEW ,就查找地址转换规则来创建本连接的地址转换结构•根据状态结构中的地址转换结构对包进行地址和端口的修改
www.kernelchina.org
ip_nat_setup_info• get_unique_tuple• ip_conntrack_alter_reply• ip_ct_tuple_src_equal• ip_ct_tuple_dst_equal• place_in_hashes
•找到一个唯一的 ip 地址和端口,生成一个新的 tuple•修改 ip_conntrack 中 REPLY 方向的tuple•如果是源转换,生成 ORIGINAL 方向和REPLY 方向的地址转换结构•如果是目的转换,生成 ORIGINAL 方向和 REPLY 方向的地址转换结构•把 ip_conntrack 放入 nat 的哈希表
www.kernelchina.org
do_bindings• manip_pkt((*pskb)->nh.iph->protocol,
(*pskb)->nh.iph, (*pskb)->len,&info->manips[i].manip, info->manips[i].maniptype,
&(*pskb)->nfcache);• ret = helper->help(ct, exp, info,
ctinfo, hooknum, pskb);
•根据 ip_conntrack 中的地址转换结构修改包的地址和端口,并重新计算校验和•如果这个 ip_conntrack 有 nat helper ,则调用它的 help 函数
www.kernelchina.org
nat 与 tcp 协议struct ip_nat_protocol_tcp{
tcp_manip_pkt // 修改包的 tcp 端口tcp_in_range // 包的 tcp 端口是否在指定的范围之内tcp_unique_tuple // 找到一个唯一的 tuple.......
}
www.kernelchina.org
nat 与 udp 协议struct ip_nat_protocol_udp{
udp_manip_pkt // 修改 udp 包的端口udp_in_range //udp 包的端口是否在指定的范围之内udp_unique_tuple // 找到一个唯一的 tuple.......
}
www.kernelchina.org
nat 与 icmp 协议struct ip_nat_protocol_icmp{
icmp_manip_pkt // 修改 icmp 包的 idicmp_in_range //icmp 的 id 是否在指定的范围之内icmp_unique_tuple // 找到一个唯一的 tuple.......
}
www.kernelchina.org
......helperhelp
nat.info.helpernat.help
......
ip_conntrack_helper
ip_nat_helper
conntrack 中与 ALG 相关的参数nat 中与 ALG 相关的参数
ip_conntrack
ip_conntrack 与 ALG 的关系
www.kernelchina.org
ip_conntrack->helper->help
ip_conntrack.nat->helper->help
ip_conntrack->helper->help
ip_conntrack.nat->helper->expect
ip_conntrack_expect->expectfn
ip_conntrack.nat->helper->help控制连接流程
数据连接流程
ALG 调用流程
www.kernelchina.org
......master
.......sibling_list
listexpected_list
expectantsibling
......
......
sibling_listmaster
.......
......
ip_conntrack_expect_list
parent ip_conntrack
child ip_conntrack
parent ip_conntrack的所有子连接都可以通过 sibling_list 找到
ip_conntrack_expect
如果找到与 ip_conntrack关联的 ip_conntrack_expect ,就把它从全局链表中删掉
ip_conntrack 与 expect 的关系
www.kernelchina.org
iptables 一个包选择、监控、过滤系统 table,chain,rule 系统预定义了三个 table :
filter,nat,mangle 用户空间的数据结构和内核空间的数据结构不同 一个可扩展的架构
www.kernelchina.org
ipt_table
struct ipt_table{
struct list_head list;char name[IPT_TABLE_MAXNAMELEN];struct ipt_replace *table; // 表初始化时的数据unsigned int valid_hooks; // 表监听的检查点rwlock_t lock;struct ipt_table_info *private; // 表的数据最后存储在这里struct module *me;
};
www.kernelchina.org
ipt_entrytarget_offsetnext_offset
matchs
targettarget_offsetnext_offset
matchs
target
hook_entry[]underflow[]
ipt_table_info
ipt_entry
www.kernelchina.org
规则匹配策略 不能从一个预定义的 chain跳转到另一个预定义
的 chain ,只能从预定义的 chain跳转用户定义的 chain ,或者用户定义的 chain 之间相互跳转
不同检查点上的 chain 不能相互跳转 预定义的 chain 有默认的包处理方式,如果
chain 上的规则都没有匹配,就使用默认的处理方式 如果用户定义的 chain 上的规则都没有匹配,则返回指向这个 chain 的规则所在的 chain 的下一条规则
www.kernelchina.org
filter 表NF_IP_PRE_ROUTING
NF_IP_LOCAL_IN NF_IP_LOCAL_OUT
NF_IP_FORWARD
NF_IP_POST_ROUTING
ROUTE
ROUTE
链路层
传输层
网络层
www.kernelchina.org
nat 表NF_IP_PRE_ROUTING
NF_IP_LOCAL_IN NF_IP_LOCAL_OUT
NF_IP_FORWARD
NF_IP_POST_ROUTING
ROUTE
ROUTE
链路层
传输层
网络层
www.kernelchina.org
mangle 表NF_IP_PRE_ROUTING
NF_IP_LOCAL_IN NF_IP_LOCAL_OUT
NF_IP_FORWARD
NF_IP_POST_ROUTING
ROUTE
ROUTE
链路层
传输层
网络层
www.kernelchina.org
hook 的顺序NF_IP_POST_ROUTINGNF_IP_PRE_ROUTING
NF_IP_FORWARDNF_IP_LOCAL_OUT
conntrack[-200]
nat[dnat][-100]mangle[-150]
conntrack[max]nat[snat][100]mangle[-150]
NF_IP_LOCAL_IN
filter[0]
conntrack[max-1]
mangle[-150]mangle[-150]
filter[0]
filter[0]nat[dnat][-100]mangle[-150]
conntrack[-200]
nat[100]
www.kernelchina.org
ipt_match
struct ipt_match{
......match // 匹配 skbuffcheckentry // 检查参数是否合法destroy //模块释放时调用
};
www.kernelchina.org
ipt_entry_match
ipt_ip
ipt_entry_matchipt_entry_target
iptables_match
ipt_match
用户空间内核空间
www.kernelchina.org
ipt_target
struct ipt_target{
......target // 对包做出修改或判断checkentry // 检查参数是否合法destroy //模块释放时调用
};
www.kernelchina.org
ipt_entry_target
ipt_ip
ipt_entry_matchipt_entry_target
iptables_target
ipt_target
用户空间内核空间
www.kernelchina.org
增加一个 table 在内核中增加一个表,注意表所监听的检查点 这个表可以使用哪些match 和
target ,这个表的用途是什么
www.kernelchina.org
增加一个 match 在 iptables 中增加一个 extension ,也就是增加一个 iptables_match 增加一个内核模块,这个内核模块注册一个 ipt_match 注意用户空间向内核空间传递数据的大小
www.kernelchina.org
增加一个 target 在 iptables 中增加一个 extension ,也就是增加一个 iptables_target 增加一个内核模块,这个内核模块注册一个 ipt_target 注意用户空间向内核空间传递数据的大小
www.kernelchina.org
programming nf_hook_ops 注意网络包的流程,不要与其他功能冲突 检查点 优先级 返回值
www.kernelchina.org
programming nf_sockopt_ops pf :协议簇的编号 get_optmin , get_optmax :
getsockopt 的 optname参数的范围,不能与其他模块冲突 set_optmin , set_optmax :
setsockopt 的 optname参数的范围,不能与其他模块冲突
www.kernelchina.org
programming nf_queue_handler_t queue_handler[NPROTO] ,每个协议簇只能注册一个 queue_handler 内核空间与用户空间通过 netlink
socket收发包 用户空间注入的包被重新发送到缓存它的检查点上继续完成后续的检查 从用户空间重新注入内核空间,需要调
用 nf_reinject
www.kernelchina.org
注意事项 在自己的钩子函数中,如果修改了包,需要执行 (*pskb)->nfcache |= NFC_ALTERED ,表示这个包已经被修改,如果不能确定,则
用 NFC_UNKNOWN