static struct batadv_socket_client *batadv_socket_client_hash[256];
 
 static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
-                                    struct batadv_icmp_packet_rr *icmp_packet,
+                                    struct batadv_icmp_header *icmph,
                                     size_t icmp_len);
 
 void batadv_socket_init(void)
        struct batadv_priv *bat_priv = socket_client->bat_priv;
        struct batadv_hard_iface *primary_if = NULL;
        struct sk_buff *skb;
-       struct batadv_icmp_packet_rr *icmp_packet;
-
+       struct batadv_icmp_packet_rr *icmp_packet_rr;
+       struct batadv_icmp_header *icmp_header;
        struct batadv_orig_node *orig_node = NULL;
        struct batadv_neigh_node *neigh_node = NULL;
        size_t packet_len = sizeof(struct batadv_icmp_packet);
 
-       if (len < sizeof(struct batadv_icmp_packet)) {
+       if (len < sizeof(struct batadv_icmp_header)) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Error - can't send packet from char device: invalid packet size\n");
                return -EINVAL;
                goto out;
        }
 
-       if (len >= sizeof(struct batadv_icmp_packet_rr))
-               packet_len = sizeof(struct batadv_icmp_packet_rr);
+       if (len >= BATADV_ICMP_MAX_PACKET_SIZE)
+               packet_len = BATADV_ICMP_MAX_PACKET_SIZE;
+       else
+               packet_len = len;
 
        skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN);
        if (!skb) {
 
        skb->priority = TC_PRIO_CONTROL;
        skb_reserve(skb, ETH_HLEN);
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len);
+       icmp_header = (struct batadv_icmp_header *)skb_put(skb, packet_len);
 
-       if (copy_from_user(icmp_packet, buff, packet_len)) {
+       if (copy_from_user(icmp_header, buff, packet_len)) {
                len = -EFAULT;
                goto free_skb;
        }
 
-       if (icmp_packet->icmph.header.packet_type != BATADV_ICMP) {
+       if (icmp_header->header.packet_type != BATADV_ICMP) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n");
                len = -EINVAL;
                goto free_skb;
        }
 
-       if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) {
+       switch (icmp_header->msg_type) {
+       case BATADV_ECHO_REQUEST:
+               if (len < sizeof(struct batadv_icmp_packet)) {
+                       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                                  "Error - can't send packet from char device: invalid packet size\n");
+                       len = -EINVAL;
+                       goto free_skb;
+               }
+
+               if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
+                       goto dst_unreach;
+
+               orig_node = batadv_orig_hash_find(bat_priv, icmp_header->dst);
+               if (!orig_node)
+                       goto dst_unreach;
+
+               neigh_node = batadv_orig_node_get_router(orig_node);
+               if (!neigh_node)
+                       goto dst_unreach;
+
+               if (!neigh_node->if_incoming)
+                       goto dst_unreach;
+
+               if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE)
+                       goto dst_unreach;
+
+               icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header;
+               if (packet_len == sizeof(*icmp_packet_rr))
+                       memcpy(icmp_packet_rr->rr,
+                              neigh_node->if_incoming->net_dev->dev_addr,
+                              ETH_ALEN);
+
+               break;
+       default:
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n");
+                          "Error - can't send packet from char device: got unknown message type\n");
                len = -EINVAL;
                goto free_skb;
        }
 
-       icmp_packet->icmph.uid = socket_client->index;
+       icmp_header->uid = socket_client->index;
 
-       if (icmp_packet->icmph.header.version != BATADV_COMPAT_VERSION) {
-               icmp_packet->icmph.msg_type = BATADV_PARAMETER_PROBLEM;
-               icmp_packet->icmph.header.version = BATADV_COMPAT_VERSION;
-               batadv_socket_add_packet(socket_client, icmp_packet,
+       if (icmp_header->header.version != BATADV_COMPAT_VERSION) {
+               icmp_header->msg_type = BATADV_PARAMETER_PROBLEM;
+               icmp_header->header.version = BATADV_COMPAT_VERSION;
+               batadv_socket_add_packet(socket_client, icmp_header,
                                         packet_len);
                goto free_skb;
        }
 
-       if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
-               goto dst_unreach;
-
-       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.dst);
-       if (!orig_node)
-               goto dst_unreach;
-
-       neigh_node = batadv_orig_node_get_router(orig_node);
-       if (!neigh_node)
-               goto dst_unreach;
-
-       if (!neigh_node->if_incoming)
-               goto dst_unreach;
-
-       if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE)
-               goto dst_unreach;
-
-       memcpy(icmp_packet->icmph.orig,
-              primary_if->net_dev->dev_addr, ETH_ALEN);
-
-       if (packet_len == sizeof(struct batadv_icmp_packet_rr))
-               memcpy(icmp_packet->rr,
-                      neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN);
+       memcpy(icmp_header->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 
        batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
        goto out;
 
 dst_unreach:
-       icmp_packet->icmph.msg_type = BATADV_DESTINATION_UNREACHABLE;
-       batadv_socket_add_packet(socket_client, icmp_packet, packet_len);
+       icmp_header->msg_type = BATADV_DESTINATION_UNREACHABLE;
+       batadv_socket_add_packet(socket_client, icmp_header, packet_len);
 free_skb:
        kfree_skb(skb);
 out:
        return -ENOMEM;
 }
 
