These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / ieee802154 / 6lowpan / tx.c
index 2349070..d4353fa 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,7 +40,14 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
                        sizeof(struct lowpan_addr_info));
 }
 
-int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
+/* 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)
 {
@@ -51,7 +62,7 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
                return 0;
 
        if (!saddr)
-               saddr = dev->dev_addr;
+               saddr = ldev->dev_addr;
 
        raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
        raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
@@ -71,28 +82,33 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
 
 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 *real_dev = lowpan_dev_info(skb->dev)->real_dev;
+       struct net_device *wdev = lowpan_dev_info(skb->dev)->wdev;
        struct sk_buff *frag;
        int rc;
 
-       frag = alloc_skb(real_dev->hard_header_len +
-                        real_dev->needed_tailroom + size,
+       frag = alloc_skb(wdev->needed_headroom + wdev->needed_tailroom + size,
                         GFP_ATOMIC);
 
        if (likely(frag)) {
-               frag->dev = real_dev;
+               frag->dev = wdev;
                frag->priority = skb->priority;
-               skb_reserve(frag, real_dev->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, real_dev, 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);
@@ -104,15 +120,15 @@ 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);
+               return PTR_ERR(frag);
 
        memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen);
        memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len);
@@ -123,19 +139,17 @@ lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
 }
 
 static int
-lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
-                      const struct ieee802154_hdr *wpan_hdr)
+lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
+                      const struct ieee802154_hdr *wpan_hdr, u16 dgram_size,
+                      u16 dgram_offset)
 {
-       u16 dgram_size, dgram_offset;
        __be16 frag_tag;
        u8 frag_hdr[5];
        int frag_cap, frag_len, payload_cap, rc;
        int skb_unprocessed, skb_offset;
 
-       dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
-                    skb->mac_len;
-       frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
-       lowpan_dev_info(dev)->fragment_tag++;
+       frag_tag = htons(lowpan_dev_info(ldev)->fragment_tag);
+       lowpan_dev_info(ldev)->fragment_tag++;
 
        frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
        frag_hdr[1] = dgram_size & 0xff;
@@ -151,7 +165,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
 
        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));
@@ -172,7 +187,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
 
                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);
@@ -180,6 +195,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
                }
        } while (skb_unprocessed > frag_cap);
 
+       ldev->stats.tx_packets++;
+       ldev->stats.tx_bytes += dgram_size;
        consume_skb(skb);
        return NET_XMIT_SUCCESS;
 
@@ -188,8 +205,10 @@ err:
        return rc;
 }
 
-static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
+static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
+                        u16 *dgram_size, u16 *dgram_offset)
 {
+       struct wpan_dev *wpan_dev = lowpan_dev_info(ldev)->wdev->ieee802154_ptr;
        struct ieee802154_addr sa, da;
        struct ieee802154_mac_cb *cb = mac_cb_init(skb);
        struct lowpan_addr_info info;
@@ -201,13 +220,16 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
        daddr = &info.daddr.u.extended_addr;
        saddr = &info.saddr.u.extended_addr;
 
-       lowpan_header_compress(skb, dev, ETH_P_IPV6, daddr, saddr, skb->len);
+       *dgram_size = 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);
 
        cb->type = IEEE802154_FC_TYPE_DATA;
 
        /* prepare wpan address data */
        sa.mode = IEEE802154_ADDR_LONG;
-       sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+       sa.pan_id = wpan_dev->pan_id;
        sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
 
        /* intra-PAN communications */
@@ -216,27 +238,30 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
        /* 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;
        } else {
                da.mode = IEEE802154_ADDR_LONG;
                da.extended_addr = ieee802154_devaddr_from_raw(daddr);
-               cb->ackreq = true;
+               cb->ackreq = wpan_dev->ackreq;
        }
 
-       return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
-                       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 *dev)
+netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 {
        struct ieee802154_hdr wpan_hdr;
        int max_single, ret;
+       u16 dgram_size, dgram_offset;
 
        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
         */
@@ -244,7 +269,7 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
        if (!skb)
                return NET_XMIT_DROP;
 
-       ret = lowpan_header(skb, dev);
+       ret = lowpan_header(skb, ldev, &dgram_size, &dgram_offset);
        if (ret < 0) {
                kfree_skb(skb);
                return NET_XMIT_DROP;
@@ -258,13 +283,16 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
        max_single = ieee802154_max_payload(&wpan_hdr);
 
        if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) {
-               skb->dev = lowpan_dev_info(dev)->real_dev;
+               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;
 
                pr_debug("frame is too big, fragmentation is needed\n");
-               rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr);
+               rc = lowpan_xmit_fragmented(skb, ldev, &wpan_hdr, dgram_size,
+                                           dgram_offset);
 
                return rc < 0 ? NET_XMIT_DROP : rc;
        }