+static u32
+vmxnet3_get_hdr_len(struct vmxnet3_adapter *adapter, struct sk_buff *skb,
+ union Vmxnet3_GenericDesc *gdesc)
+{
+ u32 hlen, maplen;
+ union {
+ void *ptr;
+ struct ethhdr *eth;
+ struct iphdr *ipv4;
+ struct ipv6hdr *ipv6;
+ struct tcphdr *tcp;
+ } hdr;
+ BUG_ON(gdesc->rcd.tcp == 0);
+
+ maplen = skb_headlen(skb);
+ if (unlikely(sizeof(struct iphdr) + sizeof(struct tcphdr) > maplen))
+ return 0;
+
+ hdr.eth = eth_hdr(skb);
+ if (gdesc->rcd.v4) {
+ BUG_ON(hdr.eth->h_proto != htons(ETH_P_IP));
+ hdr.ptr += sizeof(struct ethhdr);
+ BUG_ON(hdr.ipv4->protocol != IPPROTO_TCP);
+ hlen = hdr.ipv4->ihl << 2;
+ hdr.ptr += hdr.ipv4->ihl << 2;
+ } else if (gdesc->rcd.v6) {
+ BUG_ON(hdr.eth->h_proto != htons(ETH_P_IPV6));
+ hdr.ptr += sizeof(struct ethhdr);
+ /* Use an estimated value, since we also need to handle
+ * TSO case.
+ */
+ if (hdr.ipv6->nexthdr != IPPROTO_TCP)
+ return sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
+ hlen = sizeof(struct ipv6hdr);
+ hdr.ptr += sizeof(struct ipv6hdr);
+ } else {
+ /* Non-IP pkt, dont estimate header length */
+ return 0;
+ }
+
+ if (hlen + sizeof(struct tcphdr) > maplen)
+ return 0;
+
+ return (hlen + (hdr.tcp->doff << 2));
+}
+