All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/24] Remaining IPv6 patches for statd
@ 2010-01-14 17:28 Chuck Lever
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  0 siblings, 1 reply; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:28 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

The Fedora 13 feature freeze is next week, and RHEL 6 beta is coming
up in just a month or two.  Because both of these releases should have
at least client-side NFS/IPv6 support, we've decided to accelerate the
submission of nfs-utils IPv6 patches.

This patch set introduces basic IPv6 support to statd and sm-notify.
Functionality when IPv6 and TI-RPC are disabled should be unchanged,
except that monitor record files can now contain more than one line.

---

Chuck Lever (24):
      statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support
      statd: Support TI-RPC statd listener
      libnsm.a: retain CAP_NET_BIND when dropping privileges
      statd: Remove NL_ADDR() macro
      statd: Support IPv6 in sm_stat_1_svc()
      statd: Support IPv6 in sm_mon_1_svc()
      statd: Add API to canonicalize mon_names
      libnsm.a: Add support for multiple lines in monitor record files
      libnsm.a: Factor atomic write code out of nsm_get_state()
      sm-notify: Save mon_name and my_name strings
      statd: Support IPv6 in sm_simu_crash_1_svc
      statd: Support IPv6 is caller_is_localhost()
      statd: add IPv6 support in sm_notify_1_svc()
      statd: add nsm_present_address() API
      statd: Introduce statd version of matchhostname()
      nfs-utils: Collect socket address helpers into one location
      sm-notify: Support IPv6 DNS lookups in smn_lookup
      sm-notify: Use getaddrinfo(3) to create bind address in smn_create_socket()
      sm-notify: IPv6 support in reserved port binding in smn_create_socket()
      sm-notify: Support creating a PF_INET6 socket in smn_create_socket()
      sm-notify: factor socket creation out of notify()
      statd: Update rmtcall.c
      sm-notify: Replace RPC code
      libnsm.a: Add RPC construction helper functions


 aclocal/libcap.m4          |   15 +
 configure.ac               |    3 
 support/include/nfsrpc.h   |   12 -
 support/include/nsm.h      |   31 ++-
 support/include/rpcmisc.h  |    7 -
 support/include/sockaddr.h |  237 ++++++++++++++++++++
 support/nfs/Makefile.am    |    3 
 support/nfs/getport.c      |   21 +-
 support/nfs/rpc_socket.c   |   55 +----
 support/nfs/svc_create.c   |  252 +++++++++++++++++++++
 support/nsm/Makefile.am    |    2 
 support/nsm/file.c         |  336 +++++++++++++++++++++++-----
 support/nsm/rpc.c          |  534 ++++++++++++++++++++++++++++++++++++++++++++
 utils/mount/network.c      |   13 -
 utils/mount/stropts.c      |    7 -
 utils/statd/Makefile.am    |    9 -
 utils/statd/callback.c     |   74 +++++-
 utils/statd/hostname.c     |  284 +++++++++++++++++++++++
 utils/statd/monitor.c      |   68 +++---
 utils/statd/notlist.c      |    5 
 utils/statd/notlist.h      |    6 
 utils/statd/rmtcall.c      |  197 ++++------------
 utils/statd/simu.c         |   35 ++-
 utils/statd/sm-notify.c    |  496 ++++++++++++++++++++++++-----------------
 utils/statd/sm-notify.man  |  407 +++++++++++++++++++++++-----------
 utils/statd/stat.c         |   13 +
 utils/statd/statd.c        |   38 +++
 utils/statd/statd.h        |    7 -
 utils/statd/statd.man      |  508 ++++++++++++++++++++++++++++++------------
 29 files changed, 2831 insertions(+), 844 deletions(-)
 create mode 100644 aclocal/libcap.m4
 create mode 100644 support/include/sockaddr.h
 create mode 100644 support/nfs/svc_create.c
 create mode 100644 support/nsm/rpc.c
 create mode 100644 utils/statd/hostname.c

-- 
Signature

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 01/24] libnsm.a: Add RPC construction helper functions
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
@ 2010-01-14 17:28   ` Chuck Lever
  2010-01-14 17:29   ` [PATCH 02/24] sm-notify: Replace RPC code Chuck Lever
                     ` (23 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:28 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

To manage concurrency, both statd and sm-notify construct raw RPC
requests in socket buffers, and use a minimal request scheduler
to send these requests and manage replies.  Both statd and sm-notify
open code the RPC request construction.

Introduce helper functions that can construct and send raw
NSMPROC_NOTIFY, NLM downcalls, and portmapper calls over a datagram
socket, and receive and parse their replies.  Support for IPv6 and
RPCB_GETADDR is featured.  This code (and the IPv6 support it
introduces) can now be shared by statd and sm-notify, eliminating
code and bug duplication.

This implementation is based on what's in utils/statd/rmtcall.c now,
but is wrapped up in a nice API and includes extra error checking.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/nsm.h   |   25 ++
 support/nsm/Makefile.am |    2 
 support/nsm/rpc.c       |  534 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 560 insertions(+), 1 deletions(-)
 create mode 100644 support/nsm/rpc.c

diff --git a/support/include/nsm.h b/support/include/nsm.h
index 28314d1..ce9e294 100644
--- a/support/include/nsm.h
+++ b/support/include/nsm.h
@@ -63,4 +63,29 @@ extern void	nsm_delete_notified_host(const char *hostname);
 extern size_t	nsm_priv_to_hex(const char *priv, char *buf,
 				const size_t buflen);
 
+/* rpc.c */
+
+#define NSM_MAXMSGSIZE	(2048u)
+
+extern uint32_t nsm_xmit_getport(const int sock,
+			const struct sockaddr_in *sin,
+			const unsigned long program,
+			const unsigned long version);
+extern uint32_t nsm_xmit_getaddr(const int sock,
+			const struct sockaddr_in6 *sin6,
+			const rpcprog_t program, const rpcvers_t version);
+extern uint32_t nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
+			const rpcprog_t program, const rpcvers_t version);
+extern uint32_t nsm_xmit_notify(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const rpcprog_t program,
+			const char *mon_name, const int state);
+extern uint32_t nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const struct mon *m,
+			const int state);
+extern uint32_t nsm_parse_reply(XDR *xdrs);
+extern unsigned long
+		nsm_recv_getport(XDR *xdrs);
+extern uint16_t nsm_recv_getaddr(XDR *xdrs);
+extern uint16_t nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs);
+
 #endif	/* !NFS_UTILS_SUPPORT_NSM_H */
diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am
index e359a43..2038e68 100644
--- a/support/nsm/Makefile.am
+++ b/support/nsm/Makefile.am
@@ -10,7 +10,7 @@ GENFILES	= $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
 EXTRA_DIST	= sm_inter.x
 
 noinst_LIBRARIES = libnsm.a
-libnsm_a_SOURCES = $(GENFILES) file.c
+libnsm_a_SOURCES = $(GENFILES) file.c rpc.c
 
 BUILT_SOURCES = $(GENFILES)
 
diff --git a/support/nsm/rpc.c b/support/nsm/rpc.c
new file mode 100644
index 0000000..4e5f40e
--- /dev/null
+++ b/support/nsm/rpc.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ *
+ * Instead of using ONC or TI RPC library calls, statd constructs
+ * RPC calls directly in socket buffers.  This allows a single
+ * socket to be concurrently shared among several different RPC
+ * programs and versions using a simple RPC request dispatcher.
+ *
+ * This file contains the details of RPC header and call
+ * construction and reply parsing, and a method for creating a
+ * socket for use with these functions.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif	/* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <time.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+
+#ifdef HAVE_LIBTIRPC
+#include <netconfig.h>
+#include <rpc/rpcb_prot.h>
+#endif	/* HAVE_LIBTIRPC */
+
+#include "xlog.h"
+#include "nfsrpc.h"
+#include "nsm.h"
+#include "sm_inter.h"
+
+/*
+ * Returns a fresh XID appropriate for RPC over UDP -- never zero.
+ */
+static uint32_t
+nsm_next_xid(void)
+{
+	static uint32_t nsm_xid = 0;
+	struct timeval now;
+
+	if (nsm_xid == 0) {
+		(void)gettimeofday(&now, NULL);
+		nsm_xid = (uint32_t)getpid() ^
+				(uint32_t)now.tv_sec ^ (uint32_t)now.tv_usec;
+	}
+
+	return nsm_xid++;
+}
+
+/*
+ * Select a fresh XID and construct an RPC header in @mesg.
+ * Always use AUTH_NULL credentials and verifiers.
+ *
+ * Returns the new XID.
+ */
+static uint32_t
+nsm_init_rpc_header(const rpcprog_t program, const rpcvers_t version,
+			const rpcproc_t procedure, struct rpc_msg *mesg)
+{
+	struct call_body *cb = &mesg->rm_call;
+	uint32_t xid = nsm_next_xid();
+
+	memset(mesg, 0, sizeof(*mesg));
+
+	mesg->rm_xid = (unsigned long)xid;
+	mesg->rm_direction = CALL;
+
+	cb->cb_rpcvers = RPC_MSG_VERSION;
+	cb->cb_prog = program;
+	cb->cb_vers = version;
+	cb->cb_proc = procedure;
+
+	cb->cb_cred.oa_flavor = AUTH_NULL;
+	cb->cb_cred.oa_base = (caddr_t) NULL;
+	cb->cb_cred.oa_length = 0;
+	cb->cb_verf.oa_flavor = AUTH_NULL;
+	cb->cb_verf.oa_base = (caddr_t) NULL;
+	cb->cb_verf.oa_length = 0;
+
+	return xid;
+}
+
+/*
+ * Initialize the network send buffer and XDR memory for encoding.
+ */
+static void
+nsm_init_xdrmem(char *msgbuf, const unsigned int msgbuflen,
+		XDR *xdrp)
+{
+	memset(msgbuf, 0, (size_t)msgbuflen);
+	memset(xdrp, 0, sizeof(*xdrp));
+	xdrmem_create(xdrp, msgbuf, msgbuflen, XDR_ENCODE);
+}
+
+/*
+ * Send a completed RPC call on a socket.
+ *
+ * Returns true if all the bytes were sent successfully; otherwise
+ * false if any error occurred.
+ */
+static _Bool
+nsm_rpc_sendto(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, XDR *xdrs, void *buf)
+{
+	const size_t buflen = (size_t)xdr_getpos(xdrs);
+	ssize_t err;
+
+	err = sendto(sock, buf, buflen, 0, sap, salen);
+	if ((err < 0) || ((size_t)err != buflen)) {
+		xlog(L_ERROR, "%s: sendto failed: %m", __func__);
+		return false;
+	}
+	return true;
+}
+
+/**
+ * nsm_xmit_getport - post a PMAP_GETPORT call on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sin: pointer to AF_INET socket address of server
+ * @program: RPC program number to query
+ * @version: RPC version number to query
+ *
+ * Send a PMAP_GETPORT call to the portmap daemon at @sin using
+ * socket descriptor @sock.  This request queries the RPC program
+ * [program, version, IPPROTO_UDP].
+ *
+ * NB: PMAP_GETPORT works only for IPv4 hosts.  This implementation
+ *     works only over UDP, and queries only UDP registrations.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_getport(const int sock, const struct sockaddr_in *sin,
+			const unsigned long program,
+			const unsigned long version)
+{
+	char msgbuf[NSM_MAXMSGSIZE];
+	struct sockaddr_in addr;
+	struct rpc_msg mesg;
+	_Bool sent = false;
+	struct pmap parms = {
+		.pm_prog	= program,
+		.pm_vers	= version,
+		.pm_prot	= (unsigned long)IPPROTO_UDP,
+	};
+	uint32_t xid;
+	XDR xdr;
+
+	xlog(D_CALL, "Sending PMAP_GETPORT for %u, %u, udp", program, version);
+
+	nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
+	xid = nsm_init_rpc_header(PMAPPROG, PMAPVERS,
+					(rpcproc_t)PMAPPROC_GETPORT, &mesg);
+
+	addr = *sin;
+	addr.sin_port = htons(PMAPPORT);
+
+	if (xdr_callmsg(&xdr, &mesg) == TRUE &&
+	    xdr_pmap(&xdr, &parms) == TRUE)
+		sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr,
+					(socklen_t)sizeof(addr), &xdr, msgbuf);
+	else
+		xlog(L_ERROR, "%s: can't encode PMAP_GETPORT call", __func__);
+
+	xdr_destroy(&xdr);
+
+	if (sent == false)
+		return 0;
+	return xid;
+}
+
+/**
+ * nsm_xmit_getaddr - post an RPCB_GETADDR call on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sin: pointer to AF_INET6 socket address of server
+ * @program: RPC program number to query
+ * @version: RPC version number to query
+ *
+ * Send an RPCB_GETADDR call to the rpcbind daemon at @sap using
+ * socket descriptor @sock.  This request queries the RPC program
+ * [program, version, "udp6"].
+ *
+ * NB: RPCB_GETADDR works for both IPv4 and IPv6 hosts.  This
+ *     implementation works only over UDP and AF_INET6, and queries
+ *     only "udp6" registrations.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+#ifdef HAVE_LIBTIRPC
+uint32_t
+nsm_xmit_getaddr(const int sock, const struct sockaddr_in6 *sin6,
+			const rpcprog_t program, const rpcvers_t version)
+{
+	char msgbuf[NSM_MAXMSGSIZE];
+	struct sockaddr_in6 addr;
+	struct rpc_msg mesg;
+	_Bool sent = false;
+	struct rpcb parms = {
+		.r_prog		= program,
+		.r_vers		= version,
+		.r_netid	= "udp6",
+		.r_owner	= "",
+	};
+	uint32_t xid;
+	XDR xdr;
+
+	xlog(D_CALL, "Sending RPCB_GETADDR for %u, %u, udp6", program, version);
+
+	nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
+	xid = nsm_init_rpc_header(RPCBPROG, RPCBVERS,
+					(rpcproc_t)RPCBPROC_GETADDR, &mesg);
+
+	addr = *sin6;
+	addr.sin6_port = htons(PMAPPORT);
+	parms.r_addr = nfs_sockaddr2universal((struct sockaddr *)(char *)&addr);
+	if (parms.r_addr == NULL) {
+		xlog(L_ERROR, "%s: can't encode socket address", __func__);
+		return 0;
+	}
+
+	if (xdr_callmsg(&xdr, &mesg) == TRUE &&
+	    xdr_rpcb(&xdr, &parms) == TRUE)
+		sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr,
+					(socklen_t)sizeof(addr), &xdr, msgbuf);
+	else
+		xlog(L_ERROR, "%s: can't encode RPCB_GETADDR call", __func__);
+
+	xdr_destroy(&xdr);
+	free(parms.r_addr);
+
+	if (sent == false)
+		return 0;
+	return xid;
+}
+#else	/* !HAVE_LIBTIRPC */
+uint32_t
+nsm_xmit_getaddr(const int sock __attribute__((unused)),
+			const struct sockaddr_in6 *sin6 __attribute__((unused)),
+			const rpcprog_t program __attribute__((unused)),
+			const rpcvers_t version __attribute__((unused)))
+{
+	return 0;
+}
+#endif	/* !HAVE_LIBTIRPC */
+
+/**
+ * nsm_xmit_rpcbind - post an rpcbind request
+ * @sock: datagram socket descriptor
+ * @sap: pointer to socket address of server
+ * @program: RPC program number to query
+ * @version: RPC version number to query
+ *
+ * Send an rpcbind query to the rpcbind daemon at @sap using
+ * socket descriptor @sock.
+ *
+ * NB: This implementation works only over UDP, but can query IPv4 or IPv6
+ *     hosts.  It queries only UDP registrations.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
+			const rpcprog_t program, const rpcvers_t version)
+{
+	switch (sap->sa_family) {
+	case AF_INET:
+		return nsm_xmit_getport(sock, (const struct sockaddr_in *)sap,
+						program, version);
+	case AF_INET6:
+		return nsm_xmit_getaddr(sock, (const struct sockaddr_in6 *)sap,
+						program, version);
+	}
+	return 0;
+}
+
+/**
+ * nsm_xmit_notify - post an NSMPROC_NOTIFY call on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sap: pointer to socket address of peer to notify (port already filled in)
+ * @salen: length of socket address
+ * @program: RPC program number to use
+ * @mon_name: mon_name of local peer (ie the rebooting system)
+ * @state: state of local peer
+ *
+ * Send an NSMPROC_NOTIFY call to the peer at @sap using socket descriptor @sock.
+ * This request notifies the peer that we have rebooted.
+ *
+ * NB: This implementation works only over UDP, but supports both AF_INET
+ *     and AF_INET6.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_notify(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const rpcprog_t program,
+			const char *mon_name, const int state)
+{
+	char msgbuf[NSM_MAXMSGSIZE];
+	struct stat_chge state_change;
+	struct rpc_msg mesg;
+	_Bool sent = false;
+	uint32_t xid;
+	XDR xdr;
+
+	state_change.mon_name = strdup(mon_name);
+	if (state_change.mon_name == NULL) {
+		xlog(L_ERROR, "%s: no memory", __func__);
+		return 0;
+	}
+	state_change.state = state;
+
+	xlog(D_CALL, "Sending SM_NOTIFY for %s", mon_name);
+
+	nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
+	xid = nsm_init_rpc_header(program, SM_VERS, SM_NOTIFY, &mesg);
+
+	if (xdr_callmsg(&xdr, &mesg) == TRUE &&
+	    xdr_stat_chge(&xdr, &state_change) == TRUE)
+		sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
+	else
+		xlog(L_ERROR, "%s: can't encode NSMPROC_NOTIFY call",
+				__func__);
+
+	xdr_destroy(&xdr);
+	free(state_change.mon_name);
+
+	if (sent == false)
+		return 0;
+	return xid;
+}
+
+/**
+ * nsm_xmit_nlmcall - post an unnamed call to local NLM on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sap: address/port of NLM service to contact
+ * @salen: size of @sap
+ * @m: callback data defining RPC call to make
+ * @state: state of rebooting host
+ *
+ * Send an unnamed call (previously requested via NSMPROC_MON) to the
+ * specified local UDP-based RPC service using socket descriptor @sock.
+ *
+ * NB: This implementation works only over UDP, but supports both AF_INET
+ *     and AF_INET6.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const struct mon *m,
+			const int state)
+{
+	const struct my_id *id = &m->mon_id.my_id;
+	char msgbuf[NSM_MAXMSGSIZE];
+	struct status new_status;
+	struct rpc_msg mesg;
+	_Bool sent = false;
+	uint32_t xid;
+	XDR xdr;
+
+	xlog(D_CALL, "Sending NLM downcall for %s", m->mon_id.mon_name);
+
+	nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
+	xid = nsm_init_rpc_header((rpcprog_t)id->my_prog,
+					(rpcvers_t)id->my_vers,
+					(rpcproc_t)id->my_proc, &mesg);
+
+	new_status.mon_name = m->mon_id.mon_name;
+	new_status.state = state;
+	memcpy(&new_status.priv, &m->priv, sizeof(new_status.priv));
+
+	if (xdr_callmsg(&xdr, &mesg) == TRUE &&
+	    xdr_status(&xdr, &new_status) == TRUE)
+		sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
+	else
+		xlog(L_ERROR, "%s: can't encode NLM downcall", __func__);
+
+	xdr_destroy(&xdr);
+
+	if (sent == false)
+		return 0;
+	return xid;
+}
+
+/**
+ * nsm_parse_reply - parse and validate the header in an RPC reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the XID of the reply, or zero if an error occurred.
+ */
+uint32_t
+nsm_parse_reply(XDR *xdrs)
+{
+	struct rpc_msg mesg = {
+		.rm_reply.rp_acpt.ar_results.proc	= (xdrproc_t)xdr_void,
+	};
+	uint32_t xid;
+
+	if (xdr_replymsg(xdrs, &mesg) == FALSE) {
+		xlog(L_ERROR, "%s: can't decode RPC reply", __func__);
+		return 0;
+	}
+	xid = (uint32_t)mesg.rm_xid;
+
+	if (mesg.rm_reply.rp_stat != MSG_ACCEPTED) {
+		xlog(L_ERROR, "%s: [0x%x] RPC status %d",
+			__func__, xid, mesg.rm_reply.rp_stat);
+		return 0;
+	}
+
+	if (mesg.rm_reply.rp_acpt.ar_stat != SUCCESS) {
+		xlog(L_ERROR, "%s: [0x%x] RPC accept status %d",
+			__func__, xid, mesg.rm_reply.rp_acpt.ar_stat);
+		return 0;
+	}
+
+	return xid;
+}
+
+/**
+ * nsm_recv_getport - parse PMAP_GETPORT reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the port number from the RPC reply, or zero
+ * if an error occurred.
+ */
+unsigned long
+nsm_recv_getport(XDR *xdrs)
+{
+	unsigned long port = 0;
+
+	if (xdr_u_long(xdrs, &port) == FALSE)
+		xlog(L_ERROR, "%s: can't decode pmap reply",
+			__func__);
+	if (port > UINT16_MAX) {
+		xlog(L_ERROR, "%s: bad port number",
+			__func__);
+		port = 0;
+	}
+
+	xlog(D_CALL, "Received PMAP_GETPORT result: %lu", port);
+	return port;
+}
+
+/**
+ * nsm_recv_getaddr - parse RPCB_GETADDR reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the port number from the RPC reply, or zero
+ * if an error occurred.
+ */
+uint16_t
+nsm_recv_getaddr(XDR *xdrs)
+{
+	char *uaddr = NULL;
+	int port;
+
+	if (xdr_wrapstring(xdrs, &uaddr) == FALSE)
+		xlog(L_ERROR, "%s: can't decode rpcb reply",
+			__func__);
+
+	if ((uaddr == NULL) || (uaddr[0] == '\0')) {
+		xlog(D_CALL, "Received RPCB_GETADDR result: "
+				"program not registered");
+		return 0;
+	}
+
+	port = nfs_universal2port(uaddr);
+
+	xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr);
+
+	if (port < 0 || port > UINT16_MAX) {
+		xlog(L_ERROR, "%s: bad port number",
+			__func__);
+		return 0;
+	}
+
+	xlog(D_CALL, "Received RPCB_GETADDR result: %d", port);
+	return (uint16_t)port;
+}
+
+/**
+ * nsm_recv_rpcbind - parse rpcbind reply
+ * @af: address family of reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the port number from the RPC reply, or zero
+ * if an error occurred.
+ */
+uint16_t
+nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs)
+{
+	switch (family) {
+	case AF_INET:
+		return (uint16_t)nsm_recv_getport(xdrs);
+	case AF_INET6:
+		return nsm_recv_getaddr(xdrs);
+	}
+	return 0;
+}


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 02/24] sm-notify: Replace RPC code
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  2010-01-14 17:28   ` [PATCH 01/24] libnsm.a: Add RPC construction helper functions Chuck Lever
@ 2010-01-14 17:29   ` Chuck Lever
  2010-01-14 17:29   ` [PATCH 03/24] statd: Update rmtcall.c Chuck Lever
                     ` (22 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:29 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Replace the open code to construct SM_NOTIFY and PMAP_GETPORT RPC
requests with calls to our new library routines that support
IPv6 and RPCB_GETADDR as well.

This change allows sm-notify to send RPCB_GETADDR, but it won't do
that until the main sm-notify socket supports PF_INET6 and the DNS
resolution logic is updated to return IPv6 addresses.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |  188 +++++++++++++++++------------------------------
 1 files changed, 66 insertions(+), 122 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 581234e..e49c722 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -33,24 +33,18 @@
 #include "nsm.h"
 #include "nfsrpc.h"
 
-#define NSM_PROG	100024
-#define NSM_PROGRAM	100024
-#define NSM_VERSION	1
 #define NSM_TIMEOUT	2
-#define NSM_NOTIFY	6
 #define NSM_MAX_TIMEOUT	120	/* don't make this too big */
-#define MAXMSGSIZE	256
 
 struct nsm_host {
 	struct nsm_host *	next;
 	char *			name;
-	struct sockaddr_storage	addr;
 	struct addrinfo		*ai;
 	time_t			last_used;
 	time_t			send_next;
 	unsigned int		timeout;
 	unsigned int		retries;
-	unsigned int		xid;
+	uint32_t		xid;
 };
 
 static char		nsm_hostname[256];
@@ -387,17 +381,8 @@ notify(void)
 static int
 notify_host(int sock, struct nsm_host *host)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *dest = (struct sockaddr *)&address;
-	socklen_t destlen = sizeof(address);
-	static unsigned int	xid = 0;
-	uint32_t		msgbuf[MAXMSGSIZE], *p;
-	unsigned int		len;
-
-	if (!xid)
-		xid = getpid() + time(NULL);
-	if (!host->xid)
-		host->xid = xid++;
+	struct sockaddr *sap;
+	socklen_t salen;
 
 	if (host->ai == NULL) {
 		host->ai = smn_lookup(host->name);
@@ -408,12 +393,6 @@ notify_host(int sock, struct nsm_host *host)
 		}
 	}
 
-	memset(msgbuf, 0, sizeof(msgbuf));
-	p = msgbuf;
-	*p++ = htonl(host->xid);
-	*p++ = 0;
-	*p++ = htonl(2);
-
 	/* If we retransmitted 4 times, reset the port to force
 	 * a new portmap lookup (in case statd was restarted).
 	 * We also rotate through multiple IP addresses at this
@@ -421,10 +400,7 @@ notify_host(int sock, struct nsm_host *host)
 	 */
 	if (host->retries >= 4) {
 		/* don't rotate if there is only one addrinfo */
-		if (host->ai->ai_next == NULL)
-			memcpy(&host->addr, host->ai->ai_addr,
-						host->ai->ai_addrlen);
-		else {
+		if (host->ai->ai_next != NULL) {
 			struct addrinfo *first = host->ai;
 			struct addrinfo **next = &host->ai;
 
@@ -437,58 +413,59 @@ notify_host(int sock, struct nsm_host *host)
 				next = & (*next)->ai_next;
 			/* put first entry at end */
 			*next = first;
-			memcpy(&host->addr, first->ai_addr,
-						first->ai_addrlen);
 		}
 
-		nfs_set_port((struct sockaddr *)&host->addr, 0);
+		nfs_set_port(host->ai->ai_addr, 0);
 		host->retries = 0;
 	}
 
-	memcpy(dest, &host->addr, destlen);
-	if (nfs_get_port(dest) == 0) {
-		/* Build a PMAP packet */
-		xlog(D_GENERAL, "Sending portmap query to %s", host->name);
+	sap = host->ai->ai_addr;
+	salen = host->ai->ai_addrlen;
 
-		nfs_set_port(dest, 111);
-		*p++ = htonl(100000);
-		*p++ = htonl(2);
-		*p++ = htonl(3);
+	if (nfs_get_port(sap) == 0)
+		host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
+	else
+		host->xid = nsm_xmit_notify(sock, sap, salen,
+				SM_PROG, nsm_hostname, nsm_state);
+	
+	return 0;
+}
 
-		/* Auth and verf */
-		*p++ = 0; *p++ = 0;
-		*p++ = 0; *p++ = 0;
+/*
+ * Extract the returned port number and set up the SM_NOTIFY call.
+ */
+static void
+recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
+{
+	uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
+
+	host->send_next = time(NULL);
+	host->xid = 0;
 
-		*p++ = htonl(NSM_PROGRAM);
-		*p++ = htonl(NSM_VERSION);
-		*p++ = htonl(IPPROTO_UDP);
-		*p++ = 0;
+	if (port == 0) {
+		/* No binding for statd... */
+		xlog(D_GENERAL, "No statd on host %s", host->name);
+		host->timeout = NSM_MAX_TIMEOUT;
+		host->send_next += NSM_MAX_TIMEOUT;
 	} else {
-		/* Build an SM_NOTIFY packet */
-		xlog(D_GENERAL, "Sending SM_NOTIFY to %s", host->name);
-
-		*p++ = htonl(NSM_PROGRAM);
-		*p++ = htonl(NSM_VERSION);
-		*p++ = htonl(NSM_NOTIFY);
-
-		/* Auth and verf */
-		*p++ = 0; *p++ = 0;
-		*p++ = 0; *p++ = 0;
-
-		/* state change */
-		len = strlen(nsm_hostname);
-		*p++ = htonl(len);
-		memcpy(p, nsm_hostname, len);
-		p += (len + 3) >> 2;
-		*p++ = htonl(nsm_state);
+		nfs_set_port(sap, port);
+		if (host->timeout >= NSM_MAX_TIMEOUT / 4)
+			host->timeout = NSM_MAX_TIMEOUT / 4;
 	}
-	len = (p - msgbuf) << 2;
 
-	if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
-		xlog_warn("Sending Reboot Notification to "
-			"'%s' failed: errno %d (%m)", host->name, errno);
-	
-	return 0;
+	insert_host(host);
+}
+
+/*
+ * Successful NOTIFY call. Server returns void, so nothing
+ * we need to do here.
+ */
+static void
+recv_notify_reply(struct nsm_host *host)
+{
+	xlog(D_GENERAL, "Host %s notified successfully", host->name);
+
+	smn_forget_host(host);
 }
 
 /*
@@ -499,70 +476,37 @@ recv_reply(int sock)
 {
 	struct nsm_host	*hp;
 	struct sockaddr *sap;
-	uint32_t	msgbuf[MAXMSGSIZE], *p, *end;
+	char msgbuf[NSM_MAXMSGSIZE];
 	uint32_t	xid;
-	int		res;
+	ssize_t		msglen;
+	XDR		xdr;
 
-	res = recv(sock, msgbuf, sizeof(msgbuf), 0);
-	if (res < 0)
+	memset(msgbuf, 0 , sizeof(msgbuf));
+	msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
+	if (msglen < 0)
 		return;
 
 	xlog(D_GENERAL, "Received packet...");
 
-	p = msgbuf;
-	end = p + (res >> 2);
-
-	xid = ntohl(*p++);
-	if (*p++ != htonl(1)	/* must be REPLY */
-	 || *p++ != htonl(0)	/* must be ACCEPTED */
-	 || *p++ != htonl(0)	/* must be NULL verifier */
-	 || *p++ != htonl(0)
-	 || *p++ != htonl(0))	/* must be SUCCESS */
-		return;
+	memset(&xdr, 0, sizeof(xdr));
+	xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
+	xid = nsm_parse_reply(&xdr);
+	if (xid == 0)
+		goto out;
 
 	/* Before we look at the data, find the host struct for
 	   this reply */
 	if ((hp = find_host(xid)) == NULL)
-		return;
-	sap = (struct sockaddr *)&hp->addr;
-
-	if (nfs_get_port(sap) == 0) {
-		/* This was a portmap request */
-		unsigned int	port;
-
-		port = ntohl(*p++);
-		if (p > end)
-			goto fail;
-
-		hp->send_next = time(NULL);
-		if (port == 0) {
-			/* No binding for statd. Delay the next
-			 * portmap query for max timeout */
-			xlog(D_GENERAL, "No statd on %s", hp->name);
-			hp->timeout = NSM_MAX_TIMEOUT;
-			hp->send_next += NSM_MAX_TIMEOUT;
-		} else {
-			nfs_set_port(sap, port);
-			if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
-				hp->timeout = NSM_MAX_TIMEOUT / 4;
-		}
-		hp->xid = 0;
-	} else {
-		/* Successful NOTIFY call. Server returns void,
-		 * so nothing we need to do here (except
-		 * check that we didn't read past the end of the
-		 * packet)
-		 */
-		if (p <= end) {
-			xlog(D_GENERAL, "Host %s notified successfully",
-					hp->name);
-			smn_forget_host(hp);
-			return;
-		}
-	}
+		goto out;
+
+	sap = hp->ai->ai_addr;
+	if (nfs_get_port(sap) == 0)
+		recv_rpcbind_reply(sap, hp, &xdr);
+	else
+		recv_notify_reply(hp);
 
-fail:	/* Re-insert the host */
-	insert_host(hp);
+out:
+	xdr_destroy(&xdr);
 }
 
 /*


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 03/24] statd: Update rmtcall.c
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  2010-01-14 17:28   ` [PATCH 01/24] libnsm.a: Add RPC construction helper functions Chuck Lever
  2010-01-14 17:29   ` [PATCH 02/24] sm-notify: Replace RPC code Chuck Lever
@ 2010-01-14 17:29   ` Chuck Lever
  2010-01-14 17:29   ` [PATCH 04/24] sm-notify: factor socket creation out of notify() Chuck Lever
                     ` (21 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:29 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Replace the open code to construct NLM downcalls and PMAP_GETPORT RPC
requests with calls to our new library routines.

This clean up removes redundant code in rmtcall.c, and enables the
possibility of making NLM downcalls via IPv6 transports.  We won't
support that for a long while, however.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/notlist.h |    4 +
 utils/statd/rmtcall.c |  181 ++++++++++++-------------------------------------
 2 files changed, 47 insertions(+), 138 deletions(-)

diff --git a/utils/statd/notlist.h b/utils/statd/notlist.h
index 664c9d8..f915ae1 100644
--- a/utils/statd/notlist.h
+++ b/utils/statd/notlist.h
@@ -13,14 +13,14 @@
 struct notify_list {
   mon			mon;	/* Big honkin' NSM structure. */
   struct in_addr	addr;	/* IP address for callback. */
-  unsigned short	port;	/* port number for callback */
+  in_port_t		port;	/* port number for callback */
   short int		times;	/* Counter used for various things. */
   int			state;	/* For storing notified state for callbacks. */
   char			*dns_name; /* used for matching incoming
 				    * NOTIFY requests */
   struct notify_list	*next;	/* Linked list forward pointer. */
   struct notify_list	*prev;	/* Linked list backward pointer. */
-  u_int32_t		xid;	/* XID of MS_NOTIFY RPC call */
+  uint32_t		xid;	/* XID of MS_NOTIFY RPC call */
   time_t		when;	/* notify: timeout for re-xmit */
 };
 
diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
index 5700fc7..1a97684 100644
--- a/utils/statd/rmtcall.c
+++ b/utils/statd/rmtcall.c
@@ -37,21 +37,19 @@
 #include <netdb.h>
 #include <string.h>
 #include <unistd.h>
-#ifdef HAVE_IFADDRS_H
-#include <ifaddrs.h>
-#endif /* HAVE_IFADDRS_H */
+
 #include "sm_inter.h"
 #include "statd.h"
 #include "notlist.h"
 #include "ha-callout.h"
 
+#include "nsm.h"
+#include "nfsrpc.h"
+
 #if SIZEOF_SOCKLEN_T - 0 == 0
 #define socklen_t int
 #endif
 
-#define MAXMSGSIZE	(2048 / sizeof(unsigned int))
-
-static unsigned long	xid = 0;	/* RPC XID counter */
 static int		sockfd = -1;	/* notify socket */
 
 /*
@@ -103,144 +101,53 @@ statd_get_socket(void)
 	return sockfd;
 }
 
-static unsigned long
-xmit_call(struct sockaddr_in *sin,
-	  u_int32_t prog, u_int32_t vers, u_int32_t proc,
-	  xdrproc_t func, void *obj)
-/* 		__u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */
-{
-	unsigned int		msgbuf[MAXMSGSIZE], msglen;
-	struct rpc_msg		mesg;
-	struct pmap		pmap;
-	XDR			xdr, *xdrs = &xdr;
-	int			err;
-
-	if (!xid)
-		xid = getpid() + time(NULL);
-
-	mesg.rm_xid = ++xid;
-	mesg.rm_direction = CALL;
-	mesg.rm_call.cb_rpcvers = 2;
-	if (sin->sin_port == 0) {
-		sin->sin_port = htons(PMAPPORT);
-		mesg.rm_call.cb_prog = PMAPPROG;
-		mesg.rm_call.cb_vers = PMAPVERS;
-		mesg.rm_call.cb_proc = PMAPPROC_GETPORT;
-		pmap.pm_prog = prog;
-		pmap.pm_vers = vers;
-		pmap.pm_prot = IPPROTO_UDP;
-		pmap.pm_port = 0;
-		func = (xdrproc_t) xdr_pmap;
-		obj  = &pmap;
-	} else {
-		mesg.rm_call.cb_prog = prog;
-		mesg.rm_call.cb_vers = vers;
-		mesg.rm_call.cb_proc = proc;
-	}
-	mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL;
-	mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL;
-	mesg.rm_call.cb_cred.oa_length = 0;
-	mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL;
-	mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL;
-	mesg.rm_call.cb_verf.oa_length = 0;
-
-	/* Create XDR memory object for encoding */
-	xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE);
-
-	/* Encode the RPC header part and payload */
-	if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) {
-		xlog(D_GENERAL, "%s: can't encode RPC message!", __func__);
-		xdr_destroy(xdrs);
-		return 0;
-	}
-
-	/* Get overall length of datagram */
-	msglen = xdr_getpos(xdrs);
-
-	if ((err = sendto(sockfd, msgbuf, msglen, 0,
-			(struct sockaddr *) sin, sizeof(*sin))) < 0) {
-		xlog_warn("%s: sendto failed: %m", __func__);
-	} else if (err != msglen) {
-		xlog_warn("%s: short write: %m", __func__);
-	}
-
-	xdr_destroy(xdrs);
-
-	return err == msglen? xid : 0;
-}
-
 static notify_list *
-recv_rply(struct sockaddr_in *sin, u_long *portp)
+recv_rply(u_long *portp)
 {
-	unsigned int		msgbuf[MAXMSGSIZE], msglen;
-	struct rpc_msg		mesg;
+	char			msgbuf[NSM_MAXMSGSIZE];
+	ssize_t			msglen;
 	notify_list		*lp = NULL;
-	XDR			xdr, *xdrs = &xdr;
-	socklen_t		alen = sizeof(*sin);
+	XDR			xdr;
+	struct sockaddr_in	sin;
+	socklen_t		alen = (socklen_t)sizeof(sin);
+	uint32_t		xid;
 
-	/* Receive message */
-	if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
-			(struct sockaddr *) sin, &alen)) < 0) {
+	memset(msgbuf, 0, sizeof(msgbuf));
+	msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
+				(struct sockaddr *)(char *)&sin, &alen);
+	if (msglen == (ssize_t)-1) {
 		xlog_warn("%s: recvfrom failed: %m", __func__);
 		return NULL;
 	}
 
-	/* Create XDR object for decoding buffer */
-	xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE);
-
-	memset(&mesg, 0, sizeof(mesg));
-	mesg.rm_reply.rp_acpt.ar_results.where = NULL;
-	mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void;
-
-	if (!xdr_replymsg(xdrs, &mesg)) {
-		xlog_warn("%s: can't decode RPC message!", __func__);
-		goto done;
-	}
-
-	if (mesg.rm_reply.rp_stat != 0) {
-		xlog_warn("%s: [%s] RPC status %d", 
-				__func__,
-				inet_ntoa(sin->sin_addr),
-				mesg.rm_reply.rp_stat);
-		goto done;
-	}
-	if (mesg.rm_reply.rp_acpt.ar_stat != 0) {
-		xlog_warn("%s: [%s] RPC status %d",
-				__func__,
-				inet_ntoa(sin->sin_addr),
-				mesg.rm_reply.rp_acpt.ar_stat);
+	memset(&xdr, 0, sizeof(xdr));
+	xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
+	xid = nsm_parse_reply(&xdr);
+	if (xid == 0)
 		goto done;
-	}
 
 	for (lp = notify; lp != NULL; lp = lp->next) {
 		/* LH - this was a bug... it should have been checking
 		 * the xid from the response message from the client,
 		 * not the static, internal xid */
-		if (lp->xid != mesg.rm_xid)
+		if (lp->xid != xid)
 			continue;
-		if (lp->addr.s_addr != sin->sin_addr.s_addr) {
+		if (lp->addr.s_addr != sin.sin_addr.s_addr) {
 			char addr [18];
 			strncpy (addr, inet_ntoa(lp->addr),
 				 sizeof (addr) - 1);
 			addr [sizeof (addr) - 1] = '\0';
 			xlog_warn("%s: address mismatch: "
 				"expected %s, got %s", __func__,
-				addr, inet_ntoa(sin->sin_addr));
-		}
-		if (lp->port == 0) {
-			if (!xdr_u_long(xdrs, portp)) {
-				xlog_warn("%s: [%s] can't decode reply body!",
-					__func__,
-					inet_ntoa(sin->sin_addr));
-				lp = NULL;
-				goto done;
-			}
+				addr, inet_ntoa(sin.sin_addr));
 		}
+		if (lp->port == 0)
+			*portp = nsm_recv_getport(&xdr);
 		break;
 	}
 
 done:
-	xdr_destroy(xdrs);
+	xdr_destroy(&xdr);
 	return lp;
 }
 
