作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
IP报文的发送的入口函数是ip_output,这个函数一般是挂载到dst->output上。在发送IP报文时,都需要进行路由查找,得到dst,然后通过dst->output来发送数据包。对于IPv4来说,一般情况下,dst->output都是指向ip_output。
- int ip_output(struct sk_buff *skb)
- {
- /* 得到出口的dev */
- 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的POSTROUTING链 */
- return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
- ip_finish_output,
- !(IPCB(skb)->flags & IPSKB_REROUTED));
- }
进入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
- /* 处理需要IP分片的情况 */
- if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
- return ip_fragment(skb, ip_finish_output2);
- /* 不需要IP分片, 我们就看这种一般情况 */
- 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)) {
- /*
- skb的首部空间不足,无法保存l2层的硬件地址。
- 这时,需要重新分配一个更大bufer。
- */
- struct sk_buff *skb2;
- skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
- if (skb2 == NULL) {
- kfree_skb(skb);
- return -ENOMEM;
- }
- if (skb->sk)
- skb_set_owner_w(skb2, skb->sk);
- kfree_skb(skb);
- skb = skb2;
- }
/* dst有L2层硬件地址的cache*/
- if (dst->hh)
- return neigh_hh_output(dst->hh, skb);
- /*
- dst没有L2层地址的cache,需要调用neighbour子系统的output进行发送。
- */
- else if (dst->neighbour)
- return dst->neighbour->output(skb);
/*
正常情况下,不会到达此处
*/
- if (net_ratelimit())
- printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
- kfree_skb(skb);
- return -EINVAL;
- }
在IPv4中,neighbour subsystem使用的是arp,所以一般情况下,dst->neighbour->outpu指向的是arp_generic_ops->neigh_resolve_output(这是需要解析L2硬件地址的情况,其他情况本文暂不考虑)。
通常,neighbour subsystem使用的operation都是arp_generic_ops(在arp.c中定义,且还有其他的同类型变量,用于不同情况)。
下面看neigh_resolve_output
- int neigh_resolve_output(struct sk_buff *skb)
-
{
-
struct dst_entry *dst = skb_dst(skb);
-
struct neighbour *neigh;
-
int rc = 0;
-
-
if (!dst || !(neigh = dst->neighbour))
- goto discard;
-
__skb_pull(skb, skb_network_offset(skb));
/* 判断是否发送neigh请求,对于IPv4来说就是arp request */
-
if (!neigh_event_send(neigh, skb)) {
- /* 无需发送neigh请求, 可以直接从dev中获得 */
-
int err;
-
struct net_device *dev = neigh->dev;
/* 从dev中获得L2硬件地址*/
-
if (dev->header_ops->cache && !dst->hh) {
-
write_lock_bh(&neigh->lock);
-
if (!dst->hh)
-
neigh_hh_init(neigh, dst, dst->ops->protocol);
-
err = dev_hard_header(skb, dev, ntohs(skb->protocol),
-
neigh->ha, NULL, skb->len);
-
write_unlock_bh(&neigh->lock);
-
} else {
-
read_lock_bh(&neigh->lock);
-
err = dev_hard_header(skb, dev, ntohs(skb->protocol),
-
neigh->ha, NULL, skb->len);
-
read_unlock_bh(&neigh->lock);
-
}
-
if (err >= 0)
-
rc = neigh->ops->queue_xmit(skb); //发送数据包
-
else
-
goto out_kfree_skb;
-
}
-
out:
-
return rc;
-
discard:
-
NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n",
-
dst, dst ? dst->neighbour : NULL);
-
out_kfree_skb:
-
rc = -EINVAL;
-
kfree_skb(skb);
-
goto out;
- }
在neigh_event_send中,实际上并不立刻执行发送neigh 请求,而是将这个数据包加到一个队列中__skb_queue_tail(&neigh->arp_queue, skb);,即语句中的arp_queue。
然后通过neighbour subsystem的定时处理函数neigh_timer_handler来处理这个队列。
neigh_timer_handler主要是neighbour状态的处理和变迁,本文并将其列为重点,留作以后分析学习。对于IPv4来说,当没有对应的neighbour信息时,会先调用arp_solicit发送ARP请求,当收到ARP包时,由arp_process进行处理(可参见我前面的博文TCP/IP学习(33)——ARP数据包的处理流程分析, http://blog.chinaunix.net/space.php?uid=23629988&do=blog&id=335262)。当收到ARP reply时,会调用neigh_update,当neighbour的状态为valid且neighbour的arp_queue不为空时,会调用n1->output将之前的skb数据包发送。
- while (neigh->nud_state & NUD_VALID &&
-
(skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
- struct neighbour *n1 = neigh;
- write_unlock_bh(&neigh->lock);
- /* On shaper/eql skb->dst->neighbour != neigh :( */
- if (skb_dst(skb) && skb_dst(skb)->neighbour)
- n1 = skb_dst(skb)->neighbour;
- n1->output(skb);
- write_lock_bh(&neigh->lock);
- }
当解析neighbour信息成功时,这个n1->output是指向neigh_connected_output,并最终调用dev_queue_xmit->__dev_xmit_skb将数据包添加到dev的queue中,并发送数据包。
当发送完毕时,引发NET_TX_SOFTIRQ软中断,进行下半部处理,如释放已发送的数据包skb的内存等。
本文还遗留了不少细节有待完善,如neighbour subsystem和发送软中断等。