Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / net / rxrpc / peer_object.c
index 538e983..941b724 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/udp.h>
 #include <linux/in.h>
+#include <linux/in6.h>
 #include <linux/slab.h>
 #include <linux/hashtable.h>
 #include <net/sock.h>
 #include <net/af_rxrpc.h>
 #include <net/ip.h>
 #include <net/route.h>
+#include <net/ip6_route.h>
 #include "ar-internal.h"
 
 static DEFINE_HASHTABLE(rxrpc_peer_hash, 10);
@@ -50,6 +52,13 @@ static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local,
                size = sizeof(srx->transport.sin.sin_addr);
                p = (u16 *)&srx->transport.sin.sin_addr;
                break;
+#ifdef CONFIG_AF_RXRPC_IPV6
+       case AF_INET6:
+               hash_key += (u16 __force)srx->transport.sin.sin_port;
+               size = sizeof(srx->transport.sin6.sin6_addr);
+               p = (u16 *)&srx->transport.sin6.sin6_addr;
+               break;
+#endif
        default:
                WARN(1, "AF_RXRPC: Unsupported transport address family\n");
                return 0;
@@ -93,6 +102,14 @@ static long rxrpc_peer_cmp_key(const struct rxrpc_peer *peer,
                        memcmp(&peer->srx.transport.sin.sin_addr,
                               &srx->transport.sin.sin_addr,
                               sizeof(struct in_addr));
+#ifdef CONFIG_AF_RXRPC_IPV6
+       case AF_INET6:
+               return ((u16 __force)peer->srx.transport.sin6.sin6_port -
+                       (u16 __force)srx->transport.sin6.sin6_port) ?:
+                       memcmp(&peer->srx.transport.sin6.sin6_addr,
+                              &srx->transport.sin6.sin6_addr,
+                              sizeof(struct in6_addr));
+#endif
        default:
                BUG();
        }
@@ -130,17 +147,7 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
 
        peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
        if (peer) {
-               switch (srx->transport.family) {
-               case AF_INET:
-                       _net("PEER %d {%d,%u,%pI4+%hu}",
-                            peer->debug_id,
-                            peer->srx.transport_type,
-                            peer->srx.transport.family,
-                            &peer->srx.transport.sin.sin_addr,
-                            ntohs(peer->srx.transport.sin.sin_port));
-                       break;
-               }
-
+               _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
                _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
        }
        return peer;
@@ -152,22 +159,53 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
  */
 static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
 {
+       struct dst_entry *dst;
        struct rtable *rt;
-       struct flowi4 fl4;
+       struct flowi fl;
+       struct flowi4 *fl4 = &fl.u.ip4;
+#ifdef CONFIG_AF_RXRPC_IPV6
+       struct flowi6 *fl6 = &fl.u.ip6;
+#endif
 
        peer->if_mtu = 1500;
 
-       rt = ip_route_output_ports(&init_net, &fl4, NULL,
-                                  peer->srx.transport.sin.sin_addr.s_addr, 0,
-                                  htons(7000), htons(7001),
-                                  IPPROTO_UDP, 0, 0);
-       if (IS_ERR(rt)) {
-               _leave(" [route err %ld]", PTR_ERR(rt));
-               return;
+       memset(&fl, 0, sizeof(fl));
+       switch (peer->srx.transport.family) {
+       case AF_INET:
+               rt = ip_route_output_ports(
+                       &init_net, fl4, NULL,
+                       peer->srx.transport.sin.sin_addr.s_addr, 0,
+                       htons(7000), htons(7001), IPPROTO_UDP, 0, 0);
+               if (IS_ERR(rt)) {
+                       _leave(" [route err %ld]", PTR_ERR(rt));
+                       return;
+               }
+               dst = &rt->dst;
+               break;
+
+#ifdef CONFIG_AF_RXRPC_IPV6
+       case AF_INET6:
+               fl6->flowi6_iif = LOOPBACK_IFINDEX;
+               fl6->flowi6_scope = RT_SCOPE_UNIVERSE;
+               fl6->flowi6_proto = IPPROTO_UDP;
+               memcpy(&fl6->daddr, &peer->srx.transport.sin6.sin6_addr,
+                      sizeof(struct in6_addr));
+               fl6->fl6_dport = htons(7001);
+               fl6->fl6_sport = htons(7000);
+               dst = ip6_route_output(&init_net, NULL, fl6);
+               if (IS_ERR(dst)) {
+                       _leave(" [route err %ld]", PTR_ERR(dst));
+                       return;
+               }
+               break;
+#endif
+
+       default:
+               BUG();
        }
 
-       peer->if_mtu = dst_mtu(&rt->dst);
-       dst_release(&rt->dst);
+       peer->if_mtu = dst_mtu(dst);
+       dst_release(dst);
 
        _leave(" [if_mtu %u]", peer->if_mtu);
 }
@@ -198,6 +236,41 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp)
        return peer;
 }
 