@@ -251,11 +158,6 @@ static int
 process_entry(notify_list *lp)
 {
 	struct sockaddr_in	sin;
-	struct status		new_status;
-	xdrproc_t		func;
-	void			*objp;
-	u_int32_t		proc, vers, prog;
-/* 	__u32			proc, vers, prog; */
 
 	if (NL_TIMES(lp) == 0) {
 		xlog(D_GENERAL, "%s: Cannot notify %s, giving up",
@@ -268,22 +170,30 @@ process_entry(notify_list *lp)
 	sin.sin_port   = lp->port;
 	/* LH - moved address into switch */
 
-	prog = NL_MY_PROG(lp);
-	vers = NL_MY_VERS(lp);
-	proc = NL_MY_PROC(lp);
-
 	/* __FORCE__ loopback for callbacks to lockd ... */
 	/* Just in case we somehow ignored it thus far */
 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
-	func = (xdrproc_t) xdr_status;
-	objp = &new_status;
-	new_status.mon_name = NL_MON_NAME(lp);
-	new_status.state    = NL_STATE(lp);
-	memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE);
+	if (sin.sin_port == 0)
+		lp->xid = nsm_xmit_getport(sockfd, &sin,
+					(rpcprog_t)NL_MY_PROG(lp),
+					(rpcvers_t)NL_MY_VERS(lp));
+	else {
+		struct mon m;
+
+		memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
 
-	lp->xid = xmit_call(&sin, prog, vers, proc, func, objp);
-	if (!lp->xid) {
+		m.mon_id.mon_name = NL_MON_NAME(lp);
+		m.mon_id.my_id.my_name = NULL;
+		m.mon_id.my_id.my_prog = NL_MY_PROG(lp);
+		m.mon_id.my_id.my_vers = NL_MY_VERS(lp);
+		m.mon_id.my_id.my_proc = NL_MY_PROC(lp);
+
+		lp->xid = nsm_xmit_nlmcall(sockfd,
+				(struct sockaddr *)(char *)&sin,
+				(socklen_t)sizeof(sin), &m, NL_STATE(lp));
+	}
+	if (lp->xid == 0) {
 		xlog_warn("%s: failed to notify port %d",
 				__func__, ntohs(lp->port));
 	}
@@ -298,14 +208,13 @@ process_entry(notify_list *lp)
 int
 process_reply(FD_SET_TYPE *rfds)
 {
-	struct sockaddr_in	sin;
 	notify_list		*lp;
 	u_long			port;
 
 	if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
 		return 0;
 
-	if (!(lp = recv_rply(&sin, &port)))
+	if (!(lp = recv_rply(&port)))
 		return 1;
 
 	if (lp->port == 0) {


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 04/24] sm-notify: factor socket creation out of notify()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (2 preceding siblings ...)
  2010-01-14 17:29   ` [PATCH 03/24] statd: Update rmtcall.c Chuck Lever
@ 2010-01-14 17:29   ` Chuck Lever
  2010-01-14 17:29   ` [PATCH 05/24] sm-notify: Support creating a PF_INET6 socket in smn_create_socket() Chuck Lever
                     ` (20 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:29 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

The top half of the notify() function creates the main socket that
sm-notify uses to do its job.  To make adding IPv6 support simpler,
refactor that piece into a separate function.

The logic is modified slightly so that exit(3) is invoked only in
main().  This is not required, but it makes the code slightly easier
to understand and maintain.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |  137 ++++++++++++++++++++++++++---------------------
 1 files changed, 77 insertions(+), 60 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index e49c722..462ba79 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -55,7 +55,7 @@ static unsigned int	opt_max_retry = 15 * 60;
 static char *		opt_srcaddr = 0;
 static uint16_t		opt_srcport = 0;
 
-static void		notify(void);
+static void		notify(const int sock);
 static int		notify_host(int, struct nsm_host *);
 static void		recv_reply(int);
 static void		insert_host(struct nsm_host *);
@@ -141,11 +141,76 @@ smn_get_host(const char *hostname,
 	return 1;
 }
 
+/*
+ * Prepare a socket for sending RPC requests
+ *
+ * Returns a bound datagram socket file descriptor, or -1 if
+ * an error occurs.
+ */
+static int
+smn_create_socket(const char *srcaddr, const uint16_t srcport)
+{
+	struct sockaddr_storage address;
+	struct sockaddr *local_addr = (struct sockaddr *)&address;
+	int sock, retry_cnt = 0;
+
+retry:
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		xlog(L_ERROR, "Failed to create RPC socket: %m");
+		return -1;
+	}
+	fcntl(sock, F_SETFL, O_NONBLOCK);
+
+	memset(&address, 0, sizeof(address));
+	local_addr->sa_family = AF_INET;	/* Default to IPv4 */
+
+	/* Bind source IP if provided on command line */
+	if (srcaddr) {
+		struct addrinfo *ai = smn_lookup(srcaddr);
+		if (!ai) {
+			xlog(L_ERROR,
+				"Not a valid hostname or address: \"%s\"",
+				srcaddr);
+			(void)close(sock);
+			return -1;
+		}
+
+		/* We know it's IPv4 at this point */
+		memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
+
+		freeaddrinfo(ai);
+	}
+
+	/* Use source port if provided on the command line,
+	 * otherwise use bindresvport */
+	if (srcport) {
+		nfs_set_port(local_addr, srcport);
+		if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
+			xlog(L_ERROR, "Failed to bind RPC socket: %m");
+			(void)close(sock);
+			return -1;
+		}
+	} else {
+		struct servent *se;
+		struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
+		(void) bindresvport(sock, sin);
+		/* try to avoid known ports */
+		se = getservbyport(sin->sin_port, "udp");
+		if (se && retry_cnt < 100) {
+			retry_cnt++;
+			close(sock);
+			goto retry;
+		}
+	}
+
+	return sock;
+}
+
 int
 main(int argc, char **argv)
 {
-	int	c;
-	int	force = 0;
+	int	c, sock, force = 0;
 	char *	progname;
 
 	progname = strrchr(argv[0], '/');
@@ -242,7 +307,14 @@ usage:		fprintf(stderr,
 		close(2);
 	}
 
-	notify();
+	sock = smn_create_socket(opt_srcaddr, opt_srcport);
+	if (sock == -1)
+		exit(1);
+
+	if (!nsm_drop_privileges(-1))
+		exit(1);
+
+	notify(sock);
 
 	if (hosts) {
 		struct nsm_host	*hp;
@@ -262,68 +334,13 @@ usage:		fprintf(stderr,
  * Notify hosts
  */
 static void
-notify(void)
+notify(const int sock)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *local_addr = (struct sockaddr *)&address;
 	time_t	failtime = 0;
-	int	sock = -1;
-	int retry_cnt = 0;
-
- retry:
-	sock = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock < 0) {
-		xlog(L_ERROR, "Failed to create RPC socket: %m");
-		exit(1);
-	}
-	fcntl(sock, F_SETFL, O_NONBLOCK);
-
-	memset(&address, 0, sizeof(address));
-	local_addr->sa_family = AF_INET;	/* Default to IPv4 */
-
-	/* Bind source IP if provided on command line */
-	if (opt_srcaddr) {
-		struct addrinfo *ai = smn_lookup(opt_srcaddr);
-		if (!ai) {
-			xlog(L_ERROR,
-				"Not a valid hostname or address: \"%s\"",
-				opt_srcaddr);
-			exit(1);
-		}
-
-		/* We know it's IPv4 at this point */
-		memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
-
-		freeaddrinfo(ai);
-	}
-
-	/* Use source port if provided on the command line,
-	 * otherwise use bindresvport */
-	if (opt_srcport) {
-		nfs_set_port(local_addr, opt_srcport);
-		if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
-			xlog(L_ERROR, "Failed to bind RPC socket: %m");
-			exit(1);
-		}
-	} else {
-		struct servent *se;
-		struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
-		(void) bindresvport(sock, sin);
-		/* try to avoid known ports */
-		se = getservbyport(sin->sin_port, "udp");
-		if (se && retry_cnt < 100) {
-			retry_cnt++;
-			close(sock);
-			goto retry;
-		}
-	}
 
 	if (opt_max_retry)
 		failtime = time(NULL) + opt_max_retry;
 
-	if (!nsm_drop_privileges(-1))
-		exit(1);
-
 	while (hosts) {
 		struct pollfd	pfd;
 		time_t		now = time(NULL);


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 05/24] sm-notify: Support creating a PF_INET6 socket in smn_create_socket()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (3 preceding siblings ...)
  2010-01-14 17:29   ` [PATCH 04/24] sm-notify: factor socket creation out of notify() Chuck Lever
@ 2010-01-14 17:29   ` Chuck Lever
  2010-01-14 17:29   ` [PATCH 06/24] sm-notify: IPv6 support in reserved port binding " Chuck Lever
                     ` (19 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:29 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Socket creation is unfortunately complicated by the need to handle the
case where sm-notify is built with IPv6 support, but the local system
has disabled it entirely at run-time (ie, socket(3) returns
EAFNOSUPPORT when we try to create an AF_INET6 socket).

The run-time address family setting is made available in the global
variable nsm_family.  This setting can control the family of the
socket's bind address and what kind of addresses we want returned by
smn_lookup().  Support for that is added in subsequent patches.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   81 ++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 76 insertions(+), 5 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 462ba79..0ba817a 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -49,6 +49,7 @@ struct nsm_host {
 
 static char		nsm_hostname[256];
 static int		nsm_state;
+static int		nsm_family = AF_INET;
 static int		opt_debug = 0;
 static _Bool		opt_update_state = true;
 static unsigned int	opt_max_retry = 15 * 60;
@@ -141,6 +142,79 @@ smn_get_host(const char *hostname,
 	return 1;
 }
 
+#ifdef IPV6_SUPPORTED
+static int smn_socket(void)
+{
+	int sock;
+
+	/*
+	 * Use an AF_INET socket if IPv6 is disabled on the
+	 * local system.
+	 */
+	sock = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (sock == -1) {
+		if (errno != EAFNOSUPPORT) {
+			xlog(L_ERROR, "Failed to create RPC socket: %m");
+			return -1;
+		}
+		sock = socket(AF_INET, SOCK_DGRAM, 0);
+		if (sock < 0) {
+			xlog(L_ERROR, "Failed to create RPC socket: %m");
+			return -1;
+		}
+	} else
+		nsm_family = AF_INET6;
+
+	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+		xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
+		goto out_close;
+	}
+
+	/*
+	 * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4.  However,
+	 * since sm-notify open-codes all of its RPC support, it can
+	 * use a single socket and let the local network stack provide
+	 * the correct mapping between address families automatically.
+	 * This is the same thing that is done in the kernel.
+	 */
+	if (nsm_family == AF_INET6) {
+		const int zero = 0;
+		socklen_t zerolen = (socklen_t)sizeof(zero);
+
+		if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
+					(char *)&zero, zerolen) == -1) {
+			xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
+			goto out_close;
+		}
+	}
+
+	return sock;
+
+out_close:
+	(void)close(sock);
+	return -1;
+}
+#else	/* !IPV6_SUPPORTED */
+static int smn_socket(void)
+{
+	int sock;
+
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock == -1) {
+		xlog(L_ERROR, "Failed to create RPC socket: %m");
+		return -1;
+	}
+
+	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+		xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
+		(void)close(sock);
+		return -1;
+	}
+
+	return sock;
+}
+#endif	/* !IPV6_SUPPORTED */
+
 /*
  * Prepare a socket for sending RPC requests
  *
@@ -155,12 +229,9 @@ smn_create_socket(const char *srcaddr, const uint16_t srcport)
 	int sock, retry_cnt = 0;
 
 retry:
-	sock = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock < 0) {
-		xlog(L_ERROR, "Failed to create RPC socket: %m");
+	sock = smn_socket();
+	if (sock == -1)
 		return -1;
-	}
-	fcntl(sock, F_SETFL, O_NONBLOCK);
 
 	memset(&address, 0, sizeof(address));
 	local_addr->sa_family = AF_INET;	/* Default to IPv4 */


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 06/24] sm-notify: IPv6 support in reserved port binding in smn_create_socket()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (4 preceding siblings ...)
  2010-01-14 17:29   ` [PATCH 05/24] sm-notify: Support creating a PF_INET6 socket in smn_create_socket() Chuck Lever
@ 2010-01-14 17:29   ` Chuck Lever
  2010-01-14 17:29   ` [PATCH 07/24] sm-notify: Use getaddrinfo(3) to create bind address " Chuck Lever
                     ` (18 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:29 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

This patch updates the "bind to an arbitrary privileged port" arm of
smn_create_socket() so it can deal with IPv6 bind addresses.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   29 ++++++++++++++++++++++++++++-
 1 files changed, 28 insertions(+), 1 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 0ba817a..928d1c5 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -215,6 +215,26 @@ static int smn_socket(void)
 }
 #endif	/* !IPV6_SUPPORTED */
 
+#ifdef HAVE_LIBTIRPC
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+	return bindresvport_sa(sock, sap);
+}
+
+#else	/* !HAVE_LIBTIRPC */
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+	if (sap->sa_family != AF_INET) {
+		errno = EAFNOSUPPORT;
+		return -1;
+	}
+
+	return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
+}
+#endif	/* !HAVE_LIBTIRPC */
+
 /*
  * Prepare a socket for sending RPC requests
  *
@@ -265,7 +285,14 @@ retry:
 	} else {
 		struct servent *se;
 		struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
-		(void) bindresvport(sock, sin);
+
+		if (smn_bindresvport(sock, local_addr) == -1) {
+			xlog(L_ERROR,
+				"bindresvport on RPC socket failed: %m");
+			(void)close(sock);
+			return -1;
+		}
+
 		/* try to avoid known ports */
 		se = getservbyport(sin->sin_port, "udp");
 		if (se && retry_cnt < 100) {


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 07/24] sm-notify: Use getaddrinfo(3) to create bind address in smn_create_socket()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (5 preceding siblings ...)
  2010-01-14 17:29   ` [PATCH 06/24] sm-notify: IPv6 support in reserved port binding " Chuck Lever
@ 2010-01-14 17:29   ` Chuck Lever
  2010-01-14 17:30   ` [PATCH 08/24] sm-notify: Support IPv6 DNS lookups in smn_lookup Chuck Lever
                     ` (17 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:29 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

This patch updates the "bind to a user-specified port" arm of
smn_create_socket() so it can deal with IPv6 bind addresses.

A single getaddrinfo(3) call can convert a user-specified bind address
or hostname to a socket address, optionally plant a provided port
number, or whip up an appropriate wildcard address for use as the main
socket's bind address.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   82 +++++++++++++++++++++++++++++------------------
 1 files changed, 51 insertions(+), 31 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 928d1c5..259db09 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -53,8 +53,8 @@ static int		nsm_family = AF_INET;
 static int		opt_debug = 0;
 static _Bool		opt_update_state = true;
 static unsigned int	opt_max_retry = 15 * 60;
-static char *		opt_srcaddr = 0;
-static uint16_t		opt_srcport = 0;
+static char *		opt_srcaddr = NULL;
+static char *		opt_srcport = NULL;
 
 static void		notify(const int sock);
 static int		notify_host(int, struct nsm_host *);
@@ -215,6 +215,39 @@ static int smn_socket(void)
 }
 #endif	/* !IPV6_SUPPORTED */
 
+/*
+ * If admin specified a source address or srcport, then convert those
+ * to a sockaddr and return it.   Otherwise, return an ANYADDR address.
+ */
+__attribute_malloc__
+static struct addrinfo *
+smn_bind_address(const char *srcaddr, const char *srcport)
+{
+	struct addrinfo *ai = NULL;
+	struct addrinfo hint = {
+		.ai_flags	= AI_NUMERICSERV,
+		.ai_family	= nsm_family,
+		.ai_protocol	= (int)IPPROTO_UDP,
+	};
+	int error;
+
+	if (srcaddr == NULL)
+		hint.ai_flags |= AI_PASSIVE;
+
+	if (srcport == NULL)
+		error = getaddrinfo(srcaddr, "", &hint, &ai);
+	else
+		error = getaddrinfo(srcaddr, srcport, &hint, &ai);
+	if (error != 0) {
+		xlog(L_ERROR,
+			"Invalid bind address or port for RPC socket: %s",
+				gai_strerror(error));
+		return NULL;
+	}
+
+	return ai;
+}
+
 #ifdef HAVE_LIBTIRPC
 static int
 smn_bindresvport(int sock, struct sockaddr *sap)
@@ -242,66 +275,53 @@ smn_bindresvport(int sock, struct sockaddr *sap)
  * an error occurs.
  */
 static int
-smn_create_socket(const char *srcaddr, const uint16_t srcport)
+smn_create_socket(const char *srcaddr, const char *srcport)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *local_addr = (struct sockaddr *)&address;
 	int sock, retry_cnt = 0;
+	struct addrinfo *ai;
 
 retry:
 	sock = smn_socket();
 	if (sock == -1)
 		return -1;
 
