Linux内核分析 - 网络[八补]:IP协议补充
调用相应发送函数发送给下一层。有关hh和neighbour 参考”ARP模块”。 if (dst->hh) return neigh_hh_output(dst->hh, skb); else if (dst->neighbour) return dst->neighbour->output(skb); 在创建邻居表项时neighbour->output()被赋值,比如收到arp报文 ,在arp_process() -> neigh_event_ns()中创建报文相应的邻居表项,而neigh->ops和neigh->output根据情况赋予 不同的值。 if (dev->header_ops->cache) neigh->ops = &arp_hh_ops; else neigh->ops = &arp_generic_ops; if (neigh->nud_state&NUD_VALID) neigh->output = neigh->ops->connected_output; else neigh->output = neigh->ops->output; 邻居表项创建后,相应的hh缓存项并没有创建,当向邻居表项中的 主机发送报文时,先调用neigh->output(),假设neigh->ops被赋值arp_generiv_ops,则neigh->output= neigh_resolve_output,而在neigh_resolve_output()函数中,会创建hh缓存项,其中hh->output= dev_queue_xmit()。 所以,无论哪种情况,hh->output还是neigh->output,最终都是调用dev_queue_xmit()向下层传送报文的。这也是IP层 下传送报文的统一方式-dev_queue_xmit()。虽然调用接口相同,但IP层下的各个协议模块都是有设备的概念的,因此每个模块 的设备都不相同,在每个模块中都会更换skb->dev为下层的设备,而dev_queue_xmit()最终使用的是skb->dev特定的函数 进行发送的,这样实现了各模块的接口一致。 dev_queue_xmit() 发送函数 skb_needs_linearize()判断是否要对报文 进行线性处理,如果需要,它返回1,由__skb_linearize()完成线性处理。线性处理就是将报文的所有内容放到线性地址空间, 不能有分片的存在。在发送报文时,ip_append_data()对过长的报文进行了分片frag_list,多次添加时使用了SG特性frags(如 果支持)。skb_needs_linearize()就是判断设备能否处理ip_append_data()所做的分片工作。判断条件很简单:skb有分片即 frag_list,但设备不支持分片NETIF_F_FRAGLIST;skb应用了SG但设备不支持NETIF_F_SG或者是有一个分片在highmem中。最后 的线性化函数__skb_linearize()也很简单,它调用__pskb_pull_tail(skb, skb->data_len),data_len就是非线性空间的长 度,__pskb_pull_taill会将这部分数据拷贝到skb->data,从而完成线性化。明显看到,不支持分片的设备在做线性化处理 时会多一次数据拷贝操作。 if (skb_needs_linearize(skb, dev) && __skb_linearize(skb)) goto out_kfree_skb; ip_summed==CHECKSUM_PARTIAL表示协议栈并没有计算完校验和,只计算了IP头,伪头等,将传 输层的数据部分留给了硬件进行计算。dev_can_checksum()判断设备是否能计算校验和,如果不能的话,则skb_checksum_help ()软件的计算校验和。 if (skb->ip_summed == CHECKSUM_PARTIAL) { skb_set_transport_header(skb, skb->csum_start - skb_headroom(skb)); if (!dev_can_checksum(dev, skb) && skb_checksum_help(skb)) goto out_kfree_skb; } (编辑:应用网_丽江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |