linux/net/rxrpc/peer_object.c
<<
>>
Prefs
   1/* RxRPC remote transport endpoint record management
   2 *
   3 * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13
  14#include <linux/module.h>
  15#include <linux/net.h>
  16#include <linux/skbuff.h>
  17#include <linux/udp.h>
  18#include <linux/in.h>
  19#include <linux/in6.h>
  20#include <linux/slab.h>
  21#include <linux/hashtable.h>
  22#include <net/sock.h>
  23#include <net/af_rxrpc.h>
  24#include <net/ip.h>
  25#include <net/route.h>
  26#include <net/ip6_route.h>
  27#include "ar-internal.h"
  28
  29static DEFINE_HASHTABLE(rxrpc_peer_hash, 10);
  30static DEFINE_SPINLOCK(rxrpc_peer_hash_lock);
  31
  32/*
  33 * Hash a peer key.
  34 */
  35static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local,
  36                                         const struct sockaddr_rxrpc *srx)
  37{
  38        const u16 *p;
  39        unsigned int i, size;
  40        unsigned long hash_key;
  41
  42        _enter("");
  43
  44        hash_key = (unsigned long)local / __alignof__(*local);
  45        hash_key += srx->transport_type;
  46        hash_key += srx->transport_len;
  47        hash_key += srx->transport.family;
  48
  49        switch (srx->transport.family) {
  50        case AF_INET:
  51                hash_key += (u16 __force)srx->transport.sin.sin_port;
  52                size = sizeof(srx->transport.sin.sin_addr);
  53                p = (u16 *)&srx->transport.sin.sin_addr;
  54                break;
  55#ifdef CONFIG_AF_RXRPC_IPV6
  56        case AF_INET6:
  57                hash_key += (u16 __force)srx->transport.sin.sin_port;
  58                size = sizeof(srx->transport.sin6.sin6_addr);
  59                p = (u16 *)&srx->transport.sin6.sin6_addr;
  60                break;
  61#endif
  62        default:
  63                WARN(1, "AF_RXRPC: Unsupported transport address family\n");
  64                return 0;
  65        }
  66
  67        /* Step through the peer address in 16-bit portions for speed */
  68        for (i = 0; i < size; i += sizeof(*p), p++)
  69                hash_key += *p;
  70
  71        _leave(" 0x%lx", hash_key);
  72        return hash_key;
  73}
  74
  75/*
  76 * Compare a peer to a key.  Return -ve, 0 or +ve to indicate less than, same
  77 * or greater than.
  78 *
  79 * Unfortunately, the primitives in linux/hashtable.h don't allow for sorted
  80 * buckets and mid-bucket insertion, so we don't make full use of this
  81 * information at this point.
  82 */
  83static long rxrpc_peer_cmp_key(const struct rxrpc_peer *peer,
  84                               struct rxrpc_local *local,
  85                               const struct sockaddr_rxrpc *srx,
  86                               unsigned long hash_key)
  87{
  88        long diff;
  89
  90        diff = ((peer->hash_key - hash_key) ?:
  91                ((unsigned long)peer->local - (unsigned long)local) ?:
  92                (peer->srx.transport_type - srx->transport_type) ?:
  93                (peer->srx.transport_len - srx->transport_len) ?:
  94                (peer->srx.transport.family - srx->transport.family));
  95        if (diff != 0)
  96                return diff;
  97
  98        switch (srx->transport.family) {
  99        case AF_INET:
 100                return ((u16 __force)peer->srx.transport.sin.sin_port -
 101                        (u16 __force)srx->transport.sin.sin_port) ?:
 102                        memcmp(&peer->srx.transport.sin.sin_addr,
 103                               &srx->transport.sin.sin_addr,
 104                               sizeof(struct in_addr));
 105#ifdef CONFIG_AF_RXRPC_IPV6
 106        case AF_INET6:
 107                return ((u16 __force)peer->srx.transport.sin6.sin6_port -
 108                        (u16 __force)srx->transport.sin6.sin6_port) ?:
 109                        memcmp(&peer->srx.transport.sin6.sin6_addr,
 110                               &srx->transport.sin6.sin6_addr,
 111                               sizeof(struct in6_addr));
 112#endif
 113        default:
 114                BUG();
 115        }
 116}
 117
 118/*
 119 * Look up a remote transport endpoint for the specified address using RCU.
 120 */
 121static struct rxrpc_peer *__rxrpc_lookup_peer_rcu(
 122        struct rxrpc_local *local,
 123        const struct sockaddr_rxrpc *srx,
 124        unsigned long hash_key)
 125{
 126        struct rxrpc_peer *peer;
 127
 128        hash_for_each_possible_rcu(rxrpc_peer_hash, peer, hash_link, hash_key) {
 129                if (rxrpc_peer_cmp_key(peer, local, srx, hash_key) == 0) {
 130                        if (atomic_read(&peer->usage) == 0)
 131                                return NULL;
 132                        return peer;
 133                }
 134        }
 135
 136        return NULL;
 137}
 138
 139/*
 140 * Look up a remote transport endpoint for the specified address using RCU.
 141 */
 142struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
 143                                         const struct sockaddr_rxrpc *srx)
 144{
 145        struct rxrpc_peer *peer;
 146        unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
 147
 148        peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
 149        if (peer) {
 150                _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
 151                _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
 152        }
 153        return peer;
 154}
 155
 156/*
 157 * assess the MTU size for the network interface through which this peer is
 158 * reached
 159 */
 160static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
 161{
 162        struct dst_entry *dst;
 163        struct rtable *rt;
 164        struct flowi fl;
 165        struct flowi4 *fl4 = &fl.u.ip4;
 166#ifdef CONFIG_AF_RXRPC_IPV6
 167        struct flowi6 *fl6 = &fl.u.ip6;
 168#endif
 169
 170        peer->if_mtu = 1500;
 171
 172        memset(&fl, 0, sizeof(fl));
 173        switch (peer->srx.transport.family) {
 174        case AF_INET:
 175                rt = ip_route_output_ports(
 176                        &init_net, fl4, NULL,
 177                        peer->srx.transport.sin.sin_addr.s_addr, 0,
 178                        htons(7000), htons(7001), IPPROTO_UDP, 0, 0);
 179                if (IS_ERR(rt)) {
 180                        _leave(" [route err %ld]", PTR_ERR(rt));
 181                        return;
 182                }
 183                dst = &rt->dst;
 184                break;
 185
 186#ifdef CONFIG_AF_RXRPC_IPV6
 187        case AF_INET6:
 188                fl6->flowi6_iif = LOOPBACK_IFINDEX;
 189                fl6->flowi6_scope = RT_SCOPE_UNIVERSE;
 190                fl6->flowi6_proto = IPPROTO_UDP;
 191                memcpy(&fl6->daddr, &peer->srx.transport.sin6.sin6_addr,
 192                       sizeof(struct in6_addr));
 193                fl6->fl6_dport = htons(7001);
 194                fl6->fl6_sport = htons(7000);
 195                dst = ip6_route_output(&init_net, NULL, fl6);
 196                if (dst->error) {
 197                        _leave(" [route err %d]", dst->error);
 198                        return;
 199                }
 200                break;
 201#endif
 202
 203        default:
 204                BUG();
 205        }
 206
 207        peer->if_mtu = dst_mtu(dst);
 208        dst_release(dst);
 209
 210        _leave(" [if_mtu %u]", peer->if_mtu);
 211}
 212
 213/*
 214 * Allocate a peer.
 215 */
 216struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp)
 217{
 218        struct rxrpc_peer *peer;
 219
 220        _enter("");
 221
 222        peer = kzalloc(sizeof(struct rxrpc_peer), gfp);
 223        if (peer) {
 224                atomic_set(&peer->usage, 1);
 225                peer->local = local;
 226                INIT_HLIST_HEAD(&peer->error_targets);
 227                INIT_WORK(&peer->error_distributor,
 228                          &rxrpc_peer_error_distributor);
 229                peer->service_conns = RB_ROOT;
 230                seqlock_init(&peer->service_conn_lock);
 231                spin_lock_init(&peer->lock);
 232                peer->debug_id = atomic_inc_return(&rxrpc_debug_id);
 233        }
 234
 235        _leave(" = %p", peer);
 236        return peer;
 237}
 238
 239/*
 240 * Initialise peer record.
 241 */
 242static void rxrpc_init_peer(struct rxrpc_peer *peer, unsigned long hash_key)
 243{
 244        peer->hash_key = hash_key;
 245        rxrpc_assess_MTU_size(peer);
 246        peer->mtu = peer->if_mtu;
 247        peer->rtt_last_req = ktime_get_real();
 248
 249        switch (peer->srx.transport.family) {
 250        case AF_INET:
 251                peer->hdrsize = sizeof(struct iphdr);
 252                break;
 253#ifdef CONFIG_AF_RXRPC_IPV6
 254        case AF_INET6:
 255                peer->hdrsize = sizeof(struct ipv6hdr);
 256                break;
 257#endif
 258        default:
 259                BUG();
 260        }
 261
 262        switch (peer->srx.transport_type) {
 263        case SOCK_DGRAM:
 264                peer->hdrsize += sizeof(struct udphdr);
 265                break;
 266        default:
 267                BUG();
 268        }
 269
 270        peer->hdrsize += sizeof(struct rxrpc_wire_header);
 271        peer->maxdata = peer->mtu - peer->hdrsize;
 272}
 273
 274/*
 275 * Set up a new peer.
 276 */
 277static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_local *local,
 278                                            struct sockaddr_rxrpc *srx,
 279                                            unsigned long hash_key,
 280                                            gfp_t gfp)
 281{
 282        struct rxrpc_peer *peer;
 283
 284        _enter("");
 285
 286        peer = rxrpc_alloc_peer(local, gfp);
 287        if (peer) {
 288                memcpy(&peer->srx, srx, sizeof(*srx));
 289                rxrpc_init_peer(peer, hash_key);
 290        }
 291
 292        _leave(" = %p", peer);
 293        return peer;
 294}
 295
 296/*
 297 * Set up a new incoming peer.  The address is prestored in the preallocated
 298 * peer.
 299 */
 300struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *local,
 301                                              struct rxrpc_peer *prealloc)
 302{
 303        struct rxrpc_peer *peer;
 304        unsigned long hash_key;
 305
 306        hash_key = rxrpc_peer_hash_key(local, &prealloc->srx);
 307        prealloc->local = local;
 308        rxrpc_init_peer(prealloc, hash_key);
 309
 310        spin_lock(&rxrpc_peer_hash_lock);
 311
 312        /* Need to check that we aren't racing with someone else */
 313        peer = __rxrpc_lookup_peer_rcu(local, &prealloc->srx, hash_key);
 314        if (peer && !rxrpc_get_peer_maybe(peer))
 315                peer = NULL;
 316        if (!peer) {
 317                peer = prealloc;
 318                hash_add_rcu(rxrpc_peer_hash, &peer->hash_link, hash_key);
 319        }
 320
 321        spin_unlock(&rxrpc_peer_hash_lock);
 322        return peer;
 323}
 324
 325/*
 326 * obtain a remote transport endpoint for the specified address
 327 */
 328struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
 329                                     struct sockaddr_rxrpc *srx, gfp_t gfp)
 330{
 331        struct rxrpc_peer *peer, *candidate;
 332        unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
 333
 334        _enter("{%pISp}", &srx->transport);
 335
 336        /* search the peer list first */
 337        rcu_read_lock();
 338        peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
 339        if (peer && !rxrpc_get_peer_maybe(peer))
 340                peer = NULL;
 341        rcu_read_unlock();
 342
 343        if (!peer) {
 344                /* The peer is not yet present in hash - create a candidate
 345                 * for a new record and then redo the search.
 346                 */
 347                candidate = rxrpc_create_peer(local, srx, hash_key, gfp);
 348                if (!candidate) {
 349                        _leave(" = NULL [nomem]");
 350                        return NULL;
 351                }
 352
 353                spin_lock_bh(&rxrpc_peer_hash_lock);
 354
 355                /* Need to check that we aren't racing with someone else */
 356                peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
 357                if (peer && !rxrpc_get_peer_maybe(peer))
 358                        peer = NULL;
 359                if (!peer)
 360                        hash_add_rcu(rxrpc_peer_hash,
 361                                     &candidate->hash_link, hash_key);
 362
 363                spin_unlock_bh(&rxrpc_peer_hash_lock);
 364
 365                if (peer)
 366                        kfree(candidate);
 367                else
 368                        peer = candidate;
 369        }
 370
 371        _net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
 372
 373        _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
 374        return peer;
 375}
 376
 377/*
 378 * Discard a ref on a remote peer record.
 379 */
 380void __rxrpc_put_peer(struct rxrpc_peer *peer)
 381{
 382        ASSERT(hlist_empty(&peer->error_targets));
 383
 384        spin_lock_bh(&rxrpc_peer_hash_lock);
 385        hash_del_rcu(&peer->hash_link);
 386        spin_unlock_bh(&rxrpc_peer_hash_lock);
 387
 388        kfree_rcu(peer, rcu);
 389}
 390
 391/**
 392 * rxrpc_kernel_get_peer - Get the peer address of a call
 393 * @sock: The socket on which the call is in progress.
 394 * @call: The call to query
 395 * @_srx: Where to place the result
 396 *
 397 * Get the address of the remote peer in a call.
 398 */
 399void rxrpc_kernel_get_peer(struct socket *sock, struct rxrpc_call *call,
 400                           struct sockaddr_rxrpc *_srx)
 401{
 402        *_srx = call->peer->srx;
 403}
 404EXPORT_SYMBOL(rxrpc_kernel_get_peer);
 405