Bluetooth: 6lowpan: Route packets that are not meant to peer via correct device
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Mon, 8 Sep 2014 09:11:45 +0000 (12:11 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 9 Sep 2014 13:51:47 +0000 (15:51 +0200)
Packets that are supposed to be delivered via the peer device need to
be checked and sent to correct device. This requires that user has set
the routes properly so that the 6lowpan module can then figure out
the destination gateway and the correct Bluetooth device.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Cc: stable@vger.kernel.org # 3.17.x
net/bluetooth/6lowpan.c

index b229aed..0920cb6 100644 (file)
@@ -39,6 +39,7 @@ static struct dentry *lowpan_control_debugfs;
 
 struct skb_cb {
        struct in6_addr addr;
+       struct in6_addr gw;
        struct l2cap_chan *chan;
        int status;
 };
@@ -158,6 +159,54 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
        return NULL;
 }
 
+static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
+                                                 struct in6_addr *daddr,
+                                                 struct sk_buff *skb)
+{
+       struct lowpan_peer *peer, *tmp;
+       struct in6_addr *nexthop;
+       struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
+       int count = atomic_read(&dev->peer_count);
+
+       BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt);
+
+       /* If we have multiple 6lowpan peers, then check where we should
+        * send the packet. If only one peer exists, then we can send the
+        * packet right away.
+        */
+       if (count == 1)
+               return list_first_entry(&dev->peers, struct lowpan_peer,
+                                       list);
+
+       if (!rt) {
+               nexthop = &lowpan_cb(skb)->gw;
+
+               if (ipv6_addr_any(nexthop))
+                       return NULL;
+       } else {
+               nexthop = rt6_nexthop(rt);
+
+               /* We need to remember the address because it is needed
+                * by bt_xmit() when sending the packet. In bt_xmit(), the
+                * destination routing info is not set.
+                */
+               memcpy(&lowpan_cb(skb)->gw, nexthop, sizeof(struct in6_addr));
+       }
+
+       BT_DBG("gw %pI6c", nexthop);
+
+       list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+               BT_DBG("dst addr %pMR dst type %d ip %pI6c",
+                      &peer->chan->dst, peer->chan->dst_type,
+                      &peer->peer_addr);
+
+               if (!ipv6_addr_cmp(&peer->peer_addr, nexthop))
+                       return peer;
+       }
+
+       return NULL;
+}
+
 static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
 {
        struct lowpan_dev *entry, *tmp;
@@ -415,8 +464,18 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
                read_unlock_irqrestore(&devices_lock, flags);
 
                if (!peer) {
-                       BT_DBG("no such peer %pMR found", &addr);
-                       return -ENOENT;
+                       /* The packet might be sent to 6lowpan interface
+                        * because of routing (either via default route
+                        * or user set route) so get peer according to
+                        * the destination address.
+                        */
+                       read_lock_irqsave(&devices_lock, flags);
+                       peer = peer_lookup_dst(dev, &hdr->daddr, skb);
+                       read_unlock_irqrestore(&devices_lock, flags);
+                       if (!peer) {
+                               BT_DBG("no such peer %pMR found", &addr);
+                               return -ENOENT;
+                       }
                }
 
                daddr = peer->eui64_addr;
@@ -520,6 +579,8 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
 
                read_lock_irqsave(&devices_lock, flags);
                peer = peer_lookup_ba(dev, &addr, addr_type);
+               if (!peer)
+                       peer = peer_lookup_dst(dev, &lowpan_cb(skb)->addr, skb);
                read_unlock_irqrestore(&devices_lock, flags);
 
                BT_DBG("xmit %s to %pMR type %d IP %pI6c peer %p",