2 * Copyright 2009 Oracle. All rights reserved.
4 * This file is part of nfs-utils.
6 * nfs-utils is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * nfs-utils is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
23 * Instead of using ONC or TI RPC library calls, statd constructs
24 * RPC calls directly in socket buffers. This allows a single
25 * socket to be concurrently shared among several different RPC
26 * programs and versions using a simple RPC request dispatcher.
28 * This file contains the details of RPC header and call
29 * construction and reply parsing, and a method for creating a
30 * socket for use with these functions.
35 #endif /* HAVE_CONFIG_H */
37 #include <sys/types.h>
38 #include <sys/socket.h>
47 #include <netinet/in.h>
49 #include <arpa/inet.h>
53 #include <rpc/pmap_prot.h>
54 #include <rpc/pmap_rmt.h>
57 #include <netconfig.h>
58 #include <rpc/rpcb_prot.h>
59 #endif /* HAVE_LIBTIRPC */
67 * Returns a fresh XID appropriate for RPC over UDP -- never zero.
72 static uint32_t nsm_xid = 0;
76 (void)gettimeofday(&now, NULL);
77 nsm_xid = (uint32_t)getpid() ^
78 (uint32_t)now.tv_sec ^ (uint32_t)now.tv_usec;
85 * Select a fresh XID and construct an RPC header in @mesg.
86 * Always use AUTH_NULL credentials and verifiers.
88 * Returns the new XID.
91 nsm_init_rpc_header(const rpcprog_t program, const rpcvers_t version,
92 const rpcproc_t procedure, struct rpc_msg *mesg)
94 struct call_body *cb = &mesg->rm_call;
95 uint32_t xid = nsm_next_xid();
97 memset(mesg, 0, sizeof(*mesg));
99 mesg->rm_xid = (unsigned long)xid;
100 mesg->rm_direction = CALL;
102 cb->cb_rpcvers = RPC_MSG_VERSION;
103 cb->cb_prog = program;
104 cb->cb_vers = version;
105 cb->cb_proc = procedure;
107 cb->cb_cred.oa_flavor = AUTH_NULL;
108 cb->cb_cred.oa_base = (caddr_t) NULL;
109 cb->cb_cred.oa_length = 0;
110 cb->cb_verf.oa_flavor = AUTH_NULL;
111 cb->cb_verf.oa_base = (caddr_t) NULL;
112 cb->cb_verf.oa_length = 0;
118 * Initialize the network send buffer and XDR memory for encoding.
121 nsm_init_xdrmem(char *msgbuf, const unsigned int msgbuflen,
124 memset(msgbuf, 0, (size_t)msgbuflen);
125 memset(xdrp, 0, sizeof(*xdrp));
126 xdrmem_create(xdrp, msgbuf, msgbuflen, XDR_ENCODE);
130 * Send a completed RPC call on a socket.
132 * Returns true if all the bytes were sent successfully; otherwise
133 * false if any error occurred.
136 nsm_rpc_sendto(const int sock, const struct sockaddr *sap,
137 const socklen_t salen, XDR *xdrs, void *buf)
139 const size_t buflen = (size_t)xdr_getpos(xdrs);
142 err = sendto(sock, buf, buflen, 0, sap, salen);
143 if ((err < 0) || ((size_t)err != buflen)) {
144 xlog(L_ERROR, "%s: sendto failed: %m", __func__);
151 * nsm_xmit_getport - post a PMAP_GETPORT call on a socket descriptor
152 * @sock: datagram socket descriptor
153 * @sin: pointer to AF_INET socket address of server
154 * @program: RPC program number to query
155 * @version: RPC version number to query
157 * Send a PMAP_GETPORT call to the portmap daemon at @sin using
158 * socket descriptor @sock. This request queries the RPC program
159 * [program, version, IPPROTO_UDP].
161 * NB: PMAP_GETPORT works only for IPv4 hosts. This implementation
162 * works only over UDP, and queries only UDP registrations.
164 * Returns the XID of the call, or zero if an error occurred.
167 nsm_xmit_getport(const int sock, const struct sockaddr_in *sin,
168 const unsigned long program,
169 const unsigned long version)
171 char msgbuf[NSM_MAXMSGSIZE];
172 struct sockaddr_in addr;
175 struct pmap parms = {
178 .pm_prot = (unsigned long)IPPROTO_UDP,
183 xlog(D_CALL, "Sending PMAP_GETPORT for %u, %u, udp", program, version);
185 nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
186 xid = nsm_init_rpc_header(PMAPPROG, PMAPVERS,
187 (rpcproc_t)PMAPPROC_GETPORT, &mesg);
190 addr.sin_port = htons(PMAPPORT);
192 if (xdr_callmsg(&xdr, &mesg) == TRUE &&
193 xdr_pmap(&xdr, &parms) == TRUE)
194 sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr,
195 (socklen_t)sizeof(addr), &xdr, msgbuf);
197 xlog(L_ERROR, "%s: can't encode PMAP_GETPORT call", __func__);
207 * nsm_xmit_getaddr - post an RPCB_GETADDR call on a socket descriptor
208 * @sock: datagram socket descriptor
209 * @sin: pointer to AF_INET6 socket address of server
210 * @program: RPC program number to query
211 * @version: RPC version number to query
213 * Send an RPCB_GETADDR call to the rpcbind daemon at @sap using
214 * socket descriptor @sock. This request queries the RPC program
215 * [program, version, "udp6"].
217 * NB: RPCB_GETADDR works for both IPv4 and IPv6 hosts. This
218 * implementation works only over UDP and AF_INET6, and queries
219 * only "udp6" registrations.
221 * Returns the XID of the call, or zero if an error occurred.
225 nsm_xmit_getaddr(const int sock, const struct sockaddr_in6 *sin6,
226 const rpcprog_t program, const rpcvers_t version)
228 char msgbuf[NSM_MAXMSGSIZE];
229 struct sockaddr_in6 addr;
232 struct rpcb parms = {
241 xlog(D_CALL, "Sending RPCB_GETADDR for %u, %u, udp6", program, version);
243 nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
244 xid = nsm_init_rpc_header(RPCBPROG, RPCBVERS,
245 (rpcproc_t)RPCBPROC_GETADDR, &mesg);
248 addr.sin6_port = htons(PMAPPORT);
249 parms.r_addr = nfs_sockaddr2universal((struct sockaddr *)(char *)&addr);
250 if (parms.r_addr == NULL) {
251 xlog(L_ERROR, "%s: can't encode socket address", __func__);
255 if (xdr_callmsg(&xdr, &mesg) == TRUE &&
256 xdr_rpcb(&xdr, &parms) == TRUE)
257 sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr,
258 (socklen_t)sizeof(addr), &xdr, msgbuf);
260 xlog(L_ERROR, "%s: can't encode RPCB_GETADDR call", __func__);
269 #else /* !HAVE_LIBTIRPC */
271 nsm_xmit_getaddr(const int sock __attribute__((unused)),
272 const struct sockaddr_in6 *sin6 __attribute__((unused)),
273 const rpcprog_t program __attribute__((unused)),
274 const rpcvers_t version __attribute__((unused)))
278 #endif /* !HAVE_LIBTIRPC */
281 * nsm_xmit_rpcbind - post an rpcbind request
282 * @sock: datagram socket descriptor
283 * @sap: pointer to socket address of server
284 * @program: RPC program number to query
285 * @version: RPC version number to query
287 * Send an rpcbind query to the rpcbind daemon at @sap using
288 * socket descriptor @sock.
290 * NB: This implementation works only over UDP, but can query IPv4 or IPv6
291 * hosts. It queries only UDP registrations.
293 * Returns the XID of the call, or zero if an error occurred.
296 nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
297 const rpcprog_t program, const rpcvers_t version)
299 switch (sap->sa_family) {
301 return nsm_xmit_getport(sock, (const struct sockaddr_in *)sap,
304 return nsm_xmit_getaddr(sock, (const struct sockaddr_in6 *)sap,
311 * nsm_xmit_notify - post an NSMPROC_NOTIFY call on a socket descriptor
312 * @sock: datagram socket descriptor
313 * @sap: pointer to socket address of peer to notify (port already filled in)
314 * @salen: length of socket address
315 * @program: RPC program number to use
316 * @mon_name: mon_name of local peer (ie the rebooting system)
317 * @state: state of local peer
319 * Send an NSMPROC_NOTIFY call to the peer at @sap using socket descriptor @sock.
320 * This request notifies the peer that we have rebooted.
322 * NB: This implementation works only over UDP, but supports both AF_INET
325 * Returns the XID of the call, or zero if an error occurred.
328 nsm_xmit_notify(const int sock, const struct sockaddr *sap,
329 const socklen_t salen, const rpcprog_t program,
330 const char *mon_name, const int state)
332 char msgbuf[NSM_MAXMSGSIZE];
333 struct stat_chge state_change;
339 state_change.mon_name = strdup(mon_name);
340 if (state_change.mon_name == NULL) {
341 xlog(L_ERROR, "%s: no memory", __func__);
344 state_change.state = state;
346 xlog(D_CALL, "Sending SM_NOTIFY for %s", mon_name);
348 nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
349 xid = nsm_init_rpc_header(program, SM_VERS, SM_NOTIFY, &mesg);
351 if (xdr_callmsg(&xdr, &mesg) == TRUE &&
352 xdr_stat_chge(&xdr, &state_change) == TRUE)
353 sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
355 xlog(L_ERROR, "%s: can't encode NSMPROC_NOTIFY call",
359 free(state_change.mon_name);
367 * nsm_xmit_nlmcall - post an unnamed call to local NLM on a socket descriptor
368 * @sock: datagram socket descriptor
369 * @sap: address/port of NLM service to contact
370 * @salen: size of @sap
371 * @m: callback data defining RPC call to make
372 * @state: state of rebooting host
374 * Send an unnamed call (previously requested via NSMPROC_MON) to the
375 * specified local UDP-based RPC service using socket descriptor @sock.
377 * NB: This implementation works only over UDP, but supports both AF_INET
380 * Returns the XID of the call, or zero if an error occurred.
383 nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
384 const socklen_t salen, const struct mon *m,
387 const struct my_id *id = &m->mon_id.my_id;
388 char msgbuf[NSM_MAXMSGSIZE];
389 struct status new_status;
395 xlog(D_CALL, "Sending NLM downcall for %s", m->mon_id.mon_name);
397 nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
398 xid = nsm_init_rpc_header((rpcprog_t)id->my_prog,
399 (rpcvers_t)id->my_vers,
400 (rpcproc_t)id->my_proc, &mesg);
402 new_status.mon_name = m->mon_id.mon_name;
403 new_status.state = state;
404 memcpy(&new_status.priv, &m->priv, sizeof(new_status.priv));
406 if (xdr_callmsg(&xdr, &mesg) == TRUE &&
407 xdr_status(&xdr, &new_status) == TRUE)
408 sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
410 xlog(L_ERROR, "%s: can't encode NLM downcall", __func__);
420 * nsm_parse_reply - parse and validate the header in an RPC reply
421 * @xdrs: pointer to XDR
423 * Returns the XID of the reply, or zero if an error occurred.
426 nsm_parse_reply(XDR *xdrs)
428 struct rpc_msg mesg = {
429 .rm_reply.rp_acpt.ar_results.proc = (xdrproc_t)xdr_void,
433 if (xdr_replymsg(xdrs, &mesg) == FALSE) {
434 xlog(L_ERROR, "%s: can't decode RPC reply", __func__);
437 xid = (uint32_t)mesg.rm_xid;
439 if (mesg.rm_reply.rp_stat != MSG_ACCEPTED) {
440 xlog(L_ERROR, "%s: [0x%x] RPC status %d",
441 __func__, xid, mesg.rm_reply.rp_stat);
445 if (mesg.rm_reply.rp_acpt.ar_stat != SUCCESS) {
446 xlog(L_ERROR, "%s: [0x%x] RPC accept status %d",
447 __func__, xid, mesg.rm_reply.rp_acpt.ar_stat);
455 * nsm_recv_getport - parse PMAP_GETPORT reply
456 * @xdrs: pointer to XDR
458 * Returns the port number from the RPC reply, or zero
459 * if an error occurred.
462 nsm_recv_getport(XDR *xdrs)
464 unsigned long port = 0;
466 if (xdr_u_long(xdrs, &port) == FALSE)
467 xlog(L_ERROR, "%s: can't decode pmap reply",
469 if (port > UINT16_MAX) {
470 xlog(L_ERROR, "%s: bad port number",
475 xlog(D_CALL, "Received PMAP_GETPORT result: %lu", port);
480 * nsm_recv_getaddr - parse RPCB_GETADDR reply
481 * @xdrs: pointer to XDR
483 * Returns the port number from the RPC reply, or zero
484 * if an error occurred.
487 nsm_recv_getaddr(XDR *xdrs)
492 if (xdr_wrapstring(xdrs, &uaddr) == FALSE)
493 xlog(L_ERROR, "%s: can't decode rpcb reply",
496 if ((uaddr == NULL) || (uaddr[0] == '\0')) {
497 xlog(D_CALL, "Received RPCB_GETADDR result: "
498 "program not registered");
502 port = nfs_universal2port(uaddr);
504 xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr);
506 if (port < 0 || port > UINT16_MAX) {
507 xlog(L_ERROR, "%s: bad port number",
512 xlog(D_CALL, "Received RPCB_GETADDR result: %d", port);
513 return (uint16_t)port;
517 * nsm_recv_rpcbind - parse rpcbind reply
518 * @af: address family of reply
519 * @xdrs: pointer to XDR
521 * Returns the port number from the RPC reply, or zero
522 * if an error occurred.
525 nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs)
529 return (uint16_t)nsm_recv_getport(xdrs);
531 return nsm_recv_getaddr(xdrs);