-	memset(&address, 0, sizeof(address));
-	local_addr->sa_family = AF_INET;	/* Default to IPv4 */
-
-	/* Bind source IP if provided on command line */
-	if (srcaddr) {
-		struct addrinfo *ai = smn_lookup(srcaddr);
-		if (!ai) {
-			xlog(L_ERROR,
-				"Not a valid hostname or address: \"%s\"",
-				srcaddr);
-			(void)close(sock);
-			return -1;
-		}
-
-		/* We know it's IPv4 at this point */
-		memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
-
-		freeaddrinfo(ai);
+	ai = smn_bind_address(srcaddr, srcport);
+	if (ai == NULL) {
+		(void)close(sock);
+		return -1;
 	}
 
 	/* Use source port if provided on the command line,
 	 * otherwise use bindresvport */
 	if (srcport) {
-		nfs_set_port(local_addr, srcport);
-		if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
+		if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
 			xlog(L_ERROR, "Failed to bind RPC socket: %m");
+			freeaddrinfo(ai);
 			(void)close(sock);
 			return -1;
 		}
 	} else {
 		struct servent *se;
-		struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
 
-		if (smn_bindresvport(sock, local_addr) == -1) {
+		if (smn_bindresvport(sock, ai->ai_addr) == -1) {
 			xlog(L_ERROR,
 				"bindresvport on RPC socket failed: %m");
+			freeaddrinfo(ai);
 			(void)close(sock);
 			return -1;
 		}
 
 		/* try to avoid known ports */
-		se = getservbyport(sin->sin_port, "udp");
-		if (se && retry_cnt < 100) {
+		se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
+		if (se != NULL && retry_cnt < 100) {
 			retry_cnt++;
-			close(sock);
+			freeaddrinfo(ai);
+			(void)close(sock);
 			goto retry;
 		}
 	}
 
+	freeaddrinfo(ai);
 	return sock;
 }
 
@@ -332,7 +352,7 @@ main(int argc, char **argv)
 			opt_update_state = false;
 			break;
 		case 'p':
-			opt_srcport = atoi(optarg);
+			opt_srcport = optarg;
 			break;
 		case 'v':
 			opt_srcaddr = optarg;


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 08/24] sm-notify: Support IPv6 DNS lookups in smn_lookup
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (6 preceding siblings ...)
  2010-01-14 17:29   ` [PATCH 07/24] sm-notify: Use getaddrinfo(3) to create bind address " Chuck Lever
@ 2010-01-14 17:30   ` Chuck Lever
  2010-01-14 17:30   ` [PATCH 09/24] nfs-utils: Collect socket address helpers into one location Chuck Lever
                     ` (16 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:30 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

When IPV6_SUPPORTED is enabled and the local system has IPv6 support,
request AF_INET6 and AF_INET addresses from the DNS resolver.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   19 ++++++++++++-------
 1 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 259db09..8d90d1f 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -33,6 +33,10 @@
 #include "nsm.h"
 #include "nfsrpc.h"
 
+#ifndef HAVE_DECL_AI_ADDRCONFIG
+#define AI_ADDRCONFIG	0
+#endif
+
 #define NSM_TIMEOUT	2
 #define NSM_MAX_TIMEOUT	120	/* don't make this too big */
 
@@ -65,19 +69,20 @@ static int		record_pid(void);
 
 static struct nsm_host *	hosts = NULL;
 
-static struct addrinfo *smn_lookup(const char *name)
+__attribute_malloc__
+static struct addrinfo *
+smn_lookup(const char *name)
 {
-	struct addrinfo	*ai, hint = {
-#if HAVE_DECL_AI_ADDRCONFIG
+	struct addrinfo	*ai = NULL;
+	struct addrinfo hint = {
 		.ai_flags	= AI_ADDRCONFIG,
-#endif	/* HAVE_DECL_AI_ADDRCONFIG */
-		.ai_family	= AF_INET,
-		.ai_protocol	= IPPROTO_UDP,
+		.ai_family	= (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
+		.ai_protocol	= (int)IPPROTO_UDP,
 	};
 	int error;
 
 	error = getaddrinfo(name, NULL, &hint, &ai);
-	if (error) {
+	if (error != 0) {
 		xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
 		return NULL;
 	}


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 09/24] nfs-utils: Collect socket address helpers into one location
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (7 preceding siblings ...)
  2010-01-14 17:30   ` [PATCH 08/24] sm-notify: Support IPv6 DNS lookups in smn_lookup Chuck Lever
@ 2010-01-14 17:30   ` Chuck Lever
  2010-01-14 17:30   ` [PATCH 10/24] statd: Introduce statd version of matchhostname() Chuck Lever
                     ` (15 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:30 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Introduce generic helpers for managing socket addresses.  These are
general enough that they are useful for pretty much any component of
nfs-utils.

We also include the definition of nfs_sockaddr here, so it can be
shared.  See:

  https://bugzilla.redhat.com/show_bug.cgi?id=448743

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/nfsrpc.h   |   12 --
 support/include/sockaddr.h |  237 ++++++++++++++++++++++++++++++++++++++++++++
 support/nfs/getport.c      |   21 ++--
 support/nfs/rpc_socket.c   |   55 +---------
 utils/mount/network.c      |   13 +-
 utils/mount/stropts.c      |    7 -
 utils/statd/sm-notify.c    |    1 
 7 files changed, 264 insertions(+), 82 deletions(-)
 create mode 100644 support/include/sockaddr.h

diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
index d6d4a1c..4db35ab 100644
--- a/support/include/nfsrpc.h
+++ b/support/include/nfsrpc.h
@@ -59,16 +59,6 @@ static inline void nfs_clear_rpc_createerr(void)
 }
 
 /*
- * Extract port value from a socket address
- */
-extern uint16_t		nfs_get_port(const struct sockaddr *);
-
-/*
- * Set port value in a socket address
- */
-extern void		nfs_set_port(struct sockaddr *, const uint16_t);
-
-/*
  * Look up an RPC program name in /etc/rpc
  */
 extern rpcprog_t	nfs_getrpcbyname(const rpcprog_t, const char *table[]);
@@ -170,4 +160,4 @@ extern int		nfs_rpc_ping(const struct sockaddr *sap,
 				const unsigned short protocol,
 				const struct timeval *timeout);
 
-#endif	/* __NFS_UTILS_NFSRPC_H */
+#endif	/* !__NFS_UTILS_NFSRPC_H */
diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h
new file mode 100644
index 0000000..732514b
--- /dev/null
+++ b/support/include/sockaddr.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFS_UTILS_SOCKADDR_H
+#define NFS_UTILS_SOCKADDR_H
+
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/*
+ * This type is for defining buffers that contain network socket
+ * addresses.
+ *
+ * Casting a "struct sockaddr *" to the address of a "struct
+ * sockaddr_storage" breaks C aliasing rules.  The "union
+ * nfs_sockaddr" type follows C aliasing rules yet specifically
+ * allows converting pointers to it between "struct sockaddr *"
+ * and a few other network sockaddr-related pointer types.
+ *
+ * Note that this union is much smaller than a sockaddr_storage.
+ * It should be used only for AF_INET or AF_INET6 socket addresses.
+ * An AF_LOCAL sockaddr_un, for example, will clearly not fit into
+ * a buffer of this type.
+ */
+union nfs_sockaddr {
+	struct sockaddr		sa;
+	struct sockaddr_in	s4;
+	struct sockaddr_in6	s6;
+};
+
+#if SIZEOF_SOCKLEN_T - 0 == 0
+#define socklen_t unsigned int
+#endif
+
+#define SIZEOF_SOCKADDR_UNKNOWN	(socklen_t)0
+#define SIZEOF_SOCKADDR_IN	(socklen_t)sizeof(struct sockaddr_in)
+
+#ifdef IPV6_SUPPORTED
+#define SIZEOF_SOCKADDR_IN6	(socklen_t)sizeof(struct sockaddr_in6)
+#else	/* !IPV6_SUPPORTED */
+#define SIZEOF_SOCKADDR_IN6	SIZEOF_SOCKADDR_UNKNOWN
+#endif	/* !IPV6_SUPPORTED */
+
+/**
+ * nfs_sockaddr_length - return the size in bytes of a socket address
+ * @sap: pointer to socket address
+ *
+ * Returns the size in bytes of @sap, or zero if the family is
+ * not recognized.
+ */
+static inline socklen_t
+nfs_sockaddr_length(const struct sockaddr *sap)
+{
+	switch (sap->sa_family) {
+	case AF_INET:
+		return SIZEOF_SOCKADDR_IN;
+	case AF_INET6:
+		return SIZEOF_SOCKADDR_IN6;
+	}
+	return SIZEOF_SOCKADDR_UNKNOWN;
+}
+
+static inline uint16_t
+get_port4(const struct sockaddr *sap)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+	return ntohs(sin->sin_port);
+}
+
+#ifdef IPV6_SUPPORTED
+static inline uint16_t
+get_port6(const struct sockaddr *sap)
+{
+	const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap;
+	return ntohs(sin6->sin6_port);
+}
+#else	/* !IPV6_SUPPORTED */
+static inline uint16_t
+get_port6(__attribute__ ((unused)) const struct sockaddr *sap)
+{
+	return 0;
+}
+#endif	/* !IPV6_SUPPORTED */
+
+/**
+ * nfs_get_port - extract port value from a socket address
+ * @sap: pointer to socket address
+ *
+ * Returns port value in host byte order, or zero if the
+ * socket address contains an unrecognized family.
+ */
+static inline uint16_t
+nfs_get_port(const struct sockaddr *sap)
+{
+	switch (sap->sa_family) {
+	case AF_INET:
+		return get_port4(sap);
+	case AF_INET6:
+		return get_port6(sap);
+	}
+	return 0;
+}
+
+static inline void
+set_port4(struct sockaddr *sap, const uint16_t port)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+	sin->sin_port = htons(port);
+}
+
+#ifdef IPV6_SUPPORTED
+static inline void
+set_port6(struct sockaddr *sap, const uint16_t port)
+{
+	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+	sin6->sin6_port = htons(port);
+}
+#else	/* !IPV6_SUPPORTED */
+static inline void
+set_port6(__attribute__ ((unused)) struct sockaddr *sap,
+		__attribute__ ((unused)) const uint16_t port)
+{
+}
+#endif	/* !IPV6_SUPPORTED */
+
+/**
+ * nfs_set_port - set port value in a socket address
+ * @sap: pointer to socket address
+ * @port: port value to set
+ *
+ */
+static inline void
+nfs_set_port(struct sockaddr *sap, const uint16_t port)
+{
+	switch (sap->sa_family) {
+	case AF_INET:
+		set_port4(sap, port);
+		break;
+	case AF_INET6:
+		set_port6(sap, port);
+		break;
+	}
+}
+
+/**
+ * nfs_is_v4_loopback - test to see if socket address is AF_INET loopback
+ * @sap: pointer to socket address
+ *
+ * Returns true if the socket address is the standard IPv4 loopback
+ * address; otherwise false is returned.
+ */
+static inline _Bool
+nfs_is_v4_loopback(const struct sockaddr *sap)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+
+	if (sin->sin_family != AF_INET)
+		return false;
+	if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
+		return false;
+        return true;
+}
+
+static inline _Bool
+compare_sockaddr4(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1;
+	const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2;
+	return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr;
+}
+
+#ifdef IPV6_SUPPORTED
+static inline _Bool
+compare_sockaddr6(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1;
+	const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2;
+
+	if ((IN6_IS_ADDR_LINKLOCAL((char *)&sin1->sin6_addr) &&
+	     IN6_IS_ADDR_LINKLOCAL((char *)&sin2->sin6_addr)) ||
+	    (IN6_IS_ADDR_SITELOCAL((char *)&sin1->sin6_addr) &&
+	     IN6_IS_ADDR_SITELOCAL((char *)&sin2->sin6_addr)))
+		if (sin1->sin6_scope_id != sin2->sin6_scope_id)
+			return false;
+
+	return IN6_ARE_ADDR_EQUAL((char *)&sin1->sin6_addr,
+					(char *)&sin2->sin6_addr);
+}
+#else	/* !IPV6_SUPPORTED */
+static inline _Bool
+compare_sockaddr6(__attribute__ ((unused)) const struct sockaddr *sa1,
+		__attribute__ ((unused)) const struct sockaddr *sa2)
+{
+	return false;
+}
+#endif	/* !IPV6_SUPPORTED */
+
+/**
+ * nfs_compare_sockaddr - compare two socket addresses for equality
+ * @sa1: pointer to a socket address
+ * @sa2: pointer to a socket address
+ *
+ * Returns true if the two socket addresses contain equivalent
+ * network addresses; otherwise false is returned.
+ */
+static inline _Bool
+nfs_compare_sockaddr(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	if (sa1->sa_family == sa2->sa_family)
+		switch (sa1->sa_family) {
+		case AF_INET:
+			return compare_sockaddr4(sa1, sa2);
+		case AF_INET6:
+			return compare_sockaddr6(sa1, sa2);
+		}
+
+	return false;
+}
+
+#endif	/* !NFS_UTILS_SOCKADDR_H */
diff --git a/support/nfs/getport.c b/support/nfs/getport.c
index 7e0f798..c930539 100644
--- a/support/nfs/getport.c
+++ b/support/nfs/getport.c
@@ -45,6 +45,7 @@
 #include <rpc/rpcb_prot.h>
 #endif
 
+#include "sockaddr.h"
 #include "nfsrpc.h"
 
 /*
@@ -705,8 +706,8 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
 		 const rpcprog_t program, const rpcvers_t version,
 		 const unsigned short protocol, const struct timeval *timeout)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *saddr = (struct sockaddr *)&address;
+	union nfs_sockaddr address;
+	struct sockaddr *saddr = &address.sa;
 	CLIENT *client;
 	struct timeval tout = { -1, 0 };
 	int result = 0;
@@ -774,8 +775,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
 			   const rpcvers_t version,
 			   const unsigned short protocol)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *saddr = (struct sockaddr *)&address;
+	union nfs_sockaddr address;
+	struct sockaddr *saddr = &address.sa;
 	struct timeval timeout = { -1, 0 };
 	unsigned short port = 0;
 	CLIENT *client;
@@ -833,8 +834,8 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
 	}
 
 	if (port != 0) {
-		struct sockaddr_storage address;
-		struct sockaddr *saddr = (struct sockaddr *)&address;
+		union nfs_sockaddr address;
+		struct sockaddr *saddr = &address.sa;
 
 		memcpy(saddr, sap, (size_t)salen);
 		nfs_set_port(saddr, port);
@@ -885,8 +886,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
 				const rpcvers_t version,
 				const unsigned short protocol)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *lb_addr = (struct sockaddr *)&address;
+	union nfs_sockaddr address;
+	struct sockaddr *lb_addr = &address.sa;
 	socklen_t lb_len = sizeof(*lb_addr);
 	unsigned short port = 0;
 
@@ -969,8 +970,8 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
 				const unsigned short protocol,
 				const struct timeval *timeout)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *saddr = (struct sockaddr *)&address;
+	union nfs_sockaddr address;
+	struct sockaddr *saddr = &address.sa;
 	CLIENT *client;
 	struct rpcb parms;
 	struct timeval tout = { -1, 0 };
diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
index 9c20f61..0e20824 100644
--- a/support/nfs/rpc_socket.c
+++ b/support/nfs/rpc_socket.c
@@ -26,6 +26,8 @@
 
 #include <sys/types.h>
 #include <sys/time.h>
+
+#include <stdbool.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -38,6 +40,7 @@
 #include <rpc/rpc.h>
 #include <rpc/pmap_prot.h>
 
+#include "sockaddr.h"
 #include "nfsrpc.h"
 
 #ifdef HAVE_LIBTIRPC
@@ -51,6 +54,7 @@
 #define NFSRPC_TIMEOUT_UDP	(3)
 #define NFSRPC_TIMEOUT_TCP	(10)
 
+
 /*
  * Set up an RPC client for communicating via a AF_LOCAL socket.
  *
@@ -121,10 +125,10 @@ static int nfs_bind(const int sock, const sa_family_t family)
 
 	switch (family) {
 	case AF_INET:
-		return bind(sock, (struct sockaddr *)&sin,
+		return bind(sock, (struct sockaddr *)(char *)&sin,
 					(socklen_t)sizeof(sin));
 	case AF_INET6:
-		return bind(sock, (struct sockaddr *)&sin6,
+		return bind(sock, (struct sockaddr *)(char *)&sin6,
 					(socklen_t)sizeof(sin6));
 	}
 
@@ -153,9 +157,9 @@ static int nfs_bindresvport(const int sock, const sa_family_t family)
 
 	switch (family) {
 	case AF_INET:
-		return bindresvport_sa(sock, (struct sockaddr *)&sin);
+		return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin);
 	case AF_INET6:
-		return bindresvport_sa(sock, (struct sockaddr *)&sin6);
+		return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6);
 	}
 
 	errno = EAFNOSUPPORT;
@@ -416,49 +420,6 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
 }
 
 /**
- * nfs_get_port - extract port value from a socket address
- * @sap: pointer to socket address
- *
- * Returns port value in host byte order.
- */
-uint16_t
-nfs_get_port(const struct sockaddr *sap)
-{
-       const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
-       const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap;
-
-       switch (sap->sa_family) {
-       case AF_INET:
-               return ntohs(sin->sin_port);
-       case AF_INET6:
-               return ntohs(sin6->sin6_port);
-       }
-       return 0;
-}
-
-/**
- * nfs_set_port - set port value in a socket address
- * @sap: pointer to socket address
- * @port: port value to set
- *
- */
-void
-nfs_set_port(struct sockaddr *sap, const uint16_t port)
-{
-       struct sockaddr_in *sin = (struct sockaddr_in *)sap;
-       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
-
-       switch (sap->sa_family) {
-       case AF_INET:
-               sin->sin_port = htons(port);
-               break;
-       case AF_INET6:
-               sin6->sin6_port = htons(port);
-               break;
-       }
-}
-
-/**
  * nfs_get_rpcclient - acquire an RPC client
  * @sap: pointer to socket address of RPC server
  * @salen: length of socket address
diff --git a/utils/mount/network.c b/utils/mount/network.c
index 906e20c..92bba2d 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -42,6 +42,7 @@
 #include <rpc/pmap_prot.h>
 #include <rpc/pmap_clnt.h>
 
+#include "sockaddr.h"
 #include "xcommon.h"
 #include "mount.h"
 #include "nls.h"
@@ -56,10 +57,6 @@
 #define CONNECT_TIMEOUT	(20)
 #define MOUNT_TIMEOUT	(30)
 
-#if SIZEOF_SOCKLEN_T - 0 == 0
-#define socklen_t unsigned int
-#endif
-
 extern int nfs_mount_data_version;
 extern char *progname;
 extern int verbose;
@@ -540,8 +537,8 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
 			  struct pmap *pmap, const unsigned long *versions,
 			  const unsigned int *protos)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *saddr = (struct sockaddr *)&address;
+	union nfs_sockaddr address;
+	struct sockaddr *saddr = &address.sa;
 	const unsigned long prog = pmap->pm_prog, *p_vers;
 	const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
 	const u_short port = (u_short) pmap->pm_port;
@@ -831,8 +828,8 @@ int start_statd(void)
 int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
 		      const struct pmap *pmap, const dirpath *argp)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *saddr = (struct sockaddr *)&address;
+	union nfs_sockaddr address;
+	struct sockaddr *saddr = &address.sa;
 	struct pmap mnt_pmap = *pmap;
 	struct timeval timeout = {
 		.tv_sec		= MOUNT_TIMEOUT >> 3,
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index 8e7e8a5..74224ff 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -35,6 +35,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#include "sockaddr.h"
 #include "xcommon.h"
 #include "mount.h"
 #include "nls.h"
@@ -77,12 +78,6 @@ extern char *progname;
 extern int verbose;
 extern int sloppy;
 
-union nfs_sockaddr {
-	struct sockaddr		sa;
-	struct sockaddr_in	s4;
-	struct sockaddr_in6	s6;
-};
-
 struct nfsmount_info {
 	const char		*spec,		/* server:/path */
 				*node,		/* mounted-on dir */
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 8d90d1f..66a52eb 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <grp.h>
 
+#include "sockaddr.h"
 #include "xlog.h"
 #include "nsm.h"
 #include "nfsrpc.h"


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 10/24] statd: Introduce statd version of matchhostname()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (8 preceding siblings ...)
  2010-01-14 17:30   ` [PATCH 09/24] nfs-utils: Collect socket address helpers into one location Chuck Lever
@ 2010-01-14 17:30   ` Chuck Lever
  2010-01-14 17:30   ` [PATCH 11/24] statd: add nsm_present_address() API Chuck Lever
                     ` (14 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:30 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

For the near future, statd will support IPv6 but exportfs will not.
Thus statd will need a version of matchhostname() that can deal
properly with IPv6 remotes.  To reduce the risk of breaking exportfs,
introduce a separate version of matchhostname() for statd to use while
exportfs continues to use the existing AF_INET-only implementation.

Note that statd will never send matchhostname() a hostname string
containing export wildcards, so is_hostame() is not needed in the
statd version of matchhostname().  This saves some computational
expense when comparing hostnames.

A separate statd-specific implementation of matchhostname() allows
some flexibility in the long term, as well.  We might want to enrich
the matching heuristics of our SM_NOTIFY, for example, or replace
them entirely with a heuristic that is not dependent upon DNS.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/Makefile.am |    5 +-
 utils/statd/callback.c  |    5 +-
 utils/statd/hostname.c  |  119 +++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/monitor.c   |    5 +-
 utils/statd/notlist.c   |    4 +-
 utils/statd/statd.h     |    3 +
 6 files changed, 129 insertions(+), 12 deletions(-)
 create mode 100644 utils/statd/hostname.c

diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
index d9731b7..a94c012 100644
--- a/utils/statd/Makefile.am
+++ b/utils/statd/Makefile.am
@@ -6,14 +6,13 @@ RPCPREFIX	= rpc.
 KPREFIX		= @kprefix@
 sbin_PROGRAMS	= statd sm-notify
 dist_sbin_SCRIPTS	= start-statd
-statd_SOURCES = callback.c notlist.c misc.c monitor.c \
+statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
 	        simu.c stat.c statd.c svc_run.c rmtcall.c \
 	        notlist.h statd.h system.h version.h
 sm_notify_SOURCES = sm-notify.c
 
 BUILT_SOURCES = $(GENFILES)
-statd_LDADD = ../../support/export/libexport.a \
-	      ../../support/nsm/libnsm.a \
+statd_LDADD = ../../support/nsm/libnsm.a \
 	      ../../support/nfs/libnfs.a \
 	      ../../support/misc/libmisc.a \
 	      $(LIBWRAP) $(LIBNSL)
diff --git a/utils/statd/callback.c b/utils/statd/callback.c
index 2f98aeb..b1acd15 100644
--- a/utils/statd/callback.c
+++ b/utils/statd/callback.c
@@ -13,7 +13,6 @@
 #include <arpa/inet.h>
 
 #include "rpcmisc.h"
-#include "misc.h"
 #include "statd.h"
 #include "notlist.h"
 
@@ -52,8 +51,8 @@ sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
 	 */
 	for (lp = rtnl ; lp ; lp = lp->next)
 		if (NL_STATE(lp) != argp->state &&
-		    (matchhostname(argp->mon_name, lp->dns_name) ||
-		     matchhostname(ip_addr, lp->dns_name))) {
+		    (statd_matchhostname(argp->mon_name, lp->dns_name) ||
+		     statd_matchhostname(ip_addr, lp->dns_name))) {
 			NL_STATE(lp) = argp->state;
 			call = nlist_clone(lp);
 			nlist_insert(&notify, call);
diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
new file mode 100644
index 0000000..3d10d6f
--- /dev/null
+++ b/utils/statd/hostname.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <strings.h>
+#include <netdb.h>
+
+#include "sockaddr.h"
+#include "statd.h"
+#include "xlog.h"
+
+/*
+ * Look up the hostname; report exceptional errors.  Caller must
+ * call freeaddrinfo(3) if a valid addrinfo is returned.
+ */
+__attribute_malloc__
+static struct addrinfo *
+get_addrinfo(const char *hostname, const struct addrinfo *hint)
+{
+	struct addrinfo *ai = NULL;
+	int error;
+
+	error = getaddrinfo(hostname, NULL, hint, &ai);
+	switch (error) {
+	case 0:
+		return ai;
+	case EAI_NONAME:
+		break;
+	default:
+		xlog(D_GENERAL, "%s: failed to resolve host %s: %s",
+				__func__, hostname, gai_strerror(error));
+	}
+
+	return NULL;
+}
+
+/**
+ * statd_matchhostname - check if two hostnames are equivalent
+ * @hostname1: C string containing hostname
+ * @hostname2: C string containing hostname
+ *
+ * Returns true if the hostnames are the same, the hostnames resolve
+ * to the same canonical name, or the hostnames resolve to at least
+ * one address that is the same.  False is returned if the hostnames
+ * do not match in any of these ways, if either hostname contains
+ * wildcard characters, if either hostname is a netgroup name, or
+ * if an error occurs.
+ */
+_Bool
+statd_matchhostname(const char *hostname1, const char *hostname2)
+{
+	struct addrinfo *ai1, *ai2, *results1 = NULL, *results2 = NULL;
+	struct addrinfo hint = {
+		.ai_family	= AF_UNSPEC,
+		.ai_flags	= AI_CANONNAME,
+		.ai_protocol	= (int)IPPROTO_UDP,
+	};
+	_Bool result = false;
+
+	if (strcasecmp(hostname1, hostname2) == 0) {
+		result = true;
+		goto out;
+	}
+
+	results1 = get_addrinfo(hostname1, &hint);
+	if (results1 == NULL)
+		goto out;
+	results2 = get_addrinfo(hostname2, &hint);
+	if (results2 == NULL)
+		goto out;
+
+	if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) {
+		result = true;
+		goto out;
+	}
+
+	for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next)
+		for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next)
+			if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
+				result = true;
+				break;
+			}
+
+out:
+	freeaddrinfo(results2);
+	freeaddrinfo(results1);
+
+	xlog(D_CALL, "%s: hostnames %s", __func__,
+			(result ? "matched" : "did not match"));
+	return result;
+}
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index f818b2b..51075b5 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -22,7 +22,6 @@
 #include <dirent.h>
 
 #include "rpcmisc.h"
-#include "misc.h"
 #include "nsm.h"
 #include "statd.h"
 #include "notlist.h"
@@ -145,7 +144,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	clnt = rtnl;
 
 	while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
-		if (matchhostname(NL_MY_NAME(clnt), my_name) &&
+		if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
 		    NL_MY_PROC(clnt) == id->my_proc &&
 		    NL_MY_PROG(clnt) == id->my_prog &&
 		    NL_MY_VERS(clnt) == id->my_vers &&
@@ -298,7 +297,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 	 * entry winds up in the list the way I'm currently handling them.)
 	 */
 	while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
-		if (matchhostname(NL_MY_NAME(clnt), my_name) &&
+		if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
 			NL_MY_PROC(clnt) == id->my_proc &&
 			NL_MY_PROG(clnt) == id->my_prog &&
 			NL_MY_VERS(clnt) == id->my_vers) {
diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c
index 1698c26..de7d046 100644
--- a/utils/statd/notlist.c
+++ b/utils/statd/notlist.c
@@ -17,7 +17,6 @@
 #endif
 
 #include <string.h>
-#include "misc.h"
 #include "statd.h"
 #include "notlist.h"
 
@@ -234,7 +233,8 @@ nlist_gethost(notify_list *list, char *host, int myname)
 	notify_list	*lp;
 
 	for (lp = list; lp; lp = lp->next) {
-		if (matchhostname(host, myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
+		if (statd_matchhostname(host,
+				myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
 			return lp;
 	}
 
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index 542a877..fd6a084 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -22,7 +22,8 @@
 /*
  * Function prototypes.
  */
-extern void	change_state(void);
+extern _Bool	statd_matchhostname(const char *hostname1, const char *hostname2);
+
 extern void	my_svc_run(void);
 extern void	notify_hosts(void);
 extern void	shuffle_dirs(void);


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 11/24] statd: add nsm_present_address() API
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (9 preceding siblings ...)
  2010-01-14 17:30   ` [PATCH 10/24] statd: Introduce statd version of matchhostname() Chuck Lever
@ 2010-01-14 17:30   ` Chuck Lever
  2010-01-14 17:30   ` [PATCH 12/24] statd: add IPv6 support in sm_notify_1_svc() Chuck Lever
                     ` (13 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:30 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Add an API to convert a socket address to a presentation address
string.  This is used for displaying error messages and the like.

We prefer getnameinfo(3) over inet_?to?(3) as it supports IPv6 scope
IDs.  Since statd has to continue to build correctly on systems whose
glibc does not have getnameinfo(3), an inet_?to?(3) version is also
provided.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/hostname.c |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/statd.h    |    2 ++
 2 files changed, 66 insertions(+), 0 deletions(-)

diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
index 3d10d6f..7571b6f 100644
--- a/utils/statd/hostname.c
+++ b/utils/statd/hostname.c
@@ -30,13 +30,77 @@
 
 #include <stdlib.h>
 #include <stdbool.h>
+#include <string.h>
 #include <strings.h>
 #include <netdb.h>
+#include <arpa/inet.h>
 
 #include "sockaddr.h"
 #include "statd.h"
 #include "xlog.h"
 
+/**
+ * statd_present_address - convert sockaddr to presentation address
+ * @sap: pointer to socket address to convert
+ * @buf: pointer to buffer to fill in
+ * @buflen: length of buffer
+ *
+ * Convert the passed-in sockaddr-style address to presentation format.
+ * The presentation format address is placed in @buf and is
+ * '\0'-terminated.
+ *
+ * Returns true if successful; otherwise false.
+ *
+ * getnameinfo(3) is preferred, since it can parse IPv6 scope IDs.
+ * An alternate version of statd_present_address() is available to
+ * handle older glibcs that do not have getnameinfo(3).
+ */
+#ifdef HAVE_GETNAMEINFO
+_Bool
+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+	socklen_t salen;
+	int error;
+
+	salen = nfs_sockaddr_length(sap);
+	if (salen == 0) {
+		xlog(D_GENERAL, "%s: unsupported address family",
+				__func__);
+		return false;
+	}
+
+	error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
+						NULL, 0, NI_NUMERICHOST);
+	if (error != 0) {
+		xlog(D_GENERAL, "%s: getnameinfo(3): %s",
+				__func__, gai_strerror(error));
+		return false;
+	}
+	return true;
+}
+#else	/* !HAVE_GETNAMEINFO */
+_Bool
+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+
+	if (sin->sin_family != AF_INET) {
+		xlog(D_GENERAL, "%s: unsupported address family", __func__);
+		return false;
+	}
+
+	/* ensure '\0' termination */
+	memset(buf, 0, buflen);
+
+	if (inet_ntop(AF_INET, (char *)&sin->sin_addr,
+					buf, (socklen_t)buflen) == NULL) {
+		xlog(D_GENERAL, "%s: inet_ntop(3): %m", __func__);
+		return false;
+	}
+	return true;
+}
+#endif	/* !HAVE_GETNAMEINFO */
+
 /*
  * Look up the hostname; report exceptional errors.  Caller must
  * call freeaddrinfo(3) if a valid addrinfo is returned.
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index fd6a084..c2d5956 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -23,6 +23,8 @@
  * Function prototypes.
  */
 extern _Bool	statd_matchhostname(const char *hostname1, const char *hostname2);
+extern _Bool	statd_present_address(const struct sockaddr *sap, char *buf,
+					const size_t buflen);
 
 extern void	my_svc_run(void);
 extern void	notify_hosts(void);


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 12/24] statd: add IPv6 support in sm_notify_1_svc()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (10 preceding siblings ...)
  2010-01-14 17:30   ` [PATCH 11/24] statd: add nsm_present_address() API Chuck Lever
@ 2010-01-14 17:30   ` Chuck Lever
  2010-01-14 17:30   ` [PATCH 13/24] statd: Support IPv6 is caller_is_localhost() Chuck Lever
                     ` (12 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:30 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

We have all the pieces in place, so update sm_notify_1_svc() to handle
SM_NOTIFY requests sent from IPv6 remotes.

This also eliminates a memory leak: the strdup'd memory containing the
callers' presentation address was never freed.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/callback.c |   69 +++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 62 insertions(+), 7 deletions(-)

diff --git a/utils/statd/callback.c b/utils/statd/callback.c
index b1acd15..d1cc139 100644
--- a/utils/statd/callback.c
+++ b/utils/statd/callback.c
@@ -10,7 +10,7 @@
 #include <config.h>
 #endif
 
-#include <arpa/inet.h>
+#include <netdb.h>
 
 #include "rpcmisc.h"
 #include "statd.h"
@@ -20,19 +20,69 @@
 /* notify_list *cbnl = NULL; ... never used */
 
 
-/* 
+/*
  * Services SM_NOTIFY requests.
- * Any clients that have asked us to monitor that host are put on
- * the global callback list, which is processed as soon as statd
- * returns to svc_run.
+ *
+ * When NLM uses an SM_MON request to tell statd to monitor a remote,
+ * the request contains a "mon_name" argument.  This is usually the
+ * "caller_name" argument of an NLMPROC_LOCK request.  On Linux, the
+ * NLM can send statd the remote's IP address instead of its
+ * caller_name.  The NSM protocol does not allow both the remote's
+ * caller_name and it's IP address to be sent in the same SM_MON
+ * request.
+ *
+ * The remote's caller_name is useful because it makes it simple
+ * to identify rebooting remotes by matching the "mon_name" argument
+ * they sent via an SM_NOTIFY request.
+ *
+ * The caller_name string may not be a fully qualified domain name,
+ * or even registered in the DNS database, however.  Having the
+ * remote's IP address is useful because then there is no ambiguity
+ * about where to send an SM_NOTIFY after the local system reboots.
+ *
+ * Without the actual caller_name, however, statd must use an
+ * heuristic to match an incoming SM_NOTIFY request to one of the
+ * hosts it is currently monitoring.  The incoming mon_name in an
+ * SM_NOTIFY address is converted to a list of IP addresses using
+ * DNS.  Each mon_name on statd's monitor list is also converted to
+ * an address list, and the two lists are checked to see if there is
+ * a matching address.
+ *
+ * There are some risks to this strategy:
+ *
+ *   1.  The external DNS database is not reliable.  It can change
+ *       over time, or the forward and reverse mappings could be
+ *       inconsistent.
+ *
+ *   2.  If statd's monitor list becomes substantial, finding a match
+ *       can generate a not inconsequential amount of DNS traffic.
+ *
+ *   3.  statd is a single-threaded service.  When DNS becomes slow or
+ *       unresponsive, statd also becomes slow or unresponsive.
+ *
+ *   4.  If the remote does not have a DNS entry at all (or if the
+ *       remote can resolve itself, but the local host can't resolve
+ *       the remote's hostname), the remote cannot be monitored, and
+ *       therefore NLM locking cannot be provided for that host.
+ *
+ *   5.  Local DNS resolution can produce different results for the
+ *       mon_name than the results the remote might see for the same
+ *       query, especially if the remote did not send a caller_name
+ *       or mon_name that is a fully qualified domain name.
+ *
+ *       Note that a caller_name is passed from NFS client to server,
+ *       but the client never knows what mon_name the server might use
+ *       to notify it of a reboot.  On Linux, the client extracts the
+ *       server's name from the devname it was passed by the mount
+ *       command.  This is often not a fully-qualified domain name.
  */
 void *
 sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
 {
 	notify_list    *lp, *call;
 	static char    *result = NULL;
-	struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
-	char *ip_addr = xstrdup(inet_ntoa(sin->sin_addr));
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+	char		ip_addr[INET6_ADDRSTRLEN];
 
 	xlog(D_CALL, "Received SM_NOTIFY from %s, state: %d",
 				argp->mon_name, argp->state);
@@ -44,6 +94,11 @@ sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
 		return ((void *) &result);
 	}
 
+	if (!statd_present_address(sap, ip_addr, sizeof(ip_addr))) {
+		xlog_warn("Unrecognized sender address");
+		return ((void *) &result);
+	}
+
 	/* okir change: statd doesn't remove the remote host from its
 	 * internal monitor list when receiving an SM_NOTIFY call from
 	 * it. Lockd will want to continue monitoring the remote host


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 13/24] statd: Support IPv6 is caller_is_localhost()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (11 preceding siblings ...)
  2010-01-14 17:30   ` [PATCH 12/24] statd: add IPv6 support in sm_notify_1_svc() Chuck Lever
@ 2010-01-14 17:30   ` Chuck Lever
  2010-01-14 17:30   ` [PATCH 14/24] statd: Support IPv6 in sm_simu_crash_1_svc Chuck Lever
                     ` (11 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:30 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

For the time being, statd is not going to support receiving SM_MON
calls from the local lockd via IPv6.

However, the upcalls (SM_MON, etc.) from the local lockd arrive on the
same socket that receives calls from remote peers.  Thus
caller_is_localhost() at least has to be smart enough to notice that
the caller is not AF_INET, and to display non-AF_INET addresses
appropriately.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/monitor.c |   29 ++++++++++++++++++-----------
 1 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index 51075b5..5bedb3e 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -21,6 +21,7 @@
 #include <arpa/inet.h>
 #include <dirent.h>
 
+#include "sockaddr.h"
 #include "rpcmisc.h"
 #include "nsm.h"
 #include "statd.h"
@@ -32,20 +33,26 @@ notify_list *		rtnl = NULL;	/* Run-time notify list. */
 /*
  * Reject requests from non-loopback addresses in order
  * to prevent attack described in CERT CA-99.05.
+ *
+ * Although the kernel contacts the statd service via only IPv4
+ * transports, the statd service can receive other requests, such
+ * as SM_NOTIFY, from remote peers via IPv6.
  */
-static int
+static _Bool
 caller_is_localhost(struct svc_req *rqstp)
 {
-	struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
-	struct in_addr	caller;
-
-	caller = sin->sin_addr;
-	if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
-		xlog_warn("Call to statd from non-local host %s",
-			inet_ntoa(caller));
-		return 0;
-	}
-	return 1;
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+	char buf[INET6_ADDRSTRLEN];
+
+	if (!nfs_is_v4_loopback(sap))
+		goto out_nonlocal;
+	return true;
+
+out_nonlocal:
+	if (!statd_present_address(sap, buf, sizeof(buf)))
+		buf[0] = '\0';
+	xlog_warn("SM_MON/SM_UNMON call from non-local host %s", buf);
+	return false;
 }
 
 /*


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 14/24] statd: Support IPv6 in sm_simu_crash_1_svc
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (12 preceding siblings ...)
  2010-01-14 17:30   ` [PATCH 13/24] statd: Support IPv6 is caller_is_localhost() Chuck Lever
@ 2010-01-14 17:30   ` Chuck Lever
  2010-01-14 17:31   ` [PATCH 15/24] sm-notify: Save mon_name and my_name strings Chuck Lever
                     ` (10 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:30 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Ensure that SM_SIMU_CRASH does not allow non-AF_INET callers to
bypass the localhost check.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/simu.c |   35 +++++++++++++++++++----------------
 1 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/utils/statd/simu.c b/utils/statd/simu.c
index 7df04d9..825e428 100644
--- a/utils/statd/simu.c
+++ b/utils/statd/simu.c
@@ -8,8 +8,10 @@
 #include <config.h>
 #endif
 
+#include <netdb.h>
 #include <arpa/inet.h>
 
+#include "sockaddr.h"
 #include "rpcmisc.h"
 #include "statd.h"
 #include "notlist.h"
@@ -19,30 +21,25 @@ extern void my_svc_exit (void);
 
 /*
  * Services SM_SIMU_CRASH requests.
+ *
+ * Although the kernel contacts the statd service via only IPv4
+ * transports, the statd service can receive other requests, such
+ * as SM_NOTIFY, from remote peers via IPv6.
  */
 void *
-sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
+sm_simu_crash_1_svc (__attribute__ ((unused)) void *argp, struct svc_req *rqstp)
 {
-  struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
+  struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+  char buf[INET6_ADDRSTRLEN];
   static char *result = NULL;
-  struct in_addr caller;
 
   xlog(D_CALL, "Received SM_SIMU_CRASH");
 
-  if (sin->sin_family != AF_INET) {
-    xlog_warn("Call to statd from non-AF_INET address");
-    goto failure;
-  }
+  if (!nfs_is_v4_loopback(sap))
+    goto out_nonlocal;
 
-  caller = sin->sin_addr;
-  if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
-    xlog_warn("Call to statd from non-local host %s",
-      inet_ntoa(caller));
-    goto failure;
-  }
-
-  if (ntohs(sin->sin_port) >= 1024) {
-    xlog_warn("Call to statd-simu-crash from unprivileged port");
+  if ((int)nfs_get_port(sap) >= IPPORT_RESERVED) {
+    xlog_warn("SM_SIMU_CRASH call from unprivileged port");
     goto failure;
   }
 
@@ -54,4 +51,10 @@ sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
 
  failure:
   return ((void *)&result);
+
+ out_nonlocal:
+  if (!statd_present_address(sap, buf, sizeof(buf)))
+    buf[0] = '\0';
+  xlog_warn("SM_SIMU_CRASH call from non-local host %s", buf);
+  goto failure;
 }


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 15/24] sm-notify: Save mon_name and my_name strings
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (13 preceding siblings ...)
  2010-01-14 17:30   ` [PATCH 14/24] statd: Support IPv6 in sm_simu_crash_1_svc Chuck Lever
@ 2010-01-14 17:31   ` Chuck Lever
  2010-01-14 17:31   ` [PATCH 16/24] libnsm.a: Factor atomic write code out of nsm_get_state() Chuck Lever
                     ` (9 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:31 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Currently sm-notify does not use the mon_name and my_name strings
passed to smn_get_host().  Very soon we're going to need the mon_name
and my_name strings, so add code to store those strings in struct
nsm_host, and free them when each host is forgotten.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   22 +++++++++++++++++-----
 1 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 66a52eb..70d94a8 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -44,6 +44,8 @@
 struct nsm_host {
 	struct nsm_host *	next;
 	char *			name;
+	char *			mon_name;
+	char *			my_name;
 	struct addrinfo		*ai;
 	time_t			last_used;
 	time_t			send_next;
@@ -93,7 +95,8 @@ smn_lookup(const char *name)
 
 __attribute_malloc__
 static struct nsm_host *
-smn_alloc_host(const char *hostname, const time_t timestamp)
+smn_alloc_host(const char *hostname, const char *mon_name,
+		const char *my_name, const time_t timestamp)
 {
 	struct nsm_host	*host;
 
@@ -102,7 +105,14 @@ smn_alloc_host(const char *hostname, const time_t timestamp)
 		goto out_nomem;
 
 	host->name = strdup(hostname);
-	if (host->name == NULL) {
+	host->mon_name = strdup(mon_name);
+	host->my_name = strdup(my_name);
+	if (host->name == NULL ||
+	    host->mon_name == NULL ||
+	    host->my_name == NULL) {
+		free(host->my_name);
+		free(host->mon_name);
+		free(host->name);
 		free(host);
 		goto out_nomem;
 	}
@@ -124,6 +134,8 @@ static void smn_forget_host(struct nsm_host *host)
 
 	nsm_delete_notified_host(host->name);
 
+	free(host->my_name);
+	free(host->mon_name);
 	free(host->name);
 	if (host->ai)
 		freeaddrinfo(host->ai);
@@ -134,12 +146,12 @@ static void smn_forget_host(struct nsm_host *host)
 static unsigned int
 smn_get_host(const char *hostname,
 		__attribute__ ((unused)) const struct sockaddr *sap,
-		__attribute__ ((unused)) const struct mon *m,
-		const time_t timestamp)
+		const struct mon *m, const time_t timestamp)
 {
 	struct nsm_host	*host;
 
-	host = smn_alloc_host(hostname, timestamp);
+	host = smn_alloc_host(hostname,
+		m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
 	if (host == NULL)
 		return 0;
 


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 16/24] libnsm.a: Factor atomic write code out of nsm_get_state()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (14 preceding siblings ...)
  2010-01-14 17:31   ` [PATCH 15/24] sm-notify: Save mon_name and my_name strings Chuck Lever
@ 2010-01-14 17:31   ` Chuck Lever
  2010-01-14 17:31   ` [PATCH 17/24] libnsm.a: Add support for multiple lines in monitor record files Chuck Lever
                     ` (8 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:31 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

We're about to use the same logic (mktemp, write, rename) for
other new purposes, so pull it out into its own function.

This change also addresses a latent bug: O_TRUNC is now used when
creating the temporary file.  This eliminates the possibility of
getting stale data in the temp file, if somehow a previous "atomic
write" was interrupted and didn't remove the temporary file.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/nsm/file.c |  134 +++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 89 insertions(+), 45 deletions(-)

diff --git a/support/nsm/file.c b/support/nsm/file.c
index fc3241a..10769d9 100644
--- a/support/nsm/file.c
+++ b/support/nsm/file.c
@@ -195,6 +195,94 @@ nsm_make_pathname(const char *directory)
 	return path;
 }
 
+/*
+ * Returns a dynamically allocated, '\0'-terminated buffer
+ * containing an appropriate pathname, or NULL if an error
+ * occurs.  Caller must free the returned result with free(3).
+ */
+__attribute_malloc__
+static char *
+nsm_make_temp_pathname(const char *pathname)
+{
+	size_t size;
+	char *path;
+	int len;
+
+	size = strlen(pathname) + sizeof(".new") + 2;
+	if (size > PATH_MAX)
+		return NULL;
+
+	path = malloc(size);
+	if (path == NULL)
+		return NULL;
+
+	len = snprintf(path, size, "%s.new", pathname);
+	if (error_check(len, size)) {
+		free(path);
+		return NULL;
+	}
+
+	return path;
+}
+
+/*
+ * Use "mktemp, write, rename" to update the contents of a file atomically.
+ *
+ * Returns true if completely successful, or false if some error occurred.
+ */
+static _Bool
+nsm_atomic_write(const char *path, const void *buf, const size_t buflen)
+{
+	_Bool result = false;
+	ssize_t len;
+	char *temp;
+	int fd;
+
+	temp = nsm_make_temp_pathname(path);
+	if (temp == NULL) {
+		xlog(L_ERROR, "Failed to create new path for %s", path);
+		goto out;
+	}
+
+	fd = open(temp, O_CREAT | O_TRUNC | O_SYNC | O_WRONLY, 0644);
+	if (fd == -1) {
+		xlog(L_ERROR, "Failed to create %s: %m", temp);
+		goto out;
+	}
+
+	len = write(fd, buf, buflen);
+	if (exact_error_check(len, buflen)) {
+		xlog(L_ERROR, "Failed to write %s: %m", temp);
+		(void)close(fd);
+		(void)unlink(temp);
+		goto out;
+	}
+
+	if (close(fd) == -1) {
+		xlog(L_ERROR, "Failed to close %s: %m", temp);
+		(void)unlink(temp);
+		goto out;
+	}
+
+	if (rename(temp, path) == -1) {
+		xlog(L_ERROR, "Failed to rename %s -> %s: %m",
+				temp, path);
+		(void)unlink(temp);
+		goto out;
+	}
+
+	/* Ostensibly, a sync(2) is not needed here because
+	 * open(O_CREAT), write(O_SYNC), and rename(2) are
+	 * already synchronous with persistent storage, for
+	 * any file system we care about. */
+
+	result = true;
+
+out:
+	free(temp);
+	return result;
+}
+
 /**
  * nsm_setup_pathnames - set up pathname
  * @progname: C string containing name of program, for error messages
@@ -326,7 +414,6 @@ nsm_get_state(_Bool update)
 	int fd, state = 0;
 	ssize_t result;
 	char *path = NULL;
-	char *newpath = NULL;
 
 	path = nsm_make_pathname(NSM_STATE_FILE);
 	if (path == NULL) {
@@ -365,54 +452,11 @@ update:
 
 	if (update) {
 		state += 2;
-
-		newpath = nsm_make_pathname(NSM_STATE_FILE ".new");
-		if (newpath == NULL) {
-			xlog(L_ERROR,
-			  "Failed to create path for " NSM_STATE_FILE ".new");
-			state = 0;
-			goto out;
-		}
-
-		fd = open(newpath, O_CREAT | O_SYNC | O_WRONLY, 0644);
-		if (fd == -1) {
-			xlog(L_ERROR, "Failed to create %s: %m", newpath);
-			state = 0;
-			goto out;
-		}
-
-		result = write(fd, &state, sizeof(state));
-		if (exact_error_check(result, sizeof(state))) {
-			xlog(L_ERROR, "Failed to write %s: %m", newpath);
-			(void)close(fd);
-			(void)unlink(newpath);
-			state = 0;
-			goto out;
-		}
-
-		if (close(fd) == -1) {
-			xlog(L_ERROR, "Failed to close %s: %m", newpath);
-			(void)unlink(newpath);
-			state = 0;
-			goto out;
-		}
-
-		if (rename(newpath, path) == -1) {
-			xlog(L_ERROR, "Failed to rename %s -> %s: %m",
-					newpath, path);
-			(void)unlink(newpath);
+		if (!nsm_atomic_write(path, &state, sizeof(state)))
 			state = 0;
-			goto out;
-		}
-
-		/* Ostensibly, a sync(2) is not needed here because
-		 * open(O_CREAT), write(O_SYNC), and rename(2) are
-		 * already synchronous with persistent storage, for
-		 * any file system we care about. */
 	}
 
 out:
-	free(newpath);
 	free(path);
 	return state;
 }


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 17/24] libnsm.a: Add support for multiple lines in monitor record files
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (15 preceding siblings ...)
  2010-01-14 17:31   ` [PATCH 16/24] libnsm.a: Factor atomic write code out of nsm_get_state() Chuck Lever
@ 2010-01-14 17:31   ` Chuck Lever
  2010-01-14 17:31   ` [PATCH 18/24] statd: Add API to canonicalize mon_names Chuck Lever
                     ` (7 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:31 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

To support IPv6, statd must support multi-homed remote peers.  For our
purposes, "multi-homed peer" means that more than one unique IP
address maps to the one canonical host name for that peer.

An SM_MON request from the local lockd has a "mon_name" argument that
statd reverse maps to a canonical hostname (ie the A record for that
host).  statd assumes the canonical hostname is unique enough that
it stores the callback data for this mon_name in a file named after
that canonical hostname.

Because lockd can't distinguish between two unique IP addresses that
may be from the same physical host, the kernel can hand statd a
mon_name that maps to the same canonical hostname as some previous
mon_name.  So that the kernel can keep this instance of the mon_name
unique, it creates a fresh priv cookie for each new address.

Note that a mon_name can be a presentation address string, or the
caller_name string sent in each NLMPROC_LOCK request.  There's
nothing that requires the caller_name to be a fully-qualified
hostname, thus it's uniqueness is not guaranteed.  The current
design of statd assumes that canonical hostnames will be unique
enough.

When a mon_name for a fresh SM_MON request maps to the same canonical
hostname as an existing monitored peer, but the priv cookie is new,
statd will try to write the information for the fresh request into an
existing monitor record file, wiping out the contents of the file.
This is because the mon_name/cookie combination won't match any record
statd already has.

Currently, statd doesn't check if a record file already exists before
writing into it.  statd's logic assumes that the svc routine has
already checked that no matching record exists in the in-core monitor
list.  And, it doesn't use O_EXCL when opening the record file.  Not
only is the old data in that file wiped out, but statd's in-core
monitor list will no longer match what's in the on-disk monitor list.

Note that IPv6 isn't needed to exercise multi-homed peer support.
Any IPv4 peer that has multiple addresses that map to its canonical
hostname will trigger this behavior.  However, this scenario will
become quite common when all hosts on a network automatically get both
an IPv4 address and an IPv6 address.

I can think of a few ways to address this:

1.  Replace the current on-disk format with a database that has a
uniqueness constraint on the monitor records

2.  Create a new file naming scheme; eg. one that uses a truly
unique name such as a hash generated from the mon_name, my_name, and
priv cookie

3.  Support multiple lines in each monitor record file

Since statd's on-disk format constitutes a formal API, options 1 and 2
are right out.  This patch implements option 3.  There are two parts:
adding a new line to an existing file; and deleting a line from a file
with more than one line.  Interestingly, the existing code already
supports reading more than one line from these files, so we don't need
to add extra code here to do that.

One file may contain a line for every unique mon_name / priv cookie
where the mon_name reverse maps to the same canonical hostname.  We
use the atomic write facility added by a previous patch to ensure the
on-disk monitor record list is updated atomically.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/nsm.h   |    6 +-
 support/nsm/file.c      |  161 ++++++++++++++++++++++++++++++++++++++++++++---
 utils/statd/monitor.c   |    6 +-
 utils/statd/sm-notify.c |    5 +
 4 files changed, 162 insertions(+), 16 deletions(-)

diff --git a/support/include/nsm.h b/support/include/nsm.h
index ce9e294..fb4d823 100644
--- a/support/include/nsm.h
+++ b/support/include/nsm.h
@@ -58,8 +58,10 @@ extern unsigned int
 
 extern _Bool	nsm_insert_monitored_host(const char *hostname,
 			const struct sockaddr *sap, const struct mon *m);
-extern void	nsm_delete_monitored_host(const char *hostname);
-extern void	nsm_delete_notified_host(const char *hostname);
+extern void	nsm_delete_monitored_host(const char *hostname,
+			const char *mon_name, const char *my_name);
+extern void	nsm_delete_notified_host(const char *hostname,
+			const char *mon_name, const char *my_name);
 extern size_t	nsm_priv_to_hex(const char *priv, char *buf,
 				const size_t buflen);
 
diff --git a/support/nsm/file.c b/support/nsm/file.c
index 10769d9..8796705 100644
--- a/support/nsm/file.c
+++ b/support/nsm/file.c
@@ -625,6 +625,56 @@ nsm_create_monitor_record(char *buf, const size_t buflen,
 	return buflen - remaining;
 }
 
+static _Bool
+nsm_append_monitored_host(const char *path, const char *line)
+{
+	_Bool result = false;
+	char *buf = NULL;
+	struct stat stb;
+	size_t buflen;
+	ssize_t len;
+	int fd;
+
+	if (stat(path, &stb) == -1) {
+		xlog(L_ERROR, "Failed to insert: "
+			"could not stat original file %s: %m", path);
+		goto out;
+	}
+	buflen = (size_t)stb.st_size + strlen(line);
+
+	buf = malloc(buflen + 1);
+	if (buf == NULL) {
+		xlog(L_ERROR, "Failed to insert: no memory");
+		goto out;
+	}
+	memset(buf, 0, buflen + 1);
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1) {
+		xlog(L_ERROR, "Failed to insert: "
+			"could not open original file %s: %m", path);
+		goto out;
+	}
+
+	len = read(fd, buf, (size_t)stb.st_size);
+	if (exact_error_check(len, (size_t)stb.st_size)) {
+		xlog(L_ERROR, "Failed to insert: "
+			"could not read original file %s: %m", path);
+		(void)close(fd);
+		goto out;
+	}
+	(void)close(fd);
+
+	strcat(buf, line);
+
+	if (nsm_atomic_write(path, buf, buflen))
+		result = true;
+
+out:
+	free(buf);
+	return result;
+}
+
 /**
  * nsm_insert_monitored_host - write callback data for one host to disk
  * @hostname: C string containing a hostname
@@ -657,9 +707,18 @@ nsm_insert_monitored_host(const char *hostname, const struct sockaddr *sap,
 		goto out;
 	}
 
-	fd = open(path, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
+	/*
+	 * If exclusive create fails, we're adding a new line to an
+	 * existing file.
+	 */
+	fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_SYNC, S_IRUSR | S_IWUSR);
 	if (fd == -1) {
-		xlog(L_ERROR, "Failed to insert: creating %s: %m", path);
+		if (errno != EEXIST) {
+			xlog(L_ERROR, "Failed to insert: creating %s: %m", path);
+			goto out;
+		}
+
+		result = nsm_append_monitored_host(path, buf);
 		goto out;
 	}
 	result = true;
@@ -848,9 +907,15 @@ nsm_load_notify_list(nsm_populate_t func)
 }
 
 static void
-nsm_delete_host(const char *directory, const char *hostname)
+nsm_delete_host(const char *directory, const char *hostname,
+		const char *mon_name, const char *my_name)
 {
-	char *path;
+	char line[LINELEN + 1 + SM_MAXSTRLEN + 2];
+	char *outbuf = NULL;
+	struct stat stb;
+	char *path, *next;
+	size_t remaining;
+	FILE *f;
 
 	path = nsm_make_record_pathname(directory, hostname);
 	if (path == NULL) {
@@ -858,30 +923,106 @@ nsm_delete_host(const char *directory, const char *hostname)
 		return;
 	}
 
-	if (unlink(path) == -1)
-		xlog(L_ERROR, "Failed to unlink %s: %m", path);
+	if (stat(path, &stb) == -1) {
+		xlog(L_ERROR, "Failed to delete: "
+			"could not stat original file %s: %m", path);
+		goto out;
+	}
+	remaining = (size_t)stb.st_size + 1;
 
+	outbuf = malloc(remaining);
+	if (outbuf == NULL) {
+		xlog(L_ERROR, "Failed to delete: no memory");
+		goto out;
+	}
+
+	f = fopen(path, "r");
+	if (f == NULL) {
+		xlog(L_ERROR, "Failed to delete: "
+			"could not open original file %s: %m", path);
+		goto out;
+	}
+
+	/*
+	 * Walk the records in the file, and copy the non-matching
+	 * ones to our output buffer.
+	 */
+	next = outbuf;
+	while (fgets(line, (int)sizeof(line), f) != NULL) {
+		struct sockaddr_in sin;
+		struct mon m;
+		size_t len;
+
+		if (!nsm_parse_line(line, &sin, &m)) {
+			xlog(L_ERROR, "Failed to delete: "
+				"could not parse original file %s", path);
+			(void)fclose(f);
+			goto out;
+		}
+
+		if (strcmp(mon_name, m.mon_id.mon_name) == 0 &&
+			 strcmp(my_name, m.mon_id.my_id.my_name) == 0)
+			continue;
+
+		/* nsm_parse_line destroys the contents of line[], so
+		 * reconstruct the copy in our output buffer. */
+		len = nsm_create_monitor_record(next, remaining,
+					(struct sockaddr *)(char *)&sin, &m);
+		if (len == 0) {
+			xlog(L_ERROR, "Failed to delete: "
+				"could not construct output record");
+			(void)fclose(f);
+			goto out;
+		}
+		next += len;
+		remaining -= len;
+	}
+
+	(void)fclose(f);
+
+	/*
+	 * If nothing was copied when we're done, then unlink the file.
+	 * Otherwise, atomically update the contents of the file.
+	 */
+	if (next != outbuf) {
+		if (!nsm_atomic_write(path, outbuf, strlen(outbuf)))
+			xlog(L_ERROR, "Failed to delete: "
+				"could not write new file %s: %m", path);
+	} else {
+		if (unlink(path) == -1)
+			xlog(L_ERROR, "Failed to delete: "
+				"could not unlink file %s: %m", path);
+	}
+
+out:
+	free(outbuf);
 	free(path);
 }
 
 /**
  * nsm_delete_monitored_host - delete on-disk record for monitored host
  * @hostname: '\0'-terminated C string containing hostname of record to delete
+ * @mon_name: '\0'-terminated C string containing monname of record to delete
+ * @my_name: '\0'-terminated C string containing myname of record to delete
  *
  */
 void
-nsm_delete_monitored_host(const char *hostname)
+nsm_delete_monitored_host(const char *hostname, const char *mon_name,
+		const char *my_name)
 {
-	nsm_delete_host(NSM_MONITOR_DIR, hostname);
+	nsm_delete_host(NSM_MONITOR_DIR, hostname, mon_name, my_name);
 }
 
 /**
  * nsm_delete_notified_host - delete on-disk host record after notification
  * @hostname: '\0'-terminated C string containing hostname of record to delete
+ * @mon_name: '\0'-terminated C string containing monname of record to delete
+ * @my_name: '\0'-terminated C string containing myname of record to delete
  *
  */
 void
-nsm_delete_notified_host(const char *hostname)
+nsm_delete_notified_host(const char *hostname, const char *mon_name,
+		const char *my_name)
 {
-	nsm_delete_host(NSM_NOTIFY_DIR, hostname);
+	nsm_delete_host(NSM_NOTIFY_DIR, hostname, mon_name, my_name);
 }
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index 5bedb3e..fb32196 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -315,7 +315,8 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 			/* PRC: do the HA callout: */
 			ha_callout("del-client", mon_name, my_name, -1);
 
-			nsm_delete_monitored_host(clnt->dns_name);
+			nsm_delete_monitored_host(clnt->dns_name,
+							mon_name, my_name);
 			nlist_free(&rtnl, clnt);
 
 			return (&result);
@@ -369,7 +370,8 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
 			temp = NL_NEXT(clnt);
 			/* PRC: do the HA callout: */
 			ha_callout("del-client", mon_name, my_name, -1);
-			nsm_delete_monitored_host(clnt->dns_name);
+			nsm_delete_monitored_host(clnt->dns_name,
+							mon_name, my_name);
 			nlist_free(&rtnl, clnt);
 			++count;
 			clnt = temp;
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 70d94a8..3259a3e 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -130,9 +130,10 @@ out_nomem:
 
 static void smn_forget_host(struct nsm_host *host)
 {
-	xlog(D_CALL, "Removing %s from notify list", host->name);
+	xlog(D_CALL, "Removing %s (%s, %s) from notify list",
+			host->name, host->mon_name, host->my_name);
 
-	nsm_delete_notified_host(host->name);
+	nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
 
 	free(host->my_name);
 	free(host->mon_name);


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 18/24] statd: Add API to canonicalize mon_names
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (16 preceding siblings ...)
  2010-01-14 17:31   ` [PATCH 17/24] libnsm.a: Add support for multiple lines in monitor record files Chuck Lever
@ 2010-01-14 17:31   ` Chuck Lever
  2010-01-14 17:31   ` [PATCH 19/24] statd: Support IPv6 in sm_mon_1_svc() Chuck Lever
                     ` (6 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:31 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Provide a shared function to generate canonical names that statd
uses to index its on-disk monitor list.  This function can resolve
DNS hostnames, and IPv4 and IPv6 presentation addresses.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/hostname.c |  101 ++++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/statd.h    |    2 +
 2 files changed, 103 insertions(+), 0 deletions(-)

diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
index 7571b6f..7d704cc 100644
--- a/utils/statd/hostname.c
+++ b/utils/statd/hostname.c
@@ -39,6 +39,10 @@
 #include "statd.h"
 #include "xlog.h"
 
+#ifndef HAVE_DECL_AI_ADDRCONFIG
+#define AI_ADDRCONFIG	0
+#endif
+
 /**
  * statd_present_address - convert sockaddr to presentation address
  * @sap: pointer to socket address to convert
@@ -126,6 +130,103 @@ get_addrinfo(const char *hostname, const struct addrinfo *hint)
 	return NULL;
 }
 
+#ifdef HAVE_GETNAMEINFO
+static _Bool
+get_nameinfo(const struct sockaddr *sap, const socklen_t salen,
+		/*@out@*/ char *buf, const socklen_t buflen)
+{
+	int error;
+
+	error = getnameinfo(sap, salen, buf, buflen, NULL, 0, NI_NAMEREQD);
+	if (error != 0) {
+		xlog(D_GENERAL, "%s: failed to resolve address: %s",
+				__func__, gai_strerror(error));
+		return false;
+	}
+
+	return true;
+}
+#else	/* !HAVE_GETNAMEINFO */
+static _Bool
+get_nameinfo(const struct sockaddr *sap,
+		__attribute__ ((unused)) const socklen_t salen,
+		/*@out@*/ char *buf, socklen_t buflen)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)(char *)sap;
+	struct hostent *hp;
+
+	if (sin->sin_family != AF_INET) {
+		xlog(D_GENERAL, "%s: unknown address family: %d",
+				sin->sin_family);
+		return false;
+	}
+
+	hp = gethostbyaddr((const char *)&(sin->sin_addr.s_addr),
+				sizeof(struct in_addr), AF_INET);
+	if (hp == NULL) {
+		xlog(D_GENERAL, "%s: failed to resolve address: %m", __func__);
+		return false;
+	}
+
+	strncpy(buf, hp->h_name, (size_t)buflen);
+	return true;
+}
+#endif	/* !HAVE_GETNAMEINFO */
+
+/**
+ * statd_canonical_name - choose file name for monitor record files
+ * @hostname: C string containing hostname or presentation address
+ *
+ * Returns a '\0'-terminated ASCII string containing a fully qualified
+ * canonical hostname, or NULL if @hostname does not have a reverse
+ * mapping.  Caller must free the result with free(3).
+ *
+ * Incoming hostnames are looked up to determine the canonical hostname,
+ * and incoming presentation addresses are converted to canonical
+ * hostnames.
+ *
+ * We won't monitor peers that don't have a reverse map.  The canonical
+ * name gives us a key for our monitor list.
+ */
+__attribute_malloc__
+char *
+statd_canonical_name(const char *hostname)
+{
+	struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+		.ai_family	= AF_UNSPEC,
+#else	/* !IPV6_SUPPORTED */
+		.ai_family	= AF_INET,
+#endif	/* !IPV6_SUPPORTED */
+		.ai_flags	= AI_NUMERICHOST,
+		.ai_protocol	= (int)IPPROTO_UDP,
+	};
+	char buf[NI_MAXHOST];
+	struct addrinfo *ai;
+
+	ai = get_addrinfo(hostname, &hint);
+	if (ai != NULL) {
+		/* @hostname was a presentation address */
+		_Bool result;
+		result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
+					buf, (socklen_t)sizeof(buf));
+		freeaddrinfo(ai);
+		if (!result)
+			return NULL;
+		return strdup(buf);
+	}
+
+	/* @hostname was a hostname */
+	hint.ai_flags = AI_CANONNAME;
+	ai = get_addrinfo(hostname, &hint);
+	if (ai == NULL)
+		return NULL;
+	strcpy(buf, ai->ai_canonname);
+	freeaddrinfo(ai);
+
+	return strdup(buf);
+}
+
 /**
  * statd_matchhostname - check if two hostnames are equivalent
  * @hostname1: C string containing hostname
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index c2d5956..e89e666 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -25,6 +25,8 @@
 extern _Bool	statd_matchhostname(const char *hostname1, const char *hostname2);
 extern _Bool	statd_present_address(const struct sockaddr *sap, char *buf,
 					const size_t buflen);
+__attribute_malloc__
+extern char *	statd_canonical_name(const char *hostname);
 
 extern void	my_svc_run(void);
 extern void	notify_hosts(void);


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 19/24] statd: Support IPv6 in sm_mon_1_svc()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (17 preceding siblings ...)
  2010-01-14 17:31   ` [PATCH 18/24] statd: Add API to canonicalize mon_names Chuck Lever
@ 2010-01-14 17:31   ` Chuck Lever
  2010-01-14 17:31   ` [PATCH 20/24] statd: Support IPv6 in sm_stat_1_svc() Chuck Lever
                     ` (5 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:31 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Replace deprecated gethostbyname(3) and gethostbyaddr(3) calls in
monitor.c, and address a couple of memory leaks.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/monitor.c |   22 +++++++++-------------
 1 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index fb32196..11afe08 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -71,8 +71,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		.sin_family		= AF_INET,
 		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
 	};
-	char		*dnsname;
-	struct hostent	*hostinfo = NULL;
+	char *dnsname = NULL;
 
 	xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name);
 
@@ -114,9 +113,6 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		     "or starting '.': %s", mon_name);
 		xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
 		goto failure;
-	} else if ((hostinfo = gethostbyname(mon_name)) == NULL) {
-		xlog_warn("gethostbyname error for %s", mon_name);
-		goto failure;
 	}
 
 	/* my_name must not have white space */
@@ -129,15 +125,13 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	 * Now choose a hostname to use for matching.  We cannot
 	 * really trust much in the incoming NOTIFY, so to make
 	 * sure that multi-homed hosts work nicely, we get an
-	 * FQDN now, and use that for matching
+	 * FQDN now, and use that for matching.
 	 */
-	hostinfo = gethostbyaddr(hostinfo->h_addr,
-				 hostinfo->h_length,
-				 hostinfo->h_addrtype);
-	if (hostinfo)
-		dnsname = xstrdup(hostinfo->h_name);
-	else
-		dnsname = xstrdup(my_name);
+	dnsname = statd_canonical_name(mon_name);
+	if (dnsname == NULL) {
+		xlog(L_WARNING, "No canonical hostname found for %s", mon_name);
+		goto failure;
+	}
 
 	/* Now check to see if this is a duplicate, and warn if so.
 	 * I will also return STAT_FAIL. (I *think* this is how I should
@@ -163,6 +157,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 				mon_name, my_name);
 
 			/* But we'll let you pass anyway. */
+			free(dnsname);
 			goto success;
 		}
 		clnt = NL_NEXT(clnt);
@@ -173,6 +168,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	 * doesn't fail.  (I should probably fix this assumption.)
 	 */
 	if (!(clnt = nlist_new(my_name, mon_name, 0))) {
+		free(dnsname);
 		xlog_warn("out of memory");
 		goto failure;
 	}


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 20/24] statd: Support IPv6 in sm_stat_1_svc()
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (18 preceding siblings ...)
  2010-01-14 17:31   ` [PATCH 19/24] statd: Support IPv6 in sm_mon_1_svc() Chuck Lever
@ 2010-01-14 17:31   ` Chuck Lever
  2010-01-14 17:31   ` [PATCH 21/24] statd: Remove NL_ADDR() macro Chuck Lever
                     ` (4 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:31 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

SM_STAT is usually not used by most contemporary NSM implementations,
but for consistency, it gets the same treatment as sm_mon_1_svc(),
since both should use the same logic to determine whether a mon_name
is able to be monitored.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/stat.c |   13 ++++++++-----
 1 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/utils/statd/stat.c b/utils/statd/stat.c
index 477f632..8d8b65e 100644
--- a/utils/statd/stat.c
+++ b/utils/statd/stat.c
@@ -12,7 +12,7 @@
 #include <netdb.h>
 #include "statd.h"
 
-/* 
+/*
  * Services SM_STAT requests.
  *
  * According the the X/Open spec's on this procedure: "Implementations
@@ -37,20 +37,23 @@
  *      other way to get it.
  *   3/ That's what we always did in the past.
  */
-struct sm_stat_res * 
-sm_stat_1_svc (struct sm_name *argp, struct svc_req *rqstp)
+struct sm_stat_res *
+sm_stat_1_svc(struct sm_name *argp,
+		__attribute__ ((unused)) struct svc_req *rqstp)
 {
   static sm_stat_res result;
+  char *name;
 
   xlog(D_CALL, "Received SM_STAT from %s", argp->mon_name);
 
-  if (gethostbyname (argp->mon_name) == NULL) {
-    xlog_warn ("gethostbyname error for %s", argp->mon_name);
+  name = statd_canonical_name(argp->mon_name);
+  if (name == NULL) {
     result.res_stat = STAT_FAIL;
     xlog (D_GENERAL, "STAT_FAIL for %s", argp->mon_name);
   } else {
     result.res_stat = STAT_SUCC;
     xlog (D_GENERAL, "STAT_SUCC for %s", argp->mon_name);
+    free(name);
   }
   result.state = MY_STATE;
   return(&result);


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 21/24] statd: Remove NL_ADDR() macro
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (19 preceding siblings ...)
  2010-01-14 17:31   ` [PATCH 20/24] statd: Support IPv6 in sm_stat_1_svc() Chuck Lever
@ 2010-01-14 17:31   ` Chuck Lever
  2010-01-14 17:32   ` [PATCH 22/24] libnsm.a: retain CAP_NET_BIND when dropping privileges Chuck Lever
                     ` (3 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:31 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Clean up: The contents of NL_ADDR are fixed: they are always the IPv4
loopback address.  Some time ago, the use of NL_ADDR() was stubbed out
of the NLM downcall forward path, replaced with a constant IPv4
loopback address.

Stub it out of the reply path as well, and then remove NL_ADDR
entirely.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/monitor.c |    6 ++----
 utils/statd/notlist.c |    1 -
 utils/statd/notlist.h |    2 --
 utils/statd/rmtcall.c |   26 +++++++++++++-------------
 4 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index 11afe08..325dfd3 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -173,7 +173,6 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		goto failure;
 	}
 
-	NL_ADDR(clnt) = my_addr.sin_addr;
 	NL_MY_PROG(clnt) = id->my_prog;
 	NL_MY_VERS(clnt) = id->my_vers;
 	NL_MY_PROC(clnt) = id->my_proc;
@@ -214,11 +213,11 @@ failure:
 }
 
 static unsigned int
-load_one_host(const char *hostname, const struct sockaddr *sap,
+load_one_host(const char *hostname,
+		__attribute__ ((unused)) const struct sockaddr *sap,
 		const struct mon *m,
 		__attribute__ ((unused)) const time_t timestamp)
 {
-	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
 	notify_list *clnt;
 
 	clnt = nlist_new(m->mon_id.my_id.my_name,
@@ -235,7 +234,6 @@ load_one_host(const char *hostname, const struct sockaddr *sap,
 	xlog(D_GENERAL, "Adding record for %s to the monitor list...",
 			hostname);
 
-	NL_ADDR(clnt) = sin->sin_addr;
 	NL_MY_PROG(clnt) = m->mon_id.my_id.my_prog;
 	NL_MY_VERS(clnt) = m->mon_id.my_id.my_vers;
 	NL_MY_PROC(clnt) = m->mon_id.my_id.my_proc;
diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c
index de7d046..0341c15 100644
--- a/utils/statd/notlist.c
+++ b/utils/statd/notlist.c
@@ -189,7 +189,6 @@ nlist_clone(notify_list *entry)
 	NL_MY_PROG(new) = NL_MY_PROG(entry);
 	NL_MY_VERS(new) = NL_MY_VERS(entry);
 	NL_MY_PROC(new) = NL_MY_PROC(entry);
-	NL_ADDR(new)    = NL_ADDR(entry);
 	memcpy(NL_PRIV(new), NL_PRIV(entry), SM_PRIV_SIZE);
 
 	return new;
diff --git a/utils/statd/notlist.h b/utils/statd/notlist.h
index f915ae1..6ed0da8 100644
--- a/utils/statd/notlist.h
+++ b/utils/statd/notlist.h
@@ -12,7 +12,6 @@
  */
 struct notify_list {
   mon			mon;	/* Big honkin' NSM structure. */
-  struct in_addr	addr;	/* IP address for callback. */
   in_port_t		port;	/* port number for callback */
   short int		times;	/* Counter used for various things. */
   int			state;	/* For storing notified state for callbacks. */
@@ -53,7 +52,6 @@ extern notify_list *	nlist_gethost(notify_list *, char *, int);
 #define NL_FIRST	NL_NEXT
 #define NL_PREV(L)	((L)->prev)
 #define NL_DATA(L)	((L)->mon)
-#define NL_ADDR(L)	((L)->addr)
 #define NL_STATE(L)	((L)->state)
 #define NL_TIMES(L)	((L)->times)
 #define NL_MON_ID(L)	(NL_DATA((L)).mon_id)
diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
index 1a97684..0e52fe2 100644
--- a/utils/statd/rmtcall.c
+++ b/utils/statd/rmtcall.c
@@ -125,6 +125,15 @@ recv_rply(u_long *portp)
 	xid = nsm_parse_reply(&xdr);
 	if (xid == 0)
 		goto done;
+	if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+		struct in_addr addr = sin.sin_addr;
+		char buf[INET_ADDRSTRLEN];
+
+		xlog_warn("%s: Unrecognized reply from %s", __func__,
+				inet_ntop(AF_INET, &addr, buf,
+						(socklen_t)sizeof(buf)));
+		goto done;
+	}
 
 	for (lp = notify; lp != NULL; lp = lp->next) {
 		/* LH - this was a bug... it should have been checking
@@ -132,15 +141,6 @@ recv_rply(u_long *portp)
 		 * not the static, internal xid */
 		if (lp->xid != xid)
 			continue;
-		if (lp->addr.s_addr != sin.sin_addr.s_addr) {
-			char addr [18];
-			strncpy (addr, inet_ntoa(lp->addr),
-				 sizeof (addr) - 1);
-			addr [sizeof (addr) - 1] = '\0';
-			xlog_warn("%s: address mismatch: "
-				"expected %s, got %s", __func__,
-				addr, inet_ntoa(sin.sin_addr));
-		}
 		if (lp->port == 0)
 			*portp = nsm_recv_getport(&xdr);
 		break;
@@ -160,8 +160,8 @@ process_entry(notify_list *lp)
 	struct sockaddr_in	sin;
 
 	if (NL_TIMES(lp) == 0) {
-		xlog(D_GENERAL, "%s: Cannot notify %s, giving up",
-				__func__, inet_ntoa(NL_ADDR(lp)));
+		xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
+				__func__);
 		return 0;
 	}
 
@@ -226,8 +226,8 @@ process_reply(FD_SET_TYPE *rfds)
 			nlist_insert_timer(&notify, lp);
 			return 1;
 		}
-		xlog_warn("%s: [%s] service %d not registered",
-			__func__, inet_ntoa(lp->addr), NL_MY_PROG(lp));
+		xlog_warn("%s: service %d not registered on localhost",
+			__func__, NL_MY_PROG(lp));
 	} else {
 		xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded",
 			__func__, NL_MY_NAME(lp), NL_MON_NAME(lp));


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 22/24] libnsm.a: retain CAP_NET_BIND when dropping privileges
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (20 preceding siblings ...)
  2010-01-14 17:31   ` [PATCH 21/24] statd: Remove NL_ADDR() macro Chuck Lever
@ 2010-01-14 17:32   ` Chuck Lever
  2010-01-14 17:32   ` [PATCH 23/24] statd: Support TI-RPC statd listener Chuck Lever
                     ` (2 subsequent siblings)
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:32 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

I'm about to switch the order of listener creation and dropping root
privileges.  rpc.statd will drop privileges first, then create its
listeners.  The reason for the new ordering is explained in a
subsequent patch.

However, for non-TI-RPC builds, rpc_init() needs to use a privileged
port to do pmap registrations.  For both TI-RPC and non-TI-RPC builds,
CAP_NET_BIND is required in case the admin requests a privileged
listener port on the statd command line.

So that these requirements are met, nsm_drop_privileges() will now
retain CAP_NET_BIND while dropping root.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 aclocal/libcap.m4       |   15 +++++++++++++++
 configure.ac            |    3 +++
 support/nsm/file.c      |   41 ++++++++++++++++++++++++++++++++++++++++-
 utils/statd/Makefile.am |    4 ++--
 4 files changed, 60 insertions(+), 3 deletions(-)
 create mode 100644 aclocal/libcap.m4

diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4
new file mode 100644
index 0000000..eabe507
--- /dev/null
+++ b/aclocal/libcap.m4
@@ -0,0 +1,15 @@
+dnl Checks for libcap.so
+dnl
+AC_DEFUN([AC_LIBCAP], [
+
+  dnl look for prctl
+  AC_CHECK_FUNC([prctl], , )
+
+  dnl look for the library; do not add to LIBS if found
+  AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,)
+  AC_SUBST(LIBCAP)
+
+  AC_CHECK_HEADERS([sys/capability.h], ,
+                   [AC_MSG_ERROR([libcap headers not found.])])
+
+])dnl
diff --git a/configure.ac b/configure.ac
index ea6f4d9..c77c5ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -166,6 +166,9 @@ fi
 dnl Check for TI-RPC library and headers
 AC_LIBTIRPC
 
+dnl Check for -lcap
+AC_LIBCAP
+
 # Check whether user wants TCP wrappers support
 AC_TCP_WRAPPERS
 
diff --git a/support/nsm/file.c b/support/nsm/file.c
index 8796705..d469219 100644
--- a/support/nsm/file.c
+++ b/support/nsm/file.c
@@ -67,6 +67,8 @@
 #endif
 
 #include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/stat.h>
 
 #include <ctype.h>
@@ -335,6 +337,34 @@ nsm_is_default_parentdir(void)
 	return strcmp(nsm_base_dirname, NSM_DEFAULT_STATEDIR) == 0;
 }
 
+/*
+ * Clear all capabilities but CAP_NET_BIND_SERVICE.  This permits
+ * callers to acquire privileged source ports, but all other root
+ * capabilities are disallowed.
+ *
+ * Returns true if successful, or false if some error occurred.
+ */
+static _Bool
+nsm_clear_capabilities(void)
+{
+	cap_t caps;
+
+	caps = cap_from_text("cap_net_bind_service=ep");
+	if (caps == NULL) {
+		xlog(L_ERROR, "Failed to allocate capability: %m");
+		return false;
+	}
+
+	if (cap_set_proc(caps) == -1) {
+		xlog(L_ERROR, "Failed to set capability flags: %m");
+		(void)cap_free(caps);
+		return false;
+	}
+
+	(void)cap_free(caps);
+	return true;
+}
+
 /**
  * nsm_drop_privileges - drop root privileges
  * @pidfd: file descriptor of a pid file
@@ -382,6 +412,14 @@ nsm_drop_privileges(const int pidfd)
 		if (fchown(pidfd, st.st_uid, st.st_gid) == -1)
 			xlog_warn("Failed to change owner of pidfile: %m");
 
+	/*
+	 * Don't clear capabilities when dropping root.
+	 */
+        if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
+                xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m");
+		return 0;
+	}
+
 	if (setgroups(0, NULL) == -1) {
 		xlog(L_ERROR, "Failed to drop supplementary groups: %m");
 		return false;
@@ -399,7 +437,8 @@ nsm_drop_privileges(const int pidfd)
 	}
 
 	xlog(D_CALL, "Effective UID, GID: %u, %u", st.st_uid, st.st_gid);
-	return true;
+
+	return nsm_clear_capabilities();
 }
 
 /**
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
index a94c012..1744791 100644
--- a/utils/statd/Makefile.am
+++ b/utils/statd/Makefile.am
@@ -15,10 +15,10 @@ BUILT_SOURCES = $(GENFILES)
 statd_LDADD = ../../support/nsm/libnsm.a \
 	      ../../support/nfs/libnfs.a \
 	      ../../support/misc/libmisc.a \
-	      $(LIBWRAP) $(LIBNSL)
+	      $(LIBWRAP) $(LIBNSL) $(LIBCAP)
 sm_notify_LDADD = ../../support/nsm/libnsm.a \
 		  ../../support/nfs/libnfs.a \
-		  $(LIBNSL)
+		  $(LIBNSL) $(LIBCAP)
 
 EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c
 


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 23/24] statd: Support TI-RPC statd listener
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (21 preceding siblings ...)
  2010-01-14 17:32   ` [PATCH 22/24] libnsm.a: retain CAP_NET_BIND when dropping privileges Chuck Lever
@ 2010-01-14 17:32   ` Chuck Lever
  2010-01-14 17:32   ` [PATCH 24/24] statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support Chuck Lever
  2010-01-16 13:22   ` [PATCH 00/24] Remaining IPv6 patches for statd Steve Dickson
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:32 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

If TI-RPC is available, use it to create statd's svc listener.  If
not, use the old function, rpc_init(), to create statd's listener.

IPv6 can be supported if TI-RPC is available.  In this case,
/etc/netconfig is searched to determine which transports to advertise.

Add the new listener creation API in libnfs.a since other components
of nfs-utils (such as rpc.mountd) will eventually want to share it.

A little re-arrangement of when the statd listener is created is done
to make unregistration of the statd service more reliable.  As it is
now, the statd service is never unregistered when it exits.  After it
is gone, other programs usually hang when trying to access statd or
see if it's running, since the registration is still there but statd
itself does not respond.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/rpcmisc.h |    7 +
 support/nfs/Makefile.am   |    3 -
 support/nfs/svc_create.c  |  252 +++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/statd.c       |   38 ++++++-
 4 files changed, 291 insertions(+), 9 deletions(-)
 create mode 100644 support/nfs/svc_create.c

diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
index 93266b8..91910a6 100644
--- a/support/include/rpcmisc.h
+++ b/support/include/rpcmisc.h
@@ -41,7 +41,12 @@ struct rpc_dtable {
 		(xdrproc_t)xdr_##res_type, sizeof(res_type), \
 	}
 
-
+void		nfs_svc_unregister(const rpcprog_t program,
+				const rpcvers_t version);
+unsigned int	nfs_svc_create(char *name, const rpcprog_t program,
+				const rpcvers_t version,
+				void (*dispatch)(struct svc_req *, SVCXPRT *),
+				const uint16_t port);
 void		rpc_init(char *name, int prog, int vers,
 				void (*dispatch)(struct svc_req *, SVCXPRT *),
 				int defport);
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
index e9462fc..60400b2 100644
--- a/support/nfs/Makefile.am
+++ b/support/nfs/Makefile.am
@@ -4,7 +4,8 @@ noinst_LIBRARIES = libnfs.a
 libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
 		   xlog.c xcommon.c wildmat.c nfsclient.c \
 		   nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
-		   svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c
+		   svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \
+		   svc_create.c
 
 MAINTAINERCLEANFILES = Makefile.in
 
diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
new file mode 100644
index 0000000..fa7eab6
--- /dev/null
+++ b/support/nfs/svc_create.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <signal.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <netinet/in.h>
+
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+
+#ifdef HAVE_TCP_WRAPPER
+#include "tcpwrapper.h"
+#endif
+
+#include "rpcmisc.h"
+#include "xlog.h"
+
+#ifdef HAVE_LIBTIRPC
+
+/*
+ * Set up an appropriate bind address, given @port and @nconf.
+ *
+ * Returns getaddrinfo(3) results if successful.  Caller must
+ * invoke freeaddrinfo(3) on these results.
+ *
+ * Otherwise NULL is returned if an error occurs.
+ */
+__attribute_malloc__
+static struct addrinfo *
+svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
+{
+	struct addrinfo *ai = NULL;
+	struct addrinfo hint = {
+		.ai_flags	= AI_PASSIVE | AI_NUMERICSERV,
+	};
+	char buf[8];
+	int error;
+
+	if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
+		hint.ai_family = AF_INET;
+#ifdef IPV6_SUPPORTED
+	else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
+		hint.ai_family = AF_INET6;
+#endif	/* IPV6_SUPPORTED */
+	else {
+		xlog(L_ERROR, "Unrecognized bind address family: %s",
+			nconf->nc_protofmly);
+		return NULL;
+	}
+
+	if (strcmp(nconf->nc_proto, NC_UDP) == 0)
+		hint.ai_protocol = (int)IPPROTO_UDP;
+	else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
+		hint.ai_protocol = (int)IPPROTO_TCP;
+	else {
+		xlog(L_ERROR, "Unrecognized bind address protocol: %s",
+			nconf->nc_proto);
+		return NULL;
+	}
+
+	(void)snprintf(buf, sizeof(buf), "%u", port);
+	error = getaddrinfo(NULL, buf, &hint, &ai);
+	switch (error != 0) {
+	case 0:
+		return ai;
+	case EAI_SYSTEM:
+		xlog(L_ERROR, "Failed to construct bind address: %m");
+		break;
+	default:
+		xlog(L_ERROR, "Failed to construct bind address: %s",
+			gai_strerror(error));
+		break;
+	}
+
+	return NULL;
+}
+
+static unsigned int
+svc_create_nconf(const char *name, const rpcprog_t program,
+		const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port, struct netconfig *nconf)
+{
+	struct t_bind bindaddr;
+	struct addrinfo *ai;
+	SVCXPRT	*xprt;
+
+	ai = svc_create_bindaddr(nconf, port);
+	if (ai == NULL)
+		return 0;
+
+	bindaddr.addr.buf = ai->ai_addr;
+	bindaddr.qlen = SOMAXCONN;
+
+	xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
+	freeaddrinfo(ai);
+	if (xprt == NULL) {
+		xlog(D_GENERAL, "Failed to create listener xprt "
+				"(%s, %u, %s)", name, version, nconf->nc_netid);
+		return 0;
+	}
+
+	if (!svc_reg(xprt, program, version, dispatch, nconf)) {
+		/* svc_reg(3) destroys @xprt in this case */
+		xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
+				name, version, nconf->nc_netid);
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * nfs_svc_create - start up RPC svc listeners
+ * @name: C string containing name of new service
+ * @program: RPC program number to register
+ * @version: RPC version number to register
+ * @dispatch: address of function that handles incoming RPC requests
+ * @port: if not zero, transport listens on this port
+ *
+ * Sets up network transports for receiving RPC requests, and starts
+ * the RPC dispatcher.  Returns the number of started network transports.
+ */
+unsigned int
+nfs_svc_create(__attribute__((unused)) char *name,
+		const rpcprog_t program, const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port)
+{
+	const struct sigaction create_sigaction = {
+		.sa_handler	= SIG_IGN,
+	};
+	unsigned int visible, up;
+	struct netconfig *nconf;
+	void *handlep;
+
+	/*
+	 * Ignore SIGPIPE to avoid exiting sideways when peers
+	 * close their TCP connection while we're trying to reply
+	 * to them.
+	 */
+	(void)sigaction(SIGPIPE, &create_sigaction, NULL);
+
+	handlep = setnetconfig();
+	if (handlep == NULL) {
+		xlog(L_ERROR, "Failed to access local netconfig database: %s",
+			nc_sperror());
+		return 0;
+	}
+
+	visible = 0;
+	up = 0;
+	while ((nconf = getnetconfig(handlep)) != NULL) {
+		if (!(nconf->nc_flag & NC_VISIBLE))
+			continue;
+		visible++;
+		up += svc_create_nconf(name, program, version, dispatch,
+						port, nconf);
+	}
+
+	if (visible == 0)
+		xlog(L_ERROR, "Failed to find any visible netconfig entries");
+
+	if (endnetconfig(handlep) == -1)
+		xlog(L_ERROR, "Failed to close local netconfig database: %s",
+			nc_sperror());
+
+	return up;
+}
+
+/**
+ * nfs_svc_unregister - remove service registrations from local rpcbind database
+ * @program: RPC program number to unregister
+ * @version: RPC version number to unregister
+ *
+ * Removes all registrations for [ @program, @version ] .
+ */
+void
+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
+{
+	if (rpcb_unset(program, version, NULL) == FALSE)
+		xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
+			(unsigned long)program, (unsigned long)version);
+}
+
+#else	/* !HAVE_LIBTIRPC */
+
+/**
+ * nfs_svc_create - start up RPC svc listeners
+ * @name: C string containing name of new service
+ * @program: RPC program number to register
+ * @version: RPC version number to register
+ * @dispatch: address of function that handles incoming RPC requests
+ * @port: if not zero, transport listens on this port
+ *
+ * Sets up network transports for receiving RPC requests, and starts
+ * the RPC dispatcher.  Returns the number of started network transports.
+ */
+unsigned int
+nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port)
+{
+	rpc_init(name, (int)program, (int)version, dispatch, (int)port);
+	return 1;
+}
+
+/**
+ * nfs_svc_unregister - remove service registrations from local rpcbind database
+ * @program: RPC program number to unregister
+ * @version: RPC version number to unregister
+ *
+ * Removes all registrations for [ @program, @version ] .
+ */
+void
+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
+{
+	if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE)
+		xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
+			(unsigned long)program, (unsigned long)version);
+}
+
+#endif	/* !HAVE_LIBTIRPC */
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index 72c9b41..7be6454 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -90,13 +90,18 @@ sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
 #define sm_prog_1 sm_prog_1_wrapper
 #endif
 
+static void
+statd_unregister(void) {
+	nfs_svc_unregister(SM_PROG, SM_VERS);
+}
+
 /*
  * Signal handler.
  */
 static void 
 killer (int sig)
 {
-	pmap_unset (SM_PROG, SM_VERS);
+	statd_unregister ();
 	xlog_err ("Caught signal %d, un-registering and exiting", sig);
 }
 
@@ -125,6 +130,9 @@ static void log_modes(void)
 		strcat(buf,"No-Daemon ");
 	if (run_mode & MODE_LOG_STDERR)
 		strcat(buf,"Log-STDERR ");
+#ifdef HAVE_LIBTIRPC
+	strcat(buf, "TI-RPC ");
+#endif
 
 	xlog_warn(buf);
 }
@@ -424,10 +432,29 @@ int main (int argc, char **argv)
 	xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE);
 	nsm_update_kernel_state(MY_STATE);
 
-	pmap_unset (SM_PROG, SM_VERS);
+	/*
+	 * ORDER
+	 * Clear old listeners while still root, to override any
+	 * permission checking done by rpcbind.
+	 */
+	statd_unregister();
+
+	/*
+	 * ORDER
+	 */
+	if (!nsm_drop_privileges(pidfd))
+		exit(1);
 
-	/* this registers both UDP and TCP services */
-	rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
+	/*
+	 * ORDER
+	 * Create RPC listeners after dropping privileges.  This permits
+	 * statd to unregister its own listeners when it exits.
+	 */
+	if (nfs_svc_create("statd", SM_PROG, SM_VERS, sm_prog_1, port) == 0) {
+		xlog(L_ERROR, "failed to create RPC listeners, exiting");
+		exit(1);
+	}
+	atexit(statd_unregister);
 
 	/* If we got this far, we have successfully started, so notify parent */
 	if (pipefds[1] > 0) {
@@ -440,9 +467,6 @@ int main (int argc, char **argv)
 		pipefds[1] = -1;
 	}
 
