UDP socket流程(15)——ip_local_out及其调用的函数

320阅读 0评论2015-12-11 48576958
分类:LINUX

作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

前文将ip_local_out分析到了一半
  1. int ip_local_out(struct sk_buff *skb)
  2. {
  3.     int err;
  4.     /*
  5.     调用netfilter的hook检查该包是否可以发送。
  6.     */
  7.     err = __ip_local_out(skb);
  8.     /*
  9.     当err为1时,表明netfilter的hook函数告诉调用者允许包通过,并且需要调用者明确的调用hook函数的参数中 的回调函数。在此,需要调用dst_output来真正发送数据包
  10.     */
  11.     if (likely(err == 1))
  12.         err = dst_output(skb);

  13.     return err;
  14. }
之前将__ip_local_out已经学习完毕,下面看dst_output,
  1. /* Output packet to network from transport. */
  2. static inline int dst_output(struct sk_buff *skb)
  3. {
  4.     return skb_dst(skb)->output(skb);
  5. }
这个函数用于发送数据包,这里是使用struct dst_entry结构中的output函数指针来发送数据的。
通过查找output关键字,可以找到这里的output指向了ip_output这个函数
  1. int ip_output(struct sk_buff *skb)
  2. {
  3.     /* 获得网络设备的结构地址 */
  4.     struct net_device *dev = skb_dst(skb)->dev;
     /* 增加计数 */
  1.     IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);

  2.     skb->dev = dev;
  3.     skb->protocol = htons(ETH_P_IP);
     /* 这里又是一个netfilter的hook */
  1.     return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
  2.              ip_finish_output,
  3.              !(IPCB(skb)->flags & IPSKB_REROUTED));
  4. }
NF_HOOK_COND的调用如下
  1. static inline int
  2. NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sk_buff *skb,
  3.      struct net_device *in, struct net_device *out,
  4.      int (*okfn)(struct sk_buff *), bool cond)
  5. {
  6.     int ret;
  7.     /* 这里再次看到nf_hook_threash,就不继续追溯了。可以参加上一篇博客 */
  8.     if (!cond ||
  9.      (ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, INT_MIN) == 1))
  10.         ret = okfn(skb);
  11.     return ret;
  12. }
而这里的okfn又指向了ip_finish_output。
  1. static int ip_finish_output(struct sk_buff *skb)
  2. {
  3. #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
  4.     /* Policy lookup after SNAT yielded a new policy */
  5.     if (skb_dst(skb)->xfrm != NULL) {
  6.         IPCB(skb)->flags |= IPSKB_REROUTED;
  7.         return dst_output(skb);
  8.     }
  9. #endif
        /* 判断是否需要分片 */
  10.     if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
  11.         return ip_fragment(skb, ip_finish_output2);
  12.     else
  13.         return ip_finish_output2(skb);
  14. }
这里,我们先不考虑分片的情况,那么进入函数ip_finish_output2
  1. static inline int ip_finish_output2(struct sk_buff *skb)
  2. {
  3.     struct dst_entry *dst = skb_dst(skb);
  4.     struct rtable *rt = (struct rtable *)dst;
  5.     struct net_device *dev = dst->dev;
  6.     unsigned int hh_len = LL_RESERVED_SPACE(dev);
     /* 如果为组播或广播,则增加相应的计数 */
  1.     if (rt->rt_type == RTN_MULTICAST) {
  2.         IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
  3.     } else if (rt->rt_type == RTN_BROADCAST)
  4.         IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTBCAST, skb->len);

  5.     /* Be paranoid, rather than too clever. */
  6.     if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
  7.         /* 原有的buffer为硬件链路层预留的空间不足 */
  8.         struct sk_buff *skb2;
         /* 重新申请一块更大的buffer */
  1.         skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
  2.         if (skb2 == NULL) {
  3.             kfree_skb(skb);
  4.             return -ENOMEM;
  5.         }
  6.         /* 并替换原有的buffer */
  7.         if (skb->sk)
  8.             skb_set_owner_w(skb2, skb->sk);
  9.         kfree_skb(skb);
  10.         skb = skb2;
  11.     }

     /* 有缓存的下一跳的2层帧头部,这表明刚刚使用这个下一跳,
     可以使用其缓存的头部直接发送 */
  1.     if (dst->hh)
  2.         return neigh_hh_output(dst->hh, skb);
  3.     /* 有与dst关联的neighbour则使用neighbour的output函数发送*/
  4.     else if (dst->neighbour)
  5.         return dst->neighbour->output(skb);
     /* 
     既没有2层帧头的cache,也没有neighbour,表明没有连通的下一跳。
     那么这个数据是无法发送的。    
      */
  1.     if (net_ratelimit())
  2.         printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
  3.     kfree_skb(skb);
  4.     return -EINVAL;
  5. }
关于linux的路由查询,与neighbour系统可以参考网上的一些资料。

上一篇:socket流程(16)——neigh_hh_output和dev_queue_xmit
下一篇:UDP socket流程(14)——ip_local_out及其调用的函数