Merge branch 'x86-apic-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / net / bluetooth / 6lowpan.c
index 1555b0c6f7ecf355d8e4e1d22517347a7fee565f..9d41de1ec90f687ea9ad249c7ede9a5e37f86968 100644 (file)
@@ -164,26 +164,21 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
        int count = atomic_read(&dev->peer_count);
        const struct in6_addr *nexthop;
        struct lowpan_peer *peer;
+       struct neighbour *neigh;
 
        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) {
-               rcu_read_lock();
-               peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
-                                             list);
-               rcu_read_unlock();
-               return peer;
-       }
-
        if (!rt) {
-               nexthop = &lowpan_cb(skb)->gw;
-
-               if (ipv6_addr_any(nexthop))
-                       return NULL;
+               if (ipv6_addr_any(&lowpan_cb(skb)->gw)) {
+                       /* There is neither route nor gateway,
+                        * probably the destination is a direct peer.
+                        */
+                       nexthop = daddr;
+               } else {
+                       /* There is a known gateway
+                        */
+                       nexthop = &lowpan_cb(skb)->gw;
+               }
        } else {
                nexthop = rt6_nexthop(rt, daddr);
 
@@ -209,6 +204,20 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
                }
        }
 
+       /* use the neighbour cache for matching addresses assigned by SLAAC
+       */
+       neigh = __ipv6_neigh_lookup(dev->netdev, nexthop);
+       if (neigh) {
+               list_for_each_entry_rcu(peer, &dev->peers, list) {
+                       if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) {
+                               neigh_release(neigh);
+                               rcu_read_unlock();
+                               return peer;
+                       }
+               }
+               neigh_release(neigh);
+       }
+
        rcu_read_unlock();
 
        return NULL;