Blame view

net/rxrpc/peer_object.c 9.99 KB
be6e6707f   David Howells   rxrpc: Rework pee...
1
  /* RxRPC remote transport endpoint record management
17926a793   David Howells   [AF_RXRPC]: Provi...
2
   *
be6e6707f   David Howells   rxrpc: Rework pee...
3
   * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved.
17926a793   David Howells   [AF_RXRPC]: Provi...
4
5
6
7
8
9
10
   * Written by David Howells (dhowells@redhat.com)
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   */
9b6d53985   Joe Perches   rxrpc: Use pr_<le...
11
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17926a793   David Howells   [AF_RXRPC]: Provi...
12
13
14
15
16
  #include <linux/module.h>
  #include <linux/net.h>
  #include <linux/skbuff.h>
  #include <linux/udp.h>
  #include <linux/in.h>
75b54cb57   David Howells   rxrpc: Add IPv6 s...
17
  #include <linux/in6.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
be6e6707f   David Howells   rxrpc: Rework pee...
19
  #include <linux/hashtable.h>
17926a793   David Howells   [AF_RXRPC]: Provi...
20
21
22
  #include <net/sock.h>
  #include <net/af_rxrpc.h>
  #include <net/ip.h>
224711df5   David Howells   [AF_RXRPC]: Sort ...
23
  #include <net/route.h>
75b54cb57   David Howells   rxrpc: Add IPv6 s...
24
  #include <net/ip6_route.h>
17926a793   David Howells   [AF_RXRPC]: Provi...
25
  #include "ar-internal.h"
be6e6707f   David Howells   rxrpc: Rework pee...
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  /*
   * Hash a peer key.
   */
  static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local,
  					 const struct sockaddr_rxrpc *srx)
  {
  	const u16 *p;
  	unsigned int i, size;
  	unsigned long hash_key;
  
  	_enter("");
  
  	hash_key = (unsigned long)local / __alignof__(*local);
  	hash_key += srx->transport_type;
  	hash_key += srx->transport_len;
  	hash_key += srx->transport.family;
  
  	switch (srx->transport.family) {
  	case AF_INET:
  		hash_key += (u16 __force)srx->transport.sin.sin_port;
  		size = sizeof(srx->transport.sin.sin_addr);
  		p = (u16 *)&srx->transport.sin.sin_addr;
  		break;
d19127473   David Howells   rxrpc: Make IPv6 ...
49
  #ifdef CONFIG_AF_RXRPC_IPV6
75b54cb57   David Howells   rxrpc: Add IPv6 s...
50
51
52
53
54
  	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;
d19127473   David Howells   rxrpc: Make IPv6 ...
55
  #endif
2f9f9f521   Arnd Bergmann   rxrpc: fix uninit...
56
57
58
59
  	default:
  		WARN(1, "AF_RXRPC: Unsupported transport address family
  ");
  		return 0;
be6e6707f   David Howells   rxrpc: Rework pee...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  	}
  
  	/* Step through the peer address in 16-bit portions for speed */
  	for (i = 0; i < size; i += sizeof(*p), p++)
  		hash_key += *p;
  
  	_leave(" 0x%lx", hash_key);
  	return hash_key;
  }
  
  /*
   * Compare a peer to a key.  Return -ve, 0 or +ve to indicate less than, same
   * or greater than.
   *
   * Unfortunately, the primitives in linux/hashtable.h don't allow for sorted
   * buckets and mid-bucket insertion, so we don't make full use of this
   * information at this point.
   */
  static long rxrpc_peer_cmp_key(const struct rxrpc_peer *peer,
  			       struct rxrpc_local *local,
  			       const struct sockaddr_rxrpc *srx,
  			       unsigned long hash_key)
  {
  	long diff;
  
  	diff = ((peer->hash_key - hash_key) ?:
  		((unsigned long)peer->local - (unsigned long)local) ?:
  		(peer->srx.transport_type - srx->transport_type) ?:
  		(peer->srx.transport_len - srx->transport_len) ?:
  		(peer->srx.transport.family - srx->transport.family));
  	if (diff != 0)
  		return diff;
  
  	switch (srx->transport.family) {
  	case AF_INET:
  		return ((u16 __force)peer->srx.transport.sin.sin_port -
  			(u16 __force)srx->transport.sin.sin_port) ?:
  			memcmp(&peer->srx.transport.sin.sin_addr,
  			       &srx->transport.sin.sin_addr,
  			       sizeof(struct in_addr));
d19127473   David Howells   rxrpc: Make IPv6 ...
100
  #ifdef CONFIG_AF_RXRPC_IPV6
75b54cb57   David Howells   rxrpc: Add IPv6 s...
101
102
103
104
105
106
  	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));
d19127473   David Howells   rxrpc: Make IPv6 ...
107
  #endif
be6e6707f   David Howells   rxrpc: Rework pee...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  	default:
  		BUG();
  	}
  }
  
  /*
   * Look up a remote transport endpoint for the specified address using RCU.
   */
  static struct rxrpc_peer *__rxrpc_lookup_peer_rcu(
  	struct rxrpc_local *local,
  	const struct sockaddr_rxrpc *srx,
  	unsigned long hash_key)
  {
  	struct rxrpc_peer *peer;
2baec2c3f   David Howells   rxrpc: Support ne...
122
  	struct rxrpc_net *rxnet = local->rxnet;
be6e6707f   David Howells   rxrpc: Rework pee...
123

2baec2c3f   David Howells   rxrpc: Support ne...
124
  	hash_for_each_possible_rcu(rxnet->peer_hash, peer, hash_link, hash_key) {
be6e6707f   David Howells   rxrpc: Rework pee...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  		if (rxrpc_peer_cmp_key(peer, local, srx, hash_key) == 0) {
  			if (atomic_read(&peer->usage) == 0)
  				return NULL;
  			return peer;
  		}
  	}
  
  	return NULL;
  }
  
  /*
   * Look up a remote transport endpoint for the specified address using RCU.
   */
  struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
  					 const struct sockaddr_rxrpc *srx)
  {
  	struct rxrpc_peer *peer;
  	unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
  
  	peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
  	if (peer) {
75b54cb57   David Howells   rxrpc: Add IPv6 s...
146
  		_net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
be6e6707f   David Howells   rxrpc: Rework pee...
147
148
149
150
  		_leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
  	}
  	return peer;
  }
17926a793   David Howells   [AF_RXRPC]: Provi...
151
152
  
  /*
224711df5   David Howells   [AF_RXRPC]: Sort ...
153
154
155
156
157
   * assess the MTU size for the network interface through which this peer is
   * reached
   */
  static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
  {
75b54cb57   David Howells   rxrpc: Add IPv6 s...
158
  	struct dst_entry *dst;
224711df5   David Howells   [AF_RXRPC]: Sort ...
159
  	struct rtable *rt;
75b54cb57   David Howells   rxrpc: Add IPv6 s...
160
161
  	struct flowi fl;
  	struct flowi4 *fl4 = &fl.u.ip4;
d19127473   David Howells   rxrpc: Make IPv6 ...
162
  #ifdef CONFIG_AF_RXRPC_IPV6
75b54cb57   David Howells   rxrpc: Add IPv6 s...
163
  	struct flowi6 *fl6 = &fl.u.ip6;
d19127473   David Howells   rxrpc: Make IPv6 ...
164
  #endif
224711df5   David Howells   [AF_RXRPC]: Sort ...
165
166
  
  	peer->if_mtu = 1500;
75b54cb57   David Howells   rxrpc: Add IPv6 s...
167
168
169
170
171
172
173
174
175
176
177
178
179
  	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;
d19127473   David Howells   rxrpc: Make IPv6 ...
180
  #ifdef CONFIG_AF_RXRPC_IPV6
75b54cb57   David Howells   rxrpc: Add IPv6 s...
181
182
183
184
185
186
187
188
189
  	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);
07096f612   David Howells   rxrpc: Fix checki...
190
191
  		if (dst->error) {
  			_leave(" [route err %d]", dst->error);
75b54cb57   David Howells   rxrpc: Add IPv6 s...
192
193
194
  			return;
  		}
  		break;
d19127473   David Howells   rxrpc: Make IPv6 ...
195
  #endif
75b54cb57   David Howells   rxrpc: Add IPv6 s...
196
197
198
  
  	default:
  		BUG();
224711df5   David Howells   [AF_RXRPC]: Sort ...
199
  	}
75b54cb57   David Howells   rxrpc: Add IPv6 s...
200
201
  	peer->if_mtu = dst_mtu(dst);
  	dst_release(dst);
224711df5   David Howells   [AF_RXRPC]: Sort ...
202

a6a62b69b   David Howells   AF_RXRPC: reduce ...
203
  	_leave(" [if_mtu %u]", peer->if_mtu);
224711df5   David Howells   [AF_RXRPC]: Sort ...
204
205
206
  }
  
  /*
be6e6707f   David Howells   rxrpc: Rework pee...
207
   * Allocate a peer.
17926a793   David Howells   [AF_RXRPC]: Provi...
208
   */
be6e6707f   David Howells   rxrpc: Rework pee...
209
  struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp)
17926a793   David Howells   [AF_RXRPC]: Provi...
210
211
212
213
214
215
216
  {
  	struct rxrpc_peer *peer;
  
  	_enter("");
  
  	peer = kzalloc(sizeof(struct rxrpc_peer), gfp);
  	if (peer) {
be6e6707f   David Howells   rxrpc: Rework pee...
217
218
  		atomic_set(&peer->usage, 1);
  		peer->local = local;
f66d74901   David Howells   rxrpc: Use the pe...
219
220
221
  		INIT_HLIST_HEAD(&peer->error_targets);
  		INIT_WORK(&peer->error_distributor,
  			  &rxrpc_peer_error_distributor);
aa390bbe2   David Howells   rxrpc: Kill off t...
222
  		peer->service_conns = RB_ROOT;
8496af50e   David Howells   rxrpc: Use RCU to...
223
  		seqlock_init(&peer->service_conn_lock);
17926a793   David Howells   [AF_RXRPC]: Provi...
224
  		spin_lock_init(&peer->lock);
17926a793   David Howells   [AF_RXRPC]: Provi...
225
  		peer->debug_id = atomic_inc_return(&rxrpc_debug_id);
f7aec129a   David Howells   rxrpc: Cache the ...
226
227
228
229
230
231
232
  
  		if (RXRPC_TX_SMSS > 2190)
  			peer->cong_cwnd = 2;
  		else if (RXRPC_TX_SMSS > 1095)
  			peer->cong_cwnd = 3;
  		else
  			peer->cong_cwnd = 4;
be6e6707f   David Howells   rxrpc: Rework pee...
233
234
235
236
237
238
239
  	}
  
  	_leave(" = %p", peer);
  	return peer;
  }
  
  /*
248f219cb   David Howells   rxrpc: Rewrite th...
240
241
242
243
   * Initialise peer record.
   */
  static void rxrpc_init_peer(struct rxrpc_peer *peer, unsigned long hash_key)
  {
08a39685a   David Howells   rxrpc: Make sure ...
244
  	peer->hash_key = hash_key;
248f219cb   David Howells   rxrpc: Rewrite th...
245
246
  	rxrpc_assess_MTU_size(peer);
  	peer->mtu = peer->if_mtu;
0d4b103c0   David Howells   rxrpc: Reduce the...
247
  	peer->rtt_last_req = ktime_get_real();
248f219cb   David Howells   rxrpc: Rewrite th...
248

75b54cb57   David Howells   rxrpc: Add IPv6 s...
249
250
  	switch (peer->srx.transport.family) {
  	case AF_INET:
248f219cb   David Howells   rxrpc: Rewrite th...
251
  		peer->hdrsize = sizeof(struct iphdr);
75b54cb57   David Howells   rxrpc: Add IPv6 s...
252
  		break;
d19127473   David Howells   rxrpc: Make IPv6 ...
253
  #ifdef CONFIG_AF_RXRPC_IPV6
75b54cb57   David Howells   rxrpc: Add IPv6 s...
254
255
256
  	case AF_INET6:
  		peer->hdrsize = sizeof(struct ipv6hdr);
  		break;
d19127473   David Howells   rxrpc: Make IPv6 ...
257
  #endif
75b54cb57   David Howells   rxrpc: Add IPv6 s...
258
259
260
261
262
263
264
265
266
  	default:
  		BUG();
  	}
  
  	switch (peer->srx.transport_type) {
  	case SOCK_DGRAM:
  		peer->hdrsize += sizeof(struct udphdr);
  		break;
  	default:
248f219cb   David Howells   rxrpc: Rewrite th...
267
268
269
270
271
272
273
274
  		BUG();
  	}
  
  	peer->hdrsize += sizeof(struct rxrpc_wire_header);
  	peer->maxdata = peer->mtu - peer->hdrsize;
  }
  
  /*
be6e6707f   David Howells   rxrpc: Rework pee...
275
276
277
278
279
280
281
282
283
284
285
286
287
   * Set up a new peer.
   */
  static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_local *local,
  					    struct sockaddr_rxrpc *srx,
  					    unsigned long hash_key,
  					    gfp_t gfp)
  {
  	struct rxrpc_peer *peer;
  
  	_enter("");
  
  	peer = rxrpc_alloc_peer(local, gfp);
  	if (peer) {
17926a793   David Howells   [AF_RXRPC]: Provi...
288
  		memcpy(&peer->srx, srx, sizeof(*srx));
248f219cb   David Howells   rxrpc: Rewrite th...
289
290
  		rxrpc_init_peer(peer, hash_key);
  	}
17926a793   David Howells   [AF_RXRPC]: Provi...
291

248f219cb   David Howells   rxrpc: Rewrite th...
292
293
294
  	_leave(" = %p", peer);
  	return peer;
  }
17926a793   David Howells   [AF_RXRPC]: Provi...
295

248f219cb   David Howells   rxrpc: Rewrite th...
296
297
298
299
300
301
302
303
  /*
   * 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;
2baec2c3f   David Howells   rxrpc: Support ne...
304
  	struct rxrpc_net *rxnet = local->rxnet;
248f219cb   David Howells   rxrpc: Rewrite th...
305
306
307
308
309
  	unsigned long hash_key;
  
  	hash_key = rxrpc_peer_hash_key(local, &prealloc->srx);
  	prealloc->local = local;
  	rxrpc_init_peer(prealloc, hash_key);
2baec2c3f   David Howells   rxrpc: Support ne...
310
  	spin_lock(&rxnet->peer_hash_lock);
248f219cb   David Howells   rxrpc: Rewrite th...
311
312
313
314
315
316
317
  
  	/* 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;
2baec2c3f   David Howells   rxrpc: Support ne...
318
  		hash_add_rcu(rxnet->peer_hash, &peer->hash_link, hash_key);
17926a793   David Howells   [AF_RXRPC]: Provi...
319
  	}
2baec2c3f   David Howells   rxrpc: Support ne...
320
  	spin_unlock(&rxnet->peer_hash_lock);
17926a793   David Howells   [AF_RXRPC]: Provi...
321
322
323
324
325
326
  	return peer;
  }
  
  /*
   * obtain a remote transport endpoint for the specified address
   */
be6e6707f   David Howells   rxrpc: Rework pee...
327
328
  struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
  				     struct sockaddr_rxrpc *srx, gfp_t gfp)