+/*
+ * Initialise peer record.
+ */
+static void rxrpc_init_peer(struct rxrpc_peer *peer, unsigned long hash_key)
+{
+       peer->hash_key = hash_key;
+       rxrpc_assess_MTU_size(peer);
+       peer->mtu = peer->if_mtu;
+       peer->rtt_last_req = ktime_get_real();
+
+       switch (peer->srx.transport.family) {
+       case AF_INET:
+               peer->hdrsize = sizeof(struct iphdr);
+               break;
+#ifdef CONFIG_AF_RXRPC_IPV6
+       case AF_INET6:
+               peer->hdrsize = sizeof(struct ipv6hdr);
+               break;
+#endif
+       default:
+               BUG();
+       }
+
+       switch (peer->srx.transport_type) {
+       case SOCK_DGRAM:
+               peer->hdrsize += sizeof(struct udphdr);
+               break;
+       default:
+               BUG();
+       }
+
+       peer->hdrsize += sizeof(struct rxrpc_wire_header);
+       peer->maxdata = peer->mtu - peer->hdrsize;
+}
+
 /*
  * Set up a new peer.
  */
@@ -212,31 +285,40 @@ static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_local *local,
 
        peer = rxrpc_alloc_peer(local, gfp);
        if (peer) {
-               peer->hash_key = hash_key;
                memcpy(&peer->srx, srx, sizeof(*srx));
+               rxrpc_init_peer(peer, hash_key);
+       }
 
-               rxrpc_assess_MTU_size(peer);
-               peer->mtu = peer->if_mtu;
-
-               if (srx->transport.family == AF_INET) {
-                       peer->hdrsize = sizeof(struct iphdr);
-                       switch (srx->transport_type) {
-                       case SOCK_DGRAM:
-                               peer->hdrsize += sizeof(struct udphdr);
-                               break;
-                       default:
-                               BUG();
-                               break;
-                       }
-               } else {
-                       BUG();
-               }
+       _leave(" = %p", peer);
+       return peer;
+}
+
+/*
+ * Set up a new incoming peer.  The address is prestored in the preallocated
+ * peer.
+ */
+struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *local,
+                                             struct rxrpc_peer *prealloc)
+{
+       struct rxrpc_peer *peer;
+       unsigned long hash_key;
+
+       hash_key = rxrpc_peer_hash_key(local, &prealloc->srx);
+       prealloc->local = local;
+       rxrpc_init_peer(prealloc, hash_key);
 
-               peer->hdrsize += sizeof(struct rxrpc_wire_header);
-               peer->maxdata = peer->mtu - peer->hdrsize;
+       spin_lock(&rxrpc_peer_hash_lock);
+
+       /* Need to check that we aren't racing with someone else */
+       peer = __rxrpc_lookup_peer_rcu(local, &prealloc->srx, hash_key);
+       if (peer && !rxrpc_get_peer_maybe(peer))
+               peer = NULL;
+       if (!peer) {
+               peer = prealloc;
+               hash_add_rcu(rxrpc_peer_hash, &peer->hash_link, hash_key);
        }
 
-       _leave(" = %p", peer);
+       spin_unlock(&rxrpc_peer_hash_lock);
        return peer;
 }
 
@@ -249,11 +331,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
        struct rxrpc_peer *peer, *candidate;
        unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
 
-       _enter("{%d,%d,%pI4+%hu}",
-              srx->transport_type,
-              srx->transport_len,
-              &srx->transport.sin.sin_addr,
-              ntohs(srx->transport.sin.sin_port));
+       _enter("{%pISp}", &srx->transport);
 
        /* search the peer list first */
        rcu_read_lock();
@@ -272,7 +350,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
                        return NULL;
                }
 
-               spin_lock(&rxrpc_peer_hash_lock);
+               spin_lock_bh(&rxrpc_peer_hash_lock);
 
                /* Need to check that we aren't racing with someone else */
                peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
@@ -282,7 +360,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
                        hash_add_rcu(rxrpc_peer_hash,
                                     &candidate->hash_link, hash_key);
 
-               spin_unlock(&rxrpc_peer_hash_lock);
+               spin_unlock_bh(&rxrpc_peer_hash_lock);
 
                if (peer)
                        kfree(candidate);
@@ -290,11 +368,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
                        peer = candidate;
        }
 
-       _net("PEER %d {%d,%pI4+%hu}",
-            peer->debug_id,
-            peer->srx.transport_type,
-            &peer->srx.transport.sin.sin_addr,
-            ntohs(peer->srx.transport.sin.sin_port));
+       _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
 
        _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
        return peer;
@@ -307,9 +381,24 @@ void __rxrpc_put_peer(struct rxrpc_peer *peer)
 {
        ASSERT(hlist_empty(&peer->error_targets));
 
-       spin_lock(&rxrpc_peer_hash_lock);
+       spin_lock_bh(&rxrpc_peer_hash_lock);
        hash_del_rcu(&peer->hash_link);
-       spin_unlock(&rxrpc_peer_hash_lock);
+       spin_unlock_bh(&rxrpc_peer_hash_lock);
 
        kfree_rcu(peer, rcu);
 }
+
+/**
+ * rxrpc_kernel_get_peer - Get the peer address of a call
+ * @sock: The socket on which the call is in progress.
+ * @call: The call to query
+ * @_srx: Where to place the result
+ *
+ * Get the address of the remote peer in a call.
+ */
+void rxrpc_kernel_get_peer(struct socket *sock, struct rxrpc_call *call,
+                          struct sockaddr_rxrpc *_srx)
+{
+       *_srx = call->peer->srx;
+}
+EXPORT_SYMBOL(rxrpc_kernel_get_peer);