【Linux4.1.12源码分析】二层报文发送之报文GSO分段(IP层)

来源:互联网 发布:新三板数据 编辑:程序博客网 时间:2024/05/29 13:04

IP层的GSO/GRO定义在ip_packet_offload结构体中。

static struct packet_offload ip_packet_offload __read_mostly = {.type = cpu_to_be16(ETH_P_IP),.callbacks = {.gso_segment = inet_gso_segment,     //gso分段函数.gro_receive = inet_gro_receive,     //gro收包函数.gro_complete = inet_gro_complete,},};

inet_gso_segment函数

static struct sk_buff *inet_gso_segment(struct sk_buff *skb,netdev_features_t features){struct sk_buff *segs = ERR_PTR(-EINVAL);const struct net_offload *ops;unsigned int offset = 0;bool udpfrag, encap;struct iphdr *iph;int proto;int nhoff;int ihl;int id;if (unlikely(skb_shinfo(skb)->gso_type &     ~(SKB_GSO_TCPV4 |       SKB_GSO_UDP |       SKB_GSO_DODGY |       SKB_GSO_TCP_ECN |       SKB_GSO_GRE |       SKB_GSO_GRE_CSUM |       SKB_GSO_IPIP |       SKB_GSO_SIT |       SKB_GSO_TCPV6 |       SKB_GSO_UDP_TUNNEL |       SKB_GSO_UDP_TUNNEL_CSUM |       SKB_GSO_TUNNEL_REMCSUM |       0)))goto out;skb_reset_network_header(skb);nhoff = skb_network_header(skb) - skb_mac_header(skb);//根据network header和mac header得到IP头相对MAC的偏移if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))//检测skb是否可以移动到L4头?goto out;iph = ip_hdr(skb);ihl = iph->ihl * 4;//得到IP包头的实际长度,基于此可以得到L4的首地址if (ihl < sizeof(*iph))goto out;id = ntohs(iph->id);proto = iph->protocol;//L4层协议类型/* Warning: after this point, iph might be no longer valid */if (unlikely(!pskb_may_pull(skb, ihl)))//检测skb是否可以移动到L4头?goto out;__skb_pull(skb, ihl);//报文data指针移动到传输层encap = SKB_GSO_CB(skb)->encap_level > 0;if (encap)features &= skb->dev->hw_enc_features;//如果encap,那么feature与hw_enc_features取交集SKB_GSO_CB(skb)->encap_level += ihl;//用来标示是否为内层报文skb_reset_transport_header(skb);//设置transport header值segs = ERR_PTR(-EPROTONOSUPPORT);if (skb->encapsulation &&    skb_shinfo(skb)->gso_type & (SKB_GSO_SIT|SKB_GSO_IPIP))udpfrag = proto == IPPROTO_UDP && encap;elseudpfrag = proto == IPPROTO_UDP && !skb->encapsulation;//vxlan封装报文走此分支,此时udpfrag为falseops = rcu_dereference(inet_offloads[proto]);if (likely(ops && ops->callbacks.gso_segment))segs = ops->callbacks.gso_segment(skb, features);//UDP或TCP的分段函数if (IS_ERR_OR_NULL(segs))goto out;skb = segs;do {iph = (struct iphdr *)(skb_mac_header(skb) + nhoff);//根据分段报文的mac header 和 IP偏移if (udpfrag) {//ip分片报文iph->id = htons(id);iph->frag_off = htons(offset >> 3);//设置ip头的frag_off值if (skb->next)iph->frag_off |= htons(IP_MF);//后面还有报文,需要设置more frag标记offset += skb->len - nhoff - ihl;//计算offset值,下一个报文需要使用} else {iph->id = htons(id++);//每个报文为完整的IP报文}iph->tot_len = htons(skb->len - nhoff);ip_send_check(iph);//计算ip头 csum值if (encap)//如果encap值非空,说明当前处于内层报文中,所以需要设置inner heaer值skb_reset_inner_headers(skb);skb->network_header = (u8 *)iph - skb->head;//设置network header} while ((skb = skb->next));out:return segs;}

0 0
原创粉丝点击