Linux内核分析 - 网络[十一]:ICMP模块
ip_push_pending_frames() 将待发送的报文传递给网络层 待发送的报文分片都在sk->sk_write_queue上,这 里要做的就是从sk_write_queue上取出所有分片,合并成一个报文,添加IP报头信息,使用ip_local_out()传递给网络层处理。 要注意的是这里的合并并不是真正的合并,只有第一个分片形成了skb,剩下的分片都放到了skb_shinfo(skb)- >frag_list上,虽然最后向下传递的只是一个skb,并实际上分片工作已经完成了,网络层并不需要再次分片,由网络的上层 完成分片是出于效率的考虑,虽然与协议标准有所出入。 首先从sk_write_queue上取出第一个分片,skb是最终向下传递的报 文,tail_skb指向skb的frag_list链表尾,即最后一个分片。 if ((skb = __skb_dequeue(&sk- >sk_write_queue)) == NULL) goto out; tail_skb = &(skb_shinfo(skb)->frag_list); 将skb->data指向ip报头的位置 if (skb->data < skb_network_header(skb)) __skb_pull(skb, skb_network_offset(skb)); tmp_skb表示现在要插入skb的分片,首先通过__skb_pull()除去这些 分片的IP报头,因为分片共用skb的IP报头。然后通过tail_skb处理将tmp_skb链入frag_list中;最后增加报文长度计数,以前 说明过,skb->len代表linear buffer + paged buffer,skb->data_len代表paged_buffer,这里插入的分片是增加了 paged buffer大小,所以对skb->len和skb->data_len都增加分片的长度。 while ((tmp_skb = __skb_dequeue (&sk->sk_write_queue)) != NULL) { __skb_pull(tmp_skb, skb_network_header_len(skb)); *tail_skb = tmp_skb; tail_skb = &(tmp_skb->next); skb->len += tmp_skb->len; skb->data_len += tmp_skb->len; skb->truesize += tmp_skb->truesize; tmp_skb->destructor = NULL; tmp_skb->sk = NULL; } 这里是生成skb的IP报头,设置其中的值 iph = (struct iphdr *)skb->data; iph->version = 4; ……. skb->mark = sk->sk_mark; 最终通过ip_local_out()传递给IP层 err = ip_local_out(skb); (编辑:源码网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |