Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/rpc/clnt_rc.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2008 Isilon Inc http://www.isilon.com/
5
* Authors: Doug Rabson <[email protected]>
6
* Developed with Red Inc: Alfred Perlstein <[email protected]>
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
#include <sys/param.h>
31
#include <sys/systm.h>
32
#include <sys/kernel.h>
33
#include <sys/limits.h>
34
#include <sys/lock.h>
35
#include <sys/malloc.h>
36
#include <sys/mbuf.h>
37
#include <sys/mutex.h>
38
#include <sys/pcpu.h>
39
#include <sys/proc.h>
40
#include <sys/socket.h>
41
#include <sys/socketvar.h>
42
#include <sys/time.h>
43
#include <sys/uio.h>
44
45
#include <netinet/tcp.h>
46
47
#include <rpc/rpc.h>
48
#include <rpc/rpc_com.h>
49
#include <rpc/krpc.h>
50
#include <rpc/rpcsec_tls.h>
51
52
static enum clnt_stat clnt_reconnect_call(CLIENT *, struct rpc_callextra *,
53
rpcproc_t, struct mbuf *, struct mbuf **, struct timeval);
54
static void clnt_reconnect_geterr(CLIENT *, struct rpc_err *);
55
static bool_t clnt_reconnect_freeres(CLIENT *, xdrproc_t, void *);
56
static void clnt_reconnect_abort(CLIENT *);
57
static bool_t clnt_reconnect_control(CLIENT *, u_int, void *);
58
static void clnt_reconnect_close(CLIENT *);
59
static void clnt_reconnect_destroy(CLIENT *);
60
61
static const struct clnt_ops clnt_reconnect_ops = {
62
.cl_call = clnt_reconnect_call,
63
.cl_abort = clnt_reconnect_abort,
64
.cl_geterr = clnt_reconnect_geterr,
65
.cl_freeres = clnt_reconnect_freeres,
66
.cl_close = clnt_reconnect_close,
67
.cl_destroy = clnt_reconnect_destroy,
68
.cl_control = clnt_reconnect_control
69
};
70
71
static int fake_wchan;
72
73
CLIENT *
74
clnt_reconnect_create(
75
struct netconfig *nconf, /* network type */
76
struct sockaddr *svcaddr, /* servers address */
77
rpcprog_t program, /* program number */
78
rpcvers_t version, /* version number */
79
size_t sendsz, /* buffer recv size */
80
size_t recvsz) /* buffer send size */
81
{
82
CLIENT *cl = NULL; /* client handle */
83
struct rc_data *rc = NULL; /* private data */
84
85
if (svcaddr == NULL) {
86
rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
87
return (NULL);
88
}
89
90
cl = mem_alloc(sizeof (CLIENT));
91
rc = mem_alloc(sizeof (*rc));
92
mtx_init(&rc->rc_lock, "rc->rc_lock", NULL, MTX_DEF);
93
(void) memcpy(&rc->rc_addr, svcaddr, (size_t)svcaddr->sa_len);
94
rc->rc_nconf = nconf;
95
rc->rc_prog = program;
96
rc->rc_vers = version;
97
rc->rc_sendsz = sendsz;
98
rc->rc_recvsz = recvsz;
99
rc->rc_timeout.tv_sec = -1;
100
rc->rc_timeout.tv_usec = -1;
101
rc->rc_retry.tv_sec = 3;
102
rc->rc_retry.tv_usec = 0;
103
rc->rc_retries = INT_MAX;
104
rc->rc_privport = FALSE;
105
rc->rc_waitchan = "rpcrecv";
106
rc->rc_intr = 0;
107
rc->rc_connecting = FALSE;
108
rc->rc_closed = FALSE;
109
rc->rc_ucred = crdup(curthread->td_ucred);
110
rc->rc_client = NULL;
111
rc->rc_tls = false;
112
rc->rc_tlscertname = NULL;
113
rc->rc_reconcall = NULL;
114
rc->rc_reconarg = NULL;
115
116
cl->cl_refs = 1;
117
cl->cl_ops = &clnt_reconnect_ops;
118
cl->cl_private = (caddr_t)(void *)rc;
119
cl->cl_auth = authnone_create();
120
cl->cl_tp = NULL;
121
cl->cl_netid = NULL;
122
return (cl);
123
}
124
125
static enum clnt_stat
126
clnt_reconnect_connect(CLIENT *cl)
127
{
128
struct thread *td = curthread;
129
struct rc_data *rc = (struct rc_data *)cl->cl_private;
130
struct socket *so;
131
enum clnt_stat stat;
132
int error;
133
int one = 1;
134
struct ucred *oldcred;
135
CLIENT *newclient = NULL;
136
uint32_t reterr;
137
138
mtx_lock(&rc->rc_lock);
139
while (rc->rc_connecting) {
140
error = msleep(rc, &rc->rc_lock,
141
rc->rc_intr ? PCATCH : 0, "rpcrecon", 0);
142
if (error) {
143
mtx_unlock(&rc->rc_lock);
144
return (RPC_INTR);
145
}
146
}
147
if (rc->rc_closed) {
148
mtx_unlock(&rc->rc_lock);
149
return (RPC_CANTSEND);
150
}
151
if (rc->rc_client) {
152
mtx_unlock(&rc->rc_lock);
153
return (RPC_SUCCESS);
154
}
155
156
/*
157
* My turn to attempt a connect. The rc_connecting variable
158
* serializes the following code sequence, so it is guaranteed
159
* that rc_client will still be NULL after it is re-locked below,
160
* since that is the only place it is set non-NULL.
161
*/
162
rc->rc_connecting = TRUE;
163
mtx_unlock(&rc->rc_lock);
164
165
oldcred = td->td_ucred;
166
td->td_ucred = rc->rc_ucred;
167
so = __rpc_nconf2socket(rc->rc_nconf);
168
if (!so) {
169
stat = rpc_createerr.cf_stat = RPC_TLIERROR;
170
rpc_createerr.cf_error.re_errno = 0;
171
td->td_ucred = oldcred;
172
goto out;
173
}
174
175
if (rc->rc_privport)
176
bindresvport(so, NULL);
177
178
if (rc->rc_nconf->nc_semantics == NC_TPI_CLTS)
179
newclient = clnt_dg_create(so,
180
(struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
181
rc->rc_sendsz, rc->rc_recvsz);
182
else {
183
/*
184
* I do not believe a timeout of less than 1sec would make
185
* sense here since short delays can occur when a server is
186
* temporarily overloaded.
187
*/
188
if (rc->rc_timeout.tv_sec > 0 && rc->rc_timeout.tv_usec >= 0) {
189
error = so_setsockopt(so, SOL_SOCKET, SO_SNDTIMEO,
190
&rc->rc_timeout, sizeof(struct timeval));
191
if (error != 0) {
192
stat = rpc_createerr.cf_stat = RPC_CANTSEND;
193
rpc_createerr.cf_error.re_errno = error;
194
td->td_ucred = oldcred;
195
goto out;
196
}
197
}
198
newclient = clnt_vc_create(so,
199
(struct sockaddr *) &rc->rc_addr, rc->rc_prog, rc->rc_vers,
200
rc->rc_sendsz, rc->rc_recvsz, rc->rc_intr);
201
/*
202
* CLSET_FD_CLOSE must be done now, in case rpctls_connect()
203
* fails just below.
204
*/
205
if (newclient != NULL)
206
CLNT_CONTROL(newclient, CLSET_FD_CLOSE, 0);
207
if (rc->rc_tls && newclient != NULL) {
208
CURVNET_SET(so->so_vnet);
209
stat = rpctls_connect(newclient, rc->rc_tlscertname, so,
210
&reterr);
211
CURVNET_RESTORE();
212
if (stat != RPC_SUCCESS || reterr != RPCTLSERR_OK) {
213
if (stat == RPC_SUCCESS)
214
stat = RPC_FAILED;
215
stat = rpc_createerr.cf_stat = stat;
216
rpc_createerr.cf_error.re_errno = 0;
217
CLNT_CLOSE(newclient);
218
CLNT_RELEASE(newclient);
219
newclient = NULL;
220
td->td_ucred = oldcred;
221
goto out;
222
}
223
CLNT_CONTROL(newclient, CLSET_TLS,
224
&(int){RPCTLS_COMPLETE});
225
}
226
if (newclient != NULL) {
227
int optval = 1;
228
229
(void)so_setsockopt(so, IPPROTO_TCP, TCP_USE_DDP,
230
&optval, sizeof(optval));
231
}
232
if (newclient != NULL && rc->rc_reconcall != NULL)
233
(*rc->rc_reconcall)(newclient, rc->rc_reconarg,
234
rc->rc_ucred);
235
}
236
td->td_ucred = oldcred;
237
238
if (!newclient) {
239
soclose(so);
240
rc->rc_err = rpc_createerr.cf_error;
241
stat = rpc_createerr.cf_stat;
242
goto out;
243
}
244
245
CLNT_CONTROL(newclient, CLSET_CONNECT, &one);
246
CLNT_CONTROL(newclient, CLSET_TIMEOUT, &rc->rc_timeout);
247
CLNT_CONTROL(newclient, CLSET_RETRY_TIMEOUT, &rc->rc_retry);
248
CLNT_CONTROL(newclient, CLSET_WAITCHAN, rc->rc_waitchan);
249
CLNT_CONTROL(newclient, CLSET_INTERRUPTIBLE, &rc->rc_intr);
250
if (rc->rc_backchannel != NULL)
251
CLNT_CONTROL(newclient, CLSET_BACKCHANNEL, rc->rc_backchannel);
252
stat = RPC_SUCCESS;
253
254
out:
255
mtx_lock(&rc->rc_lock);
256
KASSERT(rc->rc_client == NULL, ("rc_client not null"));
257
if (!rc->rc_closed) {
258
rc->rc_client = newclient;
259
newclient = NULL;
260
}
261
rc->rc_connecting = FALSE;
262
wakeup(rc);
263
mtx_unlock(&rc->rc_lock);
264
265
if (newclient) {
266
/*
267
* It has been closed, so discard the new client.
268
* nb: clnt_[dg|vc]_close()/clnt_[dg|vc]_destroy() cannot
269
* be called with the rc_lock mutex held, since they may
270
* msleep() while holding a different mutex.
271
*/
272
CLNT_CLOSE(newclient);
273
CLNT_RELEASE(newclient);
274
}
275
276
return (stat);
277
}
278
279
static enum clnt_stat
280
clnt_reconnect_call(
281
CLIENT *cl, /* client handle */
282
struct rpc_callextra *ext, /* call metadata */
283
rpcproc_t proc, /* procedure number */
284
struct mbuf *args, /* pointer to args */
285
struct mbuf **resultsp, /* pointer to results */
286
struct timeval utimeout)
287
{
288
struct rc_data *rc = (struct rc_data *)cl->cl_private;
289
CLIENT *client;
290
enum clnt_stat stat;
291
int tries, error;
292
293
tries = 0;
294
do {
295
mtx_lock(&rc->rc_lock);
296
if (rc->rc_closed) {
297
mtx_unlock(&rc->rc_lock);
298
return (RPC_CANTSEND);
299
}
300
301
if (!rc->rc_client) {
302
mtx_unlock(&rc->rc_lock);
303
stat = clnt_reconnect_connect(cl);
304
if (stat == RPC_SYSTEMERROR) {
305
error = tsleep(&fake_wchan,
306
rc->rc_intr ? PCATCH : 0, "rpccon", hz);
307
if (error == EINTR || error == ERESTART)
308
return (RPC_INTR);
309
tries++;
310
if (tries >= rc->rc_retries)
311
return (stat);
312
continue;
313
}
314
if (stat != RPC_SUCCESS)
315
return (stat);
316
mtx_lock(&rc->rc_lock);
317
}
318
319
if (!rc->rc_client) {
320
mtx_unlock(&rc->rc_lock);
321
stat = RPC_FAILED;
322
continue;
323
}
324
CLNT_ACQUIRE(rc->rc_client);
325
client = rc->rc_client;
326
mtx_unlock(&rc->rc_lock);
327
stat = CLNT_CALL_MBUF(client, ext, proc, args,
328
resultsp, utimeout);
329
330
if (stat != RPC_SUCCESS) {
331
if (!ext)
332
CLNT_GETERR(client, &rc->rc_err);
333
}
334
335
if (stat == RPC_TIMEDOUT) {
336
/*
337
* Check for async send misfeature for NLM
338
* protocol.
339
*/
340
if ((rc->rc_timeout.tv_sec == 0
341
&& rc->rc_timeout.tv_usec == 0)
342
|| (rc->rc_timeout.tv_sec == -1
343
&& utimeout.tv_sec == 0
344
&& utimeout.tv_usec == 0)) {
345
CLNT_RELEASE(client);
346
break;
347
}
348
}
349
350
if (stat == RPC_TIMEDOUT || stat == RPC_CANTSEND
351
|| stat == RPC_CANTRECV) {
352
tries++;
353
if (tries >= rc->rc_retries) {
354
CLNT_RELEASE(client);
355
break;
356
}
357
358
if (ext && ext->rc_feedback)
359
ext->rc_feedback(FEEDBACK_RECONNECT, proc,
360
ext->rc_feedback_arg);
361
362
mtx_lock(&rc->rc_lock);
363
/*
364
* Make sure that someone else hasn't already
365
* reconnected by checking if rc_client has changed.
366
* If not, we are done with the client and must
367
* do CLNT_RELEASE(client) twice to dispose of it,
368
* because there is both an initial refcnt and one
369
* acquired by CLNT_ACQUIRE() above.
370
*/
371
if (rc->rc_client == client) {
372
rc->rc_client = NULL;
373
mtx_unlock(&rc->rc_lock);
374
CLNT_RELEASE(client);
375
} else {
376
mtx_unlock(&rc->rc_lock);
377
}
378
CLNT_RELEASE(client);
379
} else {
380
CLNT_RELEASE(client);
381
break;
382
}
383
} while (stat != RPC_SUCCESS);
384
385
KASSERT(stat != RPC_SUCCESS || *resultsp,
386
("RPC_SUCCESS without reply"));
387
388
return (stat);
389
}
390
391
static void
392
clnt_reconnect_geterr(CLIENT *cl, struct rpc_err *errp)
393
{
394
struct rc_data *rc = (struct rc_data *)cl->cl_private;
395
396
*errp = rc->rc_err;
397
}
398
399
/*
400
* Since this function requires that rc_client be valid, it can
401
* only be called when that is guaranteed to be the case.
402
*/
403
static bool_t
404
clnt_reconnect_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
405
{
406
struct rc_data *rc = (struct rc_data *)cl->cl_private;
407
408
return (CLNT_FREERES(rc->rc_client, xdr_res, res_ptr));
409
}
410
411
/*ARGSUSED*/
412
static void
413
clnt_reconnect_abort(CLIENT *h)
414
{
415
}
416
417
/*
418
* CLNT_CONTROL() on the client returned by clnt_reconnect_create() must
419
* always be called before CLNT_CALL_MBUF() by a single thread only.
420
*/
421
static bool_t
422
clnt_reconnect_control(CLIENT *cl, u_int request, void *info)
423
{
424
struct rc_data *rc = (struct rc_data *)cl->cl_private;
425
SVCXPRT *xprt;
426
size_t slen;
427
struct rpc_reconupcall *upcp;
428
429
if (info == NULL) {
430
return (FALSE);
431
}
432
switch (request) {
433
case CLSET_TIMEOUT:
434
rc->rc_timeout = *(struct timeval *)info;
435
if (rc->rc_client)
436
CLNT_CONTROL(rc->rc_client, request, info);
437
break;
438
439
case CLGET_TIMEOUT:
440
*(struct timeval *)info = rc->rc_timeout;
441
break;
442
443
case CLSET_RETRY_TIMEOUT:
444
rc->rc_retry = *(struct timeval *)info;
445
if (rc->rc_client)
446
CLNT_CONTROL(rc->rc_client, request, info);
447
break;
448
449
case CLGET_RETRY_TIMEOUT:
450
*(struct timeval *)info = rc->rc_retry;
451
break;
452
453
case CLGET_VERS:
454
*(uint32_t *)info = rc->rc_vers;
455
break;
456
457
case CLSET_VERS:
458
rc->rc_vers = *(uint32_t *) info;
459
if (rc->rc_client)
460
CLNT_CONTROL(rc->rc_client, CLSET_VERS, info);
461
break;
462
463
case CLGET_PROG:
464
*(uint32_t *)info = rc->rc_prog;
465
break;
466
467
case CLSET_PROG:
468
rc->rc_prog = *(uint32_t *) info;
469
if (rc->rc_client)
470
CLNT_CONTROL(rc->rc_client, request, info);
471
break;
472
473
case CLSET_WAITCHAN:
474
rc->rc_waitchan = (char *)info;
475
if (rc->rc_client)
476
CLNT_CONTROL(rc->rc_client, request, info);
477
break;
478
479
case CLGET_WAITCHAN:
480
*(const char **) info = rc->rc_waitchan;
481
break;
482
483
case CLSET_INTERRUPTIBLE:
484
rc->rc_intr = *(int *) info;
485
if (rc->rc_client)
486
CLNT_CONTROL(rc->rc_client, request, info);
487
break;
488
489
case CLGET_INTERRUPTIBLE:
490
*(int *) info = rc->rc_intr;
491
break;
492
493
case CLSET_RETRIES:
494
rc->rc_retries = *(int *) info;
495
break;
496
497
case CLGET_RETRIES:
498
*(int *) info = rc->rc_retries;
499
break;
500
501
case CLSET_PRIVPORT:
502
rc->rc_privport = *(int *) info;
503
break;
504
505
case CLGET_PRIVPORT:
506
*(int *) info = rc->rc_privport;
507
break;
508
509
case CLSET_BACKCHANNEL:
510
xprt = (SVCXPRT *)info;
511
xprt_register(xprt);
512
rc->rc_backchannel = info;
513
break;
514
515
case CLSET_TLS:
516
rc->rc_tls = true;
517
break;
518
519
case CLSET_TLSCERTNAME:
520
slen = strlen(info) + 1;
521
/*
522
* tlscertname with "key.pem" appended to it forms a file
523
* name. As such, the maximum allowable strlen(info) is
524
* NAME_MAX - 7. However, "slen" includes the nul termination
525
* byte so it can be up to NAME_MAX - 6.
526
*/
527
if (slen <= 1 || slen > NAME_MAX - 6)
528
return (FALSE);
529
rc->rc_tlscertname = mem_alloc(slen);
530
strlcpy(rc->rc_tlscertname, info, slen);
531
break;
532
533
case CLSET_RECONUPCALL:
534
upcp = (struct rpc_reconupcall *)info;
535
rc->rc_reconcall = upcp->call;
536
rc->rc_reconarg = upcp->arg;
537
break;
538
539
default:
540
return (FALSE);
541
}
542
543
return (TRUE);
544
}
545
546
static void
547
clnt_reconnect_close(CLIENT *cl)
548
{
549
struct rc_data *rc = (struct rc_data *)cl->cl_private;
550
CLIENT *client;
551
552
mtx_lock(&rc->rc_lock);
553
554
if (rc->rc_closed) {
555
mtx_unlock(&rc->rc_lock);
556
return;
557
}
558
559
rc->rc_closed = TRUE;
560
client = rc->rc_client;
561
rc->rc_client = NULL;
562
563
mtx_unlock(&rc->rc_lock);
564
565
if (client) {
566
CLNT_CLOSE(client);
567
CLNT_RELEASE(client);
568
}
569
}
570
571
static void
572
clnt_reconnect_destroy(CLIENT *cl)
573
{
574
struct rc_data *rc = (struct rc_data *)cl->cl_private;
575
SVCXPRT *xprt;
576
577
if (rc->rc_client)
578
CLNT_DESTROY(rc->rc_client);
579
if (rc->rc_backchannel) {
580
xprt = (SVCXPRT *)rc->rc_backchannel;
581
KASSERT(xprt->xp_socket == NULL,
582
("clnt_reconnect_destroy: xp_socket not NULL"));
583
xprt_unregister(xprt);
584
SVC_RELEASE(xprt);
585
}
586
crfree(rc->rc_ucred);
587
mtx_destroy(&rc->rc_lock);
588
mem_free(rc->rc_tlscertname, 0); /* 0 ok, since arg. ignored. */
589
mem_free(rc->rc_reconarg, 0);
590
mem_free(rc, sizeof(*rc));
591
mem_free(cl, sizeof (CLIENT));
592
}
593
594