| /* |
| * 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> |
| #include <linux/of.h> |
| #include <linux/string.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_IS_GEQ(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; |
| |
| #if LINUX_VERSION_IS_GEQ(3,3,0) |
| skb->wifi_acked_valid = 1; |
| skb->wifi_acked = acked; |
| #endif |
| |
| 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_IS_GEQ(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 |
| |
| #ifdef CONFIG_OF |
| /** |
| * of_find_property_value_of_size |
| * |
| * @np: device node from which the property value is to be read. |
| * @propname: name of the property to be searched. |
| * @len: requested length of property value |
| * |
| * Search for a property in a device node and valid the requested size. |
| * Returns the property value on success, -EINVAL if the property does not |
| * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the |
| * property data isn't large enough. |
| * |
| */ |
| void *of_find_property_value_of_size(const struct device_node *np, |
| const char *propname, u32 len) |
| { |
| struct property *prop = of_find_property(np, propname, NULL); |
| |
| if (!prop) |
| return ERR_PTR(-EINVAL); |
| if (!prop->value) |
| return ERR_PTR(-ENODATA); |
| if (len > prop->length) |
| return ERR_PTR(-EOVERFLOW); |
| |
| return prop->value; |
| } |
| |
| /** |
| * of_property_read_u64_array - Find and read an array of 64 bit integers |
| * from a property. |
| * |
| * @np: device node from which the property value is to be read. |
| * @propname: name of the property to be searched. |
| * @out_values: pointer to return value, modified only if return value is 0. |
| * @sz: number of array elements to read |
| * |
| * Search for a property in a device node and read 64-bit value(s) from |
| * it. Returns 0 on success, -EINVAL if the property does not exist, |
| * -ENODATA if property does not have a value, and -EOVERFLOW if the |
| * property data isn't large enough. |
| * |
| * The out_values is modified only if a valid u64 value can be decoded. |
| */ |
| int of_property_read_u64_array(const struct device_node *np, |
| const char *propname, u64 *out_values, |
| size_t sz) |
| { |
| const __be32 *val = of_find_property_value_of_size(np, propname, |
| (sz * sizeof(*out_values))); |
| |
| if (IS_ERR(val)) |
| return PTR_ERR(val); |
| |
| while (sz--) { |
| *out_values++ = of_read_number(val, 2); |
| val += 2; |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(of_property_read_u64_array); |
| #endif /* CONFIG_OF */ |
| |
| #if !(LINUX_VERSION_IS_GEQ(3,17,3) || \ |
| (LINUX_VERSION_IS_GEQ(3,14,24) && \ |
| LINUX_VERSION_IS_LESS(3,15,0)) || \ |
| (LINUX_VERSION_IS_GEQ(3,12,33) && \ |
| LINUX_VERSION_IS_LESS(3,13,0)) || \ |
| (LINUX_VERSION_IS_GEQ(3,10,60) && \ |
| LINUX_VERSION_IS_LESS(3,11,0)) || \ |
| (LINUX_VERSION_IS_GEQ(3,4,106) && \ |
| LINUX_VERSION_IS_LESS(3,5,0)) || \ |
| (LINUX_VERSION_IS_GEQ(3,2,65) && \ |
| LINUX_VERSION_IS_LESS(3,3,0))) |
| /** |
| * memzero_explicit - Fill a region of memory (e.g. sensitive |
| * keying data) with 0s. |
| * @s: Pointer to the start of the area. |
| * @count: The size of the area. |
| * |
| * Note: usually using memset() is just fine (!), but in cases |
| * where clearing out _local_ data at the end of a scope is |
| * necessary, memzero_explicit() should be used instead in |
| * order to prevent the compiler from optimising away zeroing. |
| * |
| * memzero_explicit() doesn't need an arch-specific version as |
| * it just invokes the one of memset() implicitly. |
| */ |
| void memzero_explicit(void *s, size_t count) |
| { |
| memset(s, 0, count); |
| barrier_data(s); |
| } |
| EXPORT_SYMBOL_GPL(memzero_explicit); |
| #endif |