-+ /* In case we are ported somewhere (ebtables?) where ip_hdr(skb)
-+ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
-+ int ip_hl = 4*ip_hdr(skb)->ihl;
-+
-+ if( ip_hdr(skb)->protocol == IPPROTO_TCP ) {
-+ /* 12 == offset into TCP header for the header length field.
-+ Can't get this with skb->h.th->doff because the tcphdr
-+ struct doesn't get set when routing (this is confirmed to be
-+ true in Netfilter as well as QoS.) */
-+ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);
-+
-+ return ip_hl + tcp_hl;
-+ } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) {
-+ return ip_hl + 8; /* UDP header is always 8 bytes */
-+ } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) {
-+ return ip_hl + 8; /* ICMP header is 8 bytes */
-+ } else {
-+ if (net_ratelimit())
-+ printk(KERN_ERR "layer7: tried to handle unknown "
-+ "protocol!\n");
-+ return ip_hl + 8; /* something reasonable */
++ int offset;
++ struct iphdr iphdr_tmp;
++ struct iphdr *iphdr;
++ struct tcphdr tcphdr_tmp;
++ struct tcphdr *tcphdr;
++
++ if (!ip_hdr(skb))
++ return -1;
++
++ offset = ((uintptr_t)ip_hdr(skb)) - ((uintptr_t)skb->data);
++
++ iphdr = skb_header_pointer(skb, offset, sizeof(*iphdr), &iphdr_tmp);
++ if (!iphdr)
++ return -1;
++
++ offset += iphdr->ihl * 4;
++
++ if (iphdr->protocol == IPPROTO_TCP) {
++ tcphdr = skb_header_pointer(skb, offset, sizeof(*tcphdr),
++ &tcphdr_tmp);
++ if (!tcphdr)
++ return -1;
++
++ offset += tcphdr->doff * 4;
++
++ return offset;