| /* |
| * Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de> |
| * |
| * Backport functionality introduced in Linux 3.18. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| #include <linux/if_ether.h> |
| #include <linux/if_vlan.h> |
| #include <linux/ip.h> |
| #include <linux/ipv6.h> |
| #include <scsi/fc/fc_fcoe.h> |
| #include <linux/skbuff.h> |
| #include <linux/errqueue.h> |
| #include <linux/wait.h> |
| |
| /** |
| * eth_get_headlen - determine the the length of header for an ethernet frame |
| * @data: pointer to start of frame |
| * @len: total length of frame |
| * |
| * Make a best effort attempt to pull the length for all of the headers for |
| * a given frame in a linear buffer. |
| */ |
| int eth_get_headlen(unsigned char *data, unsigned int max_len) |
| { |
| union { |
| unsigned char *network; |
| /* l2 headers */ |
| struct ethhdr *eth; |
| struct vlan_hdr *vlan; |
| /* l3 headers */ |
| struct iphdr *ipv4; |
| struct ipv6hdr *ipv6; |
| } hdr; |
| __be16 protocol; |
| u8 nexthdr = 0; /* default to not TCP */ |
| u8 hlen; |
| |
| /* this should never happen, but better safe than sorry */ |
| if (max_len < ETH_HLEN) |
| return max_len; |
| |
| /* initialize network frame pointer */ |
| hdr.network = data; |
| |
| /* set first protocol and move network header forward */ |
| protocol = hdr.eth->h_proto; |
| hdr.network += ETH_HLEN; |
| |
| /* handle any vlan tag if present */ |
| if (protocol == htons(ETH_P_8021Q)) { |
| if ((hdr.network - data) > (max_len - VLAN_HLEN)) |
| return max_len; |
| |
| protocol = hdr.vlan->h_vlan_encapsulated_proto; |
| hdr.network += VLAN_HLEN; |
| } |
| |
| /* handle L3 protocols */ |
| if (protocol == htons(ETH_P_IP)) { |
| if ((hdr.network - data) > (max_len - sizeof(struct iphdr))) |
| return max_len; |
| |
| /* access ihl as a u8 to avoid unaligned access on ia64 */ |
| hlen = (hdr.network[0] & 0x0F) << 2; |
| |
| /* verify hlen meets minimum size requirements */ |
| if (hlen < sizeof(struct iphdr)) |
| return hdr.network - data; |
| |
| /* record next protocol if header is present */ |
| if (!(hdr.ipv4->frag_off & htons(IP_OFFSET))) |
| nexthdr = hdr.ipv4->protocol; |
| } else if (protocol == htons(ETH_P_IPV6)) { |
| if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr))) |
| return max_len; |
| |
| /* record next protocol */ |
| nexthdr = hdr.ipv6->nexthdr; |
| hlen = sizeof(struct ipv6hdr); |
| } else if (protocol == htons(ETH_P_FCOE)) { |
| if ((hdr.network - data) > (max_len - FCOE_HEADER_LEN)) |
| return max_len; |
| hlen = FCOE_HEADER_LEN; |
| } else { |
| return hdr.network - data; |
| } |
| |
| /* relocate pointer to start of L4 header */ |
| hdr.network += hlen; |
| |
| /* finally sort out TCP/UDP */ |
| if (nexthdr == IPPROTO_TCP) { |
| if ((hdr.network - data) > (max_len - sizeof(struct tcphdr))) |
| return max_len; |
| |
| /* access doff as a u8 to avoid unaligned access on ia64 */ |
| hlen = (hdr.network[12] & 0xF0) >> 2; |
| |
| /* verify hlen meets minimum size requirements */ |
| if (hlen < sizeof(struct tcphdr)) |
| return hdr.network - data; |
| |
| hdr.network += hlen; |
| } else if (nexthdr == IPPROTO_UDP) { |
| if ((hdr.network - data) > (max_len - sizeof(struct udphdr))) |
| return max_len; |
| |
| hdr.network += sizeof(struct udphdr); |
| } |
| |
| /* |
| * If everything has gone correctly hdr.network should be the |
| * data section of the packet and will be the end of the header. |
| * If not then it probably represents the end of the last recognized |
| * header. |
| */ |
| if ((hdr.network - data) < max_len) |
| return hdr.network - data; |
| else |
| return max_len; |
| } |
| EXPORT_SYMBOL_GPL(eth_get_headlen); |
| |
| #define sock_efree LINUX_BACKPORT(sock_efree) |
| static void sock_efree(struct sk_buff *skb) |
| { |
| sock_put(skb->sk); |
| } |
| |
| /** |
| * skb_clone_sk - create clone of skb, and take reference to socket |
| * @skb: the skb to clone |
| * |
| * This function creates a clone of a buffer that holds a reference on |
| * sk_refcnt. Buffers created via this function are meant to be |
| * returned using sock_queue_err_skb, or free via kfree_skb. |
| * |
| * When passing buffers allocated with this function to sock_queue_err_skb |
| * it is necessary to wrap the call with sock_hold/sock_put in order to |
| * prevent the socket from being released prior to being enqueued on |
| * the sk_error_queue. |
| */ |
| struct sk_buff *skb_clone_sk(struct sk_buff *skb) |
| { |
| struct sock *sk = skb->sk; |
| struct sk_buff *clone; |
| |
| if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt)) |
| return NULL; |
| |
| clone = skb_clone(skb, GFP_ATOMIC); |
| if (!clone) { |
| sock_put(sk); |
| return NULL; |
| } |
| |
| clone->sk = sk; |
| clone->destructor = sock_efree; |
| |
| return clone; |
| } |
| EXPORT_SYMBOL_GPL(skb_clone_sk); |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) |
| /* |
| * skb_complete_wifi_ack() needs to get backported, because the version from |
| * 3.18 added the sock_hold() and sock_put() calles missing in older versions. |
| */ |
| void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) |
| { |
| struct sock *sk = skb->sk; |
| struct sock_exterr_skb *serr; |
| int err; |
| |
| skb->wifi_acked_valid = 1; |
| skb->wifi_acked = acked; |
| |
| serr = SKB_EXT_ERR(skb); |
| memset(serr, 0, sizeof(*serr)); |
| serr->ee.ee_errno = ENOMSG; |
| serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; |
| |
| /* take a reference to prevent skb_orphan() from freeing the socket */ |
| sock_hold(sk); |
| |
| err = sock_queue_err_skb(sk, skb); |
| if (err) |
| kfree_skb(skb); |
| |
| sock_put(sk); |
| } |
| EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) |
| int __sched out_of_line_wait_on_bit_timeout( |
| void *word, int bit, wait_bit_action_f *action, |
| unsigned mode, unsigned long timeout) |
| { |
| wait_queue_head_t *wq = bit_waitqueue(word, bit); |
| DEFINE_WAIT_BIT(wait, word, bit); |
| |
| wait.key.private = jiffies + timeout; |
| return __wait_on_bit(wq, &wait, action, mode); |
| } |
| EXPORT_SYMBOL_GPL(out_of_line_wait_on_bit_timeout); |
| |
| __sched int bit_wait_timeout(struct wait_bit_key *word) |
| { |
| unsigned long now = ACCESS_ONCE(jiffies); |
| if (signal_pending_state(current->state, current)) |
| return 1; |
| if (time_after_eq(now, word->private)) |
| return -EAGAIN; |
| schedule_timeout(word->private - now); |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(bit_wait_timeout); |
| #endif |