xref: /illumos-gate/usr/src/uts/common/rpc/clnt_gen.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <rpc/types.h>
39 #include <netinet/in.h>
40 #include <rpc/auth.h>
41 #include <rpc/clnt.h>
42 #include <sys/tiuser.h>
43 #include <sys/t_kuser.h>
44 #include <rpc/svc.h>
45 #include <rpc/xdr.h>
46 #include <sys/file.h>
47 #include <sys/user.h>
48 #include <sys/proc.h>
49 #include <sys/vnode.h>
50 #include <sys/stream.h>
51 #include <sys/tihdr.h>
52 #include <sys/fcntl.h>
53 #include <sys/socket.h>
54 #include <sys/sysmacros.h>
55 #include <sys/errno.h>
56 #include <sys/cred.h>
57 #include <sys/systm.h>
58 #include <sys/cmn_err.h>
59 
60 #define	NC_INET		"inet"
61 
62 #define	MAX_PRIV	(IPPORT_RESERVED-1)
63 #define	MIN_PRIV	(IPPORT_RESERVED/2)
64 
65 ushort_t clnt_udp_last_used = MIN_PRIV;
66 ushort_t clnt_tcp_last_used = MIN_PRIV;
67 
68 /*
69  * PSARC 2003/523 Contract Private Interface
70  * clnt_tli_kcreate
71  * Changes must be reviewed by Solaris File Sharing
72  * Changes must be communicated to contract-2003-523@sun.com
73  */
74 int
75 clnt_tli_kcreate(
76 	struct knetconfig	*config,
77 	struct netbuf		*svcaddr,	/* Servers address */
78 	rpcprog_t		prog,		/* Program number */
79 	rpcvers_t		vers,		/* Version number */
80 	uint_t			max_msgsize,
81 	int			retries,
82 	struct cred		*cred,
83 	CLIENT			**ncl)
84 {
85 	CLIENT			*cl;		/* Client handle */
86 	int			error;
87 	int			family = AF_UNSPEC;
88 
89 	error = 0;
90 	cl = NULL;
91 
92 	RPCLOG(8, "clnt_tli_kcreate: prog %x", prog);
93 	RPCLOG(8, ", vers %d", vers);
94 	RPCLOG(8, ", knc_semantics %d", config->knc_semantics);
95 	RPCLOG(8, ", knc_protofmly %s", config->knc_protofmly);
96 	RPCLOG(8, ", knc_proto %s\n", config->knc_proto);
97 
98 	if (config == NULL || config->knc_protofmly == NULL || ncl == NULL) {
99 		RPCLOG0(1, "clnt_tli_kcreate: bad config or handle\n");
100 		return (EINVAL);
101 	}
102 
103 	switch (config->knc_semantics) {
104 	case NC_TPI_CLTS:
105 		RPCLOG0(8, "clnt_tli_kcreate: CLTS selected\n");
106 		error = clnt_clts_kcreate(config, svcaddr, prog, vers,
107 						retries, cred, &cl);
108 		if (error != 0) {
109 			RPCLOG(1,
110 			"clnt_tli_kcreate: clnt_clts_kcreate failed error %d\n",
111 			    error);
112 			return (error);
113 		}
114 		break;
115 
116 	case NC_TPI_COTS:
117 	case NC_TPI_COTS_ORD:
118 		RPCLOG0(8, "clnt_tli_kcreate: COTS selected\n");
119 		if (strcmp(config->knc_protofmly, NC_INET) == 0)
120 			family = AF_INET;
121 		else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
122 			family = AF_INET6;
123 		error = clnt_cots_kcreate(config->knc_rdev, svcaddr, family,
124 		    prog, vers, max_msgsize, cred, &cl);
125 		if (error != 0) {
126 			RPCLOG(1,
127 			"clnt_tli_kcreate: clnt_cots_kcreate failed error %d\n",
128 			error);
129 			return (error);
130 		}
131 		break;
132 	case NC_TPI_RDMA:
133 		RPCLOG0(8, "clnt_tli_kcreate: RDMA selected\n");
134 		/*
135 		 * RDMA doesn't support TSOL. It's better to
136 		 * disallow it here.
137 		 */
138 		if (is_system_labeled()) {
139 			RPCLOG0(1, "clnt_tli_kcreate: tsol not supported\n");
140 			return (EPROTONOSUPPORT);
141 		}
142 
143 		if (strcmp(config->knc_protofmly, NC_INET) == 0)
144 			family = AF_INET;
145 		else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
146 			family = AF_INET6;
147 		error = clnt_rdma_kcreate(config->knc_proto,
148 		    (void *)config->knc_rdev, svcaddr, family, prog, vers, cred,
149 		    &cl);
150 		if (error != 0) {
151 			RPCLOG(1,
152 			"clnt_tli_kcreate: clnt_rdma_kcreate failed error %d\n",
153 			error);
154 			return (error);
155 		}
156 		break;
157 	default:
158 		error = EINVAL;
159 		RPCLOG(1, "clnt_tli_kcreate: Bad service type %d\n",
160 		    config->knc_semantics);
161 		return (error);
162 	}
163 	*ncl = cl;
164 	return (0);
165 }
166 
167 /*
168  * "Kinit" a client handle by calling the appropriate cots or clts routine.
169  *
170  * PSARC 2003/523 Contract Private Interface
171  * clnt_tli_kinit
172  * Changes must be reviewed by Solaris File Sharing
173  * Changes must be communicated to contract-2003-523@sun.com
174  */
175 int
176 clnt_tli_kinit(
177 	CLIENT		*h,
178 	struct knetconfig *config,
179 	struct netbuf	*addr,
180 	uint_t		max_msgsize,
181 	int		retries,
182 	struct cred	*cred)
183 {
184 	int error = 0;
185 	int family = AF_UNSPEC;
186 
187 	switch (config->knc_semantics) {
188 	case NC_TPI_CLTS:
189 		clnt_clts_kinit(h, addr, retries, cred);
190 		break;
191 	case NC_TPI_COTS:
192 	case NC_TPI_COTS_ORD:
193 		RPCLOG0(2, "clnt_tli_kinit: COTS selected\n");
194 		if (strcmp(config->knc_protofmly, NC_INET) == 0)
195 			family = AF_INET;
196 		else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
197 			family = AF_INET6;
198 		clnt_cots_kinit(h, config->knc_rdev, family,
199 		    addr, max_msgsize, cred);
200 		break;
201 	case NC_TPI_RDMA:
202 		RPCLOG0(2, "clnt_tli_kinit: RDMA selected\n");
203 		clnt_rdma_kinit(h, config->knc_proto,
204 		    (void *)config->knc_rdev, addr, cred);
205 		break;
206 	default:
207 		error = EINVAL;
208 	}
209 
210 	return (error);
211 }
212 
213 
214 /*
215  * try to bind to a reserved port
216  */
217 int
218 bindresvport(
219 	TIUSER		*tiptr,
220 	struct netbuf	*addr,
221 	struct netbuf	*bound_addr,
222 	bool_t		tcp)
223 {
224 	struct sockaddr_in	*sin;
225 	struct sockaddr_in6	*sin6;
226 	bool_t 			ipv6_flag = 0;
227 	int			i;
228 	struct t_bind		*req;
229 	struct t_bind		*ret;
230 	int			error;
231 	bool_t			loop_twice;
232 	int			start;
233 	int			stop;
234 	ushort_t			*last_used;
235 
236 	if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&req)) != 0) {
237 		RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
238 		return (error);
239 	}
240 
241 	if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&ret)) != 0) {
242 		RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
243 		(void) t_kfree(tiptr, (char *)req, T_BIND);
244 		return (error);
245 	}
246 
247 	/* now separate IPv4 and IPv6 by looking at len of tiptr.addr */
248 	if (tiptr->tp_info.addr == sizeof (struct sockaddr_in6)) {
249 		/* it's IPv6 */
250 		ipv6_flag = 1;
251 		sin6 = (struct sockaddr_in6 *)req->addr.buf;
252 		sin6->sin6_family = AF_INET6;
253 		bzero((char *)&sin6->sin6_addr, sizeof (struct in6_addr));
254 		req->addr.len = sizeof (struct sockaddr_in6);
255 	} else {
256 		/* LINTED pointer alignment */
257 		sin = (struct sockaddr_in *)req->addr.buf;
258 		sin->sin_family = AF_INET;
259 		sin->sin_addr.s_addr = INADDR_ANY;
260 		req->addr.len = sizeof (struct sockaddr_in);
261 	}
262 
263 	/*
264 	 * Caller wants to bind to a specific port, so don't bother with the
265 	 * loop that binds to the next free one.
266 	 */
267 	if (addr) {
268 		if (ipv6_flag) {
269 			sin6->sin6_port =
270 				((struct sockaddr_in6 *)addr->buf)->sin6_port;
271 		} else {
272 			sin->sin_port =
273 				((struct sockaddr_in *)addr->buf)->sin_port;
274 		}
275 		RPCLOG(8, "bindresvport: calling t_kbind tiptr = %p\n",
276 		    (void *)tiptr);
277 		if ((error = t_kbind(tiptr, req, ret)) != 0) {
278 			RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
279 			/*
280 			 * The unbind is called in case the bind failed
281 			 * with an EINTR potentially leaving the
282 			 * transport in bound state.
283 			 */
284 			if (error == EINTR)
285 				(void) t_kunbind(tiptr);
286 		} else if (bcmp(req->addr.buf, ret->addr.buf,
287 				ret->addr.len) != 0) {
288 			RPCLOG0(1, "bindresvport: bcmp error\n");
289 			(void) t_kunbind(tiptr);
290 			error = EADDRINUSE;
291 		}
292 	} else {
293 		if (tcp)
294 			last_used = &clnt_tcp_last_used;
295 		else
296 			last_used = &clnt_udp_last_used;
297 		error = EADDRINUSE;
298 		stop = MIN_PRIV;
299 
300 		start = (*last_used == MIN_PRIV ? MAX_PRIV : *last_used - 1);
301 		loop_twice = (start < MAX_PRIV ? TRUE : FALSE);
302 
303 bindresvport_again:
304 		for (i = start;
305 		    (error == EADDRINUSE || error == EADDRNOTAVAIL) &&
306 		    i >= stop; i--) {
307 			if (ipv6_flag)
308 				sin6->sin6_port = htons(i);
309 			else
310 				sin->sin_port = htons(i);
311 			RPCLOG(8, "bindresvport: calling t_kbind tiptr = 0%p\n",
312 			    (void *)tiptr);
313 			if ((error = t_kbind(tiptr, req, ret)) != 0) {
314 				RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
315 				/*
316 				 * The unbind is called in case the bind failed
317 				 * with an EINTR potentially leaving the
318 				 * transport in bound state.
319 				 */
320 				if (error == EINTR)
321 					(void) t_kunbind(tiptr);
322 			} else if (bcmp(req->addr.buf, ret->addr.buf,
323 			    ret->addr.len) != 0) {
324 				RPCLOG0(1, "bindresvport: bcmp error\n");
325 				(void) t_kunbind(tiptr);
326 				error = EADDRINUSE;
327 			} else
328 				error = 0;
329 		}
330 		if (!error) {
331 			if (ipv6_flag) {
332 				RPCLOG(8, "bindresvport: port assigned %d\n",
333 					sin6->sin6_port);
334 				*last_used = ntohs(sin6->sin6_port);
335 			} else {
336 				RPCLOG(8, "bindresvport: port assigned %d\n",
337 					sin->sin_port);
338 				*last_used = ntohs(sin->sin_port);
339 			}
340 		} else if (loop_twice) {
341 			loop_twice = FALSE;
342 			start = MAX_PRIV;
343 			stop = *last_used + 1;
344 			goto bindresvport_again;
345 		}
346 	}
347 
348 	if (!error && bound_addr) {
349 		if (bound_addr->maxlen < ret->addr.len) {
350 			kmem_free(bound_addr->buf, bound_addr->maxlen);
351 			bound_addr->buf = kmem_zalloc(ret->addr.len, KM_SLEEP);
352 			bound_addr->maxlen = ret->addr.len;
353 		}
354 		bcopy(ret->addr.buf, bound_addr->buf, ret->addr.len);
355 		bound_addr->len = ret->addr.len;
356 	}
357 	(void) t_kfree(tiptr, (char *)req, T_BIND);
358 	(void) t_kfree(tiptr, (char *)ret, T_BIND);
359 	return (error);
360 }
361 
362 void
363 clnt_init(void)
364 {
365 	clnt_cots_init();
366 	clnt_clts_init();
367 }
368 
369 void
370 clnt_fini(void)
371 {
372 	clnt_clts_fini();
373 	clnt_cots_fini();
374 }
375 
376 call_table_t *
377 call_table_init(int size)
378 {
379 	call_table_t *ctp;
380 	int i;
381 
382 	ctp = kmem_alloc(sizeof (call_table_t) * size, KM_SLEEP);
383 
384 	for (i = 0; i < size; i++) {
385 		ctp[i].ct_call_next = (calllist_t *)&ctp[i];
386 		ctp[i].ct_call_prev = (calllist_t *)&ctp[i];
387 		mutex_init(&ctp[i].ct_lock, NULL, MUTEX_DEFAULT, NULL);
388 		ctp[i].ct_len = 0;
389 	}
390 
391 	return (ctp);
392 }
393