+/**
+ * batadv_socket_receive_packet - schedule an icmp packet to be sent to userspace
+ *  on an icmp socket.
+ * @socket_client: the socket this packet belongs to
+ * @icmph: pointer to the header of the icmp packet
+ * @icmp_len: total length of the icmp packet
+ */
 static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
-                                    struct batadv_icmp_packet_rr *icmp_packet,
+                                    struct batadv_icmp_header *icmph,
                                     size_t icmp_len)
 {
        struct batadv_socket_packet *socket_packet;
+       size_t len;
 
        socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC);
 
        if (!socket_packet)
                return;
 
+       len = icmp_len;
+       /* check the maximum length before filling the buffer */
+       if (len > sizeof(socket_packet->icmp_packet))
+               len = sizeof(socket_packet->icmp_packet);
+
        INIT_LIST_HEAD(&socket_packet->list);
-       memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len);
-       socket_packet->icmp_len = icmp_len;
+       memcpy(&socket_packet->icmp_packet, icmph, len);
+       socket_packet->icmp_len = len;
 
        spin_lock_bh(&socket_client->lock);
 
        /* while waiting for the lock the socket_client could have been
         * deleted
         */
-       if (!batadv_socket_client_hash[icmp_packet->icmph.uid]) {
+       if (!batadv_socket_client_hash[icmph->uid]) {
                spin_unlock_bh(&socket_client->lock);
                kfree(socket_packet);
                return;
        wake_up(&socket_client->queue_wait);
 }
 
-void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet,
+/**
+ * batadv_socket_receive_packet - schedule an icmp packet to be received
+ *  locally and sent to userspace.
+ * @icmph: pointer to the header of the icmp packet
+ * @icmp_len: total length of the icmp packet
+ */
+void batadv_socket_receive_packet(struct batadv_icmp_header *icmph,
                                  size_t icmp_len)
 {
        struct batadv_socket_client *hash;
 
-       hash = batadv_socket_client_hash[icmp_packet->icmph.uid];
+       hash = batadv_socket_client_hash[icmph->uid];
        if (hash)
-               batadv_socket_add_packet(hash, icmp_packet, icmp_len);
+               batadv_socket_add_packet(hash, icmph, icmp_len);
 }
 
        return true;
 }
 
+/**
+ * batadv_recv_my_icmp_packet - receive an icmp packet locally
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: icmp packet to process
+ *
+ * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
+ * otherwise.
+ */
 static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
-                                     struct sk_buff *skb, size_t icmp_len)
+                                     struct sk_buff *skb)
 {
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_orig_node *orig_node = NULL;
-       struct batadv_icmp_packet_rr *icmp_packet;
-       int ret = NET_RX_DROP;
+       struct batadv_icmp_header *icmph;
+       int res, ret = NET_RX_DROP;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+       icmph = (struct batadv_icmp_header *)skb->data;
 
-       /* add data to device queue */
-       if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) {
-               batadv_socket_receive_packet(icmp_packet, icmp_len);
-               goto out;
-       }
+       switch (icmph->msg_type) {
+       case BATADV_ECHO_REPLY:
+       case BATADV_DESTINATION_UNREACHABLE:
+       case BATADV_TTL_EXCEEDED:
+               /* receive the packet */
+               if (skb_linearize(skb) < 0)
+                       break;
 
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
-               goto out;
+               batadv_socket_receive_packet(icmph, skb->len);
+               break;
+       case BATADV_ECHO_REQUEST:
+               /* answer echo request (ping) */
+               primary_if = batadv_primary_if_get_selected(bat_priv);
+               if (!primary_if)
+                       goto out;
 
-       /* answer echo request (ping) */
-       /* get routing information */
-       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.orig);
-       if (!orig_node)
-               goto out;
+               /* get routing information */
+               orig_node = batadv_orig_hash_find(bat_priv, icmph->orig);
+               if (!orig_node)
+                       goto out;
 
-       /* create a copy of the skb, if needed, to modify it. */
-       if (skb_cow(skb, ETH_HLEN) < 0)
-               goto out;
+               /* create a copy of the skb, if needed, to modify it. */
+               if (skb_cow(skb, ETH_HLEN) < 0)
+                       goto out;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+               icmph = (struct batadv_icmp_header *)skb->data;
 
-       memcpy(icmp_packet->icmph.dst, icmp_packet->icmph.orig, ETH_ALEN);
-       memcpy(icmp_packet->icmph.orig, primary_if->net_dev->dev_addr,
-              ETH_ALEN);
-       icmp_packet->icmph.msg_type = BATADV_ECHO_REPLY;
-       icmp_packet->icmph.header.ttl = BATADV_TTL;
+               memcpy(icmph->dst, icmph->orig, ETH_ALEN);
+               memcpy(icmph->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
+               icmph->msg_type = BATADV_ECHO_REPLY;
+               icmph->header.ttl = BATADV_TTL;
 
-       if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
-               ret = NET_RX_SUCCESS;
+               res = batadv_send_skb_to_orig(skb, orig_node, NULL);
+               if (res != NET_XMIT_DROP)
+                       ret = NET_RX_SUCCESS;
 
+               break;
+       default:
+               /* drop unknown type */
+               goto out;
+       }
 out:
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
                            struct batadv_hard_iface *recv_if)
 {
        struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
-       struct batadv_icmp_packet_rr *icmp_packet;
+       struct batadv_icmp_header *icmph;
+       struct batadv_icmp_packet_rr *icmp_packet_rr;
        struct ethhdr *ethhdr;
        struct batadv_orig_node *orig_node = NULL;
-       int hdr_size = sizeof(struct batadv_icmp_packet);
+       int hdr_size = sizeof(struct batadv_icmp_header);
        int ret = NET_RX_DROP;
 
-       /* we truncate all incoming icmp packets if they don't match our size */
-       if (skb->len >= sizeof(struct batadv_icmp_packet_rr))
-               hdr_size = sizeof(struct batadv_icmp_packet_rr);
-
        /* drop packet if it has not necessary minimum size */
        if (unlikely(!pskb_may_pull(skb, hdr_size)))
                goto out;
        if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
                goto out;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+       icmph = (struct batadv_icmp_header *)skb->data;
 
        /* add record route information if not full */
-       if ((icmp_packet->icmph.msg_type == BATADV_ECHO_REPLY ||
-            icmp_packet->icmph.msg_type == BATADV_ECHO_REQUEST) &&
-           (hdr_size == sizeof(struct batadv_icmp_packet_rr)) &&
-           (icmp_packet->rr_cur < BATADV_RR_LEN)) {
-               memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]),
+       if ((icmph->msg_type == BATADV_ECHO_REPLY ||
+            icmph->msg_type == BATADV_ECHO_REQUEST) &&
+           (skb->len >= sizeof(struct batadv_icmp_packet_rr))) {
+               if (skb_linearize(skb) < 0)
+                       goto out;
+
+               /* create a copy of the skb, if needed, to modify it. */
+               if (skb_cow(skb, ETH_HLEN) < 0)
+                       goto out;
+
+               icmph = (struct batadv_icmp_header *)skb->data;
+               icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph;
+               if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN)
+                       goto out;
+
+               memcpy(&(icmp_packet_rr->rr[icmp_packet_rr->rr_cur]),
                       ethhdr->h_dest, ETH_ALEN);
-               icmp_packet->rr_cur++;
+               icmp_packet_rr->rr_cur++;
        }
 
        /* packet for me */
-       if (batadv_is_my_mac(bat_priv, icmp_packet->icmph.dst))
-               return batadv_recv_my_icmp_packet(bat_priv, skb, hdr_size);
+       if (batadv_is_my_mac(bat_priv, icmph->dst))
+               return batadv_recv_my_icmp_packet(bat_priv, skb);
 
        /* TTL exceeded */
-       if (icmp_packet->icmph.header.ttl < 2)
+       if (icmph->header.ttl < 2)
                return batadv_recv_icmp_ttl_exceeded(bat_priv, skb);
 
        /* get routing information */
-       orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.dst);
+       orig_node = batadv_orig_hash_find(bat_priv, icmph->dst);
        if (!orig_node)
                goto out;
 
        if (skb_cow(skb, ETH_HLEN) < 0)
                goto out;
 
-       icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+       icmph = (struct batadv_icmp_header *)skb->data;
 
        /* decrement ttl */
-       icmp_packet->icmph.header.ttl--;
+       icmph->header.ttl--;
 
        /* route it */
        if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP)