1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
32 */
33
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <rpc/types.h>
37 #include <netinet/in.h>
38 #include <rpc/auth.h>
39 #include <rpc/clnt.h>
40 #include <sys/tiuser.h>
41 #include <sys/t_kuser.h>
42 #include <rpc/svc.h>
43 #include <rpc/xdr.h>
44 #include <sys/file.h>
45 #include <sys/user.h>
46 #include <sys/proc.h>
47 #include <sys/vnode.h>
48 #include <sys/stream.h>
49 #include <sys/tihdr.h>
50 #include <sys/fcntl.h>
51 #include <sys/socket.h>
52 #include <sys/sysmacros.h>
53 #include <sys/errno.h>
54 #include <sys/cred.h>
55 #include <sys/systm.h>
56 #include <sys/cmn_err.h>
57
58 #define NC_INET "inet"
59
60 #define MAX_PRIV (IPPORT_RESERVED-1)
61 #define MIN_PRIV (IPPORT_RESERVED/2)
62
63 ushort_t clnt_udp_last_used = MIN_PRIV;
64 ushort_t clnt_tcp_last_used = MIN_PRIV;
65
66 /*
67 * PSARC 2003/523 Contract Private Interface
68 * clnt_tli_kcreate
69 * Changes must be reviewed by Solaris File Sharing
70 * Changes must be communicated to contract-2003-523@sun.com
71 */
72 int
clnt_tli_kcreate(struct knetconfig * config,struct netbuf * svcaddr,rpcprog_t prog,rpcvers_t vers,uint_t max_msgsize,int retries,struct cred * cred,CLIENT ** ncl)73 clnt_tli_kcreate(
74 struct knetconfig *config,
75 struct netbuf *svcaddr, /* Servers address */
76 rpcprog_t prog, /* Program number */
77 rpcvers_t vers, /* Version number */
78 uint_t max_msgsize,
79 int retries,
80 struct cred *cred,
81 CLIENT **ncl)
82 {
83 CLIENT *cl; /* Client handle */
84 int error;
85 int family = AF_UNSPEC;
86
87 error = 0;
88 cl = NULL;
89
90 RPCLOG(8, "clnt_tli_kcreate: prog %x", prog);
91 RPCLOG(8, ", vers %d", vers);
92 RPCLOG(8, ", knc_semantics %d", config->knc_semantics);
93 RPCLOG(8, ", knc_protofmly %s", config->knc_protofmly);
94 RPCLOG(8, ", knc_proto %s\n", config->knc_proto);
95
96 if (config == NULL || config->knc_protofmly == NULL || ncl == NULL) {
97 RPCLOG0(1, "clnt_tli_kcreate: bad config or handle\n");
98 return (EINVAL);
99 }
100
101 switch (config->knc_semantics) {
102 case NC_TPI_CLTS:
103 RPCLOG0(8, "clnt_tli_kcreate: CLTS selected\n");
104 error = clnt_clts_kcreate(config, svcaddr, prog, vers,
105 retries, cred, &cl);
106 if (error != 0) {
107 RPCLOG(1,
108 "clnt_tli_kcreate: clnt_clts_kcreate failed error %d\n",
109 error);
110 return (error);
111 }
112 break;
113
114 case NC_TPI_COTS:
115 case NC_TPI_COTS_ORD:
116 RPCLOG0(8, "clnt_tli_kcreate: COTS selected\n");
117 if (strcmp(config->knc_protofmly, NC_INET) == 0)
118 family = AF_INET;
119 else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
120 family = AF_INET6;
121 error = clnt_cots_kcreate(config->knc_rdev, svcaddr, family,
122 prog, vers, max_msgsize, cred, &cl);
123 if (error != 0) {
124 RPCLOG(1,
125 "clnt_tli_kcreate: clnt_cots_kcreate failed error %d\n",
126 error);
127 return (error);
128 }
129 break;
130 case NC_TPI_RDMA:
131 RPCLOG0(8, "clnt_tli_kcreate: RDMA selected\n");
132 /*
133 * RDMA doesn't support TSOL. It's better to
134 * disallow it here.
135 */
136 if (is_system_labeled()) {
137 RPCLOG0(1, "clnt_tli_kcreate: tsol not supported\n");
138 return (EPROTONOSUPPORT);
139 }
140
141 if (strcmp(config->knc_protofmly, NC_INET) == 0)
142 family = AF_INET;
143 else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
144 family = AF_INET6;
145 error = clnt_rdma_kcreate(config->knc_proto,
146 (void *)config->knc_rdev, svcaddr, family, prog, vers, cred,
147 &cl);
148 if (error != 0) {
149 RPCLOG(1,
150 "clnt_tli_kcreate: clnt_rdma_kcreate failed error %d\n",
151 error);
152 return (error);
153 }
154 break;
155 default:
156 error = EINVAL;
157 RPCLOG(1, "clnt_tli_kcreate: Bad service type %d\n",
158 config->knc_semantics);
159 return (error);
160 }
161 *ncl = cl;
162 return (0);
163 }
164
165 /*
166 * "Kinit" a client handle by calling the appropriate cots or clts routine.
167 *
168 * PSARC 2003/523 Contract Private Interface
169 * clnt_tli_kinit
170 * Changes must be reviewed by Solaris File Sharing
171 * Changes must be communicated to contract-2003-523@sun.com
172 */
173 int
clnt_tli_kinit(CLIENT * h,struct knetconfig * config,struct netbuf * addr,uint_t max_msgsize,int retries,struct cred * cred)174 clnt_tli_kinit(
175 CLIENT *h,
176 struct knetconfig *config,
177 struct netbuf *addr,
178 uint_t max_msgsize,
179 int retries,
180 struct cred *cred)
181 {
182 int error = 0;
183 int family = AF_UNSPEC;
184
185 switch (config->knc_semantics) {
186 case NC_TPI_CLTS:
187 clnt_clts_kinit(h, addr, retries, cred);
188 break;
189 case NC_TPI_COTS:
190 case NC_TPI_COTS_ORD:
191 RPCLOG0(2, "clnt_tli_kinit: COTS selected\n");
192 if (strcmp(config->knc_protofmly, NC_INET) == 0)
193 family = AF_INET;
194 else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
195 family = AF_INET6;
196 clnt_cots_kinit(h, config->knc_rdev, family,
197 addr, max_msgsize, cred);
198 break;
199 case NC_TPI_RDMA:
200 RPCLOG0(2, "clnt_tli_kinit: RDMA selected\n");
201 clnt_rdma_kinit(h, config->knc_proto,
202 (void *)config->knc_rdev, addr, cred);
203 break;
204 default:
205 error = EINVAL;
206 }
207
208 return (error);
209 }
210
211
212 /*
213 * try to bind to a reserved port
214 */
215 int
bindresvport(TIUSER * tiptr,struct netbuf * addr,struct netbuf * bound_addr,bool_t tcp)216 bindresvport(
217 TIUSER *tiptr,
218 struct netbuf *addr,
219 struct netbuf *bound_addr,
220 bool_t tcp)
221 {
222 struct sockaddr_in *sin;
223 struct sockaddr_in6 *sin6;
224 bool_t ipv6_flag = 0;
225 int i;
226 struct t_bind *req;
227 struct t_bind *ret;
228 int error;
229 bool_t loop_twice;
230 int start;
231 int stop;
232 ushort_t *last_used;
233
234 if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&req)) != 0) {
235 RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
236 return (error);
237 }
238
239 if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&ret)) != 0) {
240 RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
241 (void) t_kfree(tiptr, (char *)req, T_BIND);
242 return (error);
243 }
244
245 /* now separate IPv4 and IPv6 by looking at len of tiptr.addr */
246 if (tiptr->tp_info.addr == sizeof (struct sockaddr_in6)) {
247 /* it's IPv6 */
248 ipv6_flag = 1;
249 sin6 = (struct sockaddr_in6 *)req->addr.buf;
250 sin6->sin6_family = AF_INET6;
251 bzero((char *)&sin6->sin6_addr, sizeof (struct in6_addr));
252 req->addr.len = sizeof (struct sockaddr_in6);
253 } else {
254 /* LINTED pointer alignment */
255 sin = (struct sockaddr_in *)req->addr.buf;
256 sin->sin_family = AF_INET;
257 sin->sin_addr.s_addr = INADDR_ANY;
258 req->addr.len = sizeof (struct sockaddr_in);
259 }
260
261 int useresvport = 0;
262 if (addr) {
263 if (ipv6_flag) {
264 bcopy(addr->buf, (char *)sin6,
265 sizeof (struct sockaddr_in6));
266 if (sin6->sin6_port != 0) {
267 useresvport = 1;
268 }
269 } else {
270 bcopy(addr->buf, req->addr.buf, addr->len);
271 if (sin->sin_port != 0) {
272 useresvport = 1;
273 }
274 }
275 req->addr.len = addr->len;
276 RPCLOG(8, "bindresvport: calling t_kbind useresvport = %d\n",
277 useresvport);
278 }
279 /*
280 * Caller wants to bind to a specific port, so don't bother with the
281 * loop that binds to the next free one.
282 */
283 if (useresvport) {
284 if ((error = t_kbind(tiptr, req, ret)) != 0) {
285 RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
286 /*
287 * The unbind is called in case the bind failed
288 * with an EINTR potentially leaving the
289 * transport in bound state.
290 */
291 if (error == EINTR)
292 (void) t_kunbind(tiptr);
293 } else if (bcmp(req->addr.buf, ret->addr.buf,
294 ret->addr.len) != 0) {
295 RPCLOG0(1, "bindresvport: bcmp error\n");
296 (void) t_kunbind(tiptr);
297 error = EADDRINUSE;
298 }
299 } else {
300 if (tcp)
301 last_used = &clnt_tcp_last_used;
302 else
303 last_used = &clnt_udp_last_used;
304 error = EADDRINUSE;
305 stop = MIN_PRIV;
306
307 start = (*last_used == MIN_PRIV ? MAX_PRIV : *last_used - 1);
308 loop_twice = (start < MAX_PRIV ? TRUE : FALSE);
309
310 bindresvport_again:
311 for (i = start;
312 (error == EADDRINUSE || error == EADDRNOTAVAIL) &&
313 i >= stop; i--) {
314 if (ipv6_flag)
315 sin6->sin6_port = htons(i);
316 else
317 sin->sin_port = htons(i);
318 RPCLOG(8, "bindresvport: calling t_kbind tiptr = 0%p\n",
319 (void *)tiptr);
320 if ((error = t_kbind(tiptr, req, ret)) != 0) {
321 RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
322 /*
323 * The unbind is called in case the bind failed
324 * with an EINTR potentially leaving the
325 * transport in bound state.
326 */
327 if (error == EINTR)
328 (void) t_kunbind(tiptr);
329 } else if (bcmp(req->addr.buf, ret->addr.buf,
330 ret->addr.len) != 0) {
331 RPCLOG0(1, "bindresvport: bcmp error\n");
332 (void) t_kunbind(tiptr);
333 error = EADDRINUSE;
334 } else
335 error = 0;
336 }
337 if (!error) {
338 if (ipv6_flag) {
339 RPCLOG(8, "bindresvport: port assigned %d\n",
340 sin6->sin6_port);
341 *last_used = ntohs(sin6->sin6_port);
342 } else {
343 RPCLOG(8, "bindresvport: port assigned %d\n",
344 sin->sin_port);
345 *last_used = ntohs(sin->sin_port);
346 }
347 } else if (loop_twice) {
348 loop_twice = FALSE;
349 start = MAX_PRIV;
350 stop = *last_used + 1;
351 goto bindresvport_again;
352 }
353 }
354
355 if (!error && bound_addr) {
356 if (bound_addr->maxlen < ret->addr.len) {
357 kmem_free(bound_addr->buf, bound_addr->maxlen);
358 bound_addr->buf = kmem_zalloc(ret->addr.len, KM_SLEEP);
359 bound_addr->maxlen = ret->addr.len;
360 }
361 bcopy(ret->addr.buf, bound_addr->buf, ret->addr.len);
362 bound_addr->len = ret->addr.len;
363 }
364 (void) t_kfree(tiptr, (char *)req, T_BIND);
365 (void) t_kfree(tiptr, (char *)ret, T_BIND);
366 return (error);
367 }
368
369 void
clnt_init(void)370 clnt_init(void)
371 {
372 clnt_cots_init();
373 clnt_clts_init();
374 }
375
376 void
clnt_fini(void)377 clnt_fini(void)
378 {
379 clnt_clts_fini();
380 clnt_cots_fini();
381 }
382
383 call_table_t *
call_table_init(int size)384 call_table_init(int size)
385 {
386 call_table_t *ctp;
387 int i;
388
389 ctp = kmem_alloc(sizeof (call_table_t) * size, KM_SLEEP);
390
391 for (i = 0; i < size; i++) {
392 ctp[i].ct_call_next = (calllist_t *)&ctp[i];
393 ctp[i].ct_call_prev = (calllist_t *)&ctp[i];
394 mutex_init(&ctp[i].ct_lock, NULL, MUTEX_DEFAULT, NULL);
395 ctp[i].ct_len = 0;
396 }
397
398 return (ctp);
399 }
400
401 /*
402 * Initialize a netbuf suitable for
403 * describing an address
404 */
405 void
clnt_init_netbuf(struct netbuf * nbuf,int len)406 clnt_init_netbuf(struct netbuf *nbuf, int len)
407 {
408 nbuf->buf = kmem_zalloc(len, KM_SLEEP);
409 nbuf->maxlen = len;
410 nbuf->len = 0;
411 }
412
413 /*
414 * Free a netbuf
415 */
416 void
clnt_free_netbuf(struct netbuf * nbuf)417 clnt_free_netbuf(struct netbuf *nbuf)
418 {
419 if (nbuf == NULL || nbuf->buf == NULL) {
420 #ifdef DEBUG
421 cmn_err(CE_PANIC, "Null netbuf in clnt_free_netbuf");
422 #endif
423 return;
424 }
425 kmem_free(nbuf->buf, nbuf->maxlen);
426 nbuf->buf = NULL;
427 nbuf->maxlen = 0;
428 nbuf->len = 0;
429 }
430
431 /*
432 * Duplicate a netbuf, must be followed by a clnt_free_netbuf().
433 */
434 void
clnt_dup_netbuf(const struct netbuf * from,struct netbuf * to)435 clnt_dup_netbuf(const struct netbuf *from, struct netbuf *to)
436 {
437 clnt_init_netbuf(to, from->len);
438 to->len = from->len;
439
440 bcopy(from->buf, to->buf, (size_t)from->len);
441 }
442
443 /*
444 * Compare a netbuf.
445 */
446 int
clnt_cmp_netaddr(const struct netbuf * from,struct netbuf * to)447 clnt_cmp_netaddr(const struct netbuf *from, struct netbuf *to)
448 {
449 if (to->len != from->len || from->len == 0)
450 return (1);
451 struct sockaddr *saddr = (struct sockaddr *)(from->buf);
452 struct sockaddr *toaddr = (struct sockaddr *)(to->buf);
453
454 if (saddr->sa_family == AF_INET && toaddr->sa_family == AF_INET) {
455 return bcmp(&((struct sockaddr_in *)from->buf)->sin_addr,
456 &((struct sockaddr_in *)to->buf)->sin_addr,
457 sizeof (struct in_addr));
458 } else if (saddr->sa_family == AF_INET6 &&
459 toaddr->sa_family == AF_INET6) {
460 return bcmp(&(((struct sockaddr_in6 *)from->buf)->sin6_addr),
461 &(((struct sockaddr_in6 *)to->buf)->sin6_addr),
462 sizeof (struct in6_addr));
463 }
464 return (1);
465 }
466