17926a793   David Howells   [AF_RXRPC]: Provi...
329
330
  {
  	struct rxrpc_peer *peer, *candidate;
2baec2c3f   David Howells   rxrpc: Support ne...
331
  	struct rxrpc_net *rxnet = local->rxnet;
be6e6707f   David Howells   rxrpc: Rework pee...
332
  	unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
17926a793   David Howells   [AF_RXRPC]: Provi...
333

75b54cb57   David Howells   rxrpc: Add IPv6 s...
334
  	_enter("{%pISp}", &srx->transport);
17926a793   David Howells   [AF_RXRPC]: Provi...
335
336
  
  	/* search the peer list first */
be6e6707f   David Howells   rxrpc: Rework pee...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
  	rcu_read_lock();
  	peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
  	if (peer && !rxrpc_get_peer_maybe(peer))
  		peer = NULL;
  	rcu_read_unlock();
  
  	if (!peer) {
  		/* The peer is not yet present in hash - create a candidate
  		 * for a new record and then redo the search.
  		 */
  		candidate = rxrpc_create_peer(local, srx, hash_key, gfp);
  		if (!candidate) {
  			_leave(" = NULL [nomem]");
  			return NULL;
  		}
17926a793   David Howells   [AF_RXRPC]: Provi...
352

2baec2c3f   David Howells   rxrpc: Support ne...
353
  		spin_lock_bh(&rxnet->peer_hash_lock);
17926a793   David Howells   [AF_RXRPC]: Provi...
354

be6e6707f   David Howells   rxrpc: Rework pee...
355
356
357
358
359
  		/* Need to check that we aren't racing with someone else */
  		peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
  		if (peer && !rxrpc_get_peer_maybe(peer))
  			peer = NULL;
  		if (!peer)
2baec2c3f   David Howells   rxrpc: Support ne...
360
  			hash_add_rcu(rxnet->peer_hash,
be6e6707f   David Howells   rxrpc: Rework pee...
361
  				     &candidate->hash_link, hash_key);
17926a793   David Howells   [AF_RXRPC]: Provi...
362

2baec2c3f   David Howells   rxrpc: Support ne...
363
  		spin_unlock_bh(&rxnet->peer_hash_lock);
17926a793   David Howells   [AF_RXRPC]: Provi...
364

be6e6707f   David Howells   rxrpc: Rework pee...
365
366
367
368
369
  		if (peer)
  			kfree(candidate);
  		else
  			peer = candidate;
  	}
17926a793   David Howells   [AF_RXRPC]: Provi...
370

75b54cb57   David Howells   rxrpc: Add IPv6 s...
371
  	_net("PEER %d {%pISp}", peer->debug_id, &peer->srx.transport);
17926a793   David Howells   [AF_RXRPC]: Provi...
372

be6e6707f   David Howells   rxrpc: Rework pee...
373
  	_leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
17926a793   David Howells   [AF_RXRPC]: Provi...
374
  	return peer;
17926a793   David Howells   [AF_RXRPC]: Provi...
375
376
377
  }
  
  /*
be6e6707f   David Howells   rxrpc: Rework pee...
378
   * Discard a ref on a remote peer record.
17926a793   David Howells   [AF_RXRPC]: Provi...
379
   */
be6e6707f   David Howells   rxrpc: Rework pee...
380
  void __rxrpc_put_peer(struct rxrpc_peer *peer)
17926a793   David Howells   [AF_RXRPC]: Provi...
381
  {
2baec2c3f   David Howells   rxrpc: Support ne...
382
  	struct rxrpc_net *rxnet = peer->local->rxnet;
f66d74901   David Howells   rxrpc: Use the pe...
383
  	ASSERT(hlist_empty(&peer->error_targets));
17926a793   David Howells   [AF_RXRPC]: Provi...
384

2baec2c3f   David Howells   rxrpc: Support ne...
385
  	spin_lock_bh(&rxnet->peer_hash_lock);
be6e6707f   David Howells   rxrpc: Rework pee...
386
  	hash_del_rcu(&peer->hash_link);
2baec2c3f   David Howells   rxrpc: Support ne...
387
  	spin_unlock_bh(&rxnet->peer_hash_lock);
17926a793   David Howells   [AF_RXRPC]: Provi...
388

be6e6707f   David Howells   rxrpc: Rework pee...
389
  	kfree_rcu(peer, rcu);
17926a793   David Howells   [AF_RXRPC]: Provi...
390
  }
8324f0bcf   David Howells   rxrpc: Provide a ...
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  
  /**
   * 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);