xref: /illumos-gate/usr/src/uts/common/rpc/clnt_gen.c (revision dd72704bd9e794056c558153663c739e2012d721)
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
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
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
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 	/*
262 	 * Caller wants to bind to a specific port, so don't bother with the
263 	 * loop that binds to the next free one.
264 	 */
265 	if (addr) {
266 		if (ipv6_flag) {
267 			sin6->sin6_port =
268 				((struct sockaddr_in6 *)addr->buf)->sin6_port;
269 		} else {
270 			sin->sin_port =
271 				((struct sockaddr_in *)addr->buf)->sin_port;
272 		}
273 		RPCLOG(8, "bindresvport: calling t_kbind tiptr = %p\n",
274 		    (void *)tiptr);
275 		if ((error = t_kbind(tiptr, req, ret)) != 0) {
276 			RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
277 			/*
278 			 * The unbind is called in case the bind failed
279 			 * with an EINTR potentially leaving the
280 			 * transport in bound state.
281 			 */
282 			if (error == EINTR)
283 				(void) t_kunbind(tiptr);
284 		} else if (bcmp(req->addr.buf, ret->addr.buf,
285 				ret->addr.len) != 0) {
286 			RPCLOG0(1, "bindresvport: bcmp error\n");
287 			(void) t_kunbind(tiptr);
288 			error = EADDRINUSE;
289 		}
290 	} else {
291 		if (tcp)
292 			last_used = &clnt_tcp_last_used;
293 		else
294 			last_used = &clnt_udp_last_used;
295 		error = EADDRINUSE;
296 		stop = MIN_PRIV;
297 
298 		start = (*last_used == MIN_PRIV ? MAX_PRIV : *last_used - 1);
299 		loop_twice = (start < MAX_PRIV ? TRUE : FALSE);
300 
301 bindresvport_again:
302 		for (i = start;
303 		    (error == EADDRINUSE || error == EADDRNOTAVAIL) &&
304 		    i >= stop; i--) {
305 			if (ipv6_flag)
306 				sin6->sin6_port = htons(i);
307 			else
308 				sin->sin_port = htons(i);
309 			RPCLOG(8, "bindresvport: calling t_kbind tiptr = 0%p\n",
310 			    (void *)tiptr);
311 			if ((error = t_kbind(tiptr, req, ret)) != 0) {
312 				RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
313 				/*
314 				 * The unbind is called in case the bind failed
315 				 * with an EINTR potentially leaving the
316 				 * transport in bound state.
317 				 */
318 				if (error == EINTR)
319 					(void) t_kunbind(tiptr);
320 			} else if (bcmp(req->addr.buf, ret->addr.buf,
321 			    ret->addr.len) != 0) {
322 				RPCLOG0(1, "bindresvport: bcmp error\n");
323 				(void) t_kunbind(tiptr);
324 				error = EADDRINUSE;
325 			} else
326 				error = 0;
327 		}
328 		if (!error) {
329 			if (ipv6_flag) {
330 				RPCLOG(8, "bindresvport: port assigned %d\n",
331 					sin6->sin6_port);
332 				*last_used = ntohs(sin6->sin6_port);
333 			} else {
334 				RPCLOG(8, "bindresvport: port assigned %d\n",
335 					sin->sin_port);
336 				*last_used = ntohs(sin->sin_port);
337 			}
338 		} else if (loop_twice) {
339 			loop_twice = FALSE;
340 			start = MAX_PRIV;
341 			stop = *last_used + 1;
342 			goto bindresvport_again;
343 		}
344 	}
345 
346 	if (!error && bound_addr) {
347 		if (bound_addr->maxlen < ret->addr.len) {
348 			kmem_free(bound_addr->buf, bound_addr->maxlen);
349 			bound_addr->buf = kmem_zalloc(ret->addr.len, KM_SLEEP);
350 			bound_addr->maxlen = ret->addr.len;
351 		}
352 		bcopy(ret->addr.buf, bound_addr->buf, ret->addr.len);
353 		bound_addr->len = ret->addr.len;
354 	}
355 	(void) t_kfree(tiptr, (char *)req, T_BIND);
356 	(void) t_kfree(tiptr, (char *)ret, T_BIND);
357 	return (error);
358 }
359 
360 void
361 clnt_init(void)
362 {
363 	clnt_cots_init();
364 	clnt_clts_init();
365 }
366 
367 void
368 clnt_fini(void)
369 {
370 	clnt_clts_fini();
371 	clnt_cots_fini();
372 }
373 
374 call_table_t *
375 call_table_init(int size)
376 {
377 	call_table_t *ctp;
378 	int i;
379 
380 	ctp = kmem_alloc(sizeof (call_table_t) * size, KM_SLEEP);
381 
382 	for (i = 0; i < size; i++) {
383 		ctp[i].ct_call_next = (calllist_t *)&ctp[i];
384 		ctp[i].ct_call_prev = (calllist_t *)&ctp[i];
385 		mutex_init(&ctp[i].ct_lock, NULL, MUTEX_DEFAULT, NULL);
386 		ctp[i].ct_len = 0;
387 	}
388 
389 	return (ctp);
390 }
391