Merge tag 'media/v4.8-4' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[cascardo/linux.git] / net / ieee802154 / 6lowpan / tx.c
index e459afd..dbb476d 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <net/6lowpan.h>
+#include <net/ndisc.h>
 #include <net/ieee802154_netdev.h>
 #include <net/mac802154.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;
-       union {
-               /* IPv6 needs big endian here */
-               __be64 extended_addr;
-               __be16 short_addr;
-       } u;
-};
-
 struct lowpan_addr_info {
-       struct lowpan_addr daddr;
-       struct lowpan_addr saddr;
+       struct ieee802154_addr daddr;
+       struct ieee802154_addr saddr;
 };
 
 static inline struct
@@ -48,12 +39,14 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
  * 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)
+                        unsigned short type, const void *daddr,
+                        const void *saddr, unsigned int len)
 {
-       const u8 *saddr = _saddr;
-       const u8 *daddr = _daddr;
-       struct lowpan_addr_info *info;
+       struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
+       struct lowpan_addr_info *info = lowpan_skb_priv(skb);
+       struct lowpan_802154_neigh *llneigh = NULL;
+       const struct ipv6hdr *hdr = ipv6_hdr(skb);
+       struct neighbour *n;
 
        /* TODO:
         * if this package isn't ipv6 one, where should it be routed?
@@ -61,21 +54,50 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
        if (type != ETH_P_IPV6)
                return 0;
 
-       if (!saddr)
-               saddr = ldev->dev_addr;
+       /* intra-pan communication */
+       info->saddr.pan_id = wpan_dev->pan_id;
+       info->daddr.pan_id = info->saddr.pan_id;
 
-       raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
-       raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
+       if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
+               info->daddr.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+               info->daddr.mode = IEEE802154_ADDR_SHORT;
+       } else {
+               __le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
+
+               n = neigh_lookup(&nd_tbl, &hdr->daddr, ldev);
+               if (n) {
+                       llneigh = lowpan_802154_neigh(neighbour_priv(n));
+                       read_lock_bh(&n->lock);
+                       short_addr = llneigh->short_addr;
+                       read_unlock_bh(&n->lock);
+               }
 
-       info = lowpan_skb_priv(skb);
+               if (llneigh &&
+                   lowpan_802154_is_valid_src_short_addr(short_addr)) {
+                       info->daddr.short_addr = short_addr;
+                       info->daddr.mode = IEEE802154_ADDR_SHORT;
+               } else {
+                       info->daddr.mode = IEEE802154_ADDR_LONG;
+                       ieee802154_be64_to_le64(&info->daddr.extended_addr,
+                                               daddr);
+               }
 
-       /* TODO: Currently we only support extended_addr */
-       info->daddr.mode = IEEE802154_ADDR_LONG;
-       memcpy(&info->daddr.u.extended_addr, daddr,
-              sizeof(info->daddr.u.extended_addr));
-       info->saddr.mode = IEEE802154_ADDR_LONG;
-       memcpy(&info->saddr.u.extended_addr, saddr,
-              sizeof(info->daddr.u.extended_addr));
+               if (n)
+                       neigh_release(n);
+       }
+
+       if (!saddr) {
+               if (lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr)) {
+                       info->saddr.mode = IEEE802154_ADDR_SHORT;
+                       info->saddr.short_addr = wpan_dev->short_addr;
+               } else {
+                       info->saddr.mode = IEEE802154_ADDR_LONG;
+                       info->saddr.extended_addr = wpan_dev->extended_addr;
+               }
+       } else {
+               info->saddr.mode = IEEE802154_ADDR_LONG;
+               ieee802154_be64_to_le64(&info->saddr.extended_addr, saddr);
+       }
 
        return 0;
 }
@@ -209,47 +231,26 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
                         u16 *dgram_size, u16 *dgram_offset)
 {
        struct wpan_dev *wpan_dev = lowpan_802154_dev(ldev)->wdev->ieee802154_ptr;
-       struct ieee802154_addr sa, da;
        struct ieee802154_mac_cb *cb = mac_cb_init(skb);
        struct lowpan_addr_info info;
-       void *daddr, *saddr;
 
        memcpy(&info, lowpan_skb_priv(skb), sizeof(info));
 
-       /* TODO: Currently we only support extended_addr */
-       daddr = &info.daddr.u.extended_addr;
-       saddr = &info.saddr.u.extended_addr;
-
        *dgram_size = skb->len;
-       lowpan_header_compress(skb, ldev, daddr, saddr);
+       lowpan_header_compress(skb, ldev, &info.daddr, &info.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 = wpan_dev->pan_id;
-       sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
-
-       /* intra-PAN communications */
-       da.pan_id = sa.pan_id;
-
-       /* if the destination address is the broadcast address, use the
-        * corresponding short address
-        */
-       if (!memcmp(daddr, ldev->broadcast, EUI64_ADDR_LEN)) {
-               da.mode = IEEE802154_ADDR_SHORT;
-               da.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+       if (info.daddr.mode == IEEE802154_ADDR_SHORT &&
+           ieee802154_is_broadcast_short_addr(info.daddr.short_addr))
                cb->ackreq = false;
-       } else {
-               da.mode = IEEE802154_ADDR_LONG;
-               da.extended_addr = ieee802154_devaddr_from_raw(daddr);
+       else
                cb->ackreq = wpan_dev->ackreq;
-       }
 
-       return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev, &da,
-                                   &sa, 0);
+       return wpan_dev_hard_header(skb, lowpan_802154_dev(ldev)->wdev,
+                                   &info.daddr, &info.saddr, 0);
 }
 
 netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)