作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
前文将ip_local_out分析到了一半
- int ip_local_out(struct sk_buff *skb)
-
{
-
int err;
-
/*
-
调用netfilter的hook检查该包是否可以发送。
-
*/
-
err = __ip_local_out(skb);
-
/*
-
当err为1时,表明netfilter的hook函数告诉调用者允许包通过,并且需要调用者明确的调用hook函数的参数中 的回调函数。在此,需要调用dst_output来真正发送数据包
-
*/
-
if (likely(err == 1))
-
err = dst_output(skb);
-
-
return err;
- }
之前将__ip_local_out已经学习完毕,下面看dst_output,
- /* Output packet to network from transport. */
-
static inline int dst_output(struct sk_buff *skb)
-
{
-
return skb_dst(skb)->output(skb);
- }
这个函数用于发送数据包,这里是使用struct dst_entry结构中的output函数指针来发送数据的。
通过查找output关键字,可以找到这里的output指向了ip_output这个函数
- int ip_output(struct sk_buff *skb)
-
{
- /* 获得网络设备的结构地址 */
-
struct net_device *dev = skb_dst(skb)->dev;
/* 增加计数 */
-
IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
-
-
skb->dev = dev;
-
skb->protocol = htons(ETH_P_IP);
/* 这里又是一个netfilter的hook */
-
return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
-
ip_finish_output,
-
!(IPCB(skb)->flags & IPSKB_REROUTED));
- }
NF_HOOK_COND的调用如下
- static inline int
-
NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sk_buff *skb,
-
struct net_device *in, struct net_device *out,
-
int (*okfn)(struct sk_buff *), bool cond)
-
{
-
int ret;
- /* 这里再次看到nf_hook_threash,就不继续追溯了。可以参加上一篇博客 */
-
if (!cond ||
-
(ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, INT_MIN) == 1))
-
ret = okfn(skb);
-
return ret;
- }
而这里的okfn又指向了ip_finish_output。
- static int ip_finish_output(struct sk_buff *skb)
-
{
-
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
-
/* Policy lookup after SNAT yielded a new policy */
-
if (skb_dst(skb)->xfrm != NULL) {
-
IPCB(skb)->flags |= IPSKB_REROUTED;
-
return dst_output(skb);
-
}
-
#endif
/* 判断是否需要分片 */ -
if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
-
return ip_fragment(skb, ip_finish_output2);
-
else
-
return ip_finish_output2(skb);
- }
这里,我们先不考虑分片的情况,那么进入函数ip_finish_output2
- static inline int ip_finish_output2(struct sk_buff *skb)
-
{
-
struct dst_entry *dst = skb_dst(skb);
-
struct rtable *rt = (struct rtable *)dst;
- struct net_device *dev = dst->dev;
-
unsigned int hh_len = LL_RESERVED_SPACE(dev);
/* 如果为组播或广播,则增加相应的计数 */
-
if (rt->rt_type == RTN_MULTICAST) {
-
IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
-
} else if (rt->rt_type == RTN_BROADCAST)
-
IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTBCAST, skb->len);
-
-
/* Be paranoid, rather than too clever. */
-
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
- /* 原有的buffer为硬件链路层预留的空间不足 */
-
struct sk_buff *skb2;
/* 重新申请一块更大的buffer */
-
skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
-
if (skb2 == NULL) {
-
kfree_skb(skb);
-
return -ENOMEM;
-
}
- /* 并替换原有的buffer */
-
if (skb->sk)
-
skb_set_owner_w(skb2, skb->sk);
-
kfree_skb(skb);
-
skb = skb2;
-
}
/* 有缓存的下一跳的2层帧头部,这表明刚刚使用这个下一跳,
可以使用其缓存的头部直接发送 */
-
if (dst->hh)
-
return neigh_hh_output(dst->hh, skb);
- /* 有与dst关联的neighbour则使用neighbour的output函数发送*/
-
else if (dst->neighbour)
-
return dst->neighbour->output(skb);
/*
既没有2层帧头的cache,也没有neighbour,表明没有连通的下一跳。
那么这个数据是无法发送的。
*/
-
if (net_ratelimit())
-
printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
-
kfree_skb(skb);
-
return -EINVAL;
- }
关于linux的路由查询,与neighbour系统可以参考网上的一些资料。