-	if (!nsm_drop_privileges(pidfd))
-		exit(1);
-
 	for (;;) {
 		/*
 		 * Handle incoming requests:  SM_NOTIFY socket requests, as


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 24/24] statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (22 preceding siblings ...)
  2010-01-14 17:32   ` [PATCH 23/24] statd: Support TI-RPC statd listener Chuck Lever
@ 2010-01-14 17:32   ` Chuck Lever
  2010-01-16 13:22   ` [PATCH 00/24] Remaining IPv6 patches for statd Steve Dickson
  24 siblings, 0 replies; 26+ messages in thread
From: Chuck Lever @ 2010-01-14 17:32 UTC (permalink / raw)
  To: steved; +Cc: chris.mason, linux-nfs

Expand and clarify the explanation of NSM operation on Linux, and
provide the same text in both man pages.

Update descriptions of the command line options to match the operation
of the current implementation.

Introduce sections discussing security and operational issues, and
IPv6 operation.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.man |  407 +++++++++++++++++++++++++-----------
 utils/statd/statd.man     |  508 ++++++++++++++++++++++++++++++++-------------
 2 files changed, 641 insertions(+), 274 deletions(-)

diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
index a5c1cc5..163713e 100644
--- a/utils/statd/sm-notify.man
+++ b/utils/statd/sm-notify.man
@@ -1,162 +1,315 @@
-.\"
-.\" sm-notify(8)
+.\"@(#)sm-notify.8"
 .\"
 .\" Copyright (C) 2004 Olaf Kirch <okir@suse.de>
-.TH sm-notify 8 "19 Mar 2007
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle.  All rights reserved.
+.\"
+.TH SM-NOTIFY 8 "1 November 2009
 .SH NAME
-sm-notify \- Send out NSM reboot notifications
+sm-notify \- send reboot notifications to NFS peers
 .SH SYNOPSIS
-.BI "/sbin/sm-notify [-df] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ]
+.BI "/usr/sbin/sm-notify [-dfn] [-m " minutes "] [-v " name "] [-p " notify-port "] [-P " path "]
 .SH DESCRIPTION
-File locking over NFS (v2 and v3) requires a facility to notify peers in
-case of a reboot, so that clients can reclaim locks after
-a server crash, and/or
-servers can release locks held by the rebooted client.
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
 .PP
-This is a two-step process: during normal
-operations, a mechanism is required to keep track of which
-hosts need to be informed of a reboot. And of course,
-notifications need to be sent out during reboot.
-The protocol used for this is called NSM, for
-.IR "Network Status Monitor" .
+For NFS version 2 and version 3, the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.TP
+.B rpc.statd
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
 .PP
-This implementation separates these into separate program.
+The local NFS lock manager alerts its local
 .B rpc.statd
-tracks hosts which need to be notified and this
+of each remote peer that should be monitored.
+When the local system reboots, the
 .B sm-notify
-performs the notification.  When
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
 .B rpc.statd
-is started it will typically started
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client records the server's hostname used on the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
 .B sm-notify
-but this is configurable.
-.SS Operation
-For each NFS client or server machine to be monitored,
+command normally sends the results of
+.BR gethostname (3)
+as the
+.I my_name
+string.
+The remote
 .B rpc.statd
-creates a file in
-.BR /var/lib/nfs/sm ", "
-and removes the file if monitoring is no longer required.
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
 .PP
-When the machine is rebooted,
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
 .B sm-notify
-iterates through these files and notifies the peer
-.B statd
-server on those machines.
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
 .PP
-Each machine has an
-.I "NSM state" ,
-which is basically an integer counter that is incremented
-each time the machine reboots. This counter is stored
-in
-.BR /var/lib/nfs/state ,
-and updated by
-.BR sm-notify .
-.SS Security
-.B sm-notify
-has little need for root privileges and so drops them as soon as
-possible.
-It continues to need to make changes to the
-.B sm
-and
-.B sm.bak
-directories so to be able to drop privileges, these must be writable
-by a non-privileged user.  If these directories are owned by a
-non-root user,
-.B sm-notify
-will drop privilege to match that user once it has created sockets for
-sending out request (for which it needs privileged) but before it
-processes any reply (which is the most likely source of possible
-privilege abuse).
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
 .SH OPTIONS
 .TP
-.BI -m " failtime
-When notifying hosts,
+.B -d
+Keeps
+.B sm-notify
+attached to its controlling terminal and running in the foreground
+so that notification progress may be monitored directly.
+.TP
+.B -f
+Send notifications even if
 .B sm-notify
-will try to contact each host for up to 15 minutes,
-and will give up if unable to reach it within this time
-frame.
+has already run since the last system reboot.
+.TP
+.BI -m " retry-time
+Specifies the length of time, in minutes, to continue retrying
+notifications to unresponsive hosts.
+If this option is not specified,
+.B sm-notify
+attempts to send notifications for 15 minutes.
+Specifying a value of 0 causes
+.B sm-notify
+to continue sending notifications to unresponsive peers
+until it is manually killed.
 .IP
-Using the
-.B -m
-option, you can override this. A value of 0 tells
-sm-notify to retry indefinitely; any other value is
-interpreted as the maximum retry time in minutes.
+Notifications are retried if sending fails,
+the remote does not respond,
+the remote's NSM service is not registered,
+or if there is a DNS failure
+which prevents the remote's
+.I mon_name
+from being resolved to an address.
+.IP
+Hosts are not removed from the notification list until a valid
+reply has been received.
+However, the SM_NOTIFY procedure has a void result.
+There is no way for
+.B sm-notify
+to tell if the remote recognized the sender and has started
+appropriate lock recovery.
 .TP
-.BI -v " ipaddr-or-hostname
-This option tells
-.B sm-notify
-to bind to the specified
-.IR ipaddr ,
-(or the ipaddr of the given
-.IR hostname )
-so that all notification packets originate from this address.
-This is useful for NFS failover.  The given name is also used as the
-.I name
-of this host in the NSM request.
+.B -n
+Prevents
+.B sm-notify
+from updating the local system's NSM state number.
 .TP
 .BI -p " port
-instructs
+Specifies the source port number
 .B sm-notify
-to bind to the indicated IP
-.IR port
-number. If this option is not given, it will try to bind to
-a randomly chosen privileged port below 1024.
+should use when sending reboot notifications.
+If this option is not specified, a randomly chosen ephemeral port is used.
+.IP
+This option can be used to traverse a firewall between client and server.
 .TP
-.BI -P " /path/to/state/directory
-If
+.BI "\-P, " "" \-\-state\-directory\-path " pathname
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
+.B sm-notify
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
 .B sm-notify
-should look in a no-standard place of state file, the path can be
-given here.  The directories
-.B sm
-and
-.B sm.bak
-and the file
-.B state
-must exist in that directory with the standard names.
+attempts to set its effective UID and GID to the owner
+and group of this directory.
 .TP
-.B -f
-If the state path has not been reset with
-.BR -P ,
+.BI -v " ipaddr " | " hostname
+Specifies the network address from which to send reboot notifications,
+and the
+.I mon_name
+argument to use when sending SM_NOTIFY requests.
+If this option is not specified,
 .B sm-notify
-will normally create a file in
-.B /var/run
-to indicate that it has been
-run.  If this file is found when
+uses a wildcard address as the transport bind address,
+and uses the results of
+.BR gethostname (3)
+as the
+.I mon_name
+argument.
+.IP
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+.IP
+This option can be useful in multi-homed configurations where
+the remote requires notification from a specific network address.
+.SH SECURITY
+The
 .B sm-notify
-starts, it will not run again (as it is normally only needed once per
-reboot).
-If
-.B -f
-(for
-.BR force )
-is given,
+command must be started as root to acquire privileges needed
+to access the state information database.
+It drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+.PP
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.IP
+The use of network addresses as a
+.I mon_name
+or a
+.I my_name
+string should be avoided when
+interoperating with non-Linux NFS implementations.
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into the
 .B sm-notify
-will run even if the file in
-.B /var/run
-is present.
-.TP
-.B -n
-Do not update the NSM state. This is for testing only.  Setting this
-flag implies
-.BR -f .
-.TP
-.B -d
-Enables debugging.
-By default,
+command ,it will choose an appropriate IPv4 or IPv6 transport
+based on the network address returned by DNS for each remote peer.
+It should be fully compatible with remote systems
+that do not support TI-RPC or IPv6.
+.PP
+Currently, the
 .B sm-notify
-forks and puts itself in the background after obtaining the
-list of hosts from
-.BR /var/lib/nfs/sm .
+command supports sending notification only via datagram transport protocols.
 .SH FILES
-.BR /var/lib/nfs/state
-.br
-.BR /var/lib/nfs/sm/*
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /proc/sys/fs/nfs/nsm_local_state
+kernel's copy of the NSM state number
+.SH SEE ALSO
+.BR rpc.statd (8),
+.BR nfs (5),
+.BR uname (2),
+.BR hostname (7)
+.PP
+RFC 1094 - "NFS: Network File System Protocol Specification"
 .br
-.BR /var/lib/nfs/sm.bak/*
+RFC 1813 - "NFS Version 3 Protocol Specification"
 .br
-.BR /var/run/sm-notify.pid
-.SH SEE ALSO
-.BR rpc.nfsd(8),
-.BR portmap(8)
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
 .SH AUTHORS
-.br
 Olaf Kirch <okir@suse.de>
+.br
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/utils/statd/statd.man b/utils/statd/statd.man
index e8be9f3..4ddb634 100644
--- a/utils/statd/statd.man
+++ b/utils/statd/statd.man
@@ -1,191 +1,403 @@
-.\"
-.\" statd(8)
+.\"@(#)rpc.statd.8"
 .\"
 .\" Copyright (C) 1999 Olaf Kirch <okir-pn4DOG8n3UYbFoVRYvo4fw@public.gmane.org>
 .\" Modified by Jeffrey A. Uphoff, 1999, 2002, 2005.
 .\" Modified by Lon Hohberger, 2000.
 .\" Modified by Paul Clements, 2004.
-.TH rpc.statd 8 "31 Aug 2004"
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle.  All rights reserved.
+.\"
+.TH RPC.STATD 8 "1 November 2009
 .SH NAME
-rpc.statd \- NSM status monitor
+rpc.statd \- NSM service daemon
 .SH SYNOPSIS
-.B "rpc.statd [-FNL] [-d] [-?] [-n " name "] [-o " port "] [-p " port "] [-H " prog "] [-V]"
+.BI "rpc.statd [-dh?FLNvVw] [-H " prog "] [-n " my-name "] [-o " outgoing-port "] [-p " listener-port "] [-P " path " ]
 .SH DESCRIPTION
-The
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
+.PP
+For NFS version 2 [RFC1094] and NFS version 3 [RFC1813], the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
 .B rpc.statd
-server implements the NSM (Network Status Monitor) RPC protocol.
-This service is somewhat misnamed, since it doesn't actually provide
-active monitoring as one might suspect; instead, NSM implements a
-reboot notification service. It is used by the NFS file locking service,
-.BR rpc.lockd ,
-to implement lock recovery when the NFS server machine crashes and
-reboots.
-.SS Operation
-For each NFS client or server machine to be monitored,
-.B rpc.statd
-creates a file in
-.BR /var/lib/nfs/sm .
-When starting, it normally runs
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.PP
+The local NFS lock manager alerts its local
+.B rpc.statd
+of each remote peer that should be monitored.
+When the local system reboots, the
 .B sm-notify
-to iterate through these files and notify the
-peer
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
 .B rpc.statd
-on those machines.
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client uses the server hostname from the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
+.B sm-notify
+command normally sends the results of
+.BR gethostname (3)
+as the
+.I my_name
+string.
+The remote
+.B rpc.statd
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
+.PP
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
+.B sm-notify
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
+.PP
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
 .SH OPTIONS
 .TP
-.B -F
-By default,
+.BR -d , " --no-syslog
+Causes
 .B rpc.statd
-forks and puts itself in the background when started. The
-.B -F
-argument tells it to remain in the foreground. This option is
-mainly for debugging purposes.
-.TP
-.B -d
-By default,
-.B rpc.statd
-sends logging messages via
-.BR syslog (3)
-to system log.  The
-.B -d
-argument forces it to log verbose output to
-.B stderr
-instead. This option is mainly for debugging purposes, and may only
-be used in conjunction with the
+to write log messages on
+.I stderr
+instead of to the system log,
+if the
 .B -F
-parameter.
+option was also specified.
 .TP
-.BI "\-n," "" " \-\-name " name 
-specify a name for
-.B rpc.statd
-to use as the local hostname. By default,
-.BR rpc.statd
-will call
-.BR gethostname (2)
-to get the local hostname. Specifying
-a local hostname may be useful for machines with more than one
-interfaces.
+.BR -F , " --foreground
+Keeps
+.B rpc.statd
+attached to its controlling terminal so that NSM
+operation can be monitored directly or run under a debugger.
+If this option is not specified,
+.B rpc.statd
+backgrounds itself soon after it starts.
 .TP
-.BI "\-o," "" " \-\-outgoing\-port "  port
-specify a port for
-.B rpc.statd
-to send outgoing status requests from.  By default,
-.BR rpc.statd
-will ask
-.BR portmap (8)
-to assign it a port number.  As of this writing, there is not
-a standard port number that
-.BR portmap
-always or usually assigns.  Specifying
-a port may be useful when implementing a firewall.
+.BR -h , " -?" , " --help
+Causes
+.B rpc.statd
+to display usage information on
+.I stderr
+and then exit.
 .TP
-.BI "\-p," "" " \-\-port " port
-specify a port for
-.B rpc.statd
-to listen on.  By default,
-.BR rpc.statd
-will ask
-.BR portmap (8)
-to assign it a port number.  As of this writing, there is not
-a standard port number that
-.BR portmap
-always or usually assigns.  Specifying
-a port may be useful when implementing a firewall.
+.BI "\-H," "" " \-\-ha-callout " prog
+Specifies a high availability callout program.
+If this option is not specified, no callouts are performed.
+See the
+.B High-availability callouts
+section below for details.
 .TP
-.BI "\-P," "" " \-\-state\-directory\-path "  directory
-specify a directory in which to place statd state information.
-If this option is not specified the default of 
-.BR /var/lib/nfs
-is used.
+.BR -L , " --no-notify
+Prevents
+.B rpc.statd
+from running the
+.B sm-notify
+command when it starts up,
+preserving the existing NSM state number and monitor list.
+.IP
+Note: the
+.B sm-notify
+command contains a check to ensure it runs only once after each system reboot.
+This prevents spurious reboot notification if
+.B rpc.statd
+restarts without the
+.B -L
+option.
 .TP
-.B -N
-Causes statd to run in the notify-only mode. When started in this mode, the
-statd program will check its state directory, send notifications to any
-monitored nodes, and exit once the notifications have been sent. This mode is
-used to enable Highly Available NFS implementations (i.e. HA-NFS).
-This mode is deprecated \-
+.BI "\-n, " "" "\-\-name " ipaddr " | " hostname
+Specifies the bind address used for RPC listener sockets.
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+If this option is not specified,
+.B rpc.statd
+uses a wildcard address as the transport bind address.
+.IP
+This string is also passed to the
 .B sm-notify
-should be used directly instead.
+command to be used as the source address from which
+to send reboot notification requests.
+See
+.BR sm-notify (8)
+for details.
 .TP
-.BR -L , " --no-notify
-Inhibits the running of
-.BR sm-notify .
-If
+.BR -N
+Causes
+.B rpc.statd
+to run the
 .B sm-notify
-is run by some other script at boot time, there is no need for
-.B statd
-to start sm-notify itself.  This can be appropriate if starting of
-statd needs to be delayed until it is actually need.  In such cases
+command, and then exit.
+Since the
+.B sm-notify
+command can also be run directly, this option is deprecated.
+.TP
+.BI "\-o," "" " \-\-outgoing\-port "  port
+Specifies the source port number the
 .B sm-notify
-should still be run at boot time.
+command should use when sending reboot notifications.
+See
+.BR sm-notify (8)
+for details.
 .TP
-.BI "\-H, " "" " \-\-ha-callout " prog
-Specify a high availability callout program, which will receive callouts
-for all client monitor and unmonitor requests. This allows
+.BI "\-p," "" " \-\-port " port
+Specifies the port number used for RPC listener sockets.
+If this option is not specified,
 .B rpc.statd
-to be used in a High Availability NFS (HA-NFS) environment. The
-program will be run with 3 arguments:  The first is either
-.B add-client
-or
-.B del-client
-depending on the reason for the callout.
-The second will be the name of the client.
-The third will be the name of the server as known to the client.
+chooses a random ephemeral port for each listener socket.
+.IP
+This option can be used to fix the port value of its listeners when
+SM_NOTIFY requests must traverse a firewall between clients and servers.
 .TP
-.B -?
-Causes
+.BI "\-P, " "" \-\-state\-directory\-path " pathname
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
 .B rpc.statd
-to print out command-line help and exit.
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
+.B rpc.statd
+attempts to set its effective UID and GID to the owner
+and group of this directory.
 .TP
-.B -V
+.BR -v ", " -V ", " --version
 Causes
 .B rpc.statd
-to print out version information and exit.
-
-
-
-.SH TCP_WRAPPERS SUPPORT
-This
+to display version information on
+.I stderr
+and then exit.
+.SH SECURITY
+The
+.B rpc.statd
+daemon must be started as root to acquire privileges needed
+to create sockets with privileged source ports, and to access the
+state information database.
+Because
+.B rpc.statd
+maintains a long-running network service, however, it drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.PP
+You can also protect your
 .B rpc.statd
-version is protected by the
+listeners using the
+.B tcp_wrapper
+library or
+.BR iptables (8).
+Note that the
+.B tcp_wrapper
+library supports only IPv4 networking.
+To use the
 .B tcp_wrapper
-library. You have to give the clients access to
-.B rpc.statd
-if they should be allowed to use it. To allow connects from clients of
-the .bar.com domain you could use the following line in /etc/hosts.allow:
-
-statd: .bar.com
-
-You have to use the daemon name 
+library, add the hostnames of peers that should be allowed access to
+.IR /etc/hosts.allow .
+Use the daemon name
 .B statd
-for the daemon name (even if the binary has a different name).
-
-For further information please have a look at the
+even if the
+.B rpc.statd
+binary has a different filename.
+.P
+For further information see the
 .BR tcpd (8)
 and
 .BR hosts_access (5)
-manual pages.
-
-.SH SIGNALS
-.BR SIGUSR1
-causes
-.B rpc.statd
-to re-read the notify list from disk
-and send notifications to clients. This can be used in High Availability NFS
-(HA-NFS) environments to notify clients to reacquire file locks upon takeover
-of an NFS export from another server.
-
+man pages.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+.PP
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.IP
+The use of network addresses as a
+.I mon_name
+or a
+.I my_name
+string should be avoided when
+interoperating with non-Linux NFS implementations.
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS High-availability callouts
+.B rpc.statd
+can exec a special callout program during processing of
+successful SM_MON, SM_UNMON, and SM_UNMON_ALL requests.
+Such a program may be used in High Availability NFS (HA-NFS)
+environments to track lock state that may need to be migrated after
+a system reboot.
+.PP
+The name of the callout program is specified with the
+.B -H
+option.
+The program is run with 3 arguments:
+The first is either
+.B add-client
+or
+.B del-client
+depending on the reason for the callout.
+The second is the
+.I mon_name
+of the monitored peer.
+The third is the
+.I caller_name
+of the requesting lock manager.
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into
+.BR rpc.statd ,
+it attempts to start listeners on network transports marked
+'visible' in
+.IR /etc/netconfig .
+As long as at least one network transport listener starts successfully,
+.B rpc.statd
+will operate.
 .SH FILES
-.BR /var/lib/nfs/state
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /var/run/run.statd.pid
+pid file
+.TP 2.5i
+.I /etc/netconfig
+network transport capability database
+.SH SEE ALSO
+.BR sm-notify (8),
+.BR nfs (5),
+.BR rpc.nfsd (8),
+.BR rpcbind (8),
+.BR tcpd (8),
+.BR hosts_access (5),
+.BR iptables (8),
+.BR netconfig (5)
+.sp
+RFC 1094 - "NFS: Network File System Protocol Specification"
 .br
-.BR /var/lib/nfs/sm/*
+RFC 1813 - "NFS Version 3 Protocol Specification"
 .br
-.BR /var/lib/nfs/sm.bak/*
-.SH SEE ALSO
-.BR rpc.nfsd(8),
-.BR portmap(8)
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
 .SH AUTHORS
-.br
 Jeff Uphoff <juphoff@users.sourceforge.net>
 .br
 Olaf Kirch <okir-pn4DOG8n3UYbFoVRYvo4fw@public.gmane.org>
@@ -195,3 +407,5 @@ H.J. Lu <hjl-mXXj517/zsQ@public.gmane.org>
 Lon Hohberger <hohberger-djc/iPCCuDZegjQ2I56v3Dti40yvJfI7@public.gmane.org>
 .br
 Paul Clements <paul.clements-G8/ITkJZaeZWk0Htik3J/w@public.gmane.org>
+.br
+Chuck Lever <chuck.lever@oracle.com>


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/24] Remaining IPv6 patches for statd
       [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
                     ` (23 preceding siblings ...)
  2010-01-14 17:32   ` [PATCH 24/24] statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support Chuck Lever
@ 2010-01-16 13:22   ` Steve Dickson
  24 siblings, 0 replies; 26+ messages in thread
From: Steve Dickson @ 2010-01-16 13:22 UTC (permalink / raw)
  To: Chuck Lever; +Cc: chris.mason, linux-nfs



On 01/14/2010 12:28 PM, Chuck Lever wrote:
> The Fedora 13 feature freeze is next week, and RHEL 6 beta is coming
> up in just a month or two.  Because both of these releases should have
> at least client-side NFS/IPv6 support, we've decided to accelerate the
> submission of nfs-utils IPv6 patches.
> 
> This patch set introduces basic IPv6 support to statd and sm-notify.
> Functionality when IPv6 and TI-RPC are disabled should be unchanged,
> except that monitor record files can now contain more than one line.
> 
> ---
> 
> Chuck Lever (24):
>       statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support
>       statd: Support TI-RPC statd listener
>       libnsm.a: retain CAP_NET_BIND when dropping privileges
>       statd: Remove NL_ADDR() macro
>       statd: Support IPv6 in sm_stat_1_svc()
>       statd: Support IPv6 in sm_mon_1_svc()
>       statd: Add API to canonicalize mon_names
>       libnsm.a: Add support for multiple lines in monitor record files
>       libnsm.a: Factor atomic write code out of nsm_get_state()
>       sm-notify: Save mon_name and my_name strings
>       statd: Support IPv6 in sm_simu_crash_1_svc
>       statd: Support IPv6 is caller_is_localhost()
>       statd: add IPv6 support in sm_notify_1_svc()
>       statd: add nsm_present_address() API
>       statd: Introduce statd version of matchhostname()
>       nfs-utils: Collect socket address helpers into one location
>       sm-notify: Support IPv6 DNS lookups in smn_lookup
>       sm-notify: Use getaddrinfo(3) to create bind address in smn_create_socket()
>       sm-notify: IPv6 support in reserved port binding in smn_create_socket()
>       sm-notify: Support creating a PF_INET6 socket in smn_create_socket()
>       sm-notify: factor socket creation out of notify()
>       statd: Update rmtcall.c
>       sm-notify: Replace RPC code
>       libnsm.a: Add RPC construction helper functions
> 
Committed...

steved.

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2010-01-16 13:22 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-01-14 17:28 [PATCH 00/24] Remaining IPv6 patches for statd Chuck Lever
     [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2010-01-14 17:28   ` [PATCH 01/24] libnsm.a: Add RPC construction helper functions Chuck Lever
2010-01-14 17:29   ` [PATCH 02/24] sm-notify: Replace RPC code Chuck Lever
2010-01-14 17:29   ` [PATCH 03/24] statd: Update rmtcall.c Chuck Lever
2010-01-14 17:29   ` [PATCH 04/24] sm-notify: factor socket creation out of notify() Chuck Lever
2010-01-14 17:29   ` [PATCH 05/24] sm-notify: Support creating a PF_INET6 socket in smn_create_socket() Chuck Lever
2010-01-14 17:29   ` [PATCH 06/24] sm-notify: IPv6 support in reserved port binding " Chuck Lever
2010-01-14 17:29   ` [PATCH 07/24] sm-notify: Use getaddrinfo(3) to create bind address " Chuck Lever
2010-01-14 17:30   ` [PATCH 08/24] sm-notify: Support IPv6 DNS lookups in smn_lookup Chuck Lever
2010-01-14 17:30   ` [PATCH 09/24] nfs-utils: Collect socket address helpers into one location Chuck Lever
2010-01-14 17:30   ` [PATCH 10/24] statd: Introduce statd version of matchhostname() Chuck Lever
2010-01-14 17:30   ` [PATCH 11/24] statd: add nsm_present_address() API Chuck Lever
2010-01-14 17:30   ` [PATCH 12/24] statd: add IPv6 support in sm_notify_1_svc() Chuck Lever
2010-01-14 17:30   ` [PATCH 13/24] statd: Support IPv6 is caller_is_localhost() Chuck Lever
2010-01-14 17:30   ` [PATCH 14/24] statd: Support IPv6 in sm_simu_crash_1_svc Chuck Lever
2010-01-14 17:31   ` [PATCH 15/24] sm-notify: Save mon_name and my_name strings Chuck Lever
2010-01-14 17:31   ` [PATCH 16/24] libnsm.a: Factor atomic write code out of nsm_get_state() Chuck Lever
2010-01-14 17:31   ` [PATCH 17/24] libnsm.a: Add support for multiple lines in monitor record files Chuck Lever
2010-01-14 17:31   ` [PATCH 18/24] statd: Add API to canonicalize mon_names Chuck Lever
2010-01-14 17:31   ` [PATCH 19/24] statd: Support IPv6 in sm_mon_1_svc() Chuck Lever
2010-01-14 17:31   ` [PATCH 20/24] statd: Support IPv6 in sm_stat_1_svc() Chuck Lever
2010-01-14 17:31   ` [PATCH 21/24] statd: Remove NL_ADDR() macro Chuck Lever
2010-01-14 17:32   ` [PATCH 22/24] libnsm.a: retain CAP_NET_BIND when dropping privileges Chuck Lever
2010-01-14 17:32   ` [PATCH 23/24] statd: Support TI-RPC statd listener Chuck Lever
2010-01-14 17:32   ` [PATCH 24/24] statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support Chuck Lever
2010-01-16 13:22   ` [PATCH 00/24] Remaining IPv6 patches for statd Steve Dickson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.