Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1637988
  • 博文数量: 511
  • 博客积分: 967
  • 博客等级: 准尉
  • 技术积分: 2560
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-06 14:19
文章分类

全部博文(511)

文章存档

2016年(11)

2015年(61)

2014年(257)

2013年(63)

2012年(119)

分类: LINUX

2014-05-17 22:59:17

作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
在前面的NAT学习中,发现NAT是建立在connection track的基础上的,那么还是需要有一些connection track的概念为好。

关键函数nf_conntrack_in
  1. unsigned int
  2. nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
  3.         struct sk_buff *skb)
  4. {
  5.     struct nf_conn *ct, *tmpl = NULL;
  6.     enum ip_conntrack_info ctinfo;
  7.     struct nf_conntrack_l3proto *l3proto;
  8.     struct nf_conntrack_l4proto *l4proto;
  9.     unsigned int dataoff;
  10.     u_int8_t protonum;
  11.     int set_reply = 0;
  12.     int ret;

     /* 
     这个条件判断不是很明白。为什么可以直接忽略以前的nfct。
     看了后面的代码后,skb->nfcth会在后面的代码中重新得到,再配合这里的注释大概明白了用意。
     当skb->nfct为有效值时,即意味着该skb已经经过了conn track,再次落到conn track时。
     如注释所说,比如发往回环的时候,会有这个情况。
     */
  1.     if (skb->nfct) {
  2.         /* Previously seen (loopback or untracked)? Ignore. */
  3.         tmpl = (struct nf_conn *)skb->nfct;
  4.         if (!nf_ct_is_template(tmpl)) {
  5.             NF_CT_STAT_INC_ATOMIC(net, ignore);
  6.             return NF_ACCEPT;
  7.         }
  8.         skb->nfct = NULL;
  9.     }

  10.     /* rcu_read_lock()ed by nf_hook_slow */
  11.     /* 
  12.     根据proto family,来得到l3的conn track的target。
  13.     这里对于netfilter来说,它将conn track的具体工作下发到具体的L3层的target处理。
  14.     这样无论是ipv4还是ipv6,netfilter的处理可以保持一致。甚至其可以支持更多的L3协议。
     对于ipv4来说,l3proto就是nf_conntrack_l3proto_ipv4。
  1.     */
  2.     l3proto = __nf_ct_l3proto_find(pf);
  3.     /* 
  4.     将L3的报文头的偏移即数据包skb传给l3->get_l4proto来得到L4的协议号即L4的起始位置。
  5.     这时L3的报文头完全由具体的L3层的proto负责解析
  6.     */
  7.     ret = l3proto->get_l4proto(skb, skb_network_offset(skb),
  8.                  &dataoff, &protonum);
  9.     if (ret <= 0) {
  10.         pr_debug("not prepared to track yet or error occured\n");
  11.         NF_CT_STAT_INC_ATOMIC(net, error);
  12.         NF_CT_STAT_INC_ATOMIC(net, invalid);
  13.         ret = -ret;
  14.         goto out;
  15.     }
     
     /*
     与L3类似,同样是通过L4的协议号得到具体的L4 proto的target。
     实现与具体协议的解耦。
     如nf_conntrack_l4proto_tcp4, nf_conntrack_l4proto_udp4等等
     */
  1.     l4proto = __nf_ct_l4proto_find(pf, protonum);

  2.     /* It may be an special packet, error, unclean...
  3.      * inverse of the return code tells to the netfilter
  4.      * core what to do with the packet. */
  5.     if (l4proto->error != NULL) {
  6.         /* 对L4数据包进行检查,由具体的L4协议负责 */
  7.         ret = l4proto->error(net, tmpl, skb, dataoff, &ctinfo,
  8.                  pf, hooknum);
  9.         if (ret <= 0) {
  10.             NF_CT_STAT_INC_ATOMIC(net, error);
  11.             NF_CT_STAT_INC_ATOMIC(net, invalid);
  12.             ret = -ret;
  13.             goto out;
  14.         }
  15.     }
    
     /* 根据L3和L4层的信息,得到conn track和其信息。后面会学习resolve_normal_ct */
  1.     ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum,
  2.              l3proto, l4proto, &set_reply, &ctinfo);
  3.     if (!ct) {
  4.         /* Not valid part of a connection */
  5.         NF_CT_STAT_INC_ATOMIC(net, invalid);
  6.         ret = NF_ACCEPT;
  7.         goto out;
  8.     }

  9.     if (IS_ERR(ct)) {
  10.         /* Too stressed to deal. */
  11.         NF_CT_STAT_INC_ATOMIC(net, drop);
  12.         ret = NF_DROP;
  13.         goto out;
  14.     }

  15.     NF_CT_ASSERT(skb->nfct);
     
     /* 
     将数据包传递给具体的L4 target进行特定的操作。
     如nf_conntrack_l4proto_udp4,会update连接conn track的age,保证不ageout
     对于nf_conntrack_l4proto_tcp4,会有更复杂的操作。
     */
  1.     ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum);
  2.     if (ret <= 0) {
  3.         /* Invalid: inverse of the return code tells
  4.          * the netfilter core what to do */
  5.         pr_debug("nf_conntrack_in: Can't track with proto module\n");
  6.         nf_conntrack_put(skb->nfct);
  7.         skb->nfct = NULL;
  8.         NF_CT_STAT_INC_ATOMIC(net, invalid);
  9.         if (ret == -NF_DROP)
  10.             NF_CT_STAT_INC_ATOMIC(net, drop);
  11.         ret = -ret;
  12.         goto out;
  13.     }
     
     /* 设置conn track的事件,不明白这里为什么叫做cache */
  1.     if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
  2.         nf_conntrack_event_cache(IPCT_REPLY, ct);
  3. out:
  4.     if (tmpl)
  5.         nf_ct_put(tmpl);

  6.     return ret;
  7. }
下面看一下resolve_normal_ct
  1. /* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
  2. static inline struct nf_conn *
  3. resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
  4.          struct sk_buff *skb,
  5.          unsigned int dataoff,
  6.          u_int16_t l3num,
  7.          u_int8_t protonum,
  8.          struct nf_conntrack_l3proto *l3proto,
  9.          struct nf_conntrack_l4proto *l4proto,
  10.          int *set_reply,
  11.          enum ip_conntrack_info *ctinfo)
  12. {
  13.     struct nf_conntrack_tuple tuple;
  14.     struct nf_conntrack_tuple_hash *h;
  15.     struct nf_conn *ct;
  16.     u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE;

     /* 通过具体的L3和L4 target得到tuple */
  1.     if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
  2.              dataoff, l3num, protonum, &tuple, l3proto,
  3.              l4proto)) {
  4.         pr_debug("resolve_normal_ct: Can't get tuple\n");
  5.         return NULL;
  6.     }

  7.     /* look for tuple match */
  8.     /* 通过tuple找到正确的conn track*/
  9.     h = nf_conntrack_find_get(net, zone, &tuple);
  10.     if (!h) {
  11.         /* 没有conn track的话,就创建一个新的conn track,并添加到hash表中 */
  12.         h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
  13.                  skb, dataoff);
  14.         if (!h)
  15.             return NULL;
  16.         if (IS_ERR(h))
  17.             return (void *)h;
  18.     }
  19.     /* 从hash 节点获得conn track */
  20.     ct = nf_ct_tuplehash_to_ctrack(h);

  21.     /* It exists; we have (non-exclusive) reference. */
  22.     if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) {
  23.         /* tuple为REPLY方向,那么ctinfo为establish+reply */
  24.         *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
  25.         /* Please set reply bit if this packet OK */
  26.         *set_reply = 1;
  27.     } else {
  28.         /* tuple为original方向,即初始的发送方向 */
  29.         /* Once we've had two way comms, always ESTABLISHED. */
  30.         if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
  31.             /* 之前收到过REPLY,那么ctinfo为established*/
  32.             pr_debug("nf_conntrack_in: normal packet for %p\n", ct);
  33.             *ctinfo = IP_CT_ESTABLISHED;
  34.         } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
  35.             pr_debug("nf_conntrack_in: related packet for %p\n",
  36.                  ct);
  37.             /* 表明这是一个相关的conn track。如ICMP error或者FTP的data session? */
  38.             *ctinfo = IP_CT_RELATED;
  39.         } else {
  40.             /* 表明这是一个新的conn track*/
  41.             pr_debug("nf_conntrack_in: new packet for %p\n", ct);
  42.             *ctinfo = IP_CT_NEW;
  43.         }
  44.         *set_reply = 0;
  45.     }
  46.     skb->nfct = &ct->ct_general;
  47.     skb->nfctinfo = *ctinfo;
  48.     return ct;
  49. }
发现Netfilter的conn track还是有点意思的。那么我后面会继续学习netfilter的conn track的。



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