xref: /illumos-gate/usr/src/stand/lib/fs/nfs/pmap.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This file contains the routines that maintain a linked list of known
29  * program to udp port mappings. There are three static members initialized
30  * by default, one for the portmapper itself (of course), one for rpcbind,
31  * and one for nfs. If a program number is not in the list, then routines
32  * in this file contact the portmapper on the server, and dynamically add
33  * new members to this list.
34  *
35  * This file also contains bpmap_rmtcall() - which lets one get the port
36  * number AND run the rpc call in one step. Only the server that successfully
37  * completes the rpc call will return a result.
38  *
39  * NOTE: Because we will end up caching the port entries we need
40  * before the kernel begins running, we can use dynamic allocation here.
41  * boot_memfree() calls bpmap_memfree() to free up any dynamically
42  * allocated entries when the boot program has finished its job.
43  */
44 
45 #include <sys/types.h>
46 #include <rpc/types.h>
47 #include <sys/errno.h>
48 #include <sys/time.h>
49 #include <sys/socket.h>
50 #include <net/if.h>
51 #include <netinet/in.h>
52 #include <netinet/if_ether.h>
53 #include <rpc/xdr.h>
54 #include <rpc/auth.h>
55 #include <sys/t_lock.h>
56 #include "clnt.h"
57 #include <rpc/pmap_prot.h>
58 #include <rpc/pmap_rmt.h>
59 #include <rpc/rpc.h>
60 #include "brpc.h"
61 #include "pmap.h"
62 #include "nfs_inet.h"
63 #include <rpcsvc/nfs_prot.h>
64 #include <rpc/rpcb_prot.h>
65 #include <sys/salib.h>
66 #include "socket_inet.h"
67 #include <sys/promif.h>
68 #include <sys/bootdebug.h>
69 
70 /* portmap structure */
71 #define	PMAP_STATIC	(3)	/* last statically allocated list entry */
72 struct pmaplist pre_init[PMAP_STATIC + 1] = {
73 	{ {PMAPPROG,	PMAPVERS,	IPPROTO_UDP, PMAPPORT}, &pre_init[1] },
74 	/* SVR4 rpcbind listens to old portmapper port */
75 	{ {RPCBPROG,	RPCBVERS,	IPPROTO_UDP, PMAPPORT}, &pre_init[2] },
76 	{ {NFS_PROGRAM, NFS_VERSION,	IPPROTO_UDP, NFS_PORT}, &pre_init[3] },
77 	{ {NFS_PROGRAM, NFS_V3,		IPPROTO_UDP, NFS_PORT}, NULL }
78 };
79 
80 struct pmaplist *map_head = &pre_init[0];
81 struct pmaplist *map_tail = &pre_init[PMAP_STATIC];
82 
83 #define	dprintf	if (boothowto & RB_DEBUG) printf
84 
85 /*
86  * bpmap_addport: adds a new entry on to the end of the pmap cache.
87  * Items are kept in host order.
88  */
89 static void
90 bpmap_addport(rpcprog_t prog, rpcvers_t vers, rpcport_t port)
91 {
92 	struct pmaplist *newp;
93 
94 	/* allocate new pmaplist */
95 	newp = (struct pmaplist *)bkmem_alloc(sizeof (struct pmaplist));
96 
97 	if (newp == NULL)
98 		return; /* not fatal here, we'll just throw out the entry */
99 
100 	newp->pml_map.pm_prog = prog;
101 	newp->pml_map.pm_vers = vers;
102 	newp->pml_map.pm_prot = (rpcprot_t)IPPROTO_UDP;
103 	newp->pml_map.pm_port = port;
104 
105 	map_tail->pml_next = newp;
106 	newp->pml_next = NULL;
107 	map_tail = newp;
108 }
109 
110 /*
111  * bpmap_delport: deletes an existing entry from the list. Caution - don't
112  * call this function to delete statically allocated entries. Why would
113  * you want to, anyway? Only IPPROTO_UDP is supported, of course.
114  */
115 static void
116 bpmap_delport(rpcprog_t prog, rpcvers_t vers)
117 {
118 	struct pmaplist *tmp, *prev;
119 
120 	prev = map_head;
121 	for (tmp = map_head; tmp != NULL; tmp = tmp->pml_next) {
122 		if ((tmp->pml_map.pm_prog == prog) &&
123 		    (tmp->pml_map.pm_vers == vers)) {
124 			if (tmp == map_head)
125 				map_head = tmp->pml_next; /* new head */
126 			else if (tmp == map_tail) {
127 				map_tail = prev;	/* new tail */
128 				map_tail->pml_next = NULL;
129 			} else {
130 				/* internal delete */
131 				prev->pml_next = tmp->pml_next;
132 			}
133 #ifdef	DEBUG
134 			printf("bpmap_delport: prog: %x, vers: %x\n", prog,
135 			    vers);
136 #endif	/* DEBUG */
137 			bkmem_free((caddr_t)tmp, sizeof (struct pmaplist));
138 			break;
139 		} else
140 			prev = tmp;
141 	}
142 }
143 
144 /*
145  * Modified strtol(3).
146  */
147 static int
148 strtoi(char *str, char **ptr)
149 {
150 	int c, val;
151 
152 	for (val = 0, c = *str++; c >= '0' && c <= '9'; c = *str++) {
153 		val *= 10;
154 		val += c - '0';
155 	}
156 	*ptr = str;
157 	return (val);
158 }
159 
160 /*
161  * (from dlboot_inet.c) (kernel)
162  * Convert a port number from a sockaddr_in expressed
163  * in universal address format.
164  */
165 static int
166 uaddr2port(char	*addr)
167 {
168 	int	p1, p2;
169 	char	*next;
170 
171 	/*
172 	 * A struct sockaddr_in expressed in universal address
173 	 * format looks like:
174 	 *
175 	 *	"IP.IP.IP.IP.PORT[top byte].PORT[bot. byte]"
176 	 *
177 	 * Where each component expresses as a charactor,
178 	 * the corresponding part of the IP address
179 	 * and port number.
180 	 * Thus 127.0.0.1, port 2345 looks like:
181 	 *
182 	 *	49 50 55 46 48 46 48 46 49 46 57 46 52 49
183 	 *	1  2  7  .  0  .  0  .  1  .  9  .  4  1
184 	 *
185 	 * 2345 = 929base16 = 9.32+9 = 9.41
186 	 */
187 	(void) strtoi(addr, &next);
188 	(void) strtoi(next, &next);
189 	(void) strtoi(next, &next);
190 	(void) strtoi(next, &next);
191 	p1 = strtoi(next, &next);
192 	p2 = strtoi(next, &next);
193 
194 	return ((p1 << 8) + p2);
195 }
196 
197 /*
198  * Xdr routines used for calling portmapper/rpcbind.
199  */
200 
201 bool_t
202 xdr_pmap(XDR *xdrs, struct pmap *regs)
203 {
204 	if (xdr_rpcprog(xdrs, &regs->pm_prog) &&
205 		xdr_rpcvers(xdrs, &regs->pm_vers) &&
206 		xdr_rpcprot(xdrs, &regs->pm_prot))
207 		return (xdr_rpcprot(xdrs, &regs->pm_port));
208 	return (FALSE);
209 }
210 
211 bool_t
212 xdr_rpcb(XDR *xdrs, RPCB *objp)
213 {
214 	if (!xdr_rpcprog(xdrs, &objp->r_prog))
215 		return (FALSE);
216 	if (!xdr_rpcvers(xdrs, &objp->r_vers))
217 		return (FALSE);
218 	if (!xdr_string(xdrs, &objp->r_netid, ~0))
219 		return (FALSE);
220 	if (!xdr_string(xdrs, &objp->r_addr, ~0))
221 		return (FALSE);
222 	if (!xdr_string(xdrs, &objp->r_owner, ~0))
223 		return (FALSE);
224 	return (TRUE);
225 }
226 
227 /*
228  * XDR remote call arguments
229  * written for XDR_ENCODE direction only
230  */
231 bool_t
232 xdr_rmtcall_args(XDR *xdrs, struct rmtcallargs *cap)
233 {
234 	uint_t	lenposition, argposition, position;
235 
236 	if (xdr_rpcprog(xdrs, &(cap->prog)) &&
237 	    xdr_rpcvers(xdrs, &(cap->vers)) &&
238 	    xdr_rpcproc(xdrs, &(cap->proc))) {
239 		lenposition = XDR_GETPOS(xdrs);
240 		if (!xdr_u_int(xdrs, &(cap->arglen)))
241 			return (FALSE);
242 		argposition = XDR_GETPOS(xdrs);
243 		if (!(*(cap->xdr_args))(xdrs, cap->args_ptr))
244 			return (FALSE);
245 		position = XDR_GETPOS(xdrs);
246 		cap->arglen = position - argposition;
247 		(void) XDR_SETPOS(xdrs, lenposition);
248 		if (!xdr_u_int(xdrs, &(cap->arglen)))
249 			return (FALSE);
250 		(void) XDR_SETPOS(xdrs, position);
251 		return (TRUE);
252 	}
253 	return (FALSE);
254 }
255 
256 /*
257  * XDR remote call results
258  * written for XDR_DECODE direction only
259  */
260 bool_t
261 xdr_rmtcallres(XDR *xdrs, struct rmtcallres *crp)
262 {
263 	caddr_t	port_ptr;
264 
265 	port_ptr = (caddr_t)crp->port_ptr;
266 	if (xdr_reference(xdrs, &port_ptr, sizeof (uint_t), xdr_u_int) &&
267 	    xdr_u_int(xdrs, &crp->resultslen)) {
268 		crp->port_ptr = (rpcport_t *)port_ptr;
269 		return ((*(crp->xdr_results))(xdrs, crp->results_ptr));
270 	}
271 	return (FALSE);
272 }
273 
274 /*
275  * XDR remote call arguments
276  * written for XDR_ENCODE direction only
277  */
278 bool_t
279 xdr_rpcb_rmtcallargs(XDR *xdrs, struct rpcb_rmtcallargs *objp)
280 {
281 	uint_t lenposition, argposition, position;
282 
283 	if (!xdr_rpcprog(xdrs, &objp->prog))
284 		return (FALSE);
285 	if (!xdr_rpcvers(xdrs, &objp->vers))
286 		return (FALSE);
287 	if (!xdr_rpcproc(xdrs, &objp->proc))
288 		return (FALSE);
289 	/*
290 	 * All the jugglery for just getting the size of the arguments
291 	 */
292 	lenposition = XDR_GETPOS(xdrs);
293 	if (!xdr_u_int(xdrs, &(objp->arglen)))
294 		return (FALSE);
295 	argposition = XDR_GETPOS(xdrs);
296 	if (!(*(objp->xdr_args))(xdrs, objp->args_ptr))
297 		return (FALSE);
298 	position = XDR_GETPOS(xdrs);
299 	objp->arglen = position - argposition;
300 	(void) XDR_SETPOS(xdrs, lenposition);
301 	if (!xdr_u_int(xdrs, &(objp->arglen)))
302 		return (FALSE);
303 	(void) XDR_SETPOS(xdrs, position);
304 	return (TRUE);
305 }
306 
307 /*
308  * XDR remote call results
309  * written for XDR_DECODE direction only
310  */
311 bool_t
312 xdr_rpcb_rmtcallres(XDR *xdrs, struct rpcb_rmtcallres *objp)
313 {
314 	if (!xdr_string(xdrs, &objp->addr_ptr, ~0))
315 		return (FALSE);
316 	if (!xdr_u_int(xdrs, &objp->resultslen))
317 		return (FALSE);
318 	return ((*(objp->xdr_results))(xdrs, objp->results_ptr));
319 }
320 
321 /*
322  * bpmap_rmtcall: does PMAPPROC_CALLIT broadcasts w/ rpc_call requests.
323  * Lets one do a PMAPGETPORT/RPC PROC call in one easy step. sockaddr_in args
324  * are taken as network order.
325  *
326  * Code adapted from bpmap_rmtcall() in dlboot_inet.c (kernel)
327  */
328 /*ARGSUSED*/
329 enum clnt_stat
330 bpmap_rmtcall(
331 	rpcprog_t		prog,	/* rpc program number to call. */
332 	rpcvers_t		vers,	/* rpc program version */
333 	rpcproc_t		proc,	/* rpc procedure to call */
334 	xdrproc_t		in_xdr,	/* routine to serialize arguments */
335 	caddr_t			args,	/* arg vector for remote call */
336 	xdrproc_t		out_xdr, /* routine to deserialize results */
337 	caddr_t			ret,	/* addr of buf to place results in */
338 	int			rexmit,	/* retransmission interval (secs) */
339 	int			wait,	/* how long (secs) to wait for a resp */
340 	struct sockaddr_in 	*to,	/* destination */
341 	struct sockaddr_in	*from,	/* filled in w/ responder's port/addr */
342 	uint_t			auth)	/* type of authentication wanted. */
343 {
344 	enum clnt_stat		status;		/* rpc_call status */
345 	rpcport_t		port = 0;	/* returned port # */
346 	struct rmtcallargs	pmap_a;		/* args for pmap call */
347 	struct rmtcallres	pmap_r;		/* results from pmap call */
348 	struct rpcb_rmtcallargs	rpcb_a;		/* args for rpcb call */
349 	struct rpcb_rmtcallres	rpcb_r;		/* results from rpcb call */
350 	char			ua[UA_SIZE];	/* universal addr buffer */
351 
352 	/* initialize pmap */
353 	pmap_a.prog = prog;
354 	pmap_a.vers = vers;
355 	pmap_a.proc = proc;
356 	pmap_a.args_ptr = args;
357 	pmap_a.xdr_args = in_xdr;
358 	pmap_r.port_ptr = &port;
359 	pmap_r.results_ptr = ret;
360 	pmap_r.xdr_results = out_xdr;
361 
362 	status = brpc_call((rpcprog_t)PMAPPROG, (rpcvers_t)PMAPVERS,
363 	    (rpcproc_t)PMAPPROC_CALLIT, xdr_rmtcall_args, (caddr_t)&pmap_a,
364 	    xdr_rmtcallres, (caddr_t)&pmap_r, rexmit, wait, to, from,
365 	    AUTH_NONE);
366 	if (status != RPC_PROGUNAVAIL) {
367 		if (status == RPC_SUCCESS) {
368 			/* delete old port mapping, if it exists */
369 			bpmap_delport(prog, vers);
370 
371 			/* save the new port mapping */
372 			bpmap_addport(prog, vers, port);
373 		}
374 		return (status);
375 	}
376 
377 	/*
378 	 * PMAP is unavailable. Maybe there's a SVR4 machine, with rpcbind.
379 	 */
380 	bzero(ua, sizeof (ua));
381 
382 	/* initialize rpcb */
383 	rpcb_a.prog = prog;
384 	rpcb_a.vers = vers;
385 	rpcb_a.proc = proc;
386 	rpcb_a.args_ptr = args;
387 	rpcb_a.xdr_args = in_xdr;
388 	rpcb_r.addr_ptr = ua;
389 	rpcb_r.results_ptr = ret;
390 	rpcb_r.xdr_results = out_xdr;
391 
392 	status = brpc_call((rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS,
393 	    (rpcproc_t)RPCBPROC_CALLIT, xdr_rpcb_rmtcallargs, (caddr_t)&rpcb_a,
394 	    xdr_rpcb_rmtcallres, (caddr_t)&rpcb_r, rexmit, wait, to, from,
395 	    AUTH_NONE);
396 	if (status == RPC_SUCCESS) {
397 		/* delete old port mapping, if it exists */
398 		bpmap_delport(prog, vers);
399 
400 		/* save the new port mapping */
401 		port = ntohs(uaddr2port(ua));
402 		bpmap_addport(prog, vers, port);
403 	}
404 	return (status);
405 }
406 
407 /*
408  * bpmap_getport: Queries current list of cached pmap_list entries,
409  * returns the port number of the entry found. If the port number
410  * is not cached, then getport makes a rpc call first to the portmapper,
411  * and then to rpcbind (SVR4) if the portmapper does not respond. The
412  * returned port is then added to the cache, and the port number is
413  * returned. If both portmapper and rpc bind fail to give us the necessary
414  * port, we return 0 to signal we hit an error, and set rpc_stat to
415  * the appropriate RPC error code. Only IPPROTO_UDP protocol is supported.
416  *
417  * Port and sockaddr_in arguments taken in network order. rpcport_t is returned
418  * in host order.
419  */
420 rpcport_t
421 bpmap_getport(rpcprog_t prog, rpcvers_t vers, enum clnt_stat *rpc_stat,
422     struct sockaddr_in *to, struct sockaddr_in *from)
423 {
424 	struct pmaplist	*walk;
425 	struct pmap	pmap_send;	/* portmap */
426 	in_port_t	pmap_port;
427 	rpcport_t	dport;
428 
429 #ifdef DEBUG
430 	printf("bpmap_getport: called with: prog: %d, vers: %d\n", prog, vers);
431 #endif /* DEBUG */
432 	for (walk = map_head; walk != 0; walk = walk->pml_next) {
433 		if ((walk->pml_map.pm_prog == prog) &&
434 		    (walk->pml_map.pm_vers == vers) &&
435 		    (walk->pml_map.pm_prot == (rpcprot_t)IPPROTO_UDP)) {
436 #ifdef DEBUG
437 			printf("bpmap_getport: Found in cache. returning: %d\n",
438 			    walk->pml_map.pm_port);
439 #endif /* DEBUG */
440 			return (walk->pml_map.pm_port);
441 		}
442 	}
443 
444 	/*
445 	 * Not in the cache. First try the portmapper (SunOS server?) and
446 	 * if that fails, try rpcbind (SVR4 server).
447 	 */
448 	pmap_send.pm_prog = prog;
449 	pmap_send.pm_vers = vers;
450 	pmap_send.pm_prot = (rpcprot_t)IPPROTO_UDP;
451 	pmap_send.pm_port = 0;	/* what we're after */
452 
453 	*rpc_stat = brpc_call(PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
454 	    xdr_pmap, (caddr_t)&pmap_send, xdr_u_short,
455 	    (caddr_t)&pmap_port, 0, 0, to, from, AUTH_NONE);
456 
457 	if (*rpc_stat == RPC_PROGUNAVAIL) {
458 		/*
459 		 * The portmapper isn't available. Try rpcbind.
460 		 * Maybe the server is a SVR4 server.
461 		 */
462 		char	*ua;			/* universal address */
463 		char	ua_buf[UA_SIZE];	/* and its buffer */
464 		RPCB	rpcb_send;
465 
466 		rpcb_send.r_prog = prog;
467 		rpcb_send.r_vers = vers;
468 		rpcb_send.r_netid = NULL;
469 		rpcb_send.r_addr = NULL;
470 		rpcb_send.r_owner = NULL;
471 
472 		bzero(ua_buf, UA_SIZE);
473 		ua = ua_buf;
474 
475 		/*
476 		 * Again, default # of retries. xdr_wrapstring()
477 		 * wants a char **.
478 		 */
479 		*rpc_stat = brpc_call(RPCBPROG, RPCBVERS, RPCBPROC_GETADDR,
480 		    xdr_rpcb, (caddr_t)&rpcb_send, xdr_wrapstring,
481 		    (char *)&ua, 0, 0, to, from, AUTH_NONE);
482 
483 		if (*rpc_stat == RPC_SUCCESS) {
484 			if (ua[0] != '\0')
485 				dport = ntohs(uaddr2port(ua));
486 			else
487 				return (0); /* Address unknown */
488 		}
489 	} else {
490 		/*
491 		 * Why are rpcport_t's uint32_t? port numbers are uint16_t
492 		 * for ipv4 AND ipv6.... XXXX
493 		 */
494 		dport = (rpcport_t)pmap_port;
495 	}
496 
497 	if (*rpc_stat != RPC_SUCCESS)  {
498 		dprintf("pmap_getport: Failed getting port.\n");
499 		return (0);	/* we failed. */
500 	}
501 
502 #ifdef DEBUG
503 	printf("bpmap_getport: prog: %d, vers: %d; returning port: %d.\n",
504 	    prog, vers, dport);
505 #endif /* DEBUG */
506 
507 	bpmap_addport(prog, vers, dport);
508 
509 	return (dport);
510 }
511 
512 /*
513  * bpmap_memfree: frees up any dynamically allocated entries.
514  */
515 void
516 bpmap_memfree(void)
517 {
518 	struct pmaplist *current, *tmp;
519 
520 	if (map_tail == &pre_init[PMAP_STATIC])
521 		return; /* no dynamic entries */
522 
523 	/* free from head of the list to the tail. */
524 	current = pre_init[PMAP_STATIC].pml_next;
525 	while (current != NULL) {
526 		tmp = current->pml_next;
527 		bkmem_free((caddr_t)current, sizeof (struct pmaplist));
528 		current = tmp;
529 	}
530 }
531