580 likes | 1.03k Views
NETFILTER 分析. droplet@kernelchina.org 2006/2. 目录. introduction netfilter conntrack nat alg extension iptables match target programming netfilter. 防火墙是什么. 网络报文的选择,监控,过滤系统 部署在网络边界,关键路径上 网络报文的表示 策略的表示. 防火墙的类型. 包过滤 应用代理 状态检测. LINUX 防火墙的发展历史. linux2.0.x:ipfw linux2.2.x:ipchains
E N D
NETFILTER分析 droplet@kernelchina.org 2006/2 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] nf_hook_ops • NPROTO = 32,支持协议族的数量 • NF_MAX_HOOKS = 8,支持检查点的数量 • 检查点上的nf_hook_ops结构,按priority的值,从小到大排列 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_HOOK nf_hook_slow nf_hookfn (okfn) 返回值 NF_DROP: 禁止包通过 NF_ACCEPT: 允许包通过 NF_STOLEN: 包被本机缓存 NF_QUEUE: 把包发送到用户空间 NF_REPEAT: 重复上一次遍历过程 www.kernelchina.org
检查点在PF_INET中的位置 链路层 NF_IP_PRE_ROUTING NF_IP_POST_ROUTING ROUTE 网络层 NF_IP_FORWARD ROUTE NF_IP_LOCAL_IN NF_IP_LOCAL_OUT 传输层 www.kernelchina.org
NF_IP_PRE_ROUTING www.kernelchina.org
NF_IP_LOCAL_IN www.kernelchina.org
NF_IP_FORWARD www.kernelchina.org
NF_IP_LOCAL_OUT www.kernelchina.org
NF_IP_POST_ROUTING www.kernelchina.org
conntrack的检查点 链路层 NF_IP_PRE_ROUTING NF_IP_POST_ROUTING ROUTE 网络层 NF_IP_FORWARD ROUTE NF_IP_LOCAL_IN NF_IP_LOCAL_OUT 传输层 www.kernelchina.org
ip_conntrack_in • 每个包与一个状态结构关联,如果关联已存在,就不需要重复检查 • 在连接跟踪前要先把分片包组装成完整的包 • 如果是icmp协议的包,需要特殊处理 • 查找是否有与此包相关的状态结构,如果有,则根据包的信息进行状态转换,如果没有,则创建新的状态结构 • 不同协议对此协议包的特殊处理 • 此状态结构是否有相关的helper,如果有,就执行相应的helper函数 • 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); www.kernelchina.org
ip_confirm • 如果是已确认过的包就不需要再次确认 • 从skbuff的信息中找到与之相关的状态结构 • 只有ORIGINAL方向的包可以创建状态结构,REPLY方向的包只需匹配状态结构 • 计算ORIGINAL方向和REPLY方向的哈希值 • 如果此状态结构不在哈希表中,就把它加入哈希表 • 设置状态结构的已确认标记 • !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); www.kernelchina.org
ip_conntrack状态结构 sk_buff ip_conntrack sk_buff ...... ...... ...... tuplehash[O] ...... ...... tuplehash[R] ...... ORIGINAL方向的包 ...... REPLY方向的包 struct ip_conntrack_tuple { src.ip src.u //不同协议,这个值不同 dst.ip dst.u //不同协议,这个值不同 dst. protonum } www.kernelchina.org
ip_conntrack与sk_buff的关系 ip_conntrack sk_buff ct_general ...... ...... nfct nf_ct_info ...... ...... • 每个sk_buff都将与一个ip_conntrack关联,所以从sk_buff中可以得到与之关联的ip_conntrack • 每个sk_buff都将与ip_conntrack的一个状态关联,所以从sk_buff可以得到相应ip_conntrack的状态 www.kernelchina.org
ip_conntrack与全局表的关系 ip_conntrack与全局表的关系 ip_conntrack_hash ip_conntrack ...... tuplehash[O] bysource (for nat) tuplehash[R] bysource byipsproto ...... byipsproto (for nat) www.kernelchina.org
ip_conntrack状态转换[1] ORIGINAL IP_CT_NEW IP_CT_RELATED REPLY IP_CT_ESTABLISHED + IP_CT_IS_REPLY ORIGINAL IP_CT_ESTABLISHED nf_ct_info 0 1 2 3 4 www.kernelchina.org
ip_conntrack状态转换[2] 任意有效状态 REPLY icmp error IP_CT_RELATED ORIGINAL icmp error IP_CT_RELATED + IP_CT_IS_REPLY nf_ct_info 0 1 2 3 4 www.kernelchina.org
ip_conntrack与tcp协议 struct ip_conntrack_protocol_tcp { tcp_pkt_to_tuple //从tcp包中得到tuple tcp_invert_tuple //从tcp包中得到反向的tuple tcp_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包中得到tuple udp_invert_tuple //从udp包中得到反向的tuple udp_packet //udp协议对状态结构的特殊处理 udp_new //创建udp协议的状态结构时调用 ....... } www.kernelchina.org
ip_conntrack与icmp协议 struct ip_conntrack_protocol_icmp { icmp_pkt_to_tuple //从icmp包中得到tuple icmp_invert_tuple //从icmp包中得到反向的tuple icmp_packet //icmp协议对状态结构的特殊处理 icmp_new //创建icmp协议的状态结构时调用 ...... } struct ip_ct_icmp 状态结构中,与icmp协议相关的私有信息 www.kernelchina.org
nat的检查点 链路层 NF_IP_PRE_ROUTING NF_IP_POST_ROUTING ROUTE 网络层 NF_IP_FORWARD ROUTE NF_IP_LOCAL_IN NF_IP_LOCAL_OUT 传输层 www.kernelchina.org
nat与ip_conntrack struct ip_nat_info_manip direction 包的方向 hooknum 检查点编号 maniptype 转换类型 manip 替换地址和端口(或id) ip_conntrack:ip_nat_info ...... initialized num_manips bysource bysource byipsproto ...... byipsproto www.kernelchina.org
ip_nat_fn • 首先找到与包关联的状态结构 • 如果这个状态结构是从expect创建出来的,就调用它的父连接的nat helper的expect函数来创建本连接的地址转换结构 • 如果状态结构的状态是IP_CT_RELATED、IP_CT_RELATED+IP_CT_IS_REPLY、IP_CT_NEW,就查找地址转换规则来创建本连接的地址转换结构 • 根据状态结构中的地址转换结构对包进行地址和端口的修改 • 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); www.kernelchina.org
ip_nat_setup_info • 找到一个唯一的ip地址和端口,生成一个新的tuple • 修改ip_conntrack中REPLY方向的tuple • 如果是源转换,生成ORIGINAL方向和REPLY方向的地址转换结构 • 如果是目的转换,生成ORIGINAL方向和REPLY方向的地址转换结构 • 把ip_conntrack放入nat的哈希表 • get_unique_tuple • ip_conntrack_alter_reply • ip_ct_tuple_src_equal • ip_ct_tuple_dst_equal • place_in_hashes www.kernelchina.org
do_bindings • 根据ip_conntrack中的地址转换结构修改包的地址和端口,并重新计算校验和 • 如果这个ip_conntrack有nat helper,则调用它的help函数 • 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); 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包的id icmp_in_range //icmp的id是否在指定的范围之内 icmp_unique_tuple //找到一个唯一的tuple ....... } www.kernelchina.org
ip_conntrack与ALG的关系 ip_conntrack_helper ip_conntrack ...... helper conntrack中与ALG相关的参数 help nat.info.helper nat.help nat中与ALG相关的参数 ...... ip_nat_helper www.kernelchina.org
ALG调用流程 ip_conntrack_expect->expectfn ip_conntrack->helper->help ip_conntrack->helper->help ip_conntrack.nat->helper->help ip_conntrack.nat->helper->expect 控制连接流程 ip_conntrack.nat->helper->help 数据连接流程 www.kernelchina.org
ip_conntrack与expect的关系 ip_conntrack_expect_list parent ip_conntrack 如果找到与ip_conntrack 关联的ip_conntrack_exp ect,就把它从全局链表中 删掉 ...... sibling_list ip_conntrack_expect master ....... ...... list expected_list expectant parent ip_conntrack的所有子连接都可以通过sibling_list找到 sibling ...... ...... sibling_list master ....... child ip_conntrack 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
target_offset next_offset hook_entry[] matchs underflow[] target target_offset next_offset matchs target ipt_entry ipt_entry ipt_table_info www.kernelchina.org
规则匹配策略 • 不能从一个预定义的chain跳转到另一个预定义的chain,只能从预定义的chain跳转用户定义的chain,或者用户定义的chain之间相互跳转 • 不同检查点上的chain不能相互跳转 • 预定义的chain有默认的包处理方式,如果chain上的规则都没有匹配,就使用默认的处理方式 • 如果用户定义的chain上的规则都没有匹配,则返回指向这个chain的规则所在的chain的下一条规则 www.kernelchina.org
filter表 链路层 NF_IP_PRE_ROUTING NF_IP_POST_ROUTING ROUTE 网络层 NF_IP_FORWARD ROUTE NF_IP_LOCAL_IN NF_IP_LOCAL_OUT 传输层 www.kernelchina.org
nat表 链路层 NF_IP_PRE_ROUTING NF_IP_POST_ROUTING ROUTE 网络层 NF_IP_FORWARD ROUTE NF_IP_LOCAL_IN NF_IP_LOCAL_OUT 传输层 www.kernelchina.org
mangle表 链路层 NF_IP_PRE_ROUTING NF_IP_POST_ROUTING ROUTE 网络层 NF_IP_FORWARD ROUTE NF_IP_LOCAL_IN NF_IP_LOCAL_OUT 传输层 www.kernelchina.org
hook的顺序 NF_IP_PRE_ROUTING NF_IP_POST_ROUTING mangle[-150] conntrack[-200] mangle[-150] nat[snat][100] nat[dnat][-100] conntrack[max] NF_IP_FORWARD NF_IP_LOCAL_IN NF_IP_LOCAL_OUT mangle[-150] mangle[-150] conntrack[-200] filter[0] filter[0] mangle[-150] nat[100] nat[dnat][-100] conntrack[max-1] filter[0] www.kernelchina.org
ipt_match struct ipt_match { ...... match //匹配skbuff checkentry //检查参数是否合法 destroy //模块释放时调用 }; www.kernelchina.org
ipt_entry_match iptables_match 用户空间 内核空间 ipt_ip ipt_entry_match ipt_match ipt_entry_target www.kernelchina.org
ipt_target struct ipt_target { ...... target //对包做出修改或判断 checkentry //检查参数是否合法 destroy //模块释放时调用 }; www.kernelchina.org
ipt_entry_target iptables_target 用户空间 内核空间 ipt_ip ipt_entry_match ipt_entry_target ipt_target www.kernelchina.org
增加一个table • 在内核中增加一个表,注意表所监听的检查点 • 这个表可以使用哪些match和target,这个表的用途是什么 www.kernelchina.org