Merge branch 'pm-domains'
[firefly-linux-kernel-4.4.55.git] / net / ieee802154 / 6lowpan / tx.c
index 54939d031ea5d21e348a3be36bc9e2ef7bcaa82f..d4353faced35cf64f98a68fe6a749120cff411d2 100644 (file)
 
 #include <net/6lowpan.h>
 #include <net/ieee802154_netdev.h>
+#include <net/mac802154.h>
 
 #include "6lowpan_i.h"
 
+#define LOWPAN_FRAG1_HEAD_SIZE 0x4
+#define LOWPAN_FRAGN_HEAD_SIZE 0x5
+
 /* don't save pan id, it's intra pan */
 struct lowpan_addr {
        u8 mode;
@@ -36,6 +40,13 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
                        sizeof(struct lowpan_addr_info));
 }
 
+/* This callback will be called from AF_PACKET and IPv6 stack, the AF_PACKET
+ * sockets gives an 8 byte array for addresses only!
+ *
+ * TODO I think AF_PACKET DGRAM (sending/receiving) RAW (sending) makes no
+ * sense here. We should disable it, the right use-case would be AF_INET6
+ * RAW/DGRAM sockets.
+ */
 int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
                         unsigned short type, const void *_daddr,
                         const void *_saddr, unsigned int len)
@@ -71,27 +82,33 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
 
 static struct sk_buff*
 lowpan_alloc_frag(struct sk_buff *skb, int size,
-                 const struct ieee802154_hdr *master_hdr)
+                 const struct ieee802154_hdr *master_hdr, bool frag1)
 {
        struct net_device *wdev = lowpan_dev_info(skb->dev)->wdev;
        struct sk_buff *frag;
        int rc;
 
-       frag = alloc_skb(wdev->hard_header_len + wdev->needed_tailroom + size,
+       frag = alloc_skb(wdev->needed_headroom + wdev->needed_tailroom + size,
                         GFP_ATOMIC);
 
        if (likely(frag)) {
                frag->dev = wdev;
                frag->priority = skb->priority;
-               skb_reserve(frag, wdev->hard_header_len);
+               skb_reserve(frag, wdev->needed_headroom);
                skb_reset_network_header(frag);
                *mac_cb(frag) = *mac_cb(skb);
 
-               rc = dev_hard_header(frag, wdev, 0, &master_hdr->dest,
-                                    &master_hdr->source, size);
-               if (rc < 0) {
-                       kfree_skb(frag);
-                       return ERR_PTR(rc);
+               if (frag1) {
+                       memcpy(skb_put(frag, skb->mac_len),
+                              skb_mac_header(skb), skb->mac_len);
+               } else {
+                       rc = wpan_dev_hard_header(frag, wdev,
+                                                 &master_hdr->dest,
+                                                 &master_hdr->source, size);
+                       if (rc < 0) {
+                               kfree_skb(frag);
+                               return ERR_PTR(rc);
+                       }
                }
        } else {
                frag = ERR_PTR(-ENOMEM);
@@ -103,13 +120,13 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
 static int
 lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
                     u8 *frag_hdr, int frag_hdrlen,
-                    int offset, int len)
+                    int offset, int len, bool frag1)
 {
        struct sk_buff *frag;
 
        raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
 
-       frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr);
+       frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr, frag1);
        if (IS_ERR(frag))
                return PTR_ERR(frag);
 
@@ -148,7 +165,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
 
        rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
                                  LOWPAN_FRAG1_HEAD_SIZE, 0,
-                                 frag_len + skb_network_header_len(skb));
+                                 frag_len + skb_network_header_len(skb),
+                                 true);
        if (rc) {
                pr_debug("%s unable to send FRAG1 packet (tag: %d)",
                         __func__, ntohs(frag_tag));
@@ -169,7 +187,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
 
                rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
                                          LOWPAN_FRAGN_HEAD_SIZE, skb_offset,
-                                         frag_len);
+                                         frag_len, false);
                if (rc) {
                        pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
                                 __func__, ntohs(frag_tag), skb_offset);
@@ -177,6 +195,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
                }
        } while (skb_unprocessed > frag_cap);
 
+       ldev->stats.tx_packets++;
+       ldev->stats.tx_bytes += dgram_size;
        consume_skb(skb);
        return NET_XMIT_SUCCESS;
 
@@ -201,7 +221,7 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
        saddr = &info.saddr.u.extended_addr;
 
        *dgram_size = skb->len;
-       lowpan_header_compress(skb, ldev, ETH_P_IPV6, daddr, saddr, skb->len);
+       lowpan_header_compress(skb, ldev, daddr, saddr);
        /* dgram_offset = (saved bytes after compression) + lowpan header len */
        *dgram_offset = (*dgram_size - skb->len) + skb_network_header_len(skb);
 
@@ -218,7 +238,7 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
        /* if the destination address is the broadcast address, use the
         * corresponding short address
         */
-       if (lowpan_is_addr_broadcast((const u8 *)daddr)) {
+       if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
                da.mode = IEEE802154_ADDR_SHORT;
                da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
                cb->ackreq = false;
@@ -228,8 +248,8 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
                cb->ackreq = wpan_dev->ackreq;
        }
 
-       return dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, ETH_P_IPV6,
-                              (void *)&da, (void *)&sa, 0);
+       return wpan_dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, &da, &sa,
+                                   0);
 }
 
 netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
@@ -240,6 +260,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 
        pr_debug("package xmit\n");
 
+       WARN_ON_ONCE(skb->len > IPV6_MIN_MTU);
+
        /* We must take a copy of the skb before we modify/replace the ipv6
         * header as the header could be used elsewhere
         */
@@ -262,6 +284,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 
        if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) {
                skb->dev = lowpan_dev_info(ldev)->wdev;
+               ldev->stats.tx_packets++;
+               ldev->stats.tx_bytes += dgram_size;
                return dev_queue_xmit(skb);
        } else {
                netdev_tx_t rc;