All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2] cld: use XDR for all messages
@ 2010-01-15 17:49 Colin McCabe
  2010-01-21  1:38 ` Jeff Garzik
                   ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: Colin McCabe @ 2010-01-15 17:49 UTC (permalink / raw)
  To: Project Hail List; +Cc: Pete Zaitcev, Jeff Garzik, Colin McCabe

This patch moves CLD from using manual data serialization to using XDR (via
rpcgen). Both the packet header and the message body are now serialized and
deserialized using XDR. This makes it easy to have a variable-length packet
header, as well as a variable-length message body.

This patch introduces a minor libcldc API change in struct cldc_call_opts.

v2 changes:
* Added __cld prefix to functions in cld_fmt.c

* When decoding CMT_GET messages, we should store the payload in the
session structure, rather than in cldc_call_opts.

* Got rid of pkt_is_first, pkt_is_last, in favor of a flags-based approach.

* Killed CLD_MAX_PKT_MSG. It's more efficient to only allocate as much space
as you need, rather than always allocating space for 128 packets in a message.

* Created CLD_MAX_PAYLOAD_SZ to mean the maximum size of the data that can be
sent with GET or PUT. This is different (and smaller than!) the maximum
message size.

* automake: Add cld_msg_rpc.h to BUILT_SOURCES

* automake: Add lib dir to INCLUDES

* automake: "make clean" now deletes XDR build products

Signed-off-by: Colin McCabe <cmccabe@alumni.cmu.edu>
---
 .gitignore             |    4 +
 include/Makefile.am    |    2 +-
 include/cld_common.h   |   10 +
 include/cld_fmt.h      |   89 +++++
 include/cld_msg.h      |  252 -------------
 include/cldc.h         |   36 +-
 lib/Makefile.am        |   19 +-
 lib/cld_fmt.c          |  193 ++++++++++
 lib/cld_msg_rpc.x      |  218 +++++++++++
 lib/cldc.c             |  966 +++++++++++++++++++++---------------------------
 lib/common.c           |    6 +-
 server/Makefile.am     |    9 +-
 server/cld.h           |   97 ++++--
 server/cldb.h          |    2 +-
 server/msg.c           |  304 ++++++----------
 server/server.c        |  743 ++++++++++++++++++++-----------------
 server/session.c       |  396 ++++++++++-----------
 test/Makefile.am       |    5 +-
 test/load-file-event.c |   14 +-
 test/save-file-event.c |    2 -
 tools/cldcli.c         |   22 +-
 21 files changed, 1777 insertions(+), 1612 deletions(-)
 create mode 100644 include/cld_fmt.h
 delete mode 100644 include/cld_msg.h
 create mode 100644 lib/cld_fmt.c
 create mode 100644 lib/cld_msg_rpc.x

diff --git a/.gitignore b/.gitignore
index 79a4cd3..805fa22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,5 +32,9 @@ cld-config.h*
 cscope.*
 ncscope.*
 
+# XDR files
+*_rpc.h
+*_rpc_xdr.c
+
 # ignore Doxygen output directory
 gendoc
diff --git a/include/Makefile.am b/include/Makefile.am
index 888088c..f90d222 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,5 +1,5 @@
 
 EXTRA_DIST = cld-private.h libtimer.h
 
-include_HEADERS = cldc.h cld_msg.h hail_log.h cld_common.h
+include_HEADERS = cldc.h hail_log.h cld_common.h cld_fmt.h
 
diff --git a/include/cld_common.h b/include/cld_common.h
index 116ef93..217d1ad 100644
--- a/include/cld_common.h
+++ b/include/cld_common.h
@@ -20,10 +20,20 @@
  */
 
 #include <stdint.h>
+#include <lib/cld_msg_rpc.h>
+
+#define CLD_ALIGN8(n) ((8 - ((n) & 7)) & 7)
 
 unsigned long long cld_sid2llu(const uint8_t *sid);
 void __cld_rand64(void *p);
 const char *cld_errstr(enum cle_err_codes ecode);
 int cld_readport(const char *fname);
 
+/*
+ *  * We use a unified format for sid so it can be searched in log files (* in
+ *  vi).
+ */
+#define SIDFMT   "%016llX"
+#define SIDARG(sid)  cld_sid2llu(sid)
+
 #endif /* __CLD_COMMON_H__ */
diff --git a/include/cld_fmt.h b/include/cld_fmt.h
new file mode 100644
index 0000000..7af84a8
--- /dev/null
+++ b/include/cld_fmt.h
@@ -0,0 +1,89 @@
+#ifndef __CLD_FMT_H__
+#define __CLD_FMT_H__
+
+/*
+ * Copyright 2010, Colin McCabe
+ *
+ * This program 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.
+ *
+ * This program 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 this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <openssl/sha.h>
+#include <lib/cld_msg_rpc.h>
+#include <stdbool.h>
+
+/* @file	cld_fmt.h
+ *
+ * This file has some definitions and helper functions pertaining to the CLD
+ * network protocol. Unlike cld_msg.x, it's not an XDR file.
+ */
+
+struct hail_log;
+
+/*** Validate the HMAC signature of a byte buffer.
+ *
+ * @param log		log to write to
+ * @param key		The key, as a NULL-terminated string
+ * @param buf		The buffer
+ * @param buf_len	Length of the buffer
+ * @param sha		The signature itself. Must be of length exactly
+ *			SHA_DIGEST_LENGTH
+ *
+ * @return		0 on success; error code otherwise
+ */
+int __cld_authcheck(struct hail_log *log, const char *key,
+		const char *buf, size_t buf_len, const char *sha);
+
+/*** Sign a byte buffer.
+ *
+ * @param log		log to write to
+ * @param key		The key, as a NULL-terminated string
+ * @param buf		The buffer
+ * @param buf_len	Length of the buffer
+ * @param sha		(out param) The signature itself. Must be of length
+ *			exactly SHA_DIGEST_LENGTH
+ *
+ * @return		0 on success; error code otherwise
+ */
+int __cld_authsign(struct hail_log *log, const char *key,
+		const char *buf, size_t buf_len, char *sha);
+
+/* Returns a constant string representing a message type */
+const char *__cld_msg_type_to_str(enum cld_msg_type type);
+
+/* Returns a string representation of a packet header
+ *
+ * @param scratch		(out param) buffer of length
+ *				PKT_HDR_TO_STR_SCRATCH_LEN
+ * @param pkt_hdr		packet header
+ * @param pkt_len		length of packet
+ *
+ * @return			pointer to 'scratch'
+ */
+const char *__cld_pkt_hdr_to_str(char *scratch,
+			   const char *pkt_hdr, size_t pkt_len);
+
+void __cld_dump_buf(const void *buf, size_t len);
+
+/** Footer that appears at the end of each packet */
+struct __attribute__((packed)) cld_pkt_ftr {
+	uint64_t seqid;				/**< packet sequence ID */
+	char sha[SHA_DIGEST_LENGTH];		/**< packet signature */
+};
+
+/** Length of the packet footer. This size is fixed */
+#define CLD_PKT_FTR_LEN sizeof(struct cld_pkt_ftr)
+
+#define PKT_HDR_TO_STR_SCRATCH_LEN 128
+
+#endif
diff --git a/include/cld_msg.h b/include/cld_msg.h
deleted file mode 100644
index 513583d..0000000
--- a/include/cld_msg.h
+++ /dev/null
@@ -1,252 +0,0 @@
-#ifndef __CLD_MSG_H__
-#define __CLD_MSG_H__
-
-/*
- * Copyright 2009 Red Hat, Inc.
- *
- * This program 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.
- *
- * This program 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 this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <stdint.h>
-
-#define CLD_PKT_MAGIC	"CLDc1pkt"
-#define CLD_MSG_MAGIC	"CLDc1msg"
-
-#define CLD_ALIGN8(n) ((8 - ((n) & 7)) & 7)
-
-enum {
-	CLD_MAGIC_SZ		= 8,		/**< length of magic number */
-	CLD_SID_SZ		= 8,		/**< length of session id */
-
-	CLD_INODE_NAME_MAX	= 256,		/**< max total pathname len */
-
-	CLD_MAX_USERNAME	= 32,		/**< includes req. nul */
-	CLD_MAX_SECRET_KEY	= 128,		/**< includes req. nul */
-
-	CLD_MAX_PKT_MSG_SZ	= 1024,
-	CLD_MAX_PKT_MSG		= 128,
-	CLD_MAX_MSG_SZ		= CLD_MAX_PKT_MSG * 1024, /**< maximum total
-					      msg size, including all packets */
-};
-
-/*
- * We use a unified format for sid so it can be searched in log files (* in vi).
- */
-#define SIDFMT   "%016llX"
-#define SIDARG(sid)  cld_sid2llu(sid)
-
-/** available RPC operations */
-enum cld_msg_ops {
-	/* client -> server */
-	cmo_nop			= 0,		/**< no op */
-	cmo_new_sess		= 1,		/**< new session */
-	cmo_open		= 2,		/**< open file */
-	cmo_get_meta		= 3,		/**< get metadata */
-	cmo_get			= 4,		/**< get metadata + data */
-	cmo_put			= 6,		/**< put data */
-	cmo_close		= 7,		/**< close file */
-	cmo_del			= 8,		/**< delete file */
-	cmo_lock		= 9,		/**< lock */
-	cmo_unlock		= 10,		/**< unlock */
-	cmo_trylock		= 11,		/**< trylock */
-	cmo_ack			= 12,		/**< ack of seqid rx'd */
-	cmo_end_sess		= 13,		/**< end session */
-
-	/* server -> client */
-	cmo_ping		= 30,		/**< server to client ping */
-	cmo_not_master		= 31,		/**< I am not the master! */
-	cmo_event		= 32,		/**< server->cli async event */
-	cmo_ack_frag		= 33,		/**< ack partial msg */
-};
-
-/** CLD error codes */
-enum cle_err_codes {
-	CLE_OK			= 0,		/**< success / no error */
-	CLE_SESS_EXISTS		= 1,		/**< session exists */
-	CLE_SESS_INVAL		= 2,		/**< session doesn't exist */
-	CLE_DB_ERR		= 3,		/**< db error */
-	CLE_BAD_PKT		= 4,		/**< invalid/corrupted packet */
-	CLE_INODE_INVAL		= 5,		/**< inode doesn't exist */
-	CLE_NAME_INVAL		= 6,		/**< inode name invalid */
-	CLE_OOM			= 7,		/**< server out of memory */
-	CLE_FH_INVAL		= 8,		/**< file handle invalid */
-	CLE_DATA_INVAL		= 9,		/**< invalid data pkt */
-	CLE_LOCK_INVAL		= 10,		/**< invalid lock */
-	CLE_LOCK_CONFLICT	= 11,		/**< conflicting lock held */
-	CLE_LOCK_PENDING	= 12,		/**< lock waiting to be acq. */
-	CLE_MODE_INVAL		= 13,		/**< op incompat. w/ file mode */
-	CLE_INODE_EXISTS	= 14,		/**< inode exists */
-	CLE_DIR_NOTEMPTY	= 15,		/**< dir not empty */
-	CLE_INTERNAL_ERR	= 16,		/**< nonspecific internal err */
-	CLE_TIMEOUT		= 17,		/**< session timed out */
-	CLE_SIG_INVAL		= 18,		/**< HMAC sig bad / auth failed */
-};
-
-/** availble OPEN mode flags */
-enum cld_open_modes {
-	COM_READ		= (1 << 0),	/**< read */
-	COM_WRITE		= (1 << 1),	/**< write */
-	COM_LOCK		= (1 << 2),	/**< lock */
-	COM_ACL			= (1 << 3),	/**< ACL update */
-	COM_CREATE		= (1 << 4),	/**< create file, if not exist */
-	COM_EXCL		= (1 << 5),	/**< fail create if file exists */
-	COM_DIRECTORY		= (1 << 6),	/**< operate on a directory */
-};
-
-/** potential events client may receive */
-enum cld_events {
-	CE_UPDATED		= (1 << 0),	/**< contents updated */
-	CE_DELETED		= (1 << 1),	/**< inode deleted */
-	CE_LOCKED		= (1 << 2),	/**< lock acquired */
-	CE_MASTER_FAILOVER	= (1 << 3),	/**< master failover */
-	CE_SESS_FAILED		= (1 << 4),
-};
-
-/** LOCK flags */
-enum cld_lock_flags {
-	CLF_SHARED		= (1 << 0),	/**< a shared (read) lock */
-};
-
-/** CLD packet flags */
-enum cld_packet_flags {
-	CPF_FIRST		= (1 << 0),	/**< first fragment */
-	CPF_LAST		= (1 << 1),	/**< last fragment */
-};
-
-/** header for each packet */
-struct cld_packet {
-	uint8_t		magic[CLD_MAGIC_SZ];	/**< magic number; constant */
-	uint64_t	seqid;			/**< sequence id */
-	uint8_t		sid[CLD_SID_SZ];	/**< client id */
-	uint32_t	flags;			/**< CPF_xxx flags */
-	uint8_t		res[4];
-	char		user[CLD_MAX_USERNAME];	/**< authenticated user */
-};
-
-/** header for each message */
-struct cld_msg_hdr {
-	uint8_t		magic[CLD_MAGIC_SZ];	/**< magic number; constant */
-	uint64_t	xid;			/**< opaque message id */
-	uint8_t		op;			/**< operation code */
-	uint8_t		res1[7];
-};
-
-/** standard response for each message */
-struct cld_msg_resp {
-	struct cld_msg_hdr	hdr;
-
-	uint32_t		code;		/**< error code, CLE_xxx */
-	uint32_t		rsv;		/**< reserved */
-	uint64_t		xid_in;		/**< C->S xid */
-};
-
-/** ACK-FRAG message */
-struct cld_msg_ack_frag {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		seqid;		/**< sequence id to ack */
-};
-
-/** OPEN message */
-struct cld_msg_open {
-	struct cld_msg_hdr	hdr;
-
-	uint32_t		mode;		/**< open mode, COM_xxx */
-	uint32_t		events;		/**< events mask, CE_xxx */
-	uint16_t		name_len;	/**< length of file name */
-	uint8_t			res[6];
-	/* inode name */
-};
-
-/** OPEN message response */
-struct cld_msg_open_resp {
-	struct cld_msg_resp	resp;
-
-	uint64_t		fh;		/**< handle opened */
-};
-
-/** GET message */
-struct cld_msg_get {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** GET message response */
-struct cld_msg_get_resp {
-	struct cld_msg_resp	resp;
-
-	uint64_t		inum;		/**< unique inode number */
-	uint32_t		ino_len;	/**< inode name len */
-	uint32_t		size;		/**< data size */
-	uint64_t		version;	/**< inode version */
-	uint64_t		time_create;	/**< creation time */
-	uint64_t		time_modify;	/**< last modification time */
-	uint32_t		flags;		/**< inode flags; CIFL_xxx */
-	uint8_t			res[4];
-	/* inode name */
-};
-
-/** PUT message */
-struct cld_msg_put {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		data_size;	/**< total size of data */
-	uint8_t			res[4];
-};
-
-/** CLOSE message */
-struct cld_msg_close {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** DEL message */
-struct cld_msg_del {
-	struct cld_msg_hdr	hdr;
-
-	uint16_t		name_len;	/**< length of file name */
-	uint8_t			res[6];
-	/* inode name */
-};
-
-/** UNLOCK message */
-struct cld_msg_unlock {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** LOCK message */
-struct cld_msg_lock {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		flags;		/**< CLF_xxx */
-	uint8_t			res[4];
-};
-
-/** Server-to-client EVENT message */
-struct cld_msg_event {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		events;		/**< CE_xxx */
-	uint8_t			res[4];
-};
-
-#endif /* __CLD_MSG_H__ */
diff --git a/include/cldc.h b/include/cldc.h
index f1db7d2..330adb0 100644
--- a/include/cldc.h
+++ b/include/cldc.h
@@ -19,10 +19,11 @@
  *
  */
 
+
 #include <sys/types.h>
 #include <stdbool.h>
 #include <glib.h>
-#include <cld_msg.h>
+#include <lib/cld_msg_rpc.h>
 #include <cld_common.h>
 #include <hail_log.h>
 
@@ -35,33 +36,30 @@ struct cldc_call_opts {
 	void		*private;
 
 	/* private; lib-owned */
-	enum cld_msg_ops op;
-	union {
-		struct {
-			struct cld_msg_get_resp resp;
-			const char *buf;
-			unsigned int size;
-			char inode_name[CLD_INODE_NAME_MAX];
-		} get;
-	} u;
+	struct cld_msg_get_resp resp;
 };
 
+void cldc_call_opts_get_data(struct cldc_call_opts *copts,
+			     char **data, size_t *data_len);
+
 struct cldc_pkt_info {
 	int		pkt_len;
+	int		hdr_len;
 	int		retries;
+	char		user[CLD_MAX_USERNAME];
 
 	/* must be at end of struct */
-	struct cld_packet pkt;
-	uint8_t		data[0];
+	char		data[0];
 };
 
 /** an outgoing message, from client to server */
 struct cldc_msg {
 	uint64_t	xid;
-
+	enum cld_msg_type type;
 	struct cldc_session *sess;
 
-	ssize_t		(*cb)(struct cldc_msg *, const void *, size_t, bool);
+	ssize_t		(*cb)(struct cldc_msg *, const void *, size_t,
+				enum cle_err_codes resp_rc);
 	void		*cb_private;
 
 	struct cldc_call_opts copts;
@@ -70,18 +68,15 @@ struct cldc_msg {
 
 	time_t		expire_time;
 
-	int		data_len;
 	int		n_pkts;
 
-	struct cldc_pkt_info *pkt_info[CLD_MAX_PKT_MSG];
-
 	/* must be at end of struct */
-	uint8_t		data[0];
+	struct cldc_pkt_info *pkt_info[0];
 };
 
 /** an open file handle associated with a session */
 struct cldc_fh {
-	uint64_t	fh_le;			/* fh id, LE */
+	uint64_t	fh;
 	struct cldc_session *sess;
 	bool		valid;
 };
@@ -128,8 +123,11 @@ struct cldc_session {
 
 	bool		confirmed;
 
+	enum cld_msg_type msg_buf_type;
 	unsigned int	msg_buf_len;
 	char		msg_buf[CLD_MAX_MSG_SZ];
+	char		payload[CLD_MAX_PAYLOAD_SZ];
+	char		inode_name_temp[CLD_INODE_NAME_MAX];
 };
 
 /** Information for a single CLD server host */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 68be429..6275ca6 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,15 +1,31 @@
 
+BUILT_SOURCES		= cld_msg_rpc.h
+
 EXTRA_DIST =
 	libcldc.pc.in libcldc-uninstalled.pc.in
 
 INCLUDES		= -I$(top_srcdir)/include	\
 			  @GLIB_CFLAGS@
 
+mostlyclean-local:
+	-rm -f *_rpc.h *_rpc_xdr.c
+
+%_rpc.h: %_rpc.x
+	rpcgen -h $< > $@
+
+%_rpc_xdr.c: %_rpc.x
+	rpcgen -c $< > $@
+
 LINK = $(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -o $@
 
 lib_LTLIBRARIES		= libcldc.la
 
-libcldc_la_SOURCES	= cldc.c cldc-udp.c cldc-dns.c common.c
+libcldc_la_SOURCES	= cldc.c \
+			cldc-udp.c \
+			cldc-dns.c \
+			common.c \
+			cld_fmt.c \
+			cld_msg_rpc_xdr.c
 
 libcldc_la_LDFLAGS = \
 	-version-info $(LIBCLDC_CURRENT):$(LIBCLDC_REVISION):$(LIBCLDC_AGE) \
@@ -21,3 +37,4 @@ noinst_LIBRARIES	= libtimer.a
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libcldc.pc
 
+include_HEADERS = cld_msg_rpc.h
diff --git a/lib/cld_fmt.c b/lib/cld_fmt.c
new file mode 100644
index 0000000..607a035
--- /dev/null
+++ b/lib/cld_fmt.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2010, Colin McCabe
+ *
+ * This program 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.
+ *
+ * This program 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 this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+#include <cld-private.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include "cld_fmt.h"
+#include "cld_msg_rpc.h"
+#include <hail_log.h>
+#include <syslog.h>
+
+int __cld_authcheck(struct hail_log *log, const char *key,
+		const char *buf, size_t buf_len,
+		const char *sha)
+{
+	unsigned char md[SHA_DIGEST_LENGTH];
+	unsigned int md_len = 0;
+
+	if (!key[0])
+		return EINVAL;
+	HMAC(EVP_sha1(), key, strlen(key),
+	     (uint8_t *)buf, buf_len, md, &md_len);
+
+	if (md_len != SHA_DIGEST_LENGTH) {
+		HAIL_CRIT(log, "__cld_authsign BUG: md_len != "
+			"SHA_DIGEST_LENGTH");
+		return EBADMSG; /* BUG */
+	}
+
+	if (memcmp(md, sha, SHA_DIGEST_LENGTH))
+		return EPERM;
+
+	return 0;
+}
+
+int __cld_authsign(struct hail_log *log, const char *key,
+		const char *buf, size_t buf_len,
+		char *sha)
+{
+	unsigned char md[SHA_DIGEST_LENGTH];
+	unsigned int md_len = 0;
+
+	if (!key[0]) {
+		HAIL_DEBUG(log, "%s: invalid key\n", __func__);
+		return EINVAL;
+	}
+
+	HMAC(EVP_sha1(), key, strlen(key),
+	     (uint8_t *)buf, buf_len, md, &md_len);
+
+	if (md_len != SHA_DIGEST_LENGTH) {
+		HAIL_CRIT(log, "__cld_authsign BUG: md_len != "
+			"SHA_DIGEST_LENGTH");
+		return EBADMSG;
+	}
+
+	memcpy(sha, md, SHA_DIGEST_LENGTH);
+	return 0;
+}
+
+const char *__cld_msg_type_to_str(enum cld_msg_type type)
+{
+	switch (type) {
+	case CMT_NOP:		return "CMT_NOP";
+	case CMT_NEW_SESS:	return "CMT_NEW_SESS";
+	case CMT_OPEN:		return "CMT_OPEN";
+	case CMT_GET_META:	return "CMT_GET_META";
+	case CMT_GET:		return "CMT_GET";
+	case CMT_PUT:		return "CMT_PUT";
+	case CMT_CLOSE:		return "CMT_CLOSE";
+	case CMT_DEL:		return "CMT_DEL";
+	case CMT_LOCK:		return "CMT_LOCK";
+	case CMT_UNLOCK:	return "CMT_UNLOCK";
+	case CMT_TRYLOCK:	return "CMT_TRYLOCK";
+	case CMT_ACK:		return "CMT_ACK";
+	case CMT_END_SESS:	return "CMT_END_SESS";
+	case CMT_PING:		return "CMT_PING";
+	case CMT_NOT_MASTER:	return "CMT_NOT_MASTER";
+	case CMT_EVENT:		return "CMT_EVENT";
+	case CMT_ACK_FRAG:	return "CMT_ACK_FRAG";
+	default:		return "(unknown)";
+	}
+}
+
+const char *__cld_pkt_hdr_to_str(char *scratch,
+			   const char *pkt_hdr, size_t pkt_len)
+{
+	XDR xin;
+	struct cld_pkt_hdr pkt;
+	bool bad_magic;
+	char temp[50], temp2[50];
+	uint64_t seqid;
+	struct cld_pkt_ftr *foot;
+	size_t hdr_len;
+
+	temp[0] = '\0';
+	temp2[0] = '\0';
+	foot = (struct cld_pkt_ftr *)(pkt_hdr + pkt_len - CLD_PKT_FTR_LEN);
+	seqid = le64_to_cpu(foot->seqid);
+
+	if (pkt_len <= CLD_PKT_FTR_LEN) {
+		snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+			 "[MALFORMED: only %d bytes]", pkt_len);
+		return scratch;
+	}
+	xdrmem_create(&xin, (void *)pkt_hdr, pkt_len - CLD_PKT_FTR_LEN,
+		      XDR_DECODE);
+	memset(&pkt, 0, sizeof(pkt));
+	if (!xdr_cld_pkt_hdr(&xin, &pkt)) {
+		xdr_destroy(&xin);
+		snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+			 "[MALFORMED: can't parse]");
+		return scratch;
+	}
+	hdr_len = xdr_getpos(&xin);
+	xdr_destroy(&xin);
+
+	bad_magic = !!(memcmp(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic)));
+	if (pkt.mi.order & CLD_PKT_IS_FIRST) {
+		struct cld_pkt_msg_infos *infos =
+			&pkt.mi.cld_pkt_msg_info_u.mi;
+		snprintf(temp, sizeof(temp), "[TYPE:%s, XID:%llx]",
+			 __cld_msg_type_to_str(infos->type), infos->xid);
+		switch (infos->type) {
+		case CMT_ACK_FRAG: {
+			XDR x;
+			struct cld_msg_ack_frag ack;
+			memset(&ack, 0, sizeof(ack));
+			xdrmem_create(&x, ((char *)pkt_hdr) + hdr_len,
+				      pkt_len - hdr_len - CLD_PKT_FTR_LEN,
+				      XDR_DECODE);
+			if (!xdr_cld_msg_ack_frag(&x, &ack)) {
+				xdr_destroy(&x);
+				snprintf(temp2, sizeof(temp2), "{MALFORMED}");
+				break;
+			}
+			snprintf(temp2, sizeof(temp2), "{seqid:%llx}",
+				ack.seqid);
+			xdr_destroy(&x);
+			break;
+		}
+		default:
+			break;
+		}
+	} else {
+		snprintf(temp, sizeof(temp), "[CONT]");
+	}
+
+	snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+		"<%s%s%s> "
+		"%s USER:'%s' SEQID:%llu %s",
+		((pkt.mi.order & CLD_PKT_IS_FIRST) ? "1st" : ""),
+		((pkt.mi.order & CLD_PKT_IS_LAST) ? "End" : ""),
+		(bad_magic ? "B" : ""),
+		temp, pkt.user, seqid, temp2);
+	xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+	return scratch;
+}
+
+void __cld_dump_buf(const void *buf, size_t len)
+{
+	const unsigned char *buff = buf;
+	size_t off = 0;
+	do {
+		int i;
+		for (i = 0; i < 8; i++) {
+			if (!len)
+				break;
+			printf("%02x ", buff[off++]);
+			len--;
+		}
+		printf("\n");
+	} while (len);
+}
diff --git a/lib/cld_msg_rpc.x b/lib/cld_msg_rpc.x
new file mode 100644
index 0000000..a819ffb
--- /dev/null
+++ b/lib/cld_msg_rpc.x
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010, Colin McCabe
+ *
+ * This program 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.
+ *
+ * This program 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 this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+const CLD_PKT_MAGIC = "CLDc1pkt";
+const CLD_SID_SZ = 8;
+
+const CLD_INODE_NAME_MAX = 256; /**< max total pathname len */
+
+const CLD_MAX_USERNAME = 32;
+
+const CLD_MAX_PKT_MSG_SZ = 1024; /**< The maximum number of message bytes we'll
+				   put in a single packet */
+
+const CLD_MAX_PAYLOAD_SZ = 131072; /**< Maximum length of the data that can be
+					sent with get or put. In some sense,
+					this is part of cld's API, and
+					shouldn't be changed lightly.  */
+
+const CLD_MAX_MSG_SZ = 196608; /**< Maximum size of a single message
+					including all packets. */
+
+const CLD_MAX_SECRET_KEY = 128; /**< includes req. nul */
+
+/** available RPC operations */
+enum cld_msg_type {
+	/* client -> server */
+	CMT_NOP			= 0,	/**< no op */
+	CMT_NEW_SESS		= 1,	/**< new session */
+	CMT_OPEN		= 2,	/**< open file */
+	CMT_GET_META		= 3,	/**< get metadata */
+	CMT_GET			= 4,	/**< get metadata + data */
+	CMT_PUT			= 6,	/**< put data */
+	CMT_CLOSE		= 7,	/**< close file */
+	CMT_DEL			= 8,	/**< delete file */
+	CMT_LOCK		= 9,	/**< lock */
+	CMT_UNLOCK		= 10,	/**< unlock */
+	CMT_TRYLOCK		= 11,	/**< trylock */
+	CMT_ACK			= 12,	/**< ack of seqid rx'd */
+	CMT_END_SESS		= 13,	/**< end session */
+
+	/* server -> client */
+	CMT_PING		= 14,	/**< server to client ping */
+	CMT_NOT_MASTER		= 15,	/**< I am not the master! */
+	CMT_EVENT		= 16,	/**< server->cli async event */
+	CMT_ACK_FRAG		= 17, 	/**< ack partial msg */
+
+	CMT_AFTER_LAST
+};
+
+/** CLD error codes */
+enum cle_err_codes {
+	CLE_OK			= 0,	/**< success / no error */
+	CLE_SESS_EXISTS		= 1,	/**< session exists */
+	CLE_SESS_INVAL		= 2,	/**< session doesn't exist */
+	CLE_DB_ERR		= 3,	/**< db error */
+	CLE_BAD_PKT		= 4,	/**< invalid/corrupted packet */
+	CLE_INODE_INVAL		= 5,	/**< inode doesn't exist */
+	CLE_NAME_INVAL		= 6,	/**< inode name invalid */
+	CLE_OOM			= 7,	/**< server out of memory */
+	CLE_FH_INVAL		= 8,	/**< file handle invalid */
+	CLE_DATA_INVAL		= 9,	/**< invalid data pkt */
+	CLE_LOCK_INVAL		= 10,	/**< invalid lock */
+	CLE_LOCK_CONFLICT 	= 11,	/**< conflicting lock held */
+	CLE_LOCK_PENDING	= 12,	/**< lock waiting to be acq. */
+	CLE_MODE_INVAL		= 13,	/**< op incompat. w/ file mode */
+	CLE_INODE_EXISTS	= 14,	/**< inode exists */
+	CLE_DIR_NOTEMPTY	= 15,	/**< dir not empty */
+	CLE_INTERNAL_ERR	= 16,	/**< nonspecific internal err */
+	CLE_TIMEOUT 		= 17,	/**< session timed out */
+	CLE_SIG_INVAL 		= 18	/**< HMAC sig bad / auth failed */
+};
+
+/** availble OPEN mode flags */
+enum cld_open_modes {
+	COM_READ		= 0x01,	/**< read */
+	COM_WRITE		= 0x02,	/**< write */
+	COM_LOCK		= 0x04,	/**< lock */
+	COM_ACL			= 0x08,	/**< ACL update */
+	COM_CREATE		= 0x10,	/**< create file, if not exist */
+	COM_EXCL		= 0x20,	/**< fail create if file exists */
+	COM_DIRECTORY		= 0x40	/**< operate on a directory */
+};
+
+/** potential events client may receive */
+enum cld_events {
+	CE_UPDATED		= 0x01,	/**< contents updated */
+	CE_DELETED		= 0x02,	/**< inode deleted */
+	CE_LOCKED		= 0x04,	/**< lock acquired */
+	CE_MASTER_FAILOVER	= 0x08,	/**< master failover */
+	CE_SESS_FAILED		= 0x10
+};
+
+/** LOCK flags */
+enum cld_lock_flags {
+	CLF_SHARED		= 0x01	/**< a shared (read) lock */
+};
+
+/** Describes whether a packet begins, continues, or ends a message. */
+enum cld_pkt_order_t {
+	CLD_PKT_ORD_MID = 0x0,
+	CLD_PKT_ORD_FIRST = 0x1,
+	CLD_PKT_ORD_LAST = 0x2,
+	CLD_PKT_ORD_FIRST_LAST = 0x3
+};
+const CLD_PKT_IS_FIRST = 0x1;
+const CLD_PKT_IS_LAST = 0x2;
+
+/** Information that appears only in the first packet */
+struct cld_pkt_msg_infos {
+	hyper			xid;		/**< opaque message id */
+	enum cld_msg_type	type;		/**< type of the message */
+};
+
+/** Information about the message contained in this packet */
+union cld_pkt_msg_info switch (enum cld_pkt_order_t order) {
+	case CLD_PKT_ORD_MID:
+	case CLD_PKT_ORD_LAST:
+		void;
+	case CLD_PKT_ORD_FIRST:
+	case CLD_PKT_ORD_FIRST_LAST:
+		struct cld_pkt_msg_infos mi;
+};
+
+/** header for each packet */
+struct cld_pkt_hdr {
+	hyper		magic;		/**< magic number; constant */
+	hyper		sid;		/**< client id */
+	string		user<CLD_MAX_USERNAME>;	/**< authenticated user */
+	struct cld_pkt_msg_info mi;
+};
+
+/** generic response for PUT, CLOSE, DEL, LOCK, UNLOCK */
+struct cld_msg_generic_resp {
+	enum cle_err_codes	code;		/**< error code, CLE_xxx */
+	hyper			xid_in;		/**< C->S xid */
+};
+
+/** ACK-FRAG message */
+struct cld_msg_ack_frag {
+	hyper			seqid;		/**< sequence id to ack */
+};
+
+/** OPEN message */
+struct cld_msg_open {
+	int			mode;		/**< open mode, COM_xxx */
+	int			events;		/**< events mask, CE_xxx */
+	string			inode_name<CLD_INODE_NAME_MAX>;
+};
+
+/** OPEN message response */
+struct cld_msg_open_resp {
+	struct cld_msg_generic_resp msg;
+	hyper			fh;		/**< handle opened */
+};
+
+/** GET message */
+struct cld_msg_get {
+	hyper			fh;		/**< open file handle */
+};
+
+/** GET message response */
+struct cld_msg_get_resp {
+	struct cld_msg_generic_resp msg;
+	hyper			inum;		/**< unique inode number */
+	hyper			vers;		/**< inode version */
+	hyper			time_create;	/**< creation time */
+	hyper			time_modify;	/**< last modification time */
+	int			flags;		/**< inode flags; CIFL_xxx */
+	string			inode_name<CLD_INODE_NAME_MAX>;
+	opaque			data<CLD_MAX_PAYLOAD_SZ>;
+};
+
+/** PUT message */
+struct cld_msg_put {
+	hyper			fh;		/**< open file handle */
+	opaque			data<CLD_MAX_PAYLOAD_SZ>;
+};
+
+/** CLOSE message */
+struct cld_msg_close {
+	hyper			fh;		/**< open file handle */
+};
+
+/** DEL message */
+struct cld_msg_del {
+	string			inode_name<CLD_INODE_NAME_MAX>;
+};
+
+/** UNLOCK message */
+struct cld_msg_unlock {
+	uint64_t		fh;		/**< open file handle */
+};
+
+/** LOCK message */
+struct cld_msg_lock {
+	hyper			fh;		/**< open file handle */
+	int			flags;		/**< CLF_xxx */
+};
+
+/** Server-to-client EVENT message */
+struct cld_msg_event {
+	hyper			fh;		/**< open file handle */
+	int			events;		/**< CE_xxx */
+};
diff --git a/lib/cldc.c b/lib/cldc.c
index bc4b48c..603700c 100644
--- a/lib/cldc.c
+++ b/lib/cldc.c
@@ -35,6 +35,8 @@
 #include <glib.h>
 #include <cld-private.h>
 #include <cldc.h>
+#include <cld_fmt.h>
+#include <lib/cld_msg_rpc.h>
 #include <syslog.h>
 
 enum {
@@ -45,14 +47,8 @@ enum {
 	CLDC_SESS_EXPIRE	= 2 * 60,
 };
 
-static bool authsign(struct cldc_session *, struct cld_packet *, size_t);
 static int sess_send_pkt(struct cldc_session *sess,
-			 const struct cld_packet *pkt, size_t pkt_len);
-
-static const struct cld_msg_hdr def_msg_ack = {
-	.magic		= CLD_MSG_MAGIC,
-	.op		= cmo_ack,
-};
+			const void *pkt, size_t pkt_len);
 
 #ifndef HAVE_STRNLEN
 static size_t strnlen(const char *s, size_t maxlen)
@@ -80,6 +76,13 @@ static size_t strnlen(const char *s, size_t maxlen)
 #define EBADE 52
 #endif
 
+void cldc_call_opts_get_data(struct cldc_call_opts *copts,
+			     char **data, size_t *data_len)
+{
+	*data = copts->resp.data.data_val;
+	*data_len = copts->resp.data.data_len;
+}
+
 static void cldc_errlog(int prio, const char *fmt, ...)
 {
 	char buf[200];
@@ -93,94 +96,128 @@ static void cldc_errlog(int prio, const char *fmt, ...)
 
 static int ack_seqid(struct cldc_session *sess, uint64_t seqid_le)
 {
-	struct cld_packet *pkt;
-	struct cld_msg_hdr *resp;
-	size_t pkt_len;
-
-	pkt_len = sizeof(*pkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	pkt = alloca(pkt_len);
-	memset(pkt, 0, pkt_len);
-
-	memcpy(pkt->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	pkt->seqid = seqid_le;
-	memcpy(pkt->sid, sess->sid, CLD_SID_SZ);
-	pkt->flags = cpu_to_le32(CPF_FIRST | CPF_LAST);
-	strncpy(pkt->user, sess->user, CLD_MAX_USERNAME - 1);
-
-	resp = (struct cld_msg_hdr *) (pkt + 1);
-	memcpy(resp, &def_msg_ack, sizeof(*resp));
-
-	if (!authsign(sess, pkt, pkt_len)) {
-		HAIL_INFO(&sess->log, "authsign failed 2");
-		return -1;
+	XDR xdrs;
+	size_t hdr_len, total_len;
+	char buf[CLD_MAX_PKT_MSG_SZ];
+	struct cld_pkt_hdr pkt;
+	struct cld_pkt_ftr *foot;
+	int ret;
+	static const char * const magic = CLD_PKT_MAGIC;
+
+	/* Construct ACK packet */
+	memset(&pkt, 0, sizeof(struct cld_pkt_hdr));
+	memcpy(&pkt.magic, magic, sizeof(pkt.magic));
+	memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+	pkt.user = sess->user;
+	pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+	pkt.mi.cld_pkt_msg_info_u.mi.xid = 0;
+	pkt.mi.cld_pkt_msg_info_u.mi.type = CMT_ACK;
+
+	/* Serialize packet */
+	xdrmem_create(&xdrs, (char *)buf,
+		      sizeof(buf) - CLD_PKT_FTR_LEN, XDR_ENCODE);
+	if (!xdr_cld_pkt_hdr(&xdrs, &pkt)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to encode header "
+			"for ack_seqid %lld", __func__, seqid_le);
+		xdr_destroy(&xdrs);
+		return -1009;
 	}
 
-	return sess_send_pkt(sess, pkt, pkt_len);
+	/* Fill in footer */
+	hdr_len = xdr_getpos(&xdrs);
+	total_len = hdr_len + CLD_PKT_FTR_LEN;
+	foot = (struct cld_pkt_ftr *)(buf + hdr_len);
+	foot->seqid = seqid_le;
+	xdr_destroy(&xdrs);
+	ret = __cld_authsign(&sess->log, sess->user,
+			buf, total_len - SHA_DIGEST_LENGTH, foot->sha);
+	if (ret)
+		return ret;
+
+	return sess_send_pkt(sess, buf, total_len);
 }
 
-static int cldc_rx_generic(struct cldc_session *sess,
-			   const struct cld_packet *pkt,
-			   const void *msgbuf,
-			   size_t buflen)
+static int rxmsg_generic(struct cldc_session *sess,
+			const struct cld_pkt_hdr *pkt,
+			const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_resp *resp = msgbuf;
+	XDR xdrs;
+	struct cld_msg_generic_resp resp;
 	struct cldc_msg *req = NULL;
-	ssize_t rc;
 	GList *tmp;
 
-	if (buflen < sizeof(*resp))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdr_cld_msg_generic_resp(&xdrs, &resp)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to decode "
+			  "cld_msg_generic_resp", __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
+	/* Find out which outbound message this was a response to */
 	tmp = sess->out_msg;
 	while (tmp) {
 		req = tmp->data;
 
-		HAIL_DEBUG(&sess->log, "rx_gen: comparing req->xid (%llu) "
-			"with resp->xid_in (%llu)",
-			(unsigned long long) le64_to_cpu(req->xid),
-			(unsigned long long) le64_to_cpu(resp->xid_in));
+		HAIL_DEBUG(&sess->log, "%s: comparing req->xid (%llu) "
+				"with resp.xid_in (%llu)",
+				__func__,
+				(unsigned long long) req->xid,
+				(unsigned long long) resp.xid_in);
 
-		if (req->xid == resp->xid_in)
+		if (req->xid == resp.xid_in)
 			break;
 		tmp = tmp->next;
 	}
-	if (!tmp)
+	if (!tmp) {
+		HAIL_DEBUG(&sess->log, "%s: no match found with "
+			   "xid_in %llu\n", __func__, resp.xid_in);
 		return -1005;
+	}
 
 	if (req->done) {
-		HAIL_DEBUG(&sess->log, "rx_gen: re-acking");
+		HAIL_DEBUG(&sess->log, "%s: re-acking\n", __func__);
 	} else {
-		HAIL_DEBUG(&sess->log, "rx_gen: issuing completion, acking");
-
+		HAIL_DEBUG(&sess->log, "%s: issuing completion, acking\n",
+			   __func__);
 		req->done = true;
 
 		if (req->cb) {
-			rc = req->cb(req, msgbuf, buflen, true);
+			ssize_t rc = req->cb(req, sess->msg_buf,
+					     sess->msg_buf_len, resp.code);
 			if (rc < 0)
 				return rc;
 		}
 	}
 
-	return ack_seqid(sess, pkt->seqid);
+	return ack_seqid(sess, foot->seqid);
 }
 
-static int cldc_rx_ack_frag(struct cldc_session *sess,
-			    const struct cld_packet *pkt,
-			    const void *msgbuf,
-			    size_t buflen)
+static int rxmsg_ack_frag(struct cldc_session *sess,
+			const struct cld_pkt_hdr *pkt,
+			const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_ack_frag *ack_msg = msgbuf;
-	struct cldc_msg *req = NULL;
+	XDR xdrs;
+	struct cld_msg_ack_frag ack_msg;
 	GList *tmp;
 
-	if (buflen < sizeof(*ack_msg))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	memset(&ack_msg, 0, sizeof(ack_msg));
+	if (!xdr_cld_msg_ack_frag(&xdrs, &ack_msg)) {
+		HAIL_INFO(&sess->log, "%s: failed to decode ack_msg",
+				__func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
-	HAIL_DEBUG(&sess->log, "ack-frag: seqid %llu, want to ack",
-		   (unsigned long long) ack_msg->seqid);
+	HAIL_INFO(&sess->log, "ack-frag: seqid %llu, want to ack",
+				ack_msg.seqid);
 
 	tmp = sess->out_msg;
 	while (tmp) {
+		struct cldc_msg *req;
 		int i;
 
 		req = tmp->data;
@@ -188,15 +225,20 @@ static int cldc_rx_ack_frag(struct cldc_session *sess,
 
 		for (i = 0; i < req->n_pkts; i++) {
 			struct cldc_pkt_info *pi;
+			struct cld_pkt_ftr *f;
+			uint64_t seqid;
 
 			pi = req->pkt_info[i];
 			if (!pi)
 				continue;
-			if (pi->pkt.seqid != ack_msg->seqid)
+			f = (struct cld_pkt_ftr *)
+				pi->data + (pi->pkt_len - CLD_PKT_FTR_LEN);
+			seqid = le64_to_cpu(f->seqid);
+			if (seqid != ack_msg.seqid)
 				continue;
 
 			HAIL_DEBUG(&sess->log, "ack-frag: seqid %llu, expiring",
-				   (unsigned long long) ack_msg->seqid);
+					(unsigned long long)ack_msg.seqid);
 
 			req->pkt_info[i] = NULL;
 			free(pi);
@@ -206,21 +248,27 @@ static int cldc_rx_ack_frag(struct cldc_session *sess,
 	return 0;
 }
 
-static int cldc_rx_event(struct cldc_session *sess,
-			 const struct cld_packet *pkt,
-			 const void *msgbuf,
-			 size_t buflen)
+static int rxmsg_event(struct cldc_session *sess,
+			const struct cld_pkt_hdr *pkt,
+			const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_event *ev = msgbuf;
+	XDR xdrs;
+	struct cld_msg_event ev;
 	struct cldc_fh *fh = NULL;
 	int i;
 
-	if (buflen < sizeof(*ev))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdr_cld_msg_event(&xdrs, &ev)) {
+		HAIL_INFO(&sess->log, "%s: failed to decode cld_msg_event",
+			 __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
 	for (i = 0; i < sess->fh->len; i++) {
 		fh = &g_array_index(sess->fh, struct cldc_fh, i);
-		if (fh->fh_le == ev->fh)
+		if (fh->fh == ev.fh)
 			break;
 		else
 			fh = NULL;
@@ -229,21 +277,11 @@ static int cldc_rx_event(struct cldc_session *sess,
 	if (!fh)
 		return -1011;
 
-	sess->ops->event(sess->private, sess, fh,
-			 le32_to_cpu(ev->events));
+	sess->ops->event(sess->private, sess, fh, ev.events);
 
 	return 0;
 }
 
-static int cldc_rx_not_master(struct cldc_session *sess,
-			      const struct cld_packet *pkt,
-			      const void *msgbuf,
-			      size_t buflen)
-{
-	HAIL_DEBUG(&sess->log, "FIXME: not-master message received");
-	return -1055;	/* FIXME */
-}
-
 static void cldc_msg_free(struct cldc_msg *msg)
 {
 	int i;
@@ -251,7 +289,7 @@ static void cldc_msg_free(struct cldc_msg *msg)
 	if (!msg)
 		return;
 
-	for (i = 0; i < CLD_MAX_PKT_MSG; i++)
+	for (i = 0; i < msg->n_pkts; i++)
 		free(msg->pkt_info[i]);
 
 	free(msg);
@@ -278,214 +316,130 @@ static void sess_expire_outmsg(struct cldc_session *sess, time_t current_time)
 	sess->msg_scan_time = current_time + CLDC_MSG_SCAN;
 }
 
-static const char *user_key(struct cldc_session *sess, const char *user)
+static int rx_complete(struct cldc_session *sess,
+		const struct cld_pkt_hdr *pkt,
+		const struct cld_pkt_ftr *foot)
 {
-	if (strcmp(sess->user, user))
-		return NULL;
-
-	return sess->secret_key;
-}
-
-static bool authcheck(struct cldc_session *sess, const struct cld_packet *pkt,
-		      size_t buflen)
-{
-	size_t userlen = strnlen(pkt->user, sizeof(pkt->user));
-	const char *key;
-	unsigned char md[SHA_DIGEST_LENGTH];
-	unsigned int md_len = 0;
-	const void *buf = pkt;
-
-	/* forbid zero-len and max-len (no nul) usernames */
-	if (userlen < 1 || userlen >= sizeof(pkt->user))
-		return false;
-
-	key = user_key(sess, pkt->user);
-	if (!key)
-		return false;
-
-	HMAC(EVP_sha1(), key, strlen(key), buf, buflen - SHA_DIGEST_LENGTH,
-	     md, &md_len);
-
-	if (md_len != SHA_DIGEST_LENGTH)
-		HAIL_INFO(&sess->log,
-			"authsign BUG: md_len != SHA_DIGEST_LENGTH");
-
-	if (memcmp(buf + buflen - SHA_DIGEST_LENGTH, md, SHA_DIGEST_LENGTH))
-		return false;
-
-	return true;
+	switch (sess->msg_buf_type) {
+	case CMT_ACK:
+		HAIL_INFO(&sess->log, "%s: received unexpected ACK\n",
+				__func__);
+		return -EBADRQC;
+	case CMT_PING:
+		/* send out an ACK */
+		return ack_seqid(sess, foot->seqid);
+	case CMT_NOT_MASTER:
+		HAIL_INFO(&sess->log, "FIXME: not-master message received");
+		return -1055;	/* FIXME */
+	case CMT_EVENT:
+		return rxmsg_event(sess, pkt, foot);
+	case CMT_ACK_FRAG:
+		return rxmsg_ack_frag(sess, pkt, foot);
+	default:
+		return rxmsg_generic(sess, pkt, foot);
+	}
 }
 
-static bool authsign(struct cldc_session *sess, struct cld_packet *pkt,
-		     size_t buflen)
+/** Accepts a packet's sequence ID.
+ * Depending on the message type, this may involve initializing the session's
+ * sequence ID, validating that the packet's ID is in range, or doing nothing.
+ *
+ * @param sess		The session
+ * @param seqid		The sequence ID
+ * @param type		The message type
+ *
+ * @return		0 on success; error code otherwise
+ */
+static int accept_seqid(struct cldc_session *sess, uint64_t seqid,
+			enum cld_msg_type type)
 {
-	const char *key;
-	unsigned char md[SHA_DIGEST_LENGTH];
-	unsigned int md_len = 0;
-	void *buf = pkt;
-
-	key = user_key(sess, pkt->user);
-	if (!key)
-		return false;
-
-	HMAC(EVP_sha1(), key, strlen(key), buf, buflen - SHA_DIGEST_LENGTH,
-	     md, &md_len);
-
-	if (md_len != SHA_DIGEST_LENGTH)
-		HAIL_INFO(&sess->log,
-			"authsign BUG: md_len != SHA_DIGEST_LENGTH");
-
-	memcpy(buf + (buflen - SHA_DIGEST_LENGTH), md, SHA_DIGEST_LENGTH);
-
-	return true;
-}
+	switch (type) {
+	case CMT_NEW_SESS:
+		/* CMT_NEW_SESS initializes the session's sequence id */
+		sess->next_seqid_in = seqid + 1;
+		sess->next_seqid_in_tr =
+			sess->next_seqid_in - CLDC_MSG_REMEMBER;
+		HAIL_DEBUG(&sess->log, "%s: setting next_seqid_in to %llu",
+			   __func__, (unsigned long long) seqid);
+		return 0;
 
-static const char *opstr(enum cld_msg_ops op)
-{
-	switch (op) {
-	case cmo_nop:		return "cmo_nop";
-	case cmo_new_sess:	return "cmo_new_sess";
-	case cmo_open:		return "cmo_open";
-	case cmo_get_meta:	return "cmo_get_meta";
-	case cmo_get:		return "cmo_get";
-	case cmo_put:		return "cmo_put";
-	case cmo_close:		return "cmo_close";
-	case cmo_del:		return "cmo_del";
-	case cmo_lock:		return "cmo_lock";
-	case cmo_unlock:	return "cmo_unlock";
-	case cmo_trylock:	return "cmo_trylock";
-	case cmo_ack:		return "cmo_ack";
-	case cmo_end_sess:	return "cmo_end_sess";
-	case cmo_ping:		return "cmo_ping";
-	case cmo_not_master:	return "cmo_not_master";
-	case cmo_event:		return "cmo_event";
-	case cmo_ack_frag:	return "cmo_ack_frag";
-	default:		return "(unknown)";
-	}
-}
+	case CMT_NOT_MASTER:
+	case CMT_ACK_FRAG:
+		/* Ignore sequence ID of these types */
+		return 0;
 
-static int cldc_receive_msg(struct cldc_session *sess,
-			    const struct cld_packet *pkt,
-			    size_t pkt_len)
-{
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) sess->msg_buf;
-	size_t msglen = sess->msg_buf_len;
+	default:
+		/* verify that the sequence id is in range */
+		if (seqid == sess->next_seqid_in) {
+			sess->next_seqid_in++;
+			sess->next_seqid_in_tr++;
+			return 0;
+		}
 
-	if (memcmp(msg->magic, CLD_MSG_MAGIC, sizeof(msg->magic))) {
-		HAIL_DEBUG(&sess->log, "receive_pkt: bad msg magic");
-		return -EPROTO;
-	}
+		if (seqid_in_range(seqid,
+				   sess->next_seqid_in_tr,
+				   sess->next_seqid_in)) {
+			return 0;
+		}
 
-	switch(msg->op) {
-	case cmo_nop:
-	case cmo_close:
-	case cmo_del:
-	case cmo_lock:
-	case cmo_unlock:
-	case cmo_trylock:
-	case cmo_put:
-	case cmo_new_sess:
-	case cmo_end_sess:
-	case cmo_open:
-	case cmo_get_meta:
-	case cmo_get:
-		return cldc_rx_generic(sess, pkt, msg, msglen);
-	case cmo_not_master:
-		return cldc_rx_not_master(sess, pkt, msg, msglen);
-	case cmo_ack_frag:
-		return cldc_rx_ack_frag(sess, pkt, msg, msglen);
-	case cmo_event:
-		return cldc_rx_event(sess, pkt, msg, msglen);
-	case cmo_ping:
-		return ack_seqid(sess, pkt->seqid);
-	case cmo_ack:
-		return -EBADRQC;
+		return -EBADSLT;
 	}
-
-	/* unknown op code */
-	return -EBADRQC;
 }
 
 int cldc_receive_pkt(struct cldc_session *sess,
 		     const void *net_addr, size_t net_addrlen,
 		     const void *pktbuf, size_t pkt_len)
 {
-	const struct cld_packet *pkt = pktbuf;
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-	size_t msglen;
+	int ret;
 	struct timeval tv;
 	time_t current_time;
+	struct cld_pkt_hdr pkt;
+	unsigned int hdr_len, msg_len;
+	const struct cld_pkt_ftr *foot;
 	uint64_t seqid;
-	uint32_t pkt_flags;
-	bool first_frag, last_frag, have_new_sess, no_seqid;
-	bool have_get;
+	XDR xdrs;
 
 	gettimeofday(&tv, NULL);
 	current_time = tv.tv_sec;
 
-	if (pkt_len < (sizeof(*pkt) + SHA_DIGEST_LENGTH)) {
-		HAIL_DEBUG(&sess->log, "receive_pkt: msg too short");
+	/* Decode the packet header */
+	if (pkt_len < CLD_PKT_FTR_LEN) {
+		HAIL_DEBUG(&sess->log, "%s: packet too short to have a "
+			   "well-formed footer\n", __func__);
 		return -EPROTO;
 	}
-
-	msglen = pkt_len - sizeof(*pkt) - SHA_DIGEST_LENGTH;
-
-	pkt_flags = le32_to_cpu(pkt->flags);
-	first_frag = pkt_flags & CPF_FIRST;
-	last_frag = pkt_flags & CPF_LAST;
-	have_get = first_frag && (msg->op == cmo_get);
-	have_new_sess = first_frag && (msg->op == cmo_new_sess);
-	no_seqid = first_frag && ((msg->op == cmo_not_master) ||
-				  (msg->op == cmo_ack_frag));
-
-	if (sess->log.verbose) {
-		if (have_get) {
-			struct cld_msg_get_resp *dp;
-			dp = (struct cld_msg_get_resp *) msg;
-			HAIL_DEBUG(&sess->log, "receive_pkt(len %u, op %s"
-				      ", seqid %llu, user %s, size %u)",
-				(unsigned int) pkt_len,
-				opstr(msg->op),
-				(unsigned long long) le64_to_cpu(pkt->seqid),
-				pkt->user,
-				le32_to_cpu(dp->size));
-		} else if (have_new_sess) {
-			struct cld_msg_resp *dp;
-			dp = (struct cld_msg_resp *) msg;
-			HAIL_DEBUG(&sess->log, "receive_pkt(len %u, op %s"
-				      ", seqid %llu, user %s, xid_in %llu)",
-				(unsigned int) pkt_len,
-				opstr(msg->op),
-				(unsigned long long) le64_to_cpu(pkt->seqid),
-				pkt->user,
-				(unsigned long long) le64_to_cpu(dp->xid_in));
-		} else {
-			HAIL_DEBUG(&sess->log, "receive_pkt(len %u, "
-				"flags %s%s, op %s, seqid %llu, user %s)",
-				(unsigned int) pkt_len,
-				first_frag ? "F" : "",
-				last_frag ? "L" : "",
-				first_frag ? opstr(msg->op) : "n/a",
-				(unsigned long long) le64_to_cpu(pkt->seqid),
-				pkt->user);
-		}
+	xdrmem_create(&xdrs, (void *)pktbuf,
+			pkt_len - CLD_PKT_FTR_LEN, XDR_DECODE);
+	memset(&pkt, 0, sizeof(pkt));
+	if (!xdr_cld_pkt_hdr(&xdrs, &pkt)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to decode packet header\n",
+				__func__);
+		xdr_destroy(&xdrs);
+		return -EPROTO;
 	}
-
-	if (memcmp(pkt->magic, CLD_PKT_MAGIC, sizeof(pkt->magic))) {
-		HAIL_DEBUG(&sess->log, "receive_pkt: bad pkt magic");
+	hdr_len = xdr_getpos(&xdrs);
+	xdr_destroy(&xdrs);
+	if (memcmp(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic))) {
+		HAIL_DEBUG(&sess->log, "%s: bad pkt magic\n", __func__);
 		return -EPROTO;
 	}
 
 	/* check HMAC signature */
-	if (!authcheck(sess, pkt, pkt_len)) {
-		HAIL_DEBUG(&sess->log, "receive_pkt: invalid auth");
+	foot = (const struct cld_pkt_ftr *)
+		(((char *)pktbuf) + (pkt_len - CLD_PKT_FTR_LEN));
+	ret = __cld_authcheck(&sess->log, pkt.user,
+		       pktbuf, pkt_len - SHA_DIGEST_LENGTH, foot->sha);
+	if (ret) {
+		HAIL_DEBUG(&sess->log, "%s: invalid auth (ret=%d)\n",
+			   __func__, ret);
 		return -EACCES;
 	}
 
 	/* verify stored server addr matches pkt addr */
 	if (((sess->addr_len != net_addrlen) ||
 	    memcmp(sess->addr, net_addr, net_addrlen))) {
-		HAIL_DEBUG(&sess->log, "receive_pkt: server address mismatch");
+		HAIL_DEBUG(&sess->log, "%s: server address mismatch",
+			   __func__);
 		return -EBADE;
 	}
 
@@ -493,48 +447,39 @@ int cldc_receive_pkt(struct cldc_session *sess,
 	if (current_time >= sess->msg_scan_time)
 		sess_expire_outmsg(sess, current_time);
 
-	if (first_frag)
-		sess->msg_buf_len = 0;
-
-	if ((sess->msg_buf_len + msglen) > CLD_MAX_MSG_SZ) {
-		HAIL_DEBUG(&sess->log, "receive_pkt: bad pkt length");
-		return -EPROTO;
+	if (pkt.mi.order & CLD_PKT_IS_FIRST) {
+		/* This packet begins a new message.
+		 * Determine the new message's type */
+		sess->msg_buf_type = pkt.mi.cld_pkt_msg_info_u.mi.type;
 	}
 
-	memcpy(sess->msg_buf + sess->msg_buf_len, msg, msglen);
-	sess->msg_buf_len += msglen;
-
-	/* verify (or set, for new-sess) sequence id */
-	seqid = le64_to_cpu(pkt->seqid);
-	if (have_new_sess) {
-		sess->next_seqid_in = seqid + 1;
-		sess->next_seqid_in_tr =
-			sess->next_seqid_in - CLDC_MSG_REMEMBER;
-
-		HAIL_DEBUG(&sess->log, "receive_pkt: "
-				      "setting next_seqid_in to %llu",
-				      (unsigned long long) seqid);
-	} else if (!no_seqid) {
-		if (seqid != sess->next_seqid_in) {
-			if (seqid_in_range(seqid,
-					   sess->next_seqid_in_tr,
-					   sess->next_seqid_in))
-				return ack_seqid(sess, pkt->seqid);
-
-			HAIL_DEBUG(&sess->log, "receive_pkt: bad seqid %llu",
-					      (unsigned long long) seqid);
-			return -EBADSLT;
-		}
-		sess->next_seqid_in++;
-		sess->next_seqid_in_tr++;
+	seqid = le64_to_cpu(foot->seqid);
+	ret = accept_seqid(sess, seqid, sess->msg_buf_type);
+	if (ret) {
+		HAIL_DEBUG(&sess->log, "%s: bad seqid %llu",
+				__func__, (unsigned long long) seqid);
+		return ret;
 	}
 
+	if (pkt.mi.order & CLD_PKT_IS_FIRST)
+		sess->msg_buf_len = 0;
+	msg_len = pkt_len - hdr_len - CLD_PKT_FTR_LEN;
+	if ((sess->msg_buf_len + msg_len) > CLD_MAX_MSG_SZ) {
+		HAIL_DEBUG(&sess->log, "%s: message too long\n", __func__);
+		return -EPROTO;
+	}
+	memcpy(sess->msg_buf + sess->msg_buf_len, pktbuf + hdr_len, msg_len);
+	sess->msg_buf_len += msg_len;
 	sess->expire_time = current_time + CLDC_SESS_EXPIRE;
 
-	if (!last_frag)
-		return sess ? ack_seqid(sess, pkt->seqid) : 0;
-
-	return cldc_receive_msg(sess, pkt, pkt_len);
+	if (pkt.mi.order & CLD_PKT_IS_LAST) {
+		HAIL_DEBUG(&sess->log, "%s: receiving complete message of "
+				"type %s\n", __func__,
+				__cld_msg_type_to_str(sess->msg_buf_type));
+		return rx_complete(sess, &pkt, foot);
+	} else {
+		return ack_seqid(sess, foot->seqid);
+	}
 }
 
 static void sess_next_seqid(struct cldc_session *sess, uint64_t *seqid)
@@ -543,68 +488,114 @@ static void sess_next_seqid(struct cldc_session *sess, uint64_t *seqid)
 	*seqid = rc;
 }
 
+/**
+ * creates a new cldc_msg
+ *
+ * @param sess		The session
+ * @param copts		The call options
+ * @param type		The type of message to create
+ * @param xdrproc	The XDR function to use to create the message body
+ * @param data		The data to pass to xdrproc
+ *
+ * @return		The cldc message, or NULL on error,
+ */
 static struct cldc_msg *cldc_new_msg(struct cldc_session *sess,
-				     const struct cldc_call_opts *copts,
-				     enum cld_msg_ops op,
-				     size_t msg_len)
+				const struct cldc_call_opts *copts,
+				enum cld_msg_type type,
+				xdrproc_t xdrproc, const void *data)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_hdr *hdr;
 	struct timeval tv;
-	int i, data_left;
-	void *p;
+	size_t i, body_len, n_pkts;
+	char *body;
+	XDR xbdy;
+
+	/* Encode the message body */
+	body_len = xdr_sizeof(xdrproc, (void *)data);
+	body = alloca(body_len);
+	xdrmem_create(&xbdy, body, body_len, XDR_ENCODE);
+	if (!xdrproc(&xbdy, (void *)data)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to encode "
+			   "message", __func__);
+		xdr_destroy(&xbdy);
+		return NULL;
+	}
+	xdr_destroy(&xbdy);
 
-	gettimeofday(&tv, NULL);
+	if (body_len == 0)
+		/* Some packets (like ACKS) just have a header, and no message
+		 * body. */
+		n_pkts = 1;
+	else {
+		/* round up */
+		n_pkts = (body_len + CLD_MAX_PKT_MSG_SZ - 1) /
+			CLD_MAX_PKT_MSG_SZ;
+	}
 
-	msg = calloc(1, sizeof(*msg) + msg_len);
+	/* Create cldc_msg */
+	msg = calloc(1, sizeof(struct cldc_msg) +
+			(sizeof(struct cldc_pkt_info *) * n_pkts));
 	if (!msg)
 		return NULL;
 
+	msg->n_pkts = n_pkts;
 	__cld_rand64(&msg->xid);
-
+	msg->type = type;
 	msg->sess = sess;
-
 	if (copts)
 		memcpy(&msg->copts, copts, sizeof(msg->copts));
-
+	gettimeofday(&tv, NULL);
 	msg->expire_time = tv.tv_sec + CLDC_MSG_EXPIRE;
 
-	msg->data_len = msg_len;
-
-	msg->n_pkts = msg_len / CLD_MAX_PKT_MSG_SZ;
-	msg->n_pkts += ((msg_len % CLD_MAX_PKT_MSG_SZ) ? 1 : 0);
-
-	p = msg->data;
-	data_left = msg_len;
 	for (i = 0; i < msg->n_pkts; i++) {
+		XDR xhdr;
+		struct cld_pkt_hdr pkt;
 		struct cldc_pkt_info *pi;
-		int pkt_len;
-
-		pkt_len = MIN(data_left, CLD_MAX_PKT_MSG_SZ);
+		int hdr_len, body_chunk_len, pkt_len;
+
+		/* Set up packet header */
+		memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+		memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+		pkt.user = sess->user;
+		if (i == 0) {
+			if (i == (msg->n_pkts - 1))
+				pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_FIRST;
+			pkt.mi.cld_pkt_msg_info_u.mi.xid = msg->xid;
+			pkt.mi.cld_pkt_msg_info_u.mi.type = type;
+		} else {
+			if (i == (msg->n_pkts - 1))
+				pkt.mi.order = CLD_PKT_ORD_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_MID;
+		}
 
-		pi = calloc(1, sizeof(*pi) + pkt_len + SHA_DIGEST_LENGTH);
+		/* Allocate memory */
+		hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, &pkt);
+		body_chunk_len = MIN(body_len, CLD_MAX_PKT_MSG_SZ);
+		pkt_len = hdr_len + body_chunk_len + CLD_PKT_FTR_LEN;
+		pi = calloc(1, sizeof(*pi) + pkt_len);
 		if (!pi)
 			goto err_out;
-
 		pi->pkt_len = pkt_len;
-
-		memcpy(pi->pkt.magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-		memcpy(pi->pkt.sid, sess->sid, CLD_SID_SZ);
-		strncpy(pi->pkt.user, sess->user, CLD_MAX_USERNAME - 1);
-
-		if (i == 0)
-			pi->pkt.flags |= cpu_to_le32(CPF_FIRST);
-		if (i == (msg->n_pkts - 1))
-			pi->pkt.flags |= cpu_to_le32(CPF_LAST);
-
 		msg->pkt_info[i] = pi;
-		data_left -= pkt_len;
-	}
+		strncpy(pi->user, sess->user, CLD_MAX_USERNAME - 1);
+
+		/* Fill in the packet header */
+		xdrmem_create(&xhdr, (char *)pi->data, hdr_len, XDR_ENCODE);
+		if (!xdr_cld_pkt_hdr(&xhdr, &pkt)) {
+			HAIL_DEBUG(&sess->log, "%s: failed to encode header "
+				"for packet %d", __func__, i);
+			xdr_destroy(&xhdr);
+			goto err_out;
+		}
 
-	hdr = (struct cld_msg_hdr *) &msg->data[0];
-	memcpy(&hdr->magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	hdr->op = op;
-	hdr->xid = msg->xid;
+		/* Fill in the body */
+		memcpy(pi->data + hdr_len, body, body_chunk_len);
+		body += body_chunk_len;
+		body_len -= body_chunk_len;
+	}
 
 	return msg;
 
@@ -623,7 +614,7 @@ static void sess_msg_drop(struct cldc_session *sess)
 		tmp = tmp->next;
 
 		if (!msg->done && msg->cb)
-			msg->cb(msg, NULL, 0, false);
+			msg->cb(msg, NULL, 0, CLE_TIMEOUT);
 
 		cldc_msg_free(msg);
 	}
@@ -644,31 +635,8 @@ static void sess_expire(struct cldc_session *sess)
 }
 
 static int sess_send_pkt(struct cldc_session *sess,
-			 const struct cld_packet *pkt, size_t pkt_len)
+			 const void *pkt, size_t pkt_len)
 {
-	if (sess->log.verbose) {
-		uint32_t flags = le32_to_cpu(pkt->flags);
-		bool first = (flags & CPF_FIRST);
-		bool last = (flags & CPF_LAST);
-		uint8_t op = cmo_nop;
-
-		if (first) {
-			struct cld_msg_hdr *hdr;
-
-			hdr = (struct cld_msg_hdr *) (pkt + 1);
-			op = hdr->op;
-		}
-
-		HAIL_DEBUG(&sess->log,
-			"send_pkt(len %zu, flags %s%s, "
-			"op %s, seqid %llu)",
-			pkt_len,
-			first ? "F" : "",
-			last ? "L" : "",
-			first ? opstr(op) : "n/a",
-			(unsigned long long) le64_to_cpu(pkt->seqid));
-	}
-
 	return sess->ops->pkt_send(sess->private,
 				   sess->addr, sess->addr_len,
 				   pkt, pkt_len);
@@ -676,7 +644,6 @@ static int sess_send_pkt(struct cldc_session *sess,
 
 static int sess_timer(struct cldc_session *sess, void *priv)
 {
-	struct cldc_msg *msg;
 	GList *tmp = sess->out_msg;
 	struct timeval tv;
 
@@ -687,28 +654,20 @@ static int sess_timer(struct cldc_session *sess, void *priv)
 	}
 
 	while (tmp) {
+		struct cldc_msg *msg = tmp->data;
 		int i;
 
-		msg = tmp->data;
 		tmp = tmp->next;
 
 		if (msg->done)
 			continue;
 
 		for (i = 0; i < msg->n_pkts; i++) {
-			struct cldc_pkt_info *pi;
-			int total_pkt_len;
-
-			pi = msg->pkt_info[i];
+			struct cldc_pkt_info *pi = msg->pkt_info[i];
 			if (!pi)
 				continue;
-
-			total_pkt_len = sizeof(struct cld_packet) +
-					pi->pkt_len + SHA_DIGEST_LENGTH;
-
 			pi->retries++;
-
-			sess_send_pkt(sess, &pi->pkt, total_pkt_len);
+			sess_send_pkt(sess, pi->data, pi->pkt_len);
 		}
 	}
 
@@ -717,35 +676,30 @@ static int sess_timer(struct cldc_session *sess, void *priv)
 	return CLDC_MSG_RETRY;
 }
 
-static int sess_send(struct cldc_session *sess,
-		     struct cldc_msg *msg)
+static int sess_send(struct cldc_session *sess, struct cldc_msg *msg)
 {
-	int i, data_left;
-	void *p;
+	int ret, i;
 
-	p = msg->data;
-	data_left = msg->data_len;
 	for (i = 0; i < msg->n_pkts; i++) {
-		struct cldc_pkt_info *pi;
-		int total_pkt_len;
-
-		pi = msg->pkt_info[i];
-		memcpy(pi->data, p, pi->pkt_len);
-
-		total_pkt_len = sizeof(struct cld_packet) +
-				pi->pkt_len + SHA_DIGEST_LENGTH;
-
-		sess_next_seqid(sess, &pi->pkt.seqid);
-
-		p += pi->pkt_len;
-		data_left -= pi->pkt_len;
-
-		if (!authsign(sess, &pi->pkt, total_pkt_len))
-			return -1;
+		struct cldc_pkt_info *pi = msg->pkt_info[i];
+		struct cld_pkt_ftr *foot;
+
+		/* Add the sequence number to the end of the packet */
+		foot = (struct cld_pkt_ftr *)
+			(pi->data + pi->pkt_len - CLD_PKT_FTR_LEN);
+		memset(foot, 0, CLD_PKT_FTR_LEN);
+		sess_next_seqid(sess, &foot->seqid);
+
+		/* Add the signature to the end of the packet */
+		ret = __cld_authsign(&sess->log, pi->user,
+				pi->data,
+				pi->pkt_len - SHA_DIGEST_LENGTH, foot->sha);
+		if (ret)
+			return ret;
 
 		/* attempt first send */
-		if (sess_send_pkt(sess, &pi->pkt, total_pkt_len) < 0)
-			return -1;
+		if (sess_send_pkt(sess, pi->data, pi->pkt_len) < 0)
+			return -EIO;
 	}
 
 	/* add to list of outgoing packets, waiting to be ack'd */
@@ -776,16 +730,8 @@ static void sess_free(struct cldc_session *sess)
 }
 
 static ssize_t end_sess_cb(struct cldc_msg *msg, const void *resp_p,
-			   size_t resp_len, bool ok)
+			size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->code);
-
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
 
@@ -793,7 +739,8 @@ static ssize_t end_sess_cb(struct cldc_msg *msg, const void *resp_p,
 	return 0;
 }
 
-int cldc_end_sess(struct cldc_session *sess, const struct cldc_call_opts *copts)
+int cldc_end_sess(struct cldc_session *sess,
+		  const struct cldc_call_opts *copts)
 {
 	struct cldc_msg *msg;
 
@@ -801,8 +748,8 @@ int cldc_end_sess(struct cldc_session *sess, const struct cldc_call_opts *copts)
 		return -EINVAL;
 
 	/* create END-SESS message */
-	msg = cldc_new_msg(sess, copts, cmo_end_sess,
-			   sizeof(struct cld_msg_hdr));
+	msg = cldc_new_msg(sess, copts, CMT_END_SESS,
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg)
 		return -ENOMEM;
 
@@ -812,19 +759,10 @@ int cldc_end_sess(struct cldc_session *sess, const struct cldc_call_opts *copts)
 }
 
 static ssize_t new_sess_cb(struct cldc_msg *msg, const void *resp_p,
-			   size_t resp_len, bool ok)
+			   size_t resp_len, enum cle_err_codes resp_rc)
 {
-	struct cldc_session *sess = msg->sess;
-	const struct cld_msg_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->code);
-
 	if (resp_rc == CLE_OK)
-		sess->confirmed = true;
+		msg->sess->confirmed = true;
 
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
@@ -845,7 +783,6 @@ int cldc_new_sess(const struct cldc_ops *ops,
 
 	if (addr_len > sizeof(sess->addr))
 		return -EINVAL;
-
 	if (!user || !*user || !secret_key || !*secret_key)
 		return -EINVAL;
 	if (strlen(user) >= sizeof(sess->user))
@@ -877,8 +814,8 @@ int cldc_new_sess(const struct cldc_ops *ops,
 	sess->addr_len = addr_len;
 
 	/* create NEW-SESS message */
-	msg = cldc_new_msg(sess, copts, cmo_new_sess,
-			   sizeof(struct cld_msg_hdr));
+	msg = cldc_new_msg(sess, copts, CMT_NEW_SESS,
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg) {
 		sess_free(sess);
 		return -ENOMEM;
@@ -908,16 +845,8 @@ void cldc_kill_sess(struct cldc_session *sess)
 }
 
 static ssize_t generic_end_cb(struct cldc_msg *msg, const void *resp_p,
-			      size_t resp_len, bool ok)
+			      size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->code);
-
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
 
@@ -932,7 +861,8 @@ int cldc_nop(struct cldc_session *sess, const struct cldc_call_opts *copts)
 		return -EINVAL;
 
 	/* create NOP message */
-	msg = cldc_new_msg(sess, copts, cmo_nop, sizeof(struct cld_msg_hdr));
+	msg = cldc_new_msg(sess, copts, CMT_NOP,
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg)
 		return -ENOMEM;
 
@@ -945,8 +875,7 @@ int cldc_del(struct cldc_session *sess, const struct cldc_call_opts *copts,
 	     const char *pathname)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_del *del;
-	void *p;
+	struct cld_msg_del del;
 	size_t plen;
 
 	if (!sess->confirmed)
@@ -957,46 +886,37 @@ int cldc_del(struct cldc_session *sess, const struct cldc_call_opts *copts,
 		return -EINVAL;
 
 	plen = strlen(pathname);
-	if (plen > 65530)
+	if (plen > CLD_INODE_NAME_MAX)
 		return -EINVAL;
 
 	/* create DEL message */
-	msg = cldc_new_msg(sess, copts, cmo_del,
-			   sizeof(struct cld_msg_del) + strlen(pathname));
+	del.inode_name = (char *)pathname;
+	msg = cldc_new_msg(sess, copts, CMT_DEL,
+			   (xdrproc_t)xdr_cld_msg_del, &del);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in DEL-specific name_len, name info */
-	del = (struct cld_msg_del *) msg->data;
-	del->name_len = cpu_to_le16(plen);
-	p = del;
-	p += sizeof(struct cld_msg_del);
-	memcpy(p, pathname, plen);
-
 	return sess_send(sess, msg);
 }
 
 static ssize_t open_end_cb(struct cldc_msg *msg, const void *resp_p,
-			   size_t resp_len, bool ok)
+			   size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_open_resp *resp = resp_p;
-	struct cldc_fh *fh = msg->cb_private;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else {
-		if (resp_len < sizeof(resp->resp))
+	if (resp_rc == CLE_OK) {
+		struct cldc_fh *fh = msg->cb_private;
+		XDR xdrs;
+		struct cld_msg_open_resp resp;
+
+		xdrmem_create(&xdrs, (void *)resp_p, resp_len, XDR_DECODE);
+		memset(&resp, 0, sizeof(resp));
+		if (!xdr_cld_msg_open_resp(&xdrs, &resp)) {
+			xdr_destroy(&xdrs);
 			return -1009;
-		resp_rc = le32_to_cpu(resp->resp.code);
-	}
+		}
 
-	if (resp_rc == CLE_OK) {
-		if (resp_len < sizeof(*resp))
-			return -1010;
-		fh->fh_le = resp->fh;
+		fh->fh = resp.fh;
 		fh->valid = true;
 	}
 
@@ -1012,9 +932,8 @@ int cldc_open(struct cldc_session *sess,
 	      uint32_t events, struct cldc_fh **fh_out)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_open *open;
+	struct cld_msg_open open;
 	struct cldc_fh fh, *fhtmp;
-	void *p;
 	size_t plen;
 	int fh_idx;
 
@@ -1028,12 +947,15 @@ int cldc_open(struct cldc_session *sess,
 		return -EINVAL;
 
 	plen = strlen(pathname);
-	if (plen > 65530)
+	if (plen > CLD_INODE_NAME_MAX)
 		return -EINVAL;
 
 	/* create OPEN message */
-	msg = cldc_new_msg(sess, copts, cmo_open,
-			   sizeof(struct cld_msg_open) + strlen(pathname));
+	open.mode = open_mode;
+	open.events = events;
+	open.inode_name = (char *)pathname;
+	msg = cldc_new_msg(sess, copts, CMT_OPEN,
+			   (xdrproc_t)xdr_cld_msg_open, &open);
 	if (!msg)
 		return -ENOMEM;
 
@@ -1048,15 +970,6 @@ int cldc_open(struct cldc_session *sess,
 	msg->cb = open_end_cb;
 	msg->cb_private = fhtmp;
 
-	/* fill in OPEN-specific info */
-	open = (struct cld_msg_open *) msg->data;
-	open->mode = cpu_to_le32(open_mode);
-	open->events = cpu_to_le32(events);
-	open->name_len = cpu_to_le16(plen);
-	p = open;
-	p += sizeof(struct cld_msg_open);
-	memcpy(p, pathname, plen);
-
 	*fh_out = fhtmp;
 
 	return sess_send(sess, msg);
@@ -1066,7 +979,7 @@ int cldc_close(struct cldc_fh *fh, const struct cldc_call_opts *copts)
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_close *close_msg;
+	struct cld_msg_close close;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1074,8 +987,9 @@ int cldc_close(struct cldc_fh *fh, const struct cldc_call_opts *copts)
 	sess = fh->sess;
 
 	/* create CLOSE message */
-	msg = cldc_new_msg(sess, copts, cmo_close,
-			   sizeof(struct cld_msg_close));
+	close.fh = fh->fh;
+	msg = cldc_new_msg(sess, copts, CMT_CLOSE,
+			   (xdrproc_t)xdr_cld_msg_close, &close);
 	if (!msg)
 		return -ENOMEM;
 
@@ -1084,10 +998,6 @@ int cldc_close(struct cldc_fh *fh, const struct cldc_call_opts *copts)
 
 	msg->cb = generic_end_cb;
 
-	/* fill in CLOSE-specific fh info */
-	close_msg = (struct cld_msg_close *) msg->data;
-	close_msg->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
@@ -1096,7 +1006,7 @@ int cldc_lock(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_lock *lock;
+	struct cld_msg_lock lock;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1104,19 +1014,16 @@ int cldc_lock(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 	sess = fh->sess;
 
 	/* create LOCK message */
+	lock.fh = fh->fh;
+	lock.flags = lock_flags;
 	msg = cldc_new_msg(sess, copts,
-			   wait_for_lock ? cmo_lock : cmo_trylock,
-			   sizeof(struct cld_msg_lock));
+			   wait_for_lock ? CMT_LOCK : CMT_TRYLOCK,
+			   (xdrproc_t)xdr_cld_msg_lock, &lock);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in LOCK-specific info */
-	lock = (struct cld_msg_lock *) msg->data;
-	lock->fh = fh->fh_le;
-	lock->flags = cpu_to_le32(lock_flags);
-
 	return sess_send(sess, msg);
 }
 
@@ -1124,7 +1031,7 @@ int cldc_unlock(struct cldc_fh *fh, const struct cldc_call_opts *copts)
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_unlock *unlock;
+	struct cld_msg_unlock unlock;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1132,17 +1039,14 @@ int cldc_unlock(struct cldc_fh *fh, const struct cldc_call_opts *copts)
 	sess = fh->sess;
 
 	/* create UNLOCK message */
-	msg = cldc_new_msg(sess, copts, cmo_unlock,
-			   sizeof(struct cld_msg_unlock));
+	unlock.fh = fh->fh;
+	msg = cldc_new_msg(sess, copts, CMT_UNLOCK,
+			   (xdrproc_t)xdr_cld_msg_unlock, &unlock);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in UNLOCK-specific info */
-	unlock = (struct cld_msg_unlock *) msg->data;
-	unlock->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
@@ -1151,9 +1055,9 @@ int cldc_put(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_put *put;
+	struct cld_msg_put put;
 
-	if (!data || !data_len || data_len > CLD_MAX_MSG_SZ)
+	if (!data || !data_len || data_len > CLD_MAX_PAYLOAD_SZ)
 		return -EINVAL;
 
 	if (!fh->valid)
@@ -1162,17 +1066,14 @@ int cldc_put(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 	sess = fh->sess;
 
 	/* create PUT message */
-	msg = cldc_new_msg(sess, copts, cmo_put,
-			   sizeof(struct cld_msg_put) + data_len);
+	put.fh = fh->fh;
+	put.data.data_len = data_len;
+	put.data.data_val = (char *)data;
+	msg = cldc_new_msg(sess, copts, CMT_PUT,
+			   (xdrproc_t)xdr_cld_msg_put, &put);
 	if (!msg)
 		return -ENOMEM;
 
-	put = (struct cld_msg_put *) msg->data;
-	put->fh = fh->fh_le;
-	put->data_size = cpu_to_le32(data_len);
-
-	memcpy((put + 1), data, data_len);
-
 	msg->cb = generic_end_cb;
 
 	sess_send(sess, msg);
@@ -1180,73 +1081,39 @@ int cldc_put(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 	return 0;
 }
 
-#undef XC32
-#undef XC64
-#define XC32(name) \
-	o->name = le32_to_cpu(resp->name)
-#define XC64(name) \
-	o->name = le64_to_cpu(resp->name)
-
 static ssize_t get_end_cb(struct cldc_msg *msg, const void *resp_p,
-			  size_t resp_len, bool ok)
+			  size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_get_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_get_resp *o = NULL;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->resp.code);
-
 	if (resp_rc == CLE_OK) {
-		bool get_body;
-
-		o = &msg->copts.u.get.resp;
-
-		get_body = (resp->resp.hdr.op == cmo_get);
-		msg->copts.op = cmo_get;
-
-		/* copy-and-swap */
-		XC64(inum);
-		XC32(ino_len);
-		XC32(size);
-		XC64(version);
-		XC64(time_create);
-		XC64(time_modify);
-		XC32(flags);
-
-		/* copy inode name */
-		if (o->ino_len <= CLD_INODE_NAME_MAX) {
-			size_t diffsz;
-			const void *p;
-
-			p = (resp + 1);
-			memcpy(&msg->copts.u.get.inode_name, p, o->ino_len);
-
-			p += o->ino_len;
-			diffsz = p - resp_p;
-
-			/* point to internal buffer holding GET data */
-			msg->copts.u.get.buf = msg->sess->msg_buf + diffsz;
-			msg->copts.u.get.size = msg->sess->msg_buf_len - diffsz;
-		} else {
-			o->ino_len = 0;		/* Probably full of garbage */
+		XDR xin;
+		struct cld_msg_get_resp *resp = &msg->copts.resp;
+
+		/* Parse GET response.
+		 * Avoid memory allocation in xdr_string by pointing
+		 * variable-length elements at static buffers. */
+		xdrmem_create(&xin, (void *)resp_p, resp_len, XDR_DECODE);
+		memset(resp, 0, sizeof(struct cld_msg_get_resp));
+		resp->inode_name = msg->sess->inode_name_temp;
+		resp->data.data_val = msg->sess->payload;
+		resp->data.data_len = 0;
+		if (!xdr_cld_msg_get_resp(&xin, resp)) {
+			xdr_destroy(&xin);
+			return -1009;
 		}
+		xdr_destroy(&xin);
 	}
 
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
 	return 0;
 }
-#undef XC
 
 int cldc_get(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 	     bool metadata_only)
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_get *get;
+	struct cld_msg_get get;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1254,17 +1121,14 @@ int cldc_get(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 	sess = fh->sess;
 
 	/* create GET message */
-	msg = cldc_new_msg(sess, copts, cmo_get,
-			   sizeof(struct cld_msg_get));
+	get.fh = fh->fh;
+	msg = cldc_new_msg(sess, copts, CMT_GET,
+			   (xdrproc_t)xdr_cld_msg_get, &get);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = get_end_cb;
 
-	/* fill in GET-specific info */
-	get = (struct cld_msg_get *) msg->data;
-	get->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
diff --git a/lib/common.c b/lib/common.c
index 276c805..27a3f4e 100644
--- a/lib/common.c
+++ b/lib/common.c
@@ -26,7 +26,11 @@
 #include <errno.h>
 #include <glib.h>
 #include <cld-private.h>
-#include "cld_msg.h"
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include "cld_msg_rpc.h"
+#include <hail_log.h>
+#include <syslog.h>
 
 /* duplicated from tools/cldcli.c; put in common header somewhere? */
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
diff --git a/server/Makefile.am b/server/Makefile.am
index faaf099..17771de 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -1,13 +1,18 @@
 
-INCLUDES	= -I$(top_srcdir)/include @GLIB_CFLAGS@ \
+INCLUDES	= -I$(top_srcdir)/include \
+		  -I$(top_srcdir)/lib \
+		  @GLIB_CFLAGS@ \
 		  -DCLD_LIBDIR=\""$(libdir)"\" \
 		  -DCLD_LOCAL_STATE_DIR="\"$(localstatedir)\""
 
 sbin_PROGRAMS	= cld cldbadm
 
 cld_SOURCES	= cldb.h cld.h \
+		  ../lib/cld_fmt.c \
 		  ../lib/common.c \
-		  cldb.c msg.c server.c session.c util.c
+		  cldb.c msg.c server.c session.c util.c \
+		  ../lib/cld_msg_rpc_xdr.c
+
 cld_LDADD	= ../lib/libtimer.a \
 		  @CRYPTO_LIBS@ @GLIB_LIBS@ @DB4_LIBS@
 
diff --git a/server/cld.h b/server/cld.h
index e6bed3c..26121b8 100644
--- a/server/cld.h
+++ b/server/cld.h
@@ -25,7 +25,7 @@
 #include <poll.h>
 #include <glib.h>
 #include "cldb.h"
-#include <cld_msg.h>
+#include <lib/cld_msg_rpc.h>
 #include <cld_common.h>
 #include <libtimer.h>
 #include <hail_log.h>
@@ -73,20 +73,12 @@ struct session {
 	bool			dead;		/* session has ended */
 
 	/* huge buffer should always come last */
+	enum cld_msg_type	msg_type;
+	uint64_t		msg_xid;
 	unsigned int		msg_buf_len;
 	char			msg_buf[CLD_MAX_MSG_SZ];
 };
 
-struct msg_params {
-	int			sock_fd;
-	const struct client	*cli;
-	struct session		*sess;
-
-	const struct cld_packet	*pkt;
-	const void		*msg;
-	size_t			msg_len;
-};
-
 struct server_stats {
 	unsigned long		poll;		/* num. polls */
 	unsigned long		event;		/* events dispatched */
@@ -123,46 +115,89 @@ struct server {
 	struct server_stats	stats;		/* global statistics */
 };
 
+struct pkt_info {
+	struct cld_pkt_hdr *pkt;
+	struct session *sess;
+	uint64_t seqid;
+	uint64_t xid;
+	enum cld_msg_type type;
+	size_t hdr_len;
+};
+
 /* msg.c */
 extern int inode_lock_rescan(DB_TXN *txn, cldino_t inum);
-extern void msg_open(struct msg_params *);
-extern void msg_put(struct msg_params *);
-extern void msg_close(struct msg_params *);
-extern void msg_del(struct msg_params *);
-extern void msg_unlock(struct msg_params *);
-extern void msg_lock(struct msg_params *, bool);
-extern void msg_ack(struct msg_params *);
-extern void msg_get(struct msg_params *, bool);
+extern void msg_get(struct session *sess, const void *v);
+extern void msg_open(struct session *sess, const void *v);
+extern void msg_put(struct session *sess, const void *v);
+extern void msg_close(struct session *sess, const void *v);
+extern void msg_del(struct session *sess, const void *v);
+extern void msg_unlock(struct session *sess, const void *v);
+extern void msg_lock(struct session *sess, const void *v);
+extern void msg_ack(struct session *sess, uint64_t seqid);
 
 /* session.c */
 extern uint64_t next_seqid_le(uint64_t *seq);
-extern void pkt_init_pkt(struct cld_packet *dest, const struct cld_packet *src);
 extern guint sess_hash(gconstpointer v);
 extern gboolean sess_equal(gconstpointer _a, gconstpointer _b);
-extern void msg_new_sess(struct msg_params *, const struct client *);
-extern void msg_end_sess(struct msg_params *, const struct client *);
+extern void msg_new_sess(int sock_fd, const struct client *cli,
+			const struct pkt_info *info);
+extern void msg_end_sess(struct session *sess, uint64_t xid);
 extern struct raw_session *session_new_raw(const struct session *sess);
 extern void sessions_free(void);
-extern bool sess_sendmsg(struct session *sess, const void *msg_, size_t msglen,
-		  void (*done_cb)(struct session_outpkt *),
-		  void *done_data);
+
+/** Send a message as part of a session.
+ *
+ * @param sess		The session
+ * @param xdrproc	The XDR function to use to serialize the data
+ * @param xdrdata	The message data
+ * @param type		The type of the message
+ * @param done_cb	The callback to call when the message has been acked
+ * @param done_data	The data to give to done_cb
+ *
+ * @return		true only if the message was sent
+ */
+extern bool sess_sendmsg(struct session *sess,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_type type,
+	void (*done_cb)(struct session_outpkt *), void *done_data);
+
+/** Send a generic response message.
+ *
+ * @param sess		The session
+ * @param code		The error code to send
+ */
+extern void sess_sendresp_generic(struct session *sess,
+				  enum cle_err_codes code);
+
 extern int session_dispose(DB_TXN *txn, struct session *sess);
 extern int session_remove_locks(DB_TXN *txn, uint8_t *sid, uint64_t fh,
 				cldino_t inum, bool *waiter);
 extern int sess_load(GHashTable *ss);
 
 /* server.c */
-extern const char *opstr(enum cld_msg_ops op);
 extern struct server cld_srv;
 extern struct hail_log srv_log;
 extern struct timeval current_time;
 extern int udp_tx(int sock_fd, struct sockaddr *, socklen_t,
 	    const void *, size_t);
-extern void resp_copy(struct cld_msg_resp *resp, const struct cld_msg_hdr *src);
-extern void resp_err(struct session *sess,
-	      const struct cld_msg_hdr *src, enum cle_err_codes errcode);
-extern void resp_ok(struct session *sess, const struct cld_msg_hdr *src);
-extern bool authsign(struct cld_packet *pkt, size_t pkt_len);
+
+/** Transmit a single packet.
+ *
+ * This function doesn't provide error-retransmission logic.
+ * It can't handle messages that are bigger than a single packet.
+ *
+ * @param fd		Socket to send the response on
+ * @param cli		Client address data
+ * @param sid		The session-id to use. Must be of length CLD_SID_SZ
+ * @param seqid		The sequence id to use
+ * @param xdrproc	The XDR function to use to serialize the data
+ * @param xdrdata	The message data
+ * @param type		The type of message to send
+ *
+ * @return		true only on success
+ */
+extern void simple_sendmsg(int fd, const struct client *cli,
+	uint64_t sid, const char *username, uint64_t seqid,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_type type);
 
 /* util.c */
 extern int write_pid_file(const char *pid_fn);
diff --git a/server/cldb.h b/server/cldb.h
index ac108ce..71dce95 100644
--- a/server/cldb.h
+++ b/server/cldb.h
@@ -23,7 +23,7 @@
 #include <stdbool.h>
 #include <db.h>
 #include <cld-private.h>
-#include <cld_msg.h>
+#include <lib/cld_msg_rpc.h>
 
 typedef uint64_t cldino_t;
 
diff --git a/server/msg.c b/server/msg.c
index e58059e..60403d0 100644
--- a/server/msg.c
+++ b/server/msg.c
@@ -25,6 +25,8 @@
 #include <syslog.h>
 #include <openssl/sha.h>
 #include <cld-private.h>
+#include <cld_common.h>
+#include <lib/cld_msg_rpc.h>
 #include "cld.h"
 
 enum {
@@ -194,7 +196,6 @@ static int inode_notify(DB_TXN *txn, cldino_t inum, bool deleted)
 	DB *hand_idx = cld_srv.cldb.handle_idx;
 	DBC *cur;
 	DBT key, val;
-	struct cld_msg_event me;
 	cldino_t inum_le = cldino_to_le(inum);
 	int gflags;
 	struct session *sess;
@@ -210,10 +211,6 @@ static int inode_notify(DB_TXN *txn, cldino_t inum, bool deleted)
 	val.ulen = sizeof(h);
 	val.flags = DB_DBT_USERMEM;
 
-	memset(&me, 0, sizeof(me));
-	memcpy(me.hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	me.hdr.op = cmo_event;
-
 	rc = hand_idx->cursor(hand_idx, txn, &cur, 0);
 	if (rc) {
 		hand_idx->err(hand_idx, rc, "inode_notify cursor");
@@ -222,6 +219,8 @@ static int inode_notify(DB_TXN *txn, cldino_t inum, bool deleted)
 
 	gflags = DB_SET;
 	while (1) {
+		struct cld_msg_event me;
+
 		rc = cur->get(cur, &key, &val, gflags);
 		if (rc) {
 			if (rc != DB_NOTFOUND)
@@ -248,10 +247,12 @@ static int inode_notify(DB_TXN *txn, cldino_t inum, bool deleted)
 			continue;
 		}
 
-		me.fh = h.fh;
-		me.events = cpu_to_le32(deleted ? CE_DELETED : CE_UPDATED);
+		memset(&me, 0, sizeof(me));
+		me.fh = le64_to_cpu(h.fh);
+		me.events = deleted ? CE_DELETED : CE_UPDATED;
 
-		if (!sess_sendmsg(sess, &me, sizeof(me), NULL, NULL))
+		if (!sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_event,
+				(void *)&me, CMT_EVENT, NULL, NULL))
 			break;
 	}
 
@@ -292,7 +293,6 @@ int inode_lock_rescan(DB_TXN *txn, cldino_t inum)
 	cldino_t inum_le = cldino_to_le(inum);
 	struct raw_lock lock;
 	uint32_t lflags;
-	struct cld_msg_event me;
 	struct session *sess;
 
 	rc = db_locks->cursor(db_locks, txn, &cur, 0);
@@ -312,15 +312,13 @@ int inode_lock_rescan(DB_TXN *txn, cldino_t inum)
 	val.ulen = sizeof(lock);
 	val.flags = DB_DBT_USERMEM;
 
-	memset(&me, 0, sizeof(me));
-	memcpy(me.hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	me.hdr.op = cmo_event;
-
 	/* loop through locks associated with this inode, searching
 	 * for pending locks that can be converted into acquired
 	 */
 	gflags = DB_SET | DB_RMW;
 	while (1) {
+		struct cld_msg_event me;
+
 		rc = cur->get(cur, &key, &val, gflags);
 		if (rc) {
 			/* no locks, or no next-dup */
@@ -376,10 +374,12 @@ int inode_lock_rescan(DB_TXN *txn, cldino_t inum)
 			continue;
 		}
 
-		me.fh = lock.fh;
-		me.events = cpu_to_le32(CE_LOCKED);
+		memset(&me, 0, sizeof(me));
+		me.fh = le64_to_cpu(lock.fh);
+		me.events = CE_LOCKED;
 
-		if (!sess_sendmsg(sess, &me, sizeof(me), NULL, NULL))
+		if (!sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_event,
+				(void *)&me, CMT_EVENT, NULL, NULL))
 			break;
 	}
 
@@ -387,12 +387,10 @@ int inode_lock_rescan(DB_TXN *txn, cldino_t inum)
 	return rc;
 }
 
-void msg_get(struct msg_params *mp, bool metadata_only)
+void msg_get(struct session *sess, const void *v)
 {
-	const struct cld_msg_get *msg = mp->msg;
-	struct cld_msg_get_resp *resp;
-	size_t resp_len;
-	uint64_t fh;
+	const struct cld_msg_get *get = v;
+	struct cld_msg_get_resp resp;
 	struct raw_handle *h = NULL;
 	struct raw_inode *inode = NULL;
 	enum cle_err_codes resp_rc = CLE_OK;
@@ -400,17 +398,10 @@ void msg_get(struct msg_params *mp, bool metadata_only)
 	uint32_t name_len, inode_size;
 	uint32_t omode;
 	int rc;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
-	void *p;
-
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	/* get filehandle from input msg */
-	fh = le64_to_cpu(msg->fh);
+	void *data_mem;
+	char *inode_name;
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -420,7 +411,7 @@ void msg_get(struct msg_params *mp, bool metadata_only)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, get->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -441,41 +432,30 @@ void msg_get(struct msg_params *mp, bool metadata_only)
 		goto err_out;
 	}
 
-	name_len = le32_to_cpu(inode->ino_len);
 	inode_size = le32_to_cpu(inode->size);
-
-	resp_len = sizeof(*resp) + name_len +
-		   (metadata_only ? 0 : inode_size);
-	resp = alloca(resp_len);
-	if (!resp) {
-		resp_rc = CLE_OOM;
-		goto err_out;
-	}
-
-	HAIL_DEBUG(&srv_log, "GET-DEBUG: sizeof(resp) %zu, name_len %u, "
-		"inode->size %u, resp_len %zu",
-		sizeof(*resp), name_len,
-		inode_size, resp_len);
+	HAIL_DEBUG(&srv_log, "GET-DEBUG: inode->size %u\n", inode_size);
 
 	/* return response containing inode metadata */
-	memset(resp, 0, resp_len);
-	resp_copy(&resp->resp, mp->msg);
-	resp->inum = inode->inum;
-	resp->ino_len = inode->ino_len;
-	resp->size = inode->size;
-	resp->version = inode->version;
-	resp->time_create = inode->time_create;
-	resp->time_modify = inode->time_modify;
-	resp->flags = inode->flags;
+	memset(&resp, 0, sizeof(resp));
+	resp.msg.code = CLE_OK;
+	resp.msg.xid_in = sess->msg_xid;
+	resp.inum = le64_to_cpu(inode->inum);
+	resp.vers = le64_to_cpu(inode->version);
+	resp.time_create = le64_to_cpu(inode->time_create);
+	resp.time_modify = le64_to_cpu(inode->time_modify);
+	resp.flags = le32_to_cpu(inode->flags);
 
-	p = (resp + 1);
-	memcpy(p, (inode + 1), name_len);
+	name_len = le32_to_cpu(inode->ino_len);
+	inode_name = alloca(name_len + 1);
+	snprintf(inode_name, name_len + 1, "%s", (char *)(inode + 1));
+	resp.inode_name = inode_name;
 
-	p += name_len;
+	resp.data.data_len = 0;
+	resp.data.data_val = NULL;
 
 	/* send data, if requested */
-	if (!metadata_only) {
-		void *data_mem;
+	data_mem = NULL;
+	if (sess->msg_type == CMT_GET) {
 		size_t data_mem_len;
 
 		rc = cldb_data_get(txn, inum, &data_mem, &data_mem_len,
@@ -484,22 +464,23 @@ void msg_get(struct msg_params *mp, bool metadata_only)
 		/* treat not-found as zero length file, as we may
 		 * not yet have created the data record
 		 */
-		if (rc == DB_NOTFOUND) {
-			resp->size = 0;
-			resp_len -= inode_size;
-		} else if (rc || (data_mem_len != inode_size)) {
-			if (!rc)
-				free(data_mem);
-			resp_rc = CLE_DB_ERR;
-			goto err_out;
-		} else {
-			memcpy(p, data_mem, data_mem_len);
-
-			free(data_mem);
+		if (rc != DB_NOTFOUND) {
+			if (rc || (data_mem_len != inode_size)) {
+				if (!rc)
+					free(data_mem);
+				resp_rc = CLE_DB_ERR;
+				goto err_out;
+			} else {
+				resp.data.data_len = data_mem_len;
+				resp.data.data_val = data_mem;
+			}
 		}
 	}
 
-	sess_sendmsg(sess, resp, resp_len, NULL, NULL);
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_get_resp,
+		     (void *)&resp, CMT_GET, NULL, NULL);
+	if (data_mem)
+		free(data_mem);
 
 	rc = txn->commit(txn, 0);
 	if (rc)
@@ -514,14 +495,14 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_get txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 	free(inode);
 }
 
-void msg_open(struct msg_params *mp)
+void msg_open(struct session *sess, const void *v)
 {
-	const struct cld_msg_open *msg = mp->msg;
+	const struct cld_msg_open *open = v;
 	struct cld_msg_open_resp resp;
 	const char *name;
 	struct raw_session *raw_sess = NULL;
@@ -532,24 +513,12 @@ void msg_open(struct msg_params *mp)
 	struct pathname_info pinfo;
 	void *parent_data = NULL;
 	size_t parent_len;
-	uint32_t msg_mode, msg_events;
 	uint64_t fh;
 	cldino_t inum;
 	enum cle_err_codes resp_rc = CLE_OK;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	msg_mode = le32_to_cpu(msg->mode);
-	msg_events = le32_to_cpu(msg->events);
-	name_len = le16_to_cpu(msg->name_len);
-
-	if (mp->msg_len < (sizeof(*msg) + name_len))
-		return;
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -557,11 +526,12 @@ void msg_open(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	name = mp->msg + sizeof(*msg);
+	name = open->inode_name;
+	name_len = strlen(name);
 
-	create = msg_mode & COM_CREATE;
-	excl = msg_mode & COM_EXCL;
-	do_dir = msg_mode & COM_DIRECTORY;
+	create = open->mode & COM_CREATE;
+	excl = open->mode & COM_EXCL;
+	do_dir = open->mode & COM_DIRECTORY;
 
 	if (!valid_inode_name(name, name_len) || (create && name_len < 2)) {
 		resp_rc = CLE_NAME_INVAL;
@@ -659,7 +629,7 @@ void msg_open(struct msg_params *mp)
 	inum = cldino_from_le(inode->inum);
 
 	/* alloc & init new handle; updates session's next_fh */
-	h = cldb_handle_new(mp->sess, inum, msg_mode, msg_events);
+	h = cldb_handle_new(sess, inum, open->mode, open->events);
 	if (!h) {
 		HAIL_CRIT(&srv_log, "cannot allocate handle");
 		resp_rc = CLE_OOM;
@@ -686,7 +656,7 @@ void msg_open(struct msg_params *mp)
 	}
 
 	/* encode in-memory session to raw database session struct */
-	raw_sess = session_new_raw(mp->sess);
+	raw_sess = session_new_raw(sess);
 
 	if (!raw_sess) {
 		HAIL_CRIT(&srv_log, "cannot allocate session");
@@ -714,11 +684,12 @@ void msg_open(struct msg_params *mp)
 	free(raw_sess);
 	free(h);
 
-	resp_copy(&resp.resp, mp->msg);
-	resp.resp.code = cpu_to_le32(CLE_OK);
-	resp.fh = cpu_to_le64(fh);
-	sess_sendmsg(mp->sess, &resp, sizeof(resp), NULL, NULL);
-
+	memset(&resp, 0, sizeof(resp));
+	resp.msg.xid_in = sess->msg_xid;
+	resp.msg.code = CLE_OK;
+	resp.fh = fh;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_open_resp,
+		     (void *)&resp, CMT_OPEN, NULL, NULL);
 	return;
 
 err_out:
@@ -726,7 +697,7 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_open txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(parent_data);
 	free(parent);
 	free(inode);
@@ -734,38 +705,18 @@ err_out_noabort:
 	free(h);
 }
 
-void msg_put(struct msg_params *mp)
+void msg_put(struct session *sess, const void *v)
 {
-	const struct cld_msg_put *msg = mp->msg;
-	struct session *sess = mp->sess;
-	uint64_t fh;
+	const struct cld_msg_put *put = v;
 	struct raw_handle *h = NULL;
 	struct raw_inode *inode = NULL;
 	enum cle_err_codes resp_rc = CLE_OK;
-	const void *mem;
 	int rc;
 	cldino_t inum;
-	uint32_t omode, data_size;
+	uint32_t omode;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as message header */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	/* make sure additional input data as large as expected */
-	data_size = le32_to_cpu(msg->data_size);
-	if (mp->msg_len != (data_size + sizeof(*msg))) {
-		HAIL_INFO(&srv_log, "PUT len mismatch: msg len %zu, "
-			"wanted %zu + %u (== %zu)",
-			mp->msg_len,
-			sizeof(*msg), data_size, data_size + sizeof(*msg));
-		resp_rc = CLE_BAD_PKT;
-		goto err_out_noabort;
-	}
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -774,7 +725,7 @@ void msg_put(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, put->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -797,14 +748,14 @@ void msg_put(struct msg_params *mp)
 	}
 
 	/* store contig. data area in db */
-	mem = (msg + 1);
-	rc = cldb_data_put(txn, inum, mem, data_size, 0);
+	rc = cldb_data_put(txn, inum,
+			   put->data.data_val, put->data.data_len, 0);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
 	}
 
-	inode->size = cpu_to_le32(data_size);
+	inode->size = cpu_to_le32(put->data.data_len);
 
 	/* update inode */
 	rc = inode_touch(txn, inode);
@@ -820,7 +771,7 @@ void msg_put(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 
 	free(h);
 	free(inode);
@@ -831,31 +782,23 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_put txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 
 	free(h);
 	free(inode);
 }
 
-void msg_close(struct msg_params *mp)
+void msg_close(struct session *sess, const void *v)
 {
-	const struct cld_msg_close *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_close *close = v;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
 	struct raw_handle *h = NULL;
 	cldino_t lock_inum = 0;
 	bool waiter = false;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -864,7 +807,7 @@ void msg_close(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, DB_RMW);
+	rc = cldb_handle_get(txn, sess->sid, close->fh, &h, DB_RMW);
 	if (rc) {
 		if (rc == DB_NOTFOUND)
 			resp_rc = CLE_FH_INVAL;
@@ -877,14 +820,15 @@ void msg_close(struct msg_params *mp)
 		lock_inum = cldino_from_le(h->inum);
 
 	/* delete handle from db */
-	rc = cldb_handle_del(txn, sess->sid, fh);
+	rc = cldb_handle_del(txn, sess->sid, close->fh);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
 	}
 
 	/* remove locks, if any */
-	rc = session_remove_locks(txn, sess->sid, fh, lock_inum, &waiter);
+	rc = session_remove_locks(txn, sess->sid,
+				  close->fh, lock_inum, &waiter);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
@@ -906,7 +850,7 @@ void msg_close(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -915,16 +859,15 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_close txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
-void msg_del(struct msg_params *mp)
+void msg_del(struct session *sess, const void *v)
 {
-	const struct cld_msg_del *msg = mp->msg;
+	const struct cld_msg_del *del = v;
 	enum cle_err_codes resp_rc = CLE_OK;
 	int rc, name_len;
-	const char *name;
 	struct pathname_info pinfo;
 	struct raw_inode *parent = NULL, *ino = NULL;
 	void *parent_data = NULL;
@@ -937,23 +880,13 @@ void msg_del(struct msg_params *mp)
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	name_len = le16_to_cpu(msg->name_len);
-
-	if (mp->msg_len < (sizeof(*msg) + name_len))
-		return;
-
-	name = mp->msg + sizeof(*msg);
-
-	if (!valid_inode_name(name, name_len) || (name_len < 2)) {
+	name_len = strlen(del->inode_name);
+	if (!valid_inode_name(del->inode_name, name_len) || (name_len < 2)) {
 		resp_rc = CLE_NAME_INVAL;
 		goto err_out_noabort;
 	}
 
-	pathname_parse(name, name_len, &pinfo);
+	pathname_parse(del->inode_name, name_len, &pinfo);
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -979,7 +912,8 @@ void msg_del(struct msg_params *mp)
 	}
 
 	/* read inode to be deleted */
-	rc = cldb_inode_get_byname(txn, name, name_len, &ino, false, 0);
+	rc = cldb_inode_get_byname(txn, del->inode_name, name_len,
+				   &ino, false, 0);
 	if (rc) {
 		if (rc == DB_NOTFOUND)
 			resp_rc = CLE_NAME_INVAL;
@@ -1088,7 +1022,7 @@ void msg_del(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(mp->sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(ino);
 	free(parent);
 	free(parent_data);
@@ -1099,31 +1033,23 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_del txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(ino);
 	free(parent);
 	free(parent_data);
 }
 
-void msg_unlock(struct msg_params *mp)
+void msg_unlock(struct session *sess, const void *v)
 {
-	const struct cld_msg_unlock *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_unlock *unlock = v;
 	struct raw_handle *h = NULL;
 	cldino_t inum;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
 	uint32_t omode;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -1132,7 +1058,7 @@ void msg_unlock(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, unlock->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -1147,7 +1073,7 @@ void msg_unlock(struct msg_params *mp)
 	}
 
 	/* attempt to given lock on filehandle */
-	rc = cldb_lock_del(txn, sess->sid, fh, inum);
+	rc = cldb_lock_del(txn, sess->sid, unlock->fh, inum);
 	if (rc) {
 		resp_rc = CLE_LOCK_INVAL;
 		goto err_out;
@@ -1160,7 +1086,7 @@ void msg_unlock(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -1169,30 +1095,22 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_unlock txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
-void msg_lock(struct msg_params *mp, bool wait)
+void msg_lock(struct session *sess, const void *v)
 {
-	const struct cld_msg_lock *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_lock *lock = v;
+	bool wait = (sess->msg_type == CMT_LOCK);
 	struct raw_handle *h = NULL;
 	cldino_t inum;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
-	uint32_t lock_flags, omode;
+	uint32_t omode;
 	bool acquired = false;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
-	struct session *sess = mp->sess;
-
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-	lock_flags = le32_to_cpu(msg->flags);
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -1202,7 +1120,7 @@ void msg_lock(struct msg_params *mp, bool wait)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, lock->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -1217,8 +1135,8 @@ void msg_lock(struct msg_params *mp, bool wait)
 	}
 
 	/* attempt to add lock */
-	rc = cldb_lock_add(txn, sess->sid, fh, inum,
-			   lock_flags & CLF_SHARED, wait, &acquired);
+	rc = cldb_lock_add(txn, sess->sid, lock->fh, inum,
+			   lock->flags & CLF_SHARED, wait, &acquired);
 	if (rc) {
 		if (rc == DB_KEYEXIST)
 			resp_rc = CLE_LOCK_CONFLICT;
@@ -1241,7 +1159,7 @@ void msg_lock(struct msg_params *mp, bool wait)
 	}
 
 	/* lock was acquired immediately */
-	resp_ok(mp->sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -1250,7 +1168,7 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_lock txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
diff --git a/server/server.c b/server/server.c
index e7cff1e..3b15891 100644
--- a/server/server.c
+++ b/server/server.c
@@ -37,6 +37,7 @@
 #include <openssl/hmac.h>
 #include <cld-private.h>
 #include "cld.h"
+#include <cld_fmt.h>
 
 #define PROGRAM_NAME "cld"
 
@@ -125,11 +126,9 @@ int udp_tx(int sock_fd, struct sockaddr *addr, socklen_t addr_len,
 {
 	ssize_t src;
 
-	HAIL_DEBUG(&srv_log, "udp_tx, fd %d", sock_fd);
-
 	src = sendto(sock_fd, data, data_len, 0, addr, addr_len);
 	if (src < 0 && errno != EAGAIN)
-		applog(LOG_ERR, "udp_tx sendto (fd %d, data_len %u): %s",
+		HAIL_ERR(&srv_log, "udp_tx sendto (fd %d, data_len %u): %s",
 		       sock_fd, (unsigned int) data_len,
 		       strerror(errno));
 
@@ -139,357 +138,415 @@ int udp_tx(int sock_fd, struct sockaddr *addr, socklen_t addr_len,
 	return 0;
 }
 
-void resp_copy(struct cld_msg_resp *resp, const struct cld_msg_hdr *src)
+static int udp_rx_handle(struct session *sess,
+		void (*msg_handler)(struct session *sess, const void *),
+		xdrproc_t xdrproc, void *xdrdata)
 {
-	memcpy(&resp->hdr, src, sizeof(*src));
-	resp->code = 0;
-	resp->rsv = 0;
-	resp->xid_in = src->xid;
-}
-
-void resp_err(struct session *sess,
-	      const struct cld_msg_hdr *src, enum cle_err_codes errcode)
-{
-	struct cld_msg_resp resp;
-
-	resp_copy(&resp, src);
-	__cld_rand64(&resp.hdr.xid);
-	resp.code = cpu_to_le32(errcode);
-
-	if (sess->sock_fd <= 0) {
-		applog(LOG_ERR, "Nul sock in response");
-		return;
+	XDR xin;
+	xdrmem_create(&xin, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdrproc(&xin, xdrdata)) {
+		HAIL_DEBUG(&srv_log, "%s: couldn't parse message of type %s\n",
+			   __func__, __cld_msg_type_to_str(sess->msg_type));
+		xdr_destroy(&xin);
+		return CLE_BAD_PKT;
 	}
-
-	sess_sendmsg(sess, &resp, sizeof(resp), NULL, NULL);
+	msg_handler(sess, xdrdata);
+	xdr_free(xdrproc, xdrdata);
+	xdr_destroy(&xin);
+	return 0;
 }
 
-void resp_ok(struct session *sess, const struct cld_msg_hdr *src)
+/** Recieve a UDP packet
+ *
+ * @param sock_fd	The UDP socket we received the packet on
+ * @param cli		Client address data
+ * @param info		Packet information
+ * @param raw_pkt	The raw packet buffer
+ * @param raw_len	Length of the raw packet buffer
+ *
+ * @return		An error code if we should send an error message
+ *			response. CLE_OK if we are done.
+ */
+static enum cle_err_codes udp_rx(int sock_fd,
+	const struct client *cli, struct pkt_info *info,
+	const char *raw_pkt, size_t raw_len)
 {
-	resp_err(sess, src, CLE_OK);
-}
+	struct cld_pkt_hdr *pkt = info->pkt;
+	struct session *sess = info->sess;
 
-static const char *user_key(const char *user)
-{
-	/* TODO: better auth scheme.
-	 * for now, use simple username==password auth scheme
-	 */
-	if (!user || !*user ||
-	    (strnlen(user, 32) >= 32))
-		return NULL;
+	if (sess) {
+		size_t msg_len;
 
-	return user;	/* our secret key */
-}
+		/* advance sequence id's and update last-contact timestamp */
+		sess->last_contact = current_time.tv_sec;
+		sess->sock_fd = sock_fd;
 
-static bool authcheck(const struct cld_packet *pkt, size_t pkt_len)
-{
-	const char *key;
-	unsigned char md[SHA_DIGEST_LENGTH];
-	unsigned int md_len = 0;
-	const void *p = pkt;
+		if (info->type != CMT_ACK) {
+			/* received message - update session */
+			sess->next_seqid_in++;
+		}
 
-	key = user_key(pkt->user);
-	if (!key)
-		return false;
+		/* copy message fragment into reassembly buffer */
+		if (pkt->mi.order & CLD_PKT_IS_FIRST) {
+			sess->msg_type = info->type;
+			sess->msg_xid = info->xid;
+			sess->msg_buf_len = 0;
+		}
+		msg_len = raw_len - info->hdr_len - CLD_PKT_FTR_LEN;
+		if ((sess->msg_buf_len + msg_len) > CLD_MAX_MSG_SZ)
+			return CLE_BAD_PKT;
 
-	HMAC(EVP_sha1(), key, strlen(key), p, pkt_len - SHA_DIGEST_LENGTH,
-	     md, &md_len);
+		memcpy(sess->msg_buf + sess->msg_buf_len,
+			raw_pkt + info->hdr_len, msg_len);
+		sess->msg_buf_len += msg_len;
+	}
 
-	if (md_len != SHA_DIGEST_LENGTH)
-		return false; /* BUG */
+	if (!(pkt->mi.order & CLD_PKT_IS_LAST)) {
+		struct cld_msg_ack_frag ack;
+		ack.seqid = info->seqid;
 
-	if (memcmp(md, p + (pkt_len - SHA_DIGEST_LENGTH), SHA_DIGEST_LENGTH))
-		return false;
+		/* transmit ack-partial-msg response (once, without retries) */
+		simple_sendmsg(sock_fd, cli, pkt->sid,
+			       pkt->user, 0xdeadbeef,
+			       (xdrproc_t)xdr_cld_msg_ack_frag, (void *)&ack,
+			       CMT_ACK_FRAG);
+		return CLE_OK;
+	}
 
-	return true;
+	/* Handle a complete message */
+	switch (info->type) {
+	case CMT_GET:
+		/* fall through */
+	case CMT_GET_META: {
+		struct cld_msg_get get = {0};
+		return udp_rx_handle(sess, msg_get,
+				     (xdrproc_t)xdr_cld_msg_get, &get);
+	}
+	case CMT_OPEN: {
+		struct cld_msg_open open = {0};
+		return udp_rx_handle(sess, msg_open,
+				     (xdrproc_t)xdr_cld_msg_open, &open);
+	}
+	case CMT_PUT: {
+		struct cld_msg_put put = {0};
+		return udp_rx_handle(sess, msg_put,
+				     (xdrproc_t)xdr_cld_msg_put, &put);
+	}
+	case CMT_CLOSE: {
+		struct cld_msg_close close = {0};
+		return udp_rx_handle(sess, msg_close,
+				     (xdrproc_t)xdr_cld_msg_close, &close);
+	}
+	case CMT_DEL: {
+		struct cld_msg_del del = {0};
+		return udp_rx_handle(sess, msg_del,
+				     (xdrproc_t)xdr_cld_msg_del, &del);
+	}
+	case CMT_UNLOCK: {
+		struct cld_msg_unlock unlock = {0};
+		return udp_rx_handle(sess, msg_unlock,
+				     (xdrproc_t)xdr_cld_msg_unlock, &unlock);
+	}
+	case CMT_TRYLOCK:
+		/* fall through */
+	case CMT_LOCK: {
+		struct cld_msg_lock lock = {0};
+		return udp_rx_handle(sess, msg_lock,
+				     (xdrproc_t)xdr_cld_msg_lock, &lock);
+	}
+	case CMT_ACK:
+		msg_ack(sess, info->seqid);
+		return 0;
+	case CMT_NOP:
+		sess_sendresp_generic(sess, CLE_OK);
+		return 0;
+	case CMT_NEW_SESS:
+		msg_new_sess(sock_fd, cli, info);
+		return 0;
+	case CMT_END_SESS:
+		msg_end_sess(sess, info->xid);
+		return 0;
+	default:
+		HAIL_DEBUG(&srv_log, "%s: unexpected packet of type %s\n",
+			   __func__, __cld_msg_type_to_str(info->type));
+		/* do nothing */
+		return 0;
+	}
 }
 
-bool authsign(struct cld_packet *pkt, size_t pkt_len)
+/** Parse a packet's header. Verify that the magic number is correct.
+ *
+ * @param raw_pkt	Pointer to the packet data
+ * @param raw_len	Length of the raw data
+ * @param pkt		(out param) the packet header
+ * @param hdr_len	(out param) the length of the packet header
+ *
+ * @return		true on success; false if this packet is garbage
+ */
+static bool parse_pkt_header(const char *raw_pkt, int raw_len,
+			struct cld_pkt_hdr *pkt, ssize_t *hdr_len)
 {
-	const char *key;
-	unsigned char md[SHA_DIGEST_LENGTH];
-	unsigned int md_len = 0;
-	void *buf = pkt;
+	XDR xin;
+	static const char * const magic = CLD_PKT_MAGIC;
 
-	key = user_key(pkt->user);
-	if (!key)
+	if (raw_len <= CLD_PKT_FTR_LEN) {
+		HAIL_DEBUG(&srv_log, "%s: packet is too short: only "
+			   "%d bytes\n", __func__, raw_len);
 		return false;
+	}
+	xdrmem_create(&xin, (void *)raw_pkt, raw_len - CLD_PKT_FTR_LEN,
+		      XDR_DECODE);
+	memset(pkt, 0, sizeof(*pkt));
+	if (!xdr_cld_pkt_hdr(&xin, pkt)) {
+		HAIL_DEBUG(&srv_log, "%s: couldn't parse packet header\n",
+			   __func__);
+		xdr_destroy(&xin);
+		return false;
+	}
+	*hdr_len = xdr_getpos(&xin);
+	xdr_destroy(&xin);
 
-	HMAC(EVP_sha1(), key, strlen(key), buf, pkt_len - SHA_DIGEST_LENGTH,
-	     md, &md_len);
-
-	if (md_len != SHA_DIGEST_LENGTH)
-		applog(LOG_ERR, "authsign BUG: md_len != SHA_DIGEST_LENGTH");
-
-	memcpy(buf + pkt_len - SHA_DIGEST_LENGTH, md, SHA_DIGEST_LENGTH);
+	if (memcmp((void *)&pkt->magic, magic, sizeof(pkt->magic))) {
+		HAIL_DEBUG(&srv_log, "%s: bad magic number\n", __func__);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)pkt);
+		return false;
+	}
 
 	return true;
 }
 
-const char *opstr(enum cld_msg_ops op)
+/** Look up some information about a packet, including its session and the
+ * type of message it carries.
+ *
+ * @param pkt		The packet's header
+ * @param raw_pkt	Pointer to the raw packet data
+ * @param raw_len	Length of the raw packet data
+ * @param info		(out param) Information about the packet
+ *
+ * @return		true on success; false if this packet is garbage
+ */
+static bool get_pkt_info(struct cld_pkt_hdr *pkt,
+			 const char *raw_pkt, size_t raw_len,
+			 size_t hdr_len, struct pkt_info *info)
 {
-	switch (op) {
-	case cmo_nop:		return "cmo_nop";
-	case cmo_new_sess:	return "cmo_new_sess";
-	case cmo_open:		return "cmo_open";
-	case cmo_get_meta:	return "cmo_get_meta";
-	case cmo_get:		return "cmo_get";
-	case cmo_put:		return "cmo_put";
-	case cmo_close:		return "cmo_close";
-	case cmo_del:		return "cmo_del";
-	case cmo_lock:		return "cmo_lock";
-	case cmo_unlock:	return "cmo_unlock";
-	case cmo_trylock:	return "cmo_trylock";
-	case cmo_ack:		return "cmo_ack";
-	case cmo_end_sess:	return "cmo_end_sess";
-	case cmo_ping:		return "cmo_ping";
-	case cmo_not_master:	return "cmo_not_master";
-	case cmo_event:		return "cmo_event";
-	case cmo_ack_frag:	return "cmo_ack_frag";
-	default:		return "(unknown)";
+	struct cld_pkt_ftr *foot;
+	struct session *s;
+
+	memset(info, 0, sizeof(info));
+	info->pkt = pkt;
+	info->sess = s = g_hash_table_lookup(cld_srv.sessions, &pkt->sid);
+	foot = (struct cld_pkt_ftr *)
+			(raw_pkt + (raw_len - CLD_PKT_FTR_LEN));
+	info->seqid = le64_to_cpu(foot->seqid);
+
+	if (pkt->mi.order & CLD_PKT_IS_FIRST) {
+		info->xid = pkt->mi.cld_pkt_msg_info_u.mi.xid;
+		info->type = pkt->mi.cld_pkt_msg_info_u.mi.type;
+	} else {
+		if (!s) {
+			HAIL_DEBUG(&srv_log, "%s: packet is not first, "
+				"but also not part of an existing session. "
+				"Protocol error.\n", __func__);
+			return false;
+		}
+		info->xid = s->msg_xid;
+		info->type = s->msg_type;
 	}
+	info->hdr_len = hdr_len;
+	return true;
 }
 
-static void show_msg(const struct cld_msg_hdr *msg)
+/** Verify that the client session matches IP and username
+ *
+ * @param info		Packet information
+ * @param cli		Client address data
+ *
+ * @return		0 on success; error code otherwise
+ */
+static enum cle_err_codes validate_pkt_session(const struct pkt_info *info,
+						const struct client *cli)
 {
-	switch (msg->op) {
-	case cmo_nop:
-	case cmo_new_sess:
-	case cmo_open:
-	case cmo_get_meta:
-	case cmo_get:
-	case cmo_put:
-	case cmo_close:
-	case cmo_del:
-	case cmo_lock:
-	case cmo_unlock:
-	case cmo_trylock:
-	case cmo_ack:
-	case cmo_end_sess:
-	case cmo_ping:
-	case cmo_not_master:
-	case cmo_event:
-	case cmo_ack_frag:
-		HAIL_DEBUG(&srv_log, "msg: op %s, xid %llu",
-		       opstr(msg->op),
-		       (unsigned long long) le64_to_cpu(msg->xid));
-		break;
+	struct session *sess = info->sess;
+
+	if (!sess) {
+		/* Packets that don't belong to a session must be new-session
+		 * packets attempting to establish a session. */
+		if (info->type != CMT_NEW_SESS) {
+			HAIL_DEBUG(&srv_log, "%s: packet doesn't belong to a "
+				   "session,but has type %d\n",
+				   __func__, info->type);
+			return CLE_SESS_INVAL;
+		}
+		return 0;
 	}
-}
-
-static void udp_rx_msg(const struct client *cli, const struct cld_packet *pkt,
-		       const struct cld_msg_hdr *msg, struct msg_params *mp)
-{
-	struct session *sess = mp->sess;
 
-	if (srv_log.verbose)
-		show_msg(msg);
+	if (info->type == CMT_NEW_SESS) {
+		HAIL_DEBUG(&srv_log, "%s: Tried to create a new session, "
+			   "but a session with that ID already exists.\n",
+			   __func__);
+		return CLE_SESS_EXISTS;
+	}
 
-	switch(msg->op) {
-	case cmo_nop:
-		resp_ok(sess, msg);
-		break;
+	/* verify that client session matches IP */
+	if ((sess->addr_len != cli->addr_len) ||
+	     memcmp(&sess->addr, &cli->addr, sess->addr_len)) {
+		HAIL_DEBUG(&srv_log, "%s: sess->addr doesn't match packet "
+			   "addr\n", __func__);
+		return CLE_SESS_INVAL;
+	}
 
-	case cmo_new_sess:	msg_new_sess(mp, cli); break;
-	case cmo_end_sess:	msg_end_sess(mp, cli); break;
-	case cmo_open:		msg_open(mp); break;
-	case cmo_get:		msg_get(mp, false); break;
-	case cmo_get_meta:	msg_get(mp, true); break;
-	case cmo_put:		msg_put(mp); break;
-	case cmo_close:		msg_close(mp); break;
-	case cmo_del:		msg_del(mp); break;
-	case cmo_unlock:	msg_unlock(mp); break;
-	case cmo_lock:		msg_lock(mp, true); break;
-	case cmo_trylock:	msg_lock(mp, false); break;
-	case cmo_ack:		msg_ack(mp); break;
+	/* verify that client session matches username */
+	if (strncmp(info->pkt->user, sess->user, CLD_MAX_USERNAME)) {
+		HAIL_DEBUG(&srv_log, "%s: session doesn't match packet's  "
+			   "username\n", __func__);
+		return CLE_SESS_INVAL;
+	}
 
-	default:
-		/* do nothing */
-		break;
+	if (sess->dead) {
+		HAIL_DEBUG(&srv_log, "%s: packet session is dead\n",
+			   __func__);
+		return CLE_SESS_INVAL;
 	}
+
+	return 0;
 }
 
-static void pkt_ack_frag(int sock_fd,
-			 const struct client *cli,
-			 const struct cld_packet *pkt)
+/** Check a packet's cryptographic signature
+ *
+ * @param raw_pkt	Pointer to the packet data
+ * @param raw_len	Length of the raw data
+ * @param pkt		the packet header
+ *
+ * @return		0 on success; error code otherwise
+ */
+static enum cle_err_codes validate_pkt_signature(const char *raw_pkt,
+		int raw_len, const struct cld_pkt_hdr *pkt)
 {
-	size_t alloc_len;
-	struct cld_packet *outpkt;
-	struct cld_msg_ack_frag *ack_msg;
-
-	alloc_len = sizeof(*outpkt) + sizeof(*ack_msg) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	ack_msg = (struct cld_msg_ack_frag *) (outpkt + 1);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, pkt);
-
-	memcpy(ack_msg->hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	__cld_rand64(&ack_msg->hdr.xid);
-	ack_msg->hdr.op = cmo_ack_frag;
-	ack_msg->seqid = pkt->seqid;
-
-	authsign(outpkt, alloc_len);
-
-	HAIL_DEBUG(&srv_log, "ack-partial-msg: "
-		"sid " SIDFMT ", op %s, seqid %llu",
-		SIDARG(outpkt->sid), opstr(ack_msg->hdr.op),
-		(unsigned long long) le64_to_cpu(outpkt->seqid));
+	int ret;
+	struct cld_pkt_ftr *foot;
+
+	foot = (struct cld_pkt_ftr *)
+			(raw_pkt + (raw_len - CLD_PKT_FTR_LEN));
+	ret = __cld_authcheck(&srv_log, pkt->user,
+			raw_pkt, raw_len - SHA_DIGEST_LENGTH,
+			foot->sha);
+	if (ret) {
+		HAIL_DEBUG(&srv_log, "%s: bad HMAC signature on packet "
+			   "ret = %d\n", __func__, ret);
+		return CLE_SIG_INVAL;
+	}
 
-	/* transmit ack-partial-msg response (once, without retries) */
-	udp_tx(sock_fd, (struct sockaddr *) &cli->addr, cli->addr_len,
-	       outpkt, alloc_len);
+	return 0;
 }
 
-static void udp_rx(int sock_fd,
-		   const struct client *cli,
-		   const void *raw_pkt, size_t pkt_len)
+/** Check if this packet is a duplicate
+ *
+ * @param info		Packet info
+ *
+ * @return		true only if the packet is a duplicate
+ */
+static bool packet_is_dupe(const struct pkt_info *info)
 {
-	const struct cld_packet *pkt = raw_pkt;
-	struct cld_packet *outpkt;
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-	struct session *sess = NULL;
-	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_resp *resp;
-	struct msg_params mp;
-	size_t alloc_len;
-	uint32_t pkt_flags;
-	bool first_frag, last_frag, have_new_sess, have_ack, have_put;
-
-	/* verify pkt data integrity and credentials via HMAC signature */
-	if (!authcheck(pkt, pkt_len)) {
-		resp_rc = CLE_SIG_INVAL;
-		goto err_out;
-	}
+	if (!info->sess)
+		return false;
+	if (info->type == CMT_ACK)
+		return false;
 
-	pkt_flags = le32_to_cpu(pkt->flags);
-	first_frag = pkt_flags & CPF_FIRST;
-	last_frag = pkt_flags & CPF_LAST;
-	have_new_sess = first_frag && (msg->op == cmo_new_sess);
-	have_ack = first_frag && (msg->op == cmo_ack);
-	have_put = first_frag && (msg->op == cmo_put);
-
-	/* look up client session, verify it matches IP and username */
-	sess = g_hash_table_lookup(cld_srv.sessions, pkt->sid);
-	if (sess &&
-	    ((sess->addr_len != cli->addr_len) ||
-	     memcmp(&sess->addr, &cli->addr, sess->addr_len) ||
-	     strncmp(pkt->user, sess->user, CLD_MAX_USERNAME) ||
-	     sess->dead)) {
-		resp_rc = CLE_SESS_INVAL;
-		goto err_out;
+	/* Check sequence IDs to discover if this packet is a dupe */
+	if (info->seqid != info->sess->next_seqid_in) {
+		HAIL_DEBUG(&srv_log, "dropping dup with seqid %llu "
+			"(expected %llu).",
+			(unsigned long long) info->seqid,
+			(unsigned long long) info->sess->next_seqid_in);
+		return true;
 	}
 
-	mp.sock_fd = sock_fd;
-	mp.cli = cli;
-	mp.sess = sess;
-	mp.pkt = pkt;
-	mp.msg = msg;
-	mp.msg_len = pkt_len - sizeof(*pkt) - SHA_DIGEST_LENGTH;
-
-	HAIL_DEBUG(&srv_log, "pkt: len %zu, seqid %llu, sid " SIDFMT ", "
-		"flags %s%s, user %s",
-		pkt_len, (unsigned long long) le64_to_cpu(pkt->seqid),
-		SIDARG(pkt->sid),
-		first_frag ? "F" : "", last_frag ? "L" : "",
-		pkt->user);
-
-	/* advance sequence id's and update last-contact timestamp */
-	if (!have_new_sess) {
-		if (!sess) {
-			resp_rc = CLE_SESS_INVAL;
-			goto err_out;
-		}
-
-		sess->last_contact = current_time.tv_sec;
-		sess->sock_fd = sock_fd;
-
-		if (!have_ack) {
-			/* eliminate duplicates; do not return any response */
-			if (le64_to_cpu(pkt->seqid) != sess->next_seqid_in) {
-				HAIL_DEBUG(&srv_log, "dropping dup");
-				return;
-			}
-
-			/* received message - update session */
-			sess->next_seqid_in++;
-		}
-	} else {
-		if (sess) {
-			/* eliminate duplicates; do not return any response */
-			if (le64_to_cpu(pkt->seqid) != sess->next_seqid_in) {
-				HAIL_DEBUG(&srv_log, "dropping dup");
-				return;
-			}
+	return false;
+}
 
-			resp_rc = CLE_SESS_EXISTS;
-			goto err_out;
-		}
+void simple_sendmsg(int fd, const struct client *cli,
+	uint64_t sid, const char *user, uint64_t seqid,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_type type)
+{
+	XDR xhdr, xmsg;
+	struct cld_pkt_hdr pkt;
+	struct cld_pkt_msg_infos *infos;
+	struct cld_pkt_ftr *foot;
+	char *buf;
+	size_t msg_len, hdr_len, buf_len;
+
+	/* Set up the packet header */
+	memset(&pkt, 0, sizeof(cld_pkt_hdr));
+	memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+	pkt.sid = sid;
+	pkt.user = (char *)user;
+	pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+	infos = &pkt.mi.cld_pkt_msg_info_u.mi;
+	__cld_rand64(&infos->xid);
+	infos->type = type;
+
+	/* Determine sizes */
+	msg_len = xdr_sizeof(xdrproc, (void *)xdrdata);
+	if (msg_len > CLD_MAX_MSG_SZ) {
+		HAIL_ERR(&srv_log, "%s: tried to put %d message bytes in a "
+			"single packet. Maximum message bytes per packet "
+			"is %d\n", __func__, msg_len, CLD_MAX_PKT_MSG_SZ);
+		return;
 	}
-
-	/* copy message fragment into reassembly buffer */
-	if (sess) {
-		if (first_frag)
-			sess->msg_buf_len = 0;
-
-		if ((sess->msg_buf_len + mp.msg_len) > CLD_MAX_MSG_SZ) {
-			resp_rc = CLE_BAD_PKT;
-			goto err_out;
-		}
-
-		memcpy(&sess->msg_buf[sess->msg_buf_len], msg, mp.msg_len);
-		sess->msg_buf_len += mp.msg_len;
-
-		if (!last_frag) {
-			pkt_ack_frag(sock_fd, cli, pkt);
-			return;
-		}
-
-		mp.msg = msg = (struct cld_msg_hdr *) sess->msg_buf;
-		mp.msg_len = sess->msg_buf_len;
-
-		if ((srv_log.verbose > 1) && !first_frag)
-			HAIL_DEBUG(&srv_log, "    final message size %u",
-			       sess->msg_buf_len);
+	hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, &pkt);
+	buf_len = msg_len + hdr_len + CLD_PKT_FTR_LEN;
+	buf = alloca(buf_len);
+
+	/* Serialize data */
+	xdrmem_create(&xhdr, buf, hdr_len, XDR_ENCODE);
+	if (!xdr_cld_pkt_hdr(&xhdr, &pkt)) {
+		xdr_destroy(&xhdr);
+		HAIL_ERR(&srv_log, "%s: xdr_cld_pkt_hdr failed\n",
+			  __func__);
+		return;
 	}
+	xdr_destroy(&xhdr);
+	xdrmem_create(&xmsg, buf + hdr_len, msg_len, XDR_ENCODE);
+	if (!xdrproc(&xmsg, (void *)xdrdata)) {
+		xdr_destroy(&xmsg);
+		HAIL_ERR(&srv_log, "%s: xdrproc failed\n", __func__);
+		return;
+	}
+	xdr_destroy(&xmsg);
+
+	foot = (struct cld_pkt_ftr *)
+		(buf + (buf_len - SHA_DIGEST_LENGTH));
+	foot->seqid = cpu_to_le64(seqid);
+	__cld_authsign(&srv_log, pkt.user, buf, buf_len - SHA_DIGEST_LENGTH,
+		foot->sha);
+	udp_tx(fd, (struct sockaddr *) &cli->addr, cli->addr_len,
+		buf, buf_len);
+}
 
-	if (last_frag)
-		udp_rx_msg(cli, pkt, msg, &mp);
-	return;
-
-err_out:
-	/* transmit error response (once, without retries) */
-	alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	resp = (struct cld_msg_resp *) (outpkt + 1);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, pkt);
-
-	resp_copy(resp, msg);
-	resp->code = cpu_to_le32(resp_rc);
-
-	authsign(outpkt, alloc_len);
-
-	HAIL_DEBUG(&srv_log, "udp_rx err: "
-		"sid " SIDFMT ", op %s, seqid %llu, code %d",
-		SIDARG(outpkt->sid), opstr(resp->hdr.op),
-		(unsigned long long) le64_to_cpu(outpkt->seqid),
-		resp_rc);
-
-	udp_tx(sock_fd, (struct sockaddr *) &cli->addr, cli->addr_len,
-	       outpkt, alloc_len);
+static void simple_sendresp(int sock_fd, const struct client *cli,
+		    const struct pkt_info *info, enum cle_err_codes code)
+{
+	const struct cld_pkt_hdr *pkt = info->pkt;
+	struct cld_msg_generic_resp resp;
+	resp.code = code;
+	resp.xid_in = info->xid;
+
+	simple_sendmsg(sock_fd, cli, pkt->sid, pkt->user, info->seqid,
+		       (xdrproc_t)xdr_cld_msg_generic_resp, (void *)&resp,
+		       info->type);
 }
 
 static bool udp_srv_event(int fd, short events, void *userdata)
 {
 	struct client cli;
 	char host[64];
-	ssize_t rrc;
+	ssize_t rrc, hdr_len;
 	struct msghdr hdr;
 	struct iovec iov[2];
-	uint8_t raw_pkt[CLD_RAW_MSG_SZ], ctl_msg[CLD_RAW_MSG_SZ];
-	struct cld_packet *pkt = (struct cld_packet *) raw_pkt;
+	char raw_pkt[CLD_RAW_MSG_SZ], ctl_msg[CLD_RAW_MSG_SZ];
+	struct cld_pkt_hdr pkt;
+	struct pkt_info info;
+	enum cle_err_codes err;
 
 	memset(&cli, 0, sizeof(cli));
 
@@ -519,40 +576,52 @@ static bool udp_srv_event(int fd, short events, void *userdata)
 
 	HAIL_DEBUG(&srv_log, "client %s message (%d bytes)", host, (int) rrc);
 
-	/* if it is complete garbage, drop immediately */
-	if ((rrc < (sizeof(*pkt) + SHA_DIGEST_LENGTH)) ||
-	    (memcmp(pkt->magic, CLD_PKT_MAGIC, sizeof(pkt->magic)))) {
+	if (!parse_pkt_header(raw_pkt, rrc, &pkt, &hdr_len)) {
 		cld_srv.stats.garbage++;
-		return true; /* continue main loop; do NOT terminate server */
+		return true;
 	}
 
-	if (cld_srv.cldb.is_master && cld_srv.cldb.up)
-		udp_rx(fd, &cli, raw_pkt, rrc);
-
-	else {
-		struct cld_packet *outpkt;
-		struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-		struct cld_msg_resp *resp;
-		size_t alloc_len;
-
-		alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-		outpkt = alloca(alloc_len);
-		memset(outpkt, 0, alloc_len);
+	if (!get_pkt_info(&pkt, raw_pkt, rrc, hdr_len, &info)) {
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		cld_srv.stats.garbage++;
+		return true;
+	}
 
-		pkt_init_pkt(outpkt, pkt);
+	if (packet_is_dupe(&info)) {
+		/* silently drop dupes */
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
 
-		/* transmit not-master error msg */
-		resp = (struct cld_msg_resp *) (outpkt + 1);
-		resp_copy(resp, msg);
-		resp->hdr.op = cmo_not_master;
+	err = validate_pkt_session(&info, &cli);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
 
-		authsign(outpkt, alloc_len);
+	err = validate_pkt_signature(raw_pkt, rrc, &pkt);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
 
-		udp_tx(fd, (struct sockaddr *) &cli.addr, cli.addr_len,
-		       outpkt, alloc_len);
+	if (!(cld_srv.cldb.is_master && cld_srv.cldb.up)) {
+		simple_sendmsg(fd, &cli, pkt.sid, pkt.user, 0xdeadbeef,
+			       (xdrproc_t)xdr_void, NULL, CMT_NOT_MASTER);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
 	}
 
-	return true;	/* continue main loop; do NOT terminate server */
+	err = udp_rx(fd, &cli, &info, raw_pkt, rrc);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
+	xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+	return true;
 }
 
 static void add_chkpt_timer(void)
@@ -586,7 +655,7 @@ static int net_write_port(const char *port_file, const char *port_str)
 	portf = fopen(port_file, "w");
 	if (portf == NULL) {
 		rc = errno;
-		applog(LOG_INFO, "Cannot create port file %s: %s",
+		HAIL_INFO(&srv_log, "Cannot create port file %s: %s",
 		       port_file, strerror(rc));
 		return -rc;
 	}
@@ -607,7 +676,7 @@ static void net_close(void)
 		pfd = &g_array_index(cld_srv.polls, struct pollfd, i);
 		if (pfd->fd >= 0) {
 			if (close(pfd->fd) < 0)
-				applog(LOG_WARNING, "net_close(%d): %s",
+				HAIL_WARN(&srv_log, "net_close(%d): %s",
 				       pfd->fd, strerror(errno));
 			pfd->fd = -1;
 		}
@@ -674,7 +743,8 @@ static int net_open_any(void)
 		if (getsockname(fd6, (struct sockaddr *) &addr6,
 				&addr_len) != 0) {
 			rc = errno;
-			applog(LOG_ERR, "getsockname failed: %s", strerror(rc));
+			HAIL_ERR(&srv_log, "getsockname failed: %s",
+				 strerror(rc));
 			return -rc;
 		}
 		port = ntohs(addr6.sin6_port);
@@ -696,17 +766,18 @@ static int net_open_any(void)
 		if (getsockname(fd4, (struct sockaddr *) &addr4,
 				&addr_len) != 0) {
 			rc = errno;
-			applog(LOG_ERR, "getsockname failed: %s", strerror(rc));
+			HAIL_ERR(&srv_log, "getsockname failed: %s",
+				 strerror(rc));
 			return -rc;
 		}
 		port = ntohs(addr4.sin_port);
 	}
 
-	applog(LOG_INFO, "Listening on port %u", port);
+	HAIL_INFO(&srv_log, "Listening on port %u", port);
 
 	if (cld_srv.port_file) {
 		char portstr[7];
-		snprintf(portstr, sizeof(portstr), "%u", port);
+		snprintf(portstr, sizeof(portstr), "%u\n", port);
 		return net_write_port(cld_srv.port_file, portstr);
 	}
 	return 0;
@@ -725,7 +796,7 @@ static int net_open_known(const char *portstr)
 
 	rc = getaddrinfo(NULL, portstr, &hints, &res0);
 	if (rc) {
-		applog(LOG_ERR, "getaddrinfo(*:%s) failed: %s",
+		HAIL_ERR(&srv_log, "getaddrinfo(*:%s) failed: %s",
 		       portstr, gai_strerror(rc));
 		rc = -EINVAL;
 		goto err_addr;
@@ -765,7 +836,7 @@ static int net_open_known(const char *portstr)
 			    listen_serv, sizeof(listen_serv),
 			    NI_NUMERICHOST | NI_NUMERICSERV);
 
-		applog(LOG_INFO, "Listening on %s port %s",
+		HAIL_INFO(&srv_log, "Listening on %s port %s",
 		       listen_host, listen_serv);
 	}
 
@@ -791,7 +862,7 @@ static int net_open(void)
 
 static void segv_signal(int signo)
 {
-	applog(LOG_ERR, "SIGSEGV");
+	HAIL_ERR(&srv_log, "SIGSEGV");
 	exit(1);
 }
 
@@ -806,7 +877,7 @@ static void stats_signal(int signo)
 }
 
 #define X(stat) \
-	applog(LOG_INFO, "STAT %s %lu", #stat, cld_srv.stats.stat)
+	HAIL_INFO(&srv_log, "STAT %s %lu", #stat, cld_srv.stats.stat)
 
 static void stats_dump(void)
 {
@@ -1037,7 +1108,7 @@ int main (int argc, char *argv[])
 	 */
 	rc = main_loop();
 
-	applog(LOG_INFO, "shutting down");
+	HAIL_INFO(&srv_log, "shutting down");
 
 	if (strict_free)
 		timer_del(&cld_srv.chkpt_timer);
@@ -1087,7 +1158,7 @@ static void ensure_root()
 	} else if (rc == DB_NOTFOUND) {
 		inode = cldb_inode_mem("/", sizeof("/")-1, CIFL_DIR, CLD_INO_ROOT);
 		if (!inode) {
-			applog(LOG_CRIT, "Cannot allocate new root inode");
+			HAIL_CRIT(&srv_log, "Cannot allocate new root inode");
 			goto err_;
 		}
 
@@ -1098,7 +1169,7 @@ static void ensure_root()
 		rc = cldb_inode_put(txn, inode, 0);
 		if (rc) {
 			free(inode);
-			applog(LOG_CRIT, "Cannot allocate new root inode");
+			HAIL_CRIT(&srv_log, "Cannot allocate new root inode");
 			goto err_;
 		}
 
diff --git a/server/session.c b/server/session.c
index 44cd65a..768af81 100644
--- a/server/session.c
+++ b/server/session.c
@@ -18,6 +18,7 @@
  */
 
 #define _GNU_SOURCE
+
 #include "cld-config.h"
 
 #include <sys/socket.h>
@@ -29,17 +30,15 @@
 #include <openssl/sha.h>
 #include <cld-private.h>
 #include "cld.h"
-
-struct session_outpkt;
+#include <cld_fmt.h>
 
 struct session_outpkt {
 	struct session		*sess;
 
-	struct cld_packet	*pkt;
+	char			*pkt_data;
 	size_t			pkt_len;
 
 	uint64_t		next_retry;
-	uint64_t		src_seqid;
 	unsigned int		refs;
 
 	void			(*done_cb)(struct session_outpkt *);
@@ -62,26 +61,6 @@ uint64_t next_seqid_le(uint64_t *seq)
 	return rc;
 }
 
-void pkt_init_pkt(struct cld_packet *dest, const struct cld_packet *src)
-{
-	memset(dest, 0, sizeof(*dest));
-	memcpy(dest->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	dest->seqid = cpu_to_le64(0xdeadbeef);
-	memcpy(dest->sid, src->sid, CLD_SID_SZ);
-	dest->flags = cpu_to_le32(CPF_FIRST | CPF_LAST);
-	strncpy(dest->user, src->user, CLD_MAX_USERNAME - 1);
-}
-
-static void pkt_init_sess(struct cld_packet *dest, struct session *sess)
-{
-	memset(dest, 0, sizeof(*dest));
-	memcpy(dest->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	dest->seqid = next_seqid_le(&sess->next_seqid_out);
-	memcpy(dest->sid, sess->sid, CLD_SID_SZ);
-	dest->flags = 0;
-	strncpy(dest->user, sess->user, CLD_MAX_USERNAME - 1);
-}
-
 guint sess_hash(gconstpointer v)
 {
 	const struct session *sess = v;
@@ -399,20 +378,6 @@ static void session_ping_done(struct session_outpkt *outpkt)
 	outpkt->sess->ping_open = false;
 }
 
-static void session_ping(struct session *sess)
-{
-	struct cld_msg_hdr resp;
-
-	memset(&resp, 0, sizeof(resp));
-	memcpy(resp.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	__cld_rand64(&resp.xid);
-	resp.op = cmo_ping;
-
-	sess->ping_open = true;
-
-	sess_sendmsg(sess, &resp, sizeof(resp), session_ping_done, NULL);
-}
-
 static void session_timeout(struct timer *timer)
 {
 	struct session *sess = timer->userdata;
@@ -426,8 +391,11 @@ static void session_timeout(struct timer *timer)
 	if (!sess->dead && (sess_expire > now)) {
 		if (!sess->ping_open &&
 		    (sess_expire > (sess->last_contact + (CLD_SESS_TIMEOUT / 2) &&
-		    (sess->sock_fd > 0))))
-			session_ping(sess);
+		    (sess->sock_fd > 0)))) {
+			sess_sendmsg(sess,
+				     (xdrproc_t)xdr_void, NULL, CMT_PING,
+				     session_ping_done, NULL);
+		}
 
 		timer_add(&sess->timer, now + ((sess_expire - now) / 2) + 1);
 		return;	/* timer added; do not time out session */
@@ -520,8 +488,8 @@ static struct session_outpkt *op_alloc(size_t pkt_len)
 	if (!op)
 		return NULL;
 
-	op->pkt = calloc(1, pkt_len);
-	if (!op->pkt) {
+	op->pkt_data = calloc(1, pkt_len);
+	if (!op->pkt_data) {
 		free(op);
 		return NULL;
 	}
@@ -543,14 +511,13 @@ static void op_unref(struct session_outpkt *op)
 			return;
 	}
 
-	free(op->pkt);
+	free(op->pkt_data);
 	free(op);
 }
 
 static int sess_retry_output(struct session *sess, time_t *next_retry_out)
 {
-	GList *tmp, *tmp1;
-	struct session_outpkt *op;
+	GList *tmp;
 	int rc = 0;
 	time_t next_retry = 0;
 
@@ -558,28 +525,25 @@ static int sess_retry_output(struct session *sess, time_t *next_retry_out)
 
 	tmp = sess->out_q;
 	while (tmp) {
-		struct cld_packet *outpkt;
-		struct cld_msg_hdr *outmsg;
-
-		tmp1 = tmp;
+		struct session_outpkt *op;
+		op = tmp->data;
 		tmp = tmp->next;
 
-		op = tmp1->data;
-		outpkt = op->pkt;
-		outmsg = (struct cld_msg_hdr *) (outpkt + 1);
-
 		if (!next_retry || (op->next_retry < next_retry))
 			*next_retry_out = next_retry = op->next_retry;
 
 		if (current_time.tv_sec < op->next_retry)
 			continue;
 
-		HAIL_DEBUG(&srv_log, "retry: sid " SIDFMT ", op %s, seqid %llu",
-			SIDARG(outpkt->sid), opstr(outmsg->op),
-			(unsigned long long) le64_to_cpu(outpkt->seqid));
+		if (srv_log.verbose) {
+			char scratch[PKT_HDR_TO_STR_SCRATCH_LEN];
+			HAIL_DEBUG(&srv_log, "%s: retrying %s\n", __func__,
+				__cld_pkt_hdr_to_str(scratch, op->pkt_data,
+				op->pkt_len));
+		}
 
 		rc = udp_tx(sess->sock_fd, (struct sockaddr *) &sess->addr,
-			    sess->addr_len, op->pkt, op->pkt_len);
+			    sess->addr_len, op->pkt_data, op->pkt_len);
 		if (rc)
 			break;
 
@@ -611,141 +575,155 @@ static void session_outq(struct session *sess, GList *new_pkts)
 	sess->out_q = g_list_concat(sess->out_q, new_pkts);
 }
 
-bool sess_sendmsg(struct session *sess, const void *msg_, size_t msglen,
-		  void (*done_cb)(struct session_outpkt *),
-		  void *done_data)
+bool sess_sendmsg(struct session *sess,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_type type,
+	void (*done_cb)(struct session_outpkt *), void *done_data)
 {
-	struct cld_packet *outpkt;
-	unsigned int n_pkts, i;
-	size_t pkt_len, msg_left = msglen;
-	struct session_outpkt *pkts[CLD_MAX_PKT_MSG], *op;
-	GList *tmp_root = NULL;
-	const void *p;
-	bool first_frag = true;
-
-	n_pkts = (msglen / CLD_MAX_PKT_MSG_SZ);
-	n_pkts += (msglen % CLD_MAX_PKT_MSG_SZ) ? 1 : 0;
-
-	if (srv_log.verbose) {
-		const struct cld_msg_hdr *hdr = msg_;
-		const struct cld_msg_resp *rsp;
-
-		switch (hdr->op) {
-		/* This is the command set that gets to cldc_rx_generic */
-		case cmo_nop:
-		case cmo_close:
-		case cmo_del:
-		case cmo_lock:
-		case cmo_unlock:
-		case cmo_trylock:
-		case cmo_put:
-		case cmo_new_sess:
-		case cmo_end_sess:
-		case cmo_open:
-		case cmo_get_meta:
-		case cmo_get:
-			rsp = (struct cld_msg_resp *) msg_;
-			HAIL_DEBUG(&srv_log, "sendmsg: "
-			       "sid " SIDFMT ", op %s, msglen %u, code %u, "
-			       "xid %llu, xid_in %llu",
-			       SIDARG(sess->sid),
-			       opstr(hdr->op),
-			       (unsigned int) msglen,
-			       le32_to_cpu(rsp->code),
-			       (unsigned long long) le64_to_cpu(hdr->xid),
-			       (unsigned long long) le64_to_cpu(rsp->xid_in));
-			break;
-		default:
-			HAIL_DEBUG(&srv_log, "sendmsg: "
-			       "sid " SIDFMT ", op %s, msglen %u",
-			       SIDARG(sess->sid),
-			       opstr(hdr->op),
-			       (unsigned int) msglen);
-		}
-	}
+	XDR xmsg;
+	size_t msg_rem, msg_len, msg_chunk_len;
+	char *msg_bytes, *msg_cur;
+	GList *tmp_list, *new_pkts = NULL;
+	int first, last;
 
-	if (n_pkts > CLD_MAX_PKT_MSG)
+	/* Use XDR to serialize the message */
+	msg_len = xdr_sizeof(xdrproc, (void *)xdrdata);
+	if (msg_len > CLD_MAX_MSG_SZ)
+		return false;
+	msg_bytes = alloca(msg_len);
+	xdrmem_create(&xmsg, msg_bytes, msg_len, XDR_ENCODE);
+	if (!xdrproc(&xmsg, (void *)xdrdata)) {
+		xdr_destroy(&xmsg);
+		HAIL_ERR(&srv_log, "%s: xdrproc failed\n", __func__);
 		return false;
-
-	/* pass 1: perform allocations */
-	for (i = 0; i < n_pkts; i++) {
-		pkts[i] = op = op_alloc(sizeof(*outpkt) +
-					CLD_MAX_PKT_MSG_SZ +
-					SHA_DIGEST_LENGTH);
-		if (!op)
-			goto err_out;
-
-		tmp_root = g_list_append(tmp_root, op);
 	}
+	xdr_destroy(&xmsg);
+
+	/* Break the message into packets */
+	first = 1;
+	msg_rem = msg_len;
+	msg_cur = msg_bytes;
+	do {
+		XDR xout;
+		struct cld_pkt_hdr pkt;
+		size_t hdr_len;
+		struct session_outpkt *op;
 
-	/* pass 2: fill packets */
-	p = msg_;
-	for (i = 0; i < n_pkts; i++) {
-		struct cld_msg_hdr *outmsg;
-		void *outmsg_mem;
-		size_t copy_len;
-
-		op = pkts[i];
-
-		op->sess = sess;
-
-		outpkt = op->pkt;
-		pkt_len = op->pkt_len;
-
-		outmsg_mem = (outpkt + 1);
-		outmsg = outmsg_mem;
-
-		/* init packet header */
-		pkt_init_sess(outpkt, sess);
-
-		if (first_frag) {
-			first_frag = false;
-			outpkt->flags |= cpu_to_le32(CPF_FIRST);
+		if (msg_rem <= CLD_MAX_PKT_MSG_SZ) {
+			msg_chunk_len = msg_rem;
+			last = 1;
+		} else {
+			msg_chunk_len = CLD_MAX_PKT_MSG_SZ;
+			last = 0;
 		}
 
-		copy_len = MIN(pkt_len - sizeof(*outpkt) - SHA_DIGEST_LENGTH,
-			       msg_left);
-		memcpy(outmsg_mem, p, copy_len);
-
-		p += copy_len;
-		msg_left -= copy_len;
-
-		op->pkt_len =
-		pkt_len = sizeof(*outpkt) + copy_len + SHA_DIGEST_LENGTH;
-
-		if (!msg_left) {
-			op->done_cb = done_cb;
-			op->done_data = done_data;
-
-			outpkt->flags |= cpu_to_le32(CPF_LAST);
+		/* Set up packet header */
+		memset(&pkt, 0, sizeof(pkt));
+		memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+		memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+		pkt.user = sess->user;
+		if (first) {
+			struct cld_pkt_msg_infos *infos =
+				&pkt.mi.cld_pkt_msg_info_u.mi;
+			if (last)
+				pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_FIRST;
+			__cld_rand64(&infos->xid);
+			infos->type = type;
+		} else {
+			if (last)
+				pkt.mi.order = CLD_PKT_ORD_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_MID;
 		}
 
+		/* Allocate space and initialize session_outpkt structure */
+		hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, (void *)&pkt);
+		op = op_alloc(hdr_len + msg_chunk_len + CLD_PKT_FTR_LEN);
+		if (!op) {
+			HAIL_DEBUG(&srv_log, "%s: op_alloc failed\n",
+				   __func__);
+			goto err_out;
+		}
+		op->sess = sess;
 		op->next_retry = current_time.tv_sec + CLD_RETRY_START;
+		op->done_cb = done_cb;
+		op->done_data = done_data;
+		xdrmem_create(&xout, op->pkt_data, hdr_len, XDR_ENCODE);
+		if (!xdr_cld_pkt_hdr(&xout, &pkt)) {
+			xdr_destroy(&xout);
+			HAIL_ERR(&srv_log, "%s: xdr_cld_pkt_hdr failed\n",
+				  __func__);
+			goto err_out;
+		}
+		xdr_destroy(&xout);
+
+		/* Fill in data */
+		memcpy(op->pkt_data + hdr_len, msg_cur, msg_chunk_len);
+		msg_cur += msg_chunk_len;
+		msg_rem -= msg_chunk_len;
+		first = 0;
+
+		new_pkts = g_list_prepend(new_pkts, op);
+	} while (!last);
+
+	/* add sequence IDs and SHAs */
+	new_pkts = g_list_reverse(new_pkts);
+	for (tmp_list = g_list_first(new_pkts);
+	     tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op =
+			(struct session_outpkt *) tmp_list->data;
+		struct cld_pkt_ftr *foot = (struct cld_pkt_ftr *)
+			(op->pkt_data + (op->pkt_len - CLD_PKT_FTR_LEN));
+		int ret;
+
+		foot->seqid = next_seqid_le(&sess->next_seqid_out);
+		ret = __cld_authsign(&srv_log, sess->user,
+				op->pkt_data, op->pkt_len - SHA_DIGEST_LENGTH,
+				foot->sha);
+		if (ret)
+			goto err_out;
+	}
 
-		if (!authsign(outpkt, pkt_len))
-			goto err_out;	/* FIXME: we free all pkts -- wrong! */
-
+	/* send packets */
+	for (tmp_list = g_list_first(new_pkts);
+	     tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op =
+			(struct session_outpkt *) tmp_list->data;
 		udp_tx(sess->sock_fd, (struct sockaddr *) &sess->addr,
-		       sess->addr_len, outpkt, pkt_len);
+			sess->addr_len, op->pkt_data, op->pkt_len);
 	}
 
-	session_outq(sess, tmp_root);
+	session_outq(sess, new_pkts);
 
 	return true;
 
 err_out:
-	for (i = 0; i < n_pkts; i++)
-		op_unref(pkts[i]);
-	g_list_free(tmp_root);
+	for (tmp_list = g_list_first(new_pkts); tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op;
+		op = (struct session_outpkt *)tmp_list->data;
+		op_unref(op);
+	}
+	g_list_free(new_pkts);
 	return false;
 }
 
-void msg_ack(struct msg_params *mp)
+void sess_sendresp_generic(struct session *sess, enum cle_err_codes code)
+{
+	struct cld_msg_generic_resp resp;
+	resp.code = code;
+	resp.xid_in = sess->msg_xid;
+
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+		     (void *)&resp, sess->msg_type, NULL, NULL);
+}
+
+void msg_ack(struct session *sess, uint64_t seqid)
 {
-	struct cld_packet *outpkt;
-	struct cld_msg_hdr *outmsg;
 	GList *tmp, *tmp1;
-	struct session *sess = mp->sess;
 	struct session_outpkt *op;
 
 	if (!sess->out_q)
@@ -754,19 +732,22 @@ void msg_ack(struct msg_params *mp)
 	/* look through output queue */
 	tmp = sess->out_q;
 	while (tmp) {
+		uint64_t op_seqid;
+		struct cld_pkt_ftr *foot;
 		tmp1 = tmp;
 		tmp = tmp->next;
 
 		op = tmp1->data;
-		outpkt = op->pkt;
-		outmsg = (struct cld_msg_hdr *) (outpkt + 1);
+		foot = (struct cld_pkt_ftr *)
+			(op->pkt_data + (op->pkt_len - CLD_PKT_FTR_LEN));
+		op_seqid = le64_to_cpu(foot->seqid);
 
 		/* if matching seqid found, we ack'd a message in out_q */
-		if (mp->pkt->seqid != outpkt->seqid)
+		if (seqid != op_seqid)
 			continue;
 
 		HAIL_DEBUG(&srv_log, "    expiring seqid %llu",
-			(unsigned long long) le64_to_cpu(outpkt->seqid));
+			(unsigned long long) op_seqid);
 
 		/* remove and delete the ack'd msg; call ack'd callback */
 		sess->out_q = g_list_delete_link(sess->out_q, tmp1);
@@ -780,17 +761,17 @@ void msg_ack(struct msg_params *mp)
 		timer_del(&sess->retry_timer);
 }
 
-void msg_new_sess(struct msg_params *mp, const struct client *cli)
+void msg_new_sess(int sock_fd, const struct client *cli,
+		  const struct pkt_info *info)
 {
+	const struct cld_pkt_hdr *pkt = info->pkt;
 	DB *db = cld_srv.cldb.sessions;
 	struct raw_session raw_sess;
 	struct session *sess = NULL;
 	DBT key, val;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_resp *resp;
-	struct cld_packet *outpkt;
-	size_t alloc_len;
+	struct cld_msg_generic_resp resp;
 
 	sess = session_new();
 	if (!sess) {
@@ -799,17 +780,17 @@ void msg_new_sess(struct msg_params *mp, const struct client *cli)
 	}
 
 	/* build raw_session database record */
-	memcpy(sess->sid, mp->pkt->sid, sizeof(sess->sid));
+	memcpy(sess->sid, &pkt->sid, sizeof(sess->sid));
 	memcpy(&sess->addr, &cli->addr, sizeof(sess->addr));
 
-	strncpy(sess->user, mp->pkt->user, sizeof(sess->user));
-	sess->user[sizeof(sess->user) - 1] = 0;
+	snprintf(sess->user, sizeof(sess->user), "%s",
+		pkt->user);
 
-	sess->sock_fd = mp->sock_fd;
+	sess->sock_fd = sock_fd;
 	sess->addr_len = cli->addr_len;
 	strncpy(sess->ipaddr, cli->addr_host, sizeof(sess->ipaddr));
 	sess->last_contact = current_time.tv_sec;
-	sess->next_seqid_in = le64_to_cpu(mp->pkt->seqid) + 1;
+	sess->next_seqid_in = info->seqid + 1;
 
 	session_encode(&raw_sess, sess);
 
@@ -835,35 +816,33 @@ void msg_new_sess(struct msg_params *mp, const struct client *cli)
 		goto err_out;
 	}
 
+	HAIL_DEBUG(&srv_log, "%s: created new session " SIDFMT " with "
+			"sess->next_seqid_in = %llu\n",
+			__func__, SIDARG(sess->sid), sess->next_seqid_in);
 	g_hash_table_insert(cld_srv.sessions, sess->sid, sess);
 
 	/* begin session timer */
 	timer_add(&sess->timer, time(NULL) + (CLD_SESS_TIMEOUT / 2));
 
-	resp_ok(sess, mp->msg);
+	/* send new-sess reply */
+	resp.code = CLE_OK;
+	resp.xid_in = info->xid;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+			(void *)&resp, CMT_NEW_SESS, NULL, NULL);
+
 	return;
 
 err_out:
 	session_free(sess, true);
 
-	alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, mp->pkt);
-
-	resp = (struct cld_msg_resp *) (outpkt + 1);
-	resp_copy(resp, mp->msg);
-	resp->code = cpu_to_le32(resp_rc);
+	HAIL_DEBUG(&srv_log, "new_sess err: sid " SIDFMT ", op %s",
+		pkt->sid, __cld_msg_type_to_str(CMT_NEW_SESS));
 
-	authsign(outpkt, alloc_len);
-
-	HAIL_DEBUG(&srv_log, "new_sess err: sid " SIDFMT ", op %s, seqid %llu",
-		SIDARG(outpkt->sid), opstr(resp->hdr.op),
-		(unsigned long long) le64_to_cpu(outpkt->seqid));
-
-	udp_tx(mp->sock_fd, (struct sockaddr *) &mp->cli->addr,
-	       mp->cli->addr_len, outpkt, alloc_len);
+	resp.code = resp_rc;
+	resp.xid_in = info->xid;
+	simple_sendmsg(sock_fd, cli, pkt->sid, pkt->user, 0xdeadbeef,
+			(xdrproc_t)xdr_cld_msg_generic_resp, (void *)&resp,
+			CMT_NEW_SESS);
 
 	HAIL_DEBUG(&srv_log, "NEW-SESS failed: %d", resp_rc);
 }
@@ -873,18 +852,17 @@ static void end_sess_done(struct session_outpkt *outpkt)
 	session_trash(outpkt->sess);
 }
 
-void msg_end_sess(struct msg_params *mp, const struct client *cli)
+void msg_end_sess(struct session *sess, uint64_t xid)
 {
-	struct session *sess = mp->sess;
-	struct cld_msg_resp resp;
+	struct cld_msg_generic_resp resp;
 
 	/* do nothing; let message acknowledgement via
-	 * end_sess_done mark session dead
-	 */
-
-	memset(&resp, 0, sizeof(resp));
-	resp_copy(&resp, mp->msg);
-	sess_sendmsg(sess, &resp, sizeof(resp), end_sess_done, NULL);
+	 * end_sess_done mark session dead */
+	resp.code = CLE_OK;
+	resp.xid_in = xid;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+			&resp, CMT_END_SESS,
+			end_sess_done, NULL);
 }
 
 /*
diff --git a/test/Makefile.am b/test/Makefile.am
index 20dba03..d8048a7 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,6 +1,7 @@
 
-INCLUDES        = -I$(top_srcdir)/include       \
-		  @GLIB_CFLAGS@
+INCLUDES =	-I$(top_srcdir)/include \
+		-I$(top_srcdir)/lib \
+		@GLIB_CFLAGS@
 
 EXTRA_DIST =			\
 	test.h			\
diff --git a/test/load-file-event.c b/test/load-file-event.c
index 7b309b3..091e3a4 100644
--- a/test/load-file-event.c
+++ b/test/load-file-event.c
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2009 Red Hat, Inc.
  *
@@ -20,7 +19,6 @@
 /*
  * Load a file from CLD.
  */
-
 #define _GNU_SOURCE
 #include "cld-config.h"
 
@@ -34,6 +32,8 @@
 #include <cldc.h>
 #include "test.h"
 
+#include "cld_fmt.h"
+
 struct run {
 	struct cldc_udp *udp;
 	struct timer tmr_udp;
@@ -146,6 +146,8 @@ static int read_1_cb(struct cldc_call_opts *coptarg, enum cle_err_codes errc)
 {
 	struct run *rp = coptarg->private;
 	struct cldc_call_opts copts;
+	char *data;
+	size_t data_len;
 	int rc;
 
 	if (errc != CLE_OK) {
@@ -153,12 +155,14 @@ static int read_1_cb(struct cldc_call_opts *coptarg, enum cle_err_codes errc)
 		exit(1);
 	}
 
-	if (coptarg->u.get.size != TESTLEN) {
-		fprintf(stderr, "Bad CLD file length %d\n", coptarg->u.get.size);
+	cldc_call_opts_get_data(coptarg, &data, &data_len);
+
+	if (data_len != TESTLEN) {
+		fprintf(stderr, "Bad CLD file length %d\n", data_len);
 		exit(1);
 	}
 
-	if (memcmp(coptarg->u.get.buf, TESTSTR, TESTLEN)) {
+	if (memcmp(data, TESTSTR, TESTLEN)) {
 		fprintf(stderr, "Bad CLD file content\n");
 		exit(1);
 	}
diff --git a/test/save-file-event.c b/test/save-file-event.c
index 2b13bc8..03c6ca6 100644
--- a/test/save-file-event.c
+++ b/test/save-file-event.c
@@ -34,8 +34,6 @@
 #include <libtimer.h>
 #include "test.h"
 
-#define DATAMAX  10000
-
 struct run {
 	struct cldc_udp *udp;
 	struct timer tmr_udp;
diff --git a/tools/cldcli.c b/tools/cldcli.c
index a06473d..e4d5c7c 100644
--- a/tools/cldcli.c
+++ b/tools/cldcli.c
@@ -267,6 +267,8 @@ static int cb_ls_2(struct cldc_call_opts *copts_in, enum cle_err_codes errc)
 	struct cldc_call_opts copts = { NULL, };
 	struct cld_dirent_cur dc;
 	int rc, i;
+	char *data;
+	size_t data_len;
 	bool first = true;
 
 	if (errc != CLE_OK) {
@@ -274,8 +276,9 @@ static int cb_ls_2(struct cldc_call_opts *copts_in, enum cle_err_codes errc)
 		write_from_thread(&cresp, sizeof(cresp));
 		return 0;
 	}
+	cldc_call_opts_get_data(copts_in, &data, &data_len);
 
-	rc = cldc_dirent_count(copts_in->u.get.buf, copts_in->u.get.size);
+	rc = cldc_dirent_count(data, data_len);
 	if (rc < 0) {
 		write_from_thread(&cresp, sizeof(cresp));
 		return 0;
@@ -286,7 +289,7 @@ static int cb_ls_2(struct cldc_call_opts *copts_in, enum cle_err_codes errc)
 
 	write_from_thread(&cresp, sizeof(cresp));
 
-	cldc_dirent_cur_init(&dc, copts_in->u.get.buf, copts_in->u.get.size);
+	cldc_dirent_cur_init(&dc, data, data_len);
 
 	for (i = 0; i < rc; i++) {
 		struct ls_rec lsr;
@@ -343,6 +346,8 @@ static int cb_cat_2(struct cldc_call_opts *copts_in, enum cle_err_codes errc)
 {
 	struct cresp cresp = { .tcode = TC_FAILED, };
 	struct cldc_call_opts copts = { NULL, };
+	char *data;
+	size_t data_len;
 
 	if (errc != CLE_OK) {
 		errc_msg(&cresp, errc);
@@ -350,11 +355,13 @@ static int cb_cat_2(struct cldc_call_opts *copts_in, enum cle_err_codes errc)
 		return 0;
 	}
 
+	cldc_call_opts_get_data(copts_in, &data, &data_len);
+
 	cresp.tcode = TC_OK;
-	cresp.u.file_len = copts_in->u.get.size;
+	cresp.u.file_len = data_len;
 
 	write_from_thread(&cresp, sizeof(cresp));
-	write_from_thread(copts_in->u.get.buf, copts_in->u.get.size);
+	write_from_thread(data, data_len);
 
 	/* FIXME: race; should wait until close succeeds/fails before
 	 * returning any data.  'fh' may still be in use, otherwise.
@@ -387,6 +394,8 @@ static int cb_cp_cf_2(struct cldc_call_opts *copts_in, enum cle_err_codes errc)
 {
 	struct cresp cresp = { .tcode = TC_FAILED, };
 	struct cldc_call_opts copts = { NULL, };
+	char *data;
+	size_t data_len;
 
 	if (errc != CLE_OK) {
 		errc_msg(&cresp, errc);
@@ -394,11 +403,12 @@ static int cb_cp_cf_2(struct cldc_call_opts *copts_in, enum cle_err_codes errc)
 		return 0;
 	}
 
+	cldc_call_opts_get_data(copts_in, &data, &data_len);
 	cresp.tcode = TC_OK;
-	cresp.u.file_len = copts_in->u.get.size;
+	cresp.u.file_len = data_len;
 
 	write_from_thread(&cresp, sizeof(cresp));
-	write_from_thread(copts_in->u.get.buf, copts_in->u.get.size);
+	write_from_thread(data, data_len);
 
 	/* FIXME: race; should wait until close succeeds/fails before
 	 * returning any data.  'fh' may still be in use, otherwise.
-- 
1.6.2.5

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-15 17:49 [PATCHv2] cld: use XDR for all messages Colin McCabe
@ 2010-01-21  1:38 ` Jeff Garzik
  2010-01-21  2:38   ` Colin McCabe
  2010-02-05  9:48 ` Jeff Garzik
  2010-02-09  9:20 ` Jeff Garzik
  2 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-01-21  1:38 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev

On 01/15/2010 12:49 PM, Colin McCabe wrote:
> This patch moves CLD from using manual data serialization to using XDR (via
> rpcgen). Both the packet header and the message body are now serialized and
> deserialized using XDR. This makes it easy to have a variable-length packet
> header, as well as a variable-length message body.
>
> This patch introduces a minor libcldc API change in struct cldc_call_opts.
>
> v2 changes:
> * Added __cld prefix to functions in cld_fmt.c
>
> * When decoding CMT_GET messages, we should store the payload in the
> session structure, rather than in cldc_call_opts.
>
> * Got rid of pkt_is_first, pkt_is_last, in favor of a flags-based approach.
>
> * Killed CLD_MAX_PKT_MSG. It's more efficient to only allocate as much space
> as you need, rather than always allocating space for 128 packets in a message.
>
> * Created CLD_MAX_PAYLOAD_SZ to mean the maximum size of the data that can be
> sent with GET or PUT. This is different (and smaller than!) the maximum
> message size.
>
> * automake: Add cld_msg_rpc.h to BUILT_SOURCES
>
> * automake: Add lib dir to INCLUDES
>
> * automake: "make clean" now deletes XDR build products
>
> Signed-off-by: Colin McCabe<cmccabe@alumni.cmu.edu>
> ---
>   .gitignore             |    4 +
>   include/Makefile.am    |    2 +-
>   include/cld_common.h   |   10 +
>   include/cld_fmt.h      |   89 +++++
>   include/cld_msg.h      |  252 -------------
>   include/cldc.h         |   36 +-
>   lib/Makefile.am        |   19 +-
>   lib/cld_fmt.c          |  193 ++++++++++
>   lib/cld_msg_rpc.x      |  218 +++++++++++
>   lib/cldc.c             |  966 +++++++++++++++++++++---------------------------
>   lib/common.c           |    6 +-
>   server/Makefile.am     |    9 +-
>   server/cld.h           |   97 ++++--
>   server/cldb.h          |    2 +-
>   server/msg.c           |  304 ++++++----------
>   server/server.c        |  743 ++++++++++++++++++++-----------------
>   server/session.c       |  396 ++++++++++-----------
>   test/Makefile.am       |    5 +-
>   test/load-file-event.c |   14 +-
>   test/save-file-event.c |    2 -
>   tools/cldcli.c         |   22 +-
>   21 files changed, 1777 insertions(+), 1612 deletions(-)
>   create mode 100644 include/cld_fmt.h
>   delete mode 100644 include/cld_msg.h
>   create mode 100644 lib/cld_fmt.c
>   create mode 100644 lib/cld_msg_rpc.x

Looking good...  we are definitely getting closer.

Currently, it still does not pass "make distcheck", which presages a lot 
of installation problems.  Notably,
	#include <lib/cld_msg_rpc.h>
is not consistent with the rest of the headers, producing things such as 
"make distcheck" build breakage:

> libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../../lib -I.. -I../../include -pthread -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -g -O2 -MT cldc.lo -MD -MP -MF .deps/cldc.Tpo -c ../../lib/cldc.c  -fPIC -DPIC -o .libs/cldc.o
> In file included from ../../lib/cldc.c:37:
> ../../include/cldc.h:26:29: error: lib/cld_msg_rpc.h: No such file or directory

You will either need to move the header to $cld/include/ or update 
INCLUDES in various */Makefile.am files to reference $(top_srcdir)/lib 
(which I see you actually did, in a few cases).

Also, it would be nice if this did not introduce warnings.  A big point 
with the testsuite, and minor point with compiler warnings, is that -- 
more than anything else -- we do not want to introduce regressions.

	Jeff




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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-21  1:38 ` Jeff Garzik
@ 2010-01-21  2:38   ` Colin McCabe
  2010-01-26 21:22     ` Colin McCabe
  0 siblings, 1 reply; 25+ messages in thread
From: Colin McCabe @ 2010-01-21  2:38 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Project Hail List, Pete Zaitcev

On Wed, Jan 20, 2010 at 5:38 PM, Jeff Garzik <jeff@garzik.org> wrote:
> On 01/15/2010 12:49 PM, Colin McCabe wrote:
>>
>> This patch moves CLD from using manual data serialization to using XDR
>> (via
>> rpcgen). Both the packet header and the message body are now serialized
>> and
>> deserialized using XDR. This makes it easy to have a variable-length
>> packet
>> header, as well as a variable-length message body.
>>
>> This patch introduces a minor libcldc API change in struct cldc_call_opts.
>>
>> v2 changes:
>> * Added __cld prefix to functions in cld_fmt.c
>>
>> * When decoding CMT_GET messages, we should store the payload in the
>> session structure, rather than in cldc_call_opts.
>>
>> * Got rid of pkt_is_first, pkt_is_last, in favor of a flags-based
>> approach.
>>
>> * Killed CLD_MAX_PKT_MSG. It's more efficient to only allocate as much
>> space
>> as you need, rather than always allocating space for 128 packets in a
>> message.
>>
>> * Created CLD_MAX_PAYLOAD_SZ to mean the maximum size of the data that can
>> be
>> sent with GET or PUT. This is different (and smaller than!) the maximum
>> message size.
>>
>> * automake: Add cld_msg_rpc.h to BUILT_SOURCES
>>
>> * automake: Add lib dir to INCLUDES
>>
>> * automake: "make clean" now deletes XDR build products
>>
>> Signed-off-by: Colin McCabe<cmccabe@alumni.cmu.edu>
>> ---
>>  .gitignore             |    4 +
>>  include/Makefile.am    |    2 +-
>>  include/cld_common.h   |   10 +
>>  include/cld_fmt.h      |   89 +++++
>>  include/cld_msg.h      |  252 -------------
>>  include/cldc.h         |   36 +-
>>  lib/Makefile.am        |   19 +-
>>  lib/cld_fmt.c          |  193 ++++++++++
>>  lib/cld_msg_rpc.x      |  218 +++++++++++
>>  lib/cldc.c             |  966
>> +++++++++++++++++++++---------------------------
>>  lib/common.c           |    6 +-
>>  server/Makefile.am     |    9 +-
>>  server/cld.h           |   97 ++++--
>>  server/cldb.h          |    2 +-
>>  server/msg.c           |  304 ++++++----------
>>  server/server.c        |  743 ++++++++++++++++++++-----------------
>>  server/session.c       |  396 ++++++++++-----------
>>  test/Makefile.am       |    5 +-
>>  test/load-file-event.c |   14 +-
>>  test/save-file-event.c |    2 -
>>  tools/cldcli.c         |   22 +-
>>  21 files changed, 1777 insertions(+), 1612 deletions(-)
>>  create mode 100644 include/cld_fmt.h
>>  delete mode 100644 include/cld_msg.h
>>  create mode 100644 lib/cld_fmt.c
>>  create mode 100644 lib/cld_msg_rpc.x
>
> Looking good...  we are definitely getting closer.
>
> Currently, it still does not pass "make distcheck", which presages a lot of
> installation problems.  Notably,
>        #include <lib/cld_msg_rpc.h>
> is not consistent with the rest of the headers, producing things such as
> "make distcheck" build breakage:

I guess distcheck is pretty important to run when making a change like
this. For some reason I forgot to try that. :(

>
>> libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../../lib -I..
>> -I../../include -pthread -I/usr/include/glib-2.0
>> -I/usr/lib64/glib-2.0/include -g -O2 -MT cldc.lo -MD -MP -MF .deps/cldc.Tpo
>> -c ../../lib/cldc.c  -fPIC -DPIC -o .libs/cldc.o
>> In file included from ../../lib/cldc.c:37:
>> ../../include/cldc.h:26:29: error: lib/cld_msg_rpc.h: No such file or
>> directory
>
> You will either need to move the header to $cld/include/ or update INCLUDES
> in various */Makefile.am files to reference $(top_srcdir)/lib (which I see
> you actually did, in a few cases).
>
> Also, it would be nice if this did not introduce warnings.  A big point with
> the testsuite, and minor point with compiler warnings, is that -- more than
> anything else -- we do not want to introduce regressions.

When I did "make all" with -Wall, the only warnings I saw were from
the generated xdr serialization files. I'd like to figure out how to
suppress those (we really should somehow tack -Wnone onto the CFLAGs
of machine-generated files), but I haven't figured out how to do that
yet.

I was thinking of doing it in a follow-on change, since this change is
already pretty big.

Colin

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-21  2:38   ` Colin McCabe
@ 2010-01-26 21:22     ` Colin McCabe
  2010-01-26 22:11       ` Jeff Garzik
  0 siblings, 1 reply; 25+ messages in thread
From: Colin McCabe @ 2010-01-26 21:22 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Project Hail List, Pete Zaitcev

Hi all,

I'm currently reworking this patch to address the concerns mentioned
here (mostly autotools-related). I'm also going to separate it into
multiple patches.

A few questions:

I would really like cld_msg_rpc.h to show up in "include," for
consistency with the other headers.

Is it possible for cld_msg_rpc.x to live in lib/ and export the header
into the include/ directory?
I've been reading everything I can about automake, but I still haven't
been able to figure out if this kind of cross-directory dependency
between two directories at the same depth is possible.

I really don't want to put cld_msg_rpc_xdr.c in include/. But so far I
can't figure out how to have the header there, without the .c file...

Colin


On Wed, Jan 20, 2010 at 6:38 PM, Colin McCabe <cmccabe@alumni.cmu.edu> wrote:
> On Wed, Jan 20, 2010 at 5:38 PM, Jeff Garzik <jeff@garzik.org> wrote:
>> On 01/15/2010 12:49 PM, Colin McCabe wrote:
>>>
>>> This patch moves CLD from using manual data serialization to using XDR
>>> (via
>>> rpcgen). Both the packet header and the message body are now serialized
>>> and
>>> deserialized using XDR. This makes it easy to have a variable-length
>>> packet
>>> header, as well as a variable-length message body.
>>>
>>> This patch introduces a minor libcldc API change in struct cldc_call_opts.
>>>
>>> v2 changes:
>>> * Added __cld prefix to functions in cld_fmt.c
>>>
>>> * When decoding CMT_GET messages, we should store the payload in the
>>> session structure, rather than in cldc_call_opts.
>>>
>>> * Got rid of pkt_is_first, pkt_is_last, in favor of a flags-based
>>> approach.
>>>
>>> * Killed CLD_MAX_PKT_MSG. It's more efficient to only allocate as much
>>> space
>>> as you need, rather than always allocating space for 128 packets in a
>>> message.
>>>
>>> * Created CLD_MAX_PAYLOAD_SZ to mean the maximum size of the data that can
>>> be
>>> sent with GET or PUT. This is different (and smaller than!) the maximum
>>> message size.
>>>
>>> * automake: Add cld_msg_rpc.h to BUILT_SOURCES
>>>
>>> * automake: Add lib dir to INCLUDES
>>>
>>> * automake: "make clean" now deletes XDR build products
>>>
>>> Signed-off-by: Colin McCabe<cmccabe@alumni.cmu.edu>
>>> ---
>>>  .gitignore             |    4 +
>>>  include/Makefile.am    |    2 +-
>>>  include/cld_common.h   |   10 +
>>>  include/cld_fmt.h      |   89 +++++
>>>  include/cld_msg.h      |  252 -------------
>>>  include/cldc.h         |   36 +-
>>>  lib/Makefile.am        |   19 +-
>>>  lib/cld_fmt.c          |  193 ++++++++++
>>>  lib/cld_msg_rpc.x      |  218 +++++++++++
>>>  lib/cldc.c             |  966
>>> +++++++++++++++++++++---------------------------
>>>  lib/common.c           |    6 +-
>>>  server/Makefile.am     |    9 +-
>>>  server/cld.h           |   97 ++++--
>>>  server/cldb.h          |    2 +-
>>>  server/msg.c           |  304 ++++++----------
>>>  server/server.c        |  743 ++++++++++++++++++++-----------------
>>>  server/session.c       |  396 ++++++++++-----------
>>>  test/Makefile.am       |    5 +-
>>>  test/load-file-event.c |   14 +-
>>>  test/save-file-event.c |    2 -
>>>  tools/cldcli.c         |   22 +-
>>>  21 files changed, 1777 insertions(+), 1612 deletions(-)
>>>  create mode 100644 include/cld_fmt.h
>>>  delete mode 100644 include/cld_msg.h
>>>  create mode 100644 lib/cld_fmt.c
>>>  create mode 100644 lib/cld_msg_rpc.x
>>
>> Looking good...  we are definitely getting closer.
>>
>> Currently, it still does not pass "make distcheck", which presages a lot of
>> installation problems.  Notably,
>>        #include <lib/cld_msg_rpc.h>
>> is not consistent with the rest of the headers, producing things such as
>> "make distcheck" build breakage:
>
> I guess distcheck is pretty important to run when making a change like
> this. For some reason I forgot to try that. :(
>
>>
>>> libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I../../lib -I..
>>> -I../../include -pthread -I/usr/include/glib-2.0
>>> -I/usr/lib64/glib-2.0/include -g -O2 -MT cldc.lo -MD -MP -MF .deps/cldc.Tpo
>>> -c ../../lib/cldc.c  -fPIC -DPIC -o .libs/cldc.o
>>> In file included from ../../lib/cldc.c:37:
>>> ../../include/cldc.h:26:29: error: lib/cld_msg_rpc.h: No such file or
>>> directory
>>
>> You will either need to move the header to $cld/include/ or update INCLUDES
>> in various */Makefile.am files to reference $(top_srcdir)/lib (which I see
>> you actually did, in a few cases).
>>
>> Also, it would be nice if this did not introduce warnings.  A big point with
>> the testsuite, and minor point with compiler warnings, is that -- more than
>> anything else -- we do not want to introduce regressions.
>
> When I did "make all" with -Wall, the only warnings I saw were from
> the generated xdr serialization files. I'd like to figure out how to
> suppress those (we really should somehow tack -Wnone onto the CFLAGs
> of machine-generated files), but I haven't figured out how to do that
> yet.
>
> I was thinking of doing it in a follow-on change, since this change is
> already pretty big.
>
> Colin
>

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-26 21:22     ` Colin McCabe
@ 2010-01-26 22:11       ` Jeff Garzik
  2010-01-26 23:47         ` Colin McCabe
  0 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-01-26 22:11 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev

On 01/26/2010 04:22 PM, Colin McCabe wrote:
> Hi all,
>
> I'm currently reworking this patch to address the concerns mentioned
> here (mostly autotools-related). I'm also going to separate it into
> multiple patches.
>
> A few questions:
>
> I would really like cld_msg_rpc.h to show up in "include," for
> consistency with the other headers.
>
> Is it possible for cld_msg_rpc.x to live in lib/ and export the header
> into the include/ directory?
> I've been reading everything I can about automake, but I still haven't
> been able to figure out if this kind of cross-directory dependency
> between two directories at the same depth is possible.
>
> I really don't want to put cld_msg_rpc_xdr.c in include/. But so far I
> can't figure out how to have the header there, without the .c file...

Actually I poked at it this weekend, and was about to finish up.  So 
hold off for another day, if you would.

The answer is, there are two different build environments to consider 
for headers:  the in-tree build environment, and the installed-header 
build environment.  That is why including "lib/foo.h" does not work, 
particularly in nested headers.  You need to fiddle INCLUDES in 
Makefile.am to get the header path correct at build time, and then 
ensure that all headers are installed in the proper location (by 
default, $includedir) using include_HEADERS.  That way, cld_msg_rpc* 
remains in lib/ while the other headers remain in include/

Also, while splitting this up, I noticed that ping_open is no longer set 
to true.

	Jeff



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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-26 22:11       ` Jeff Garzik
@ 2010-01-26 23:47         ` Colin McCabe
  2010-01-29  7:01           ` Jeff Garzik
  2010-02-05  0:59           ` Jeff Garzik
  0 siblings, 2 replies; 25+ messages in thread
From: Colin McCabe @ 2010-01-26 23:47 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Project Hail List, Pete Zaitcev

On Tue, Jan 26, 2010 at 2:11 PM, Jeff Garzik <jeff@garzik.org> wrote:
> Actually I poked at it this weekend, and was about to finish up.  So hold
> off for another day, if you would.
>
> The answer is, there are two different build environments to consider for
> headers:  the in-tree build environment, and the installed-header build
> environment.  That is why including "lib/foo.h" does not work, particularly
> in nested headers.  You need to fiddle INCLUDES in Makefile.am to get the
> header path correct at build time, and then ensure that all headers are
> installed in the proper location (by default, $includedir) using
> include_HEADERS.  That way, cld_msg_rpc* remains in lib/ while the other
> headers remain in include/

We should probably also add an AC_CHECK_PROG(rpcgen, ...) to configure.ac

On an unrelated note, I just discovered "make check". I feel kind of
silly that I didn't try that before. :)

>
> Also, while splitting this up, I noticed that ping_open is no longer set to
> true.

Good catch. Clearly session_timeout should be setting that to true
when it sends the PING message.

Colin

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-26 23:47         ` Colin McCabe
@ 2010-01-29  7:01           ` Jeff Garzik
  2010-01-30  1:01             ` Colin McCabe
  2010-02-05  0:59           ` Jeff Garzik
  1 sibling, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-01-29  7:01 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev

Oh, FYI, I am also folding cld_fmt.h into cld_common.h.  One less 
header, and the contents of cld_fmt.h did not really seem to align 
perfectly with the implied mnemonic "cld format" IMO.

	Jeff




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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-29  7:01           ` Jeff Garzik
@ 2010-01-30  1:01             ` Colin McCabe
  2010-02-03  6:35               ` [PATCH] " Jeff Garzik
  0 siblings, 1 reply; 25+ messages in thread
From: Colin McCabe @ 2010-01-30  1:01 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Project Hail List, Pete Zaitcev

That seems reasonable. All of those functions could be looked at as "common."

The intention was to group together a bunch of functions that were
useful for packet formatting. Although the bulk of the formatting is
done by XDR, there are some things that XDR doesn't do, like
generating and checking SHA sums. cld_dump_buf and cld_pkt_hdr_to_str
are purely for debugging purposes, but they seemed like something that
would be generally useful for developers making packet format changes.
I know that they were useful to me.

Colin


On Thu, Jan 28, 2010 at 11:01 PM, Jeff Garzik <jeff@garzik.org> wrote:
> Oh, FYI, I am also folding cld_fmt.h into cld_common.h.  One less header,
> and the contents of cld_fmt.h did not really seem to align perfectly with
> the implied mnemonic "cld format" IMO.
>
>        Jeff
>
>
>
>
>

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

* [PATCH] Re: [PATCHv2] cld: use XDR for all messages
  2010-01-30  1:01             ` Colin McCabe
@ 2010-02-03  6:35               ` Jeff Garzik
  2010-02-03 21:45                 ` Colin McCabe
  0 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-02-03  6:35 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev

[-- Attachment #1: Type: text/plain, Size: 1542 bytes --]

On 01/29/2010 08:01 PM, Colin McCabe wrote:
> That seems reasonable. All of those functions could be looked at as "common."
>
> The intention was to group together a bunch of functions that were
> useful for packet formatting. Although the bulk of the formatting is
> done by XDR, there are some things that XDR doesn't do, like
> generating and checking SHA sums. cld_dump_buf and cld_pkt_hdr_to_str
> are purely for debugging purposes, but they seemed like something that
> would be generally useful for developers making packet format changes.
> I know that they were useful to me.


I finally pushed out several changes split off from your main XDR patch, 
to the upstream cld git repo.  It took longer than expected because I 
would make changes of my own along the way, which, each time, 
necessitated a rediff+rebuild between "current cld" working tree and 
"cld + xdr" working tree.

The attached patch is what remains to be split up and committed.  I have 
definitely whittled it down, and along the way, moved and renamed some 
things on my own.  With regards to cld_fmt.*, my main objection was to 
creating too many to-be-installed headers.  One more header can be a 
pain for us and for users, while one more source file in cld/lib/ is no 
big deal.

Thus, the intention is to eliminate cld_fmt.h (newly renamed to 
cld_pkt.h) altogether, while keeping the arrangement you created in 
cld_fmt.c (newly renamed to lib/pkt.c).

I will continue whittling down the patch until it just contains the XDR 
changes themselves.

	Jeff




[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 130859 bytes --]

diff -X /garz/tmp/dontdiff -urNp cld/.gitignore cld.rpcgen/.gitignore
--- cld/.gitignore	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/.gitignore	2010-01-22 18:15:07.000000000 -0500
@@ -32,5 +32,9 @@ cld-config.h*
 cscope.*
 ncscope.*
 
+# XDR files
+*_rpc.h
+*_rpc_xdr.c
+
 # ignore Doxygen output directory
 gendoc
diff -X /garz/tmp/dontdiff -urNp cld/include/cldc.h cld.rpcgen/include/cldc.h
--- cld/include/cldc.h	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/include/cldc.h	2010-02-03 01:21:23.000000000 -0500
@@ -22,7 +22,7 @@
 #include <sys/types.h>
 #include <stdbool.h>
 #include <glib.h>
-#include <cld_msg.h>
+#include <cld_msg_rpc.h>
 #include <cld_common.h>
 #include <hail_log.h>
 
@@ -35,33 +35,30 @@ struct cldc_call_opts {
 	void		*private;
 
 	/* private; lib-owned */
-	enum cld_msg_ops op;
-	union {
-		struct {
-			struct cld_msg_get_resp resp;
-			const char *buf;
-			unsigned int size;
-			char inode_name[CLD_INODE_NAME_MAX];
-		} get;
-	} u;
+	struct cld_msg_get_resp resp;
 };
 
+void cldc_call_opts_get_data(struct cldc_call_opts *copts,
+			     char **data, size_t *data_len);
+
 struct cldc_pkt_info {
 	int		pkt_len;
+	int		hdr_len;
 	int		retries;
+	char		user[CLD_MAX_USERNAME];
 
 	/* must be at end of struct */
-	struct cld_packet pkt;
-	uint8_t		data[0];
+	char		data[0];
 };
 
 /** an outgoing message, from client to server */
 struct cldc_msg {
 	uint64_t	xid;
-
+	enum cld_msg_op type;
 	struct cldc_session *sess;
 
-	ssize_t		(*cb)(struct cldc_msg *, const void *, size_t, bool);
+	ssize_t		(*cb)(struct cldc_msg *, const void *, size_t,
+				enum cle_err_codes resp_rc);
 	void		*cb_private;
 
 	struct cldc_call_opts copts;
@@ -70,18 +67,15 @@ struct cldc_msg {
 
 	time_t		expire_time;
 
-	int		data_len;
 	int		n_pkts;
 
-	struct cldc_pkt_info *pkt_info[CLD_MAX_PKT_MSG];
-
 	/* must be at end of struct */
-	uint8_t		data[0];
+	struct cldc_pkt_info *pkt_info[0];
 };
 
 /** an open file handle associated with a session */
 struct cldc_fh {
-	uint64_t	fh_le;			/* fh id, LE */
+	uint64_t	fh;
 	struct cldc_session *sess;
 	bool		valid;
 };
@@ -128,8 +122,11 @@ struct cldc_session {
 
 	bool		confirmed;
 
+	enum cld_msg_op msg_buf_type;
 	unsigned int	msg_buf_len;
 	char		msg_buf[CLD_MAX_MSG_SZ];
+	char		payload[CLD_MAX_PAYLOAD_SZ];
+	char		inode_name_temp[CLD_INODE_NAME_MAX];
 };
 
 /** Information for a single CLD server host */
diff -X /garz/tmp/dontdiff -urNp cld/include/cld_common.h cld.rpcgen/include/cld_common.h
--- cld/include/cld_common.h	2010-02-03 00:13:44.000000000 -0500
+++ cld.rpcgen/include/cld_common.h	2010-02-03 01:21:49.000000000 -0500
@@ -3,7 +3,6 @@
 
 /*
  * Copyright 2009 Red Hat, Inc.
- * Copyright 2010, Colin McCabe
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,6 +23,7 @@
 #include <stdbool.h>
 #include <string.h>
 #include <time.h>
+#include <cld_msg_rpc.h>
 
 #define CLD_ALIGN8(n) ((8 - ((n) & 7)) & 7)
 
@@ -90,8 +90,8 @@ extern int __cld_authcheck(struct hail_l
 extern int __cld_authsign(struct hail_log *log, const char *key,
 			  const void *buf, size_t buf_len, void *sha);
 
-/* Returns a constant string representing a message operation */
-extern const char *__cld_opstr(enum cld_msg_ops op);
+/* Returns a constant string representing a message type */
+extern const char *__cld_opstr(enum cld_msg_op type);
 
 /*
  * We use a unified format for sid so it can be searched in log files (* in vi).
diff -X /garz/tmp/dontdiff -urNp cld/include/cld_msg.h cld.rpcgen/include/cld_msg.h
--- cld/include/cld_msg.h	2010-02-03 01:15:26.000000000 -0500
+++ cld.rpcgen/include/cld_msg.h	1969-12-31 19:00:00.000000000 -0500
@@ -1,244 +0,0 @@
-#ifndef __CLD_MSG_H__
-#define __CLD_MSG_H__
-
-/*
- * Copyright 2009 Red Hat, Inc.
- *
- * This program 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.
- *
- * This program 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 this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <stdint.h>
-
-#define CLD_PKT_MAGIC	"CLDc1pkt"
-#define CLD_MSG_MAGIC	"CLDc1msg"
-
-enum {
-	CLD_MAGIC_SZ		= 8,		/**< length of magic number */
-	CLD_SID_SZ		= 8,		/**< length of session id */
-
-	CLD_INODE_NAME_MAX	= 256,		/**< max total pathname len */
-
-	CLD_MAX_USERNAME	= 32,		/**< includes req. nul */
-	CLD_MAX_SECRET_KEY	= 128,		/**< includes req. nul */
-
-	CLD_MAX_PKT_MSG_SZ	= 1024,
-	CLD_MAX_PKT_MSG		= 128,
-	CLD_MAX_MSG_SZ		= CLD_MAX_PKT_MSG * 1024, /**< maximum total
-					      msg size, including all packets */
-};
-
-/** available RPC operations */
-enum cld_msg_ops {
-	/* client -> server */
-	CMO_NOP			= 0,		/**< no op */
-	CMO_NEW_SESS		= 1,		/**< new session */
-	CMO_OPEN		= 2,		/**< open file */
-	CMO_GET_META		= 3,		/**< get metadata */
-	CMO_GET			= 4,		/**< get metadata + data */
-	CMO_PUT			= 6,		/**< put data */
-	CMO_CLOSE		= 7,		/**< close file */
-	CMO_DEL			= 8,		/**< delete file */
-	CMO_LOCK		= 9,		/**< lock */
-	CMO_UNLOCK		= 10,		/**< unlock */
-	CMO_TRYLOCK		= 11,		/**< trylock */
-	CMO_ACK			= 12,		/**< ack of seqid rx'd */
-	CMO_END_SESS		= 13,		/**< end session */
-
-	/* server -> client */
-	CMO_PING		= 30,		/**< server to client ping */
-	CMO_NOT_MASTER		= 31,		/**< I am not the master! */
-	CMO_EVENT		= 32,		/**< server->cli async event */
-	CMO_ACK_FRAG		= 33,		/**< ack partial msg */
-};
-
-/** CLD error codes */
-enum cle_err_codes {
-	CLE_OK			= 0,		/**< success / no error */
-	CLE_SESS_EXISTS		= 1,		/**< session exists */
-	CLE_SESS_INVAL		= 2,		/**< session doesn't exist */
-	CLE_DB_ERR		= 3,		/**< db error */
-	CLE_BAD_PKT		= 4,		/**< invalid/corrupted packet */
-	CLE_INODE_INVAL		= 5,		/**< inode doesn't exist */
-	CLE_NAME_INVAL		= 6,		/**< inode name invalid */
-	CLE_OOM			= 7,		/**< server out of memory */
-	CLE_FH_INVAL		= 8,		/**< file handle invalid */
-	CLE_DATA_INVAL		= 9,		/**< invalid data pkt */
-	CLE_LOCK_INVAL		= 10,		/**< invalid lock */
-	CLE_LOCK_CONFLICT	= 11,		/**< conflicting lock held */
-	CLE_LOCK_PENDING	= 12,		/**< lock waiting to be acq. */
-	CLE_MODE_INVAL		= 13,		/**< op incompat. w/ file mode */
-	CLE_INODE_EXISTS	= 14,		/**< inode exists */
-	CLE_DIR_NOTEMPTY	= 15,		/**< dir not empty */
-	CLE_INTERNAL_ERR	= 16,		/**< nonspecific internal err */
-	CLE_TIMEOUT		= 17,		/**< session timed out */
-	CLE_SIG_INVAL		= 18,		/**< HMAC sig bad / auth failed */
-};
-
-/** availble OPEN mode flags */
-enum cld_open_modes {
-	COM_READ		= (1 << 0),	/**< read */
-	COM_WRITE		= (1 << 1),	/**< write */
-	COM_LOCK		= (1 << 2),	/**< lock */
-	COM_ACL			= (1 << 3),	/**< ACL update */
-	COM_CREATE		= (1 << 4),	/**< create file, if not exist */
-	COM_EXCL		= (1 << 5),	/**< fail create if file exists */
-	COM_DIRECTORY		= (1 << 6),	/**< operate on a directory */
-};
-
-/** potential events client may receive */
-enum cld_events {
-	CE_UPDATED		= (1 << 0),	/**< contents updated */
-	CE_DELETED		= (1 << 1),	/**< inode deleted */
-	CE_LOCKED		= (1 << 2),	/**< lock acquired */
-	CE_MASTER_FAILOVER	= (1 << 3),	/**< master failover */
-	CE_SESS_FAILED		= (1 << 4),
-};
-
-/** LOCK flags */
-enum cld_lock_flags {
-	CLF_SHARED		= (1 << 0),	/**< a shared (read) lock */
-};
-
-/** CLD packet flags */
-enum cld_packet_flags {
-	CPF_FIRST		= (1 << 0),	/**< first fragment */
-	CPF_LAST		= (1 << 1),	/**< last fragment */
-};
-
-/** header for each packet */
-struct cld_packet {
-	uint8_t		magic[CLD_MAGIC_SZ];	/**< magic number; constant */
-	uint64_t	seqid;			/**< sequence id */
-	uint8_t		sid[CLD_SID_SZ];	/**< client id */
-	uint32_t	flags;			/**< CPF_xxx flags */
-	uint8_t		res[4];
-	char		user[CLD_MAX_USERNAME];	/**< authenticated user */
-};
-
-/** header for each message */
-struct cld_msg_hdr {
-	uint8_t		magic[CLD_MAGIC_SZ];	/**< magic number; constant */
-	uint64_t	xid;			/**< opaque message id */
-	uint8_t		op;			/**< operation code */
-	uint8_t		res1[7];
-};
-
-/** standard response for each message */
-struct cld_msg_resp {
-	struct cld_msg_hdr	hdr;
-
-	uint32_t		code;		/**< error code, CLE_xxx */
-	uint32_t		rsv;		/**< reserved */
-	uint64_t		xid_in;		/**< C->S xid */
-};
-
-/** ACK-FRAG message */
-struct cld_msg_ack_frag {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		seqid;		/**< sequence id to ack */
-};
-
-/** OPEN message */
-struct cld_msg_open {
-	struct cld_msg_hdr	hdr;
-
-	uint32_t		mode;		/**< open mode, COM_xxx */
-	uint32_t		events;		/**< events mask, CE_xxx */
-	uint16_t		name_len;	/**< length of file name */
-	uint8_t			res[6];
-	/* inode name */
-};
-
-/** OPEN message response */
-struct cld_msg_open_resp {
-	struct cld_msg_resp	resp;
-
-	uint64_t		fh;		/**< handle opened */
-};
-
-/** GET message */
-struct cld_msg_get {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** GET message response */
-struct cld_msg_get_resp {
-	struct cld_msg_resp	resp;
-
-	uint64_t		inum;		/**< unique inode number */
-	uint32_t		ino_len;	/**< inode name len */
-	uint32_t		size;		/**< data size */
-	uint64_t		version;	/**< inode version */
-	uint64_t		time_create;	/**< creation time */
-	uint64_t		time_modify;	/**< last modification time */
-	uint32_t		flags;		/**< inode flags; CIFL_xxx */
-	uint8_t			res[4];
-	/* inode name */
-};
-
-/** PUT message */
-struct cld_msg_put {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		data_size;	/**< total size of data */
-	uint8_t			res[4];
-};
-
-/** CLOSE message */
-struct cld_msg_close {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** DEL message */
-struct cld_msg_del {
-	struct cld_msg_hdr	hdr;
-
-	uint16_t		name_len;	/**< length of file name */
-	uint8_t			res[6];
-	/* inode name */
-};
-
-/** UNLOCK message */
-struct cld_msg_unlock {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** LOCK message */
-struct cld_msg_lock {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		flags;		/**< CLF_xxx */
-	uint8_t			res[4];
-};
-
-/** Server-to-client EVENT message */
-struct cld_msg_event {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		events;		/**< CE_xxx */
-	uint8_t			res[4];
-};
-
-#endif /* __CLD_MSG_H__ */
diff -X /garz/tmp/dontdiff -urNp cld/include/cld_pkt.h cld.rpcgen/include/cld_pkt.h
--- cld/include/cld_pkt.h	1969-12-31 19:00:00.000000000 -0500
+++ cld.rpcgen/include/cld_pkt.h	2010-02-03 00:12:58.000000000 -0500
@@ -0,0 +1,56 @@
+#ifndef __CLD_PKT_H__
+#define __CLD_PKT_H__
+
+/*
+ * Copyright 2010, Colin McCabe
+ *
+ * This program 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.
+ *
+ * This program 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 this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <openssl/sha.h>
+#include <cld_msg_rpc.h>
+#include <stdbool.h>
+
+/* @file	cld_pkt.h
+ *
+ * This file has some definitions and helper functions pertaining to the CLD
+ * network protocol. Unlike cld_msg.x, it's not an XDR file.
+ */
+
+/* Returns a string representation of a packet header
+ *
+ * @param scratch		(out param) buffer of length
+ *				PKT_HDR_TO_STR_SCRATCH_LEN
+ * @param pkt_hdr		packet header
+ * @param pkt_len		length of packet
+ *
+ * @return			pointer to 'scratch'
+ */
+const char *__cld_pkt_hdr_to_str(char *scratch,
+			   const char *pkt_hdr, size_t pkt_len);
+
+void __cld_dump_buf(const void *buf, size_t len);
+
+/** Footer that appears at the end of each packet */
+struct __attribute__((packed)) cld_pkt_ftr {
+	uint64_t seqid;				/**< packet sequence ID */
+	char sha[SHA_DIGEST_LENGTH];		/**< packet signature */
+};
+
+/** Length of the packet footer. This size is fixed */
+#define CLD_PKT_FTR_LEN sizeof(struct cld_pkt_ftr)
+
+#define PKT_HDR_TO_STR_SCRATCH_LEN 128
+
+#endif
diff -X /garz/tmp/dontdiff -urNp cld/include/Makefile.am cld.rpcgen/include/Makefile.am
--- cld/include/Makefile.am	2010-02-02 20:22:12.000000000 -0500
+++ cld.rpcgen/include/Makefile.am	2010-02-02 22:51:08.000000000 -0500
@@ -1,5 +1,5 @@
 
 EXTRA_DIST = cld-private.h
 
-include_HEADERS = cldc.h hail_log.h cld_common.h cld_msg.h
+include_HEADERS = cldc.h hail_log.h cld_common.h cld_pkt.h
 
diff -X /garz/tmp/dontdiff -urNp cld/lib/cldc.c cld.rpcgen/lib/cldc.c
--- cld/lib/cldc.c	2010-02-03 01:15:26.000000000 -0500
+++ cld.rpcgen/lib/cldc.c	2010-02-03 01:21:49.000000000 -0500
@@ -35,6 +35,8 @@
 #include <glib.h>
 #include <cld-private.h>
 #include <cldc.h>
+#include <cld_pkt.h>
+#include <cld_msg_rpc.h>
 #include <syslog.h>
 
 enum {
@@ -45,14 +47,8 @@ enum {
 	CLDC_SESS_EXPIRE	= 2 * 60,
 };
 
-static const char *user_key(struct cldc_session *sess, const char *user);
 static int sess_send_pkt(struct cldc_session *sess,
-			 const struct cld_packet *pkt, size_t pkt_len);
-
-static const struct cld_msg_hdr def_msg_ack = {
-	.magic		= CLD_MSG_MAGIC,
-	.op		= CMO_ACK,
-};
+			const void *pkt, size_t pkt_len);
 
 #ifndef HAVE_STRNLEN
 static size_t strnlen(const char *s, size_t maxlen)
@@ -80,6 +76,13 @@ static size_t strnlen(const char *s, siz
 #define EBADE 52
 #endif
 
+void cldc_call_opts_get_data(struct cldc_call_opts *copts,
+			     char **data, size_t *data_len)
+{
+	*data = copts->resp.data.data_val;
+	*data_len = copts->resp.data.data_len;
+}
+
 static void cldc_errlog(int prio, const char *fmt, ...)
 {
 	char buf[200];
@@ -93,46 +96,64 @@ static void cldc_errlog(int prio, const 
 
 static int ack_seqid(struct cldc_session *sess, uint64_t seqid_le)
 {
-	struct cld_packet *pkt;
-	struct cld_msg_hdr *resp;
-	size_t pkt_len;
-	const char *secret_key;
-
-	pkt_len = sizeof(*pkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	pkt = alloca(pkt_len);
-	memset(pkt, 0, pkt_len);
-
-	memcpy(pkt->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	pkt->seqid = seqid_le;
-	memcpy(pkt->sid, sess->sid, CLD_SID_SZ);
-	pkt->flags = cpu_to_le32(CPF_FIRST | CPF_LAST);
-	strncpy(pkt->user, sess->user, CLD_MAX_USERNAME - 1);
-
-	resp = (struct cld_msg_hdr *) (pkt + 1);
-	memcpy(resp, &def_msg_ack, sizeof(*resp));
-
-	secret_key = user_key(sess, pkt->user);
-	if (__cld_authsign(&sess->log, secret_key,
-			   pkt, pkt_len - SHA_DIGEST_LENGTH,
-			   (uint8_t *)pkt + pkt_len - SHA_DIGEST_LENGTH)) {
-		HAIL_INFO(&sess->log, "%s: authsign failed 2", __func__);
-		return -1;
-	}
-
-	return sess_send_pkt(sess, pkt, pkt_len);
-}
-
-static int cldc_rx_generic(struct cldc_session *sess,
-			   const struct cld_packet *pkt,
-			   const void *msgbuf,
-			   size_t buflen)
+	XDR xdrs;
+	size_t hdr_len, total_len;
+	char buf[CLD_MAX_PKT_MSG_SZ];
+	struct cld_pkt_hdr pkt;
+	struct cld_pkt_ftr *foot;
+	int ret;
+	static const char * const magic = CLD_PKT_MAGIC;
+
+	/* Construct ACK packet */
+	memset(&pkt, 0, sizeof(struct cld_pkt_hdr));
+	memcpy(&pkt.magic, magic, sizeof(pkt.magic));
+	memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+	pkt.user = sess->user;
+	pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+	pkt.mi.cld_pkt_msg_info_u.mi.xid = 0;
+	pkt.mi.cld_pkt_msg_info_u.mi.type = CMO_ACK;
+
+	/* Serialize packet */
+	xdrmem_create(&xdrs, (char *)buf,
+		      sizeof(buf) - CLD_PKT_FTR_LEN, XDR_ENCODE);
+	if (!xdr_cld_pkt_hdr(&xdrs, &pkt)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to encode header "
+			   "for ack_seqid %lld", __func__, seqid_le);
+		xdr_destroy(&xdrs);
+		return -1009;
+	}
+
+	/* Fill in footer */
+	hdr_len = xdr_getpos(&xdrs);
+	total_len = hdr_len + CLD_PKT_FTR_LEN;
+	foot = (struct cld_pkt_ftr *)(buf + hdr_len);
+	foot->seqid = seqid_le;
+	xdr_destroy(&xdrs);
+	ret = __cld_authsign(&sess->log, sess->user,
+			buf, total_len - SHA_DIGEST_LENGTH, foot->sha);
+	if (ret)
+		return ret;
+
+	return sess_send_pkt(sess, buf, total_len);
+}
+
+static int rxmsg_generic(struct cldc_session *sess,
+			const struct cld_pkt_hdr *pkt,
+			const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_resp *resp = msgbuf;
+	XDR xdrs;
+	struct cld_msg_generic_resp resp;
 	struct cldc_msg *req = NULL;
 	GList *tmp;
 
-	if (buflen < sizeof(*resp))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdr_cld_msg_generic_resp(&xdrs, &resp)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to decode "
+			   "cld_msg_generic_resp", __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
 	/* Find out which outbound message this was a response to */
 	tmp = sess->out_msg;
@@ -142,16 +163,16 @@ static int cldc_rx_generic(struct cldc_s
 		HAIL_DEBUG(&sess->log, "%s: comparing req->xid (%llu) "
 			   "with resp.xid_in (%llu)",
 			   __func__,
-			   (unsigned long long) le64_to_cpu(req->xid),
-			   (unsigned long long) le64_to_cpu(resp->xid_in));
+			   (unsigned long long) req->xid,
+			   (unsigned long long) resp.xid_in);
 
-		if (req->xid == resp->xid_in)
+		if (req->xid == resp.xid_in)
 			break;
 		tmp = tmp->next;
 	}
 	if (!tmp) {
 		HAIL_DEBUG(&sess->log, "%s: no match found with "
-			   "xid_in %llu", __func__, (unsigned long long) le64_to_cpu(resp->xid_in));
+			   "xid_in %llu", __func__, resp.xid_in);
 		return -1005;
 	}
 
@@ -164,32 +185,40 @@ static int cldc_rx_generic(struct cldc_s
 		req->done = true;
 
 		if (req->cb) {
-			ssize_t rc = req->cb(req, msgbuf, buflen, true);
+			ssize_t rc = req->cb(req, sess->msg_buf,
+					     sess->msg_buf_len, resp.code);
 			if (rc < 0)
 				return rc;
 		}
 	}
 
-	return ack_seqid(sess, pkt->seqid);
+	return ack_seqid(sess, foot->seqid);
 }
 
-static int cldc_rx_ack_frag(struct cldc_session *sess,
-			    const struct cld_packet *pkt,
-			    const void *msgbuf,
-			    size_t buflen)
+static int rxmsg_ack_frag(struct cldc_session *sess,
+			const struct cld_pkt_hdr *pkt,
+			const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_ack_frag *ack_msg = msgbuf;
-	struct cldc_msg *req = NULL;
+	XDR xdrs;
+	struct cld_msg_ack_frag ack_msg;
 	GList *tmp;
 
-	if (buflen < sizeof(*ack_msg))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	memset(&ack_msg, 0, sizeof(ack_msg));
+	if (!xdr_cld_msg_ack_frag(&xdrs, &ack_msg)) {
+		HAIL_INFO(&sess->log, "%s: failed to decode ack_msg",
+			  __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
 	HAIL_INFO(&sess->log, "%s: seqid %llu, want to ack",
-		  __func__, (unsigned long long) ack_msg->seqid);
+		  __func__, ack_msg.seqid);
 
 	tmp = sess->out_msg;
 	while (tmp) {
+		struct cldc_msg *req;
 		int i;
 
 		req = tmp->data;
@@ -197,16 +226,21 @@ static int cldc_rx_ack_frag(struct cldc_
 
 		for (i = 0; i < req->n_pkts; i++) {
 			struct cldc_pkt_info *pi;
+			struct cld_pkt_ftr *f;
+			uint64_t seqid;
 
 			pi = req->pkt_info[i];
 			if (!pi)
 				continue;
-			if (pi->pkt.seqid != ack_msg->seqid)
+			f = (struct cld_pkt_ftr *)
+				pi->data + (pi->pkt_len - CLD_PKT_FTR_LEN);
+			seqid = le64_to_cpu(f->seqid);
+			if (seqid != ack_msg.seqid)
 				continue;
 
 			HAIL_DEBUG(&sess->log, "%s: seqid %llu, expiring",
 				   __func__,
-				   (unsigned long long) ack_msg->seqid);
+				   (unsigned long long) ack_msg.seqid);
 
 			req->pkt_info[i] = NULL;
 			free(pi);
@@ -216,21 +250,27 @@ static int cldc_rx_ack_frag(struct cldc_
 	return 0;
 }
 
-static int cldc_rx_event(struct cldc_session *sess,
-			 const struct cld_packet *pkt,
-			 const void *msgbuf,
-			 size_t buflen)
+static int rxmsg_event(struct cldc_session *sess,
+			const struct cld_pkt_hdr *pkt,
+			const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_event *ev = msgbuf;
+	XDR xdrs;
+	struct cld_msg_event ev;
 	struct cldc_fh *fh = NULL;
 	int i;
 
-	if (buflen < sizeof(*ev))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdr_cld_msg_event(&xdrs, &ev)) {
+		HAIL_INFO(&sess->log, "%s: failed to decode cld_msg_event",
+			  __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
 	for (i = 0; i < sess->fh->len; i++) {
 		fh = &g_array_index(sess->fh, struct cldc_fh, i);
-		if (fh->fh_le == ev->fh)
+		if (fh->fh == ev.fh)
 			break;
 		else
 			fh = NULL;
@@ -239,20 +279,11 @@ static int cldc_rx_event(struct cldc_ses
 	if (!fh)
 		return -1011;
 
-	sess->ops->event(sess->private, sess, fh, le32_to_cpu(ev->events));
+	sess->ops->event(sess->private, sess, fh, ev.events);
 
 	return 0;
 }
 
-static int cldc_rx_not_master(struct cldc_session *sess,
-			      const struct cld_packet *pkt,
-			      const void *msgbuf,
-			      size_t buflen)
-{
-	HAIL_DEBUG(&sess->log, "FIXME: not-master message received");
-	return -1055;	/* FIXME */
-}
-
 static void cldc_msg_free(struct cldc_msg *msg)
 {
 	int i;
@@ -260,7 +291,7 @@ static void cldc_msg_free(struct cldc_ms
 	if (!msg)
 		return;
 
-	for (i = 0; i < CLD_MAX_PKT_MSG; i++)
+	for (i = 0; i < msg->n_pkts; i++)
 		free(msg->pkt_info[i]);
 
 	free(msg);
@@ -287,139 +318,122 @@ static void sess_expire_outmsg(struct cl
 	sess->msg_scan_time = current_time + CLDC_MSG_SCAN;
 }
 
-static const char *user_key(struct cldc_session *sess, const char *user)
+static int rx_complete(struct cldc_session *sess,
+		const struct cld_pkt_hdr *pkt,
+		const struct cld_pkt_ftr *foot)
 {
-	if (!sess || !user || !*user ||
-	    (strnlen(user, CLD_MAX_USERNAME) >= CLD_MAX_USERNAME))
-		return NULL;
-	if (strcmp(sess->user, user))
-		return NULL;
-
-	return sess->secret_key;
+	switch (sess->msg_buf_type) {
+	case CMO_ACK:
+		HAIL_INFO(&sess->log, "%s: received unexpected ACK",
+			  __func__);
+		return -EBADRQC;
+	case CMO_PING:
+		/* send out an ACK */
+		return ack_seqid(sess, foot->seqid);
+	case CMO_NOT_MASTER:
+		HAIL_INFO(&sess->log, "FIXME: not-master message received");
+		return -1055;	/* FIXME */
+	case CMO_EVENT:
+		return rxmsg_event(sess, pkt, foot);
+	case CMO_ACK_FRAG:
+		return rxmsg_ack_frag(sess, pkt, foot);
+	default:
+		return rxmsg_generic(sess, pkt, foot);
+	}
 }
 
-static int cldc_receive_msg(struct cldc_session *sess,
-			    const struct cld_packet *pkt,
-			    size_t pkt_len)
+/** Accepts a packet's sequence ID.
+ * Depending on the message type, this may involve initializing the session's
+ * sequence ID, validating that the packet's ID is in range, or doing nothing.
+ *
+ * @param sess		The session
+ * @param seqid		The sequence ID
+ * @param type		The message type
+ *
+ * @return		0 on success; error code otherwise
+ */
+static int accept_seqid(struct cldc_session *sess, uint64_t seqid,
+			enum cld_msg_op type)
 {
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) sess->msg_buf;
-	size_t msglen = sess->msg_buf_len;
-
-	if (memcmp(msg->magic, CLD_MSG_MAGIC, sizeof(msg->magic))) {
-		HAIL_DEBUG(&sess->log, "%s: bad msg magic", __func__);
-		return -EPROTO;
-	}
-
-	switch(msg->op) {
-	case CMO_NOP:
-	case CMO_CLOSE:
-	case CMO_DEL:
-	case CMO_LOCK:
-	case CMO_UNLOCK:
-	case CMO_TRYLOCK:
-	case CMO_PUT:
+	switch (type) {
 	case CMO_NEW_SESS:
-	case CMO_END_SESS:
-	case CMO_OPEN:
-	case CMO_GET_META:
-	case CMO_GET:
-		return cldc_rx_generic(sess, pkt, msg, msglen);
+		/* CMO_NEW_SESS initializes the session's sequence id */
+		sess->next_seqid_in = seqid + 1;
+		sess->next_seqid_in_tr =
+			sess->next_seqid_in - CLDC_MSG_REMEMBER;
+		HAIL_DEBUG(&sess->log, "%s: setting next_seqid_in to %llu",
+			   __func__, (unsigned long long) seqid);
+		return 0;
+
 	case CMO_NOT_MASTER:
-		return cldc_rx_not_master(sess, pkt, msg, msglen);
 	case CMO_ACK_FRAG:
-		return cldc_rx_ack_frag(sess, pkt, msg, msglen);
-	case CMO_EVENT:
-		return cldc_rx_event(sess, pkt, msg, msglen);
-	case CMO_PING:
-		return ack_seqid(sess, pkt->seqid);
-	case CMO_ACK:
-		return -EBADRQC;
-	}
+		/* Ignore sequence ID of these types */
+		return 0;
+
+	default:
+		/* verify that the sequence id is in range */
+		if (seqid == sess->next_seqid_in) {
+			sess->next_seqid_in++;
+			sess->next_seqid_in_tr++;
+			return 0;
+		}
+
+		if (seqid_in_range(seqid,
+				   sess->next_seqid_in_tr,
+				   sess->next_seqid_in)) {
+			return 0;
+		}
 
-	/* unknown op code */
-	return -EBADRQC;
+		return -EBADSLT;
+	}
 }
 
 int cldc_receive_pkt(struct cldc_session *sess,
 		     const void *net_addr, size_t net_addrlen,
 		     const void *pktbuf, size_t pkt_len)
 {
-	const struct cld_packet *pkt = pktbuf;
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-	const char *secret_key;
-	size_t msglen;
+	int ret;
 	struct timeval tv;
 	time_t current_time;
+	struct cld_pkt_hdr pkt;
+	unsigned int hdr_len, msg_len;
+	const struct cld_pkt_ftr *foot;
 	uint64_t seqid;
-	uint32_t pkt_flags;
-	bool first_frag, last_frag, have_new_sess, no_seqid;
-	bool have_get;
+	XDR xdrs;
 
 	gettimeofday(&tv, NULL);
 	current_time = tv.tv_sec;
 
-	if (pkt_len < (sizeof(*pkt) + SHA_DIGEST_LENGTH)) {
-		HAIL_DEBUG(&sess->log, "%s: msg too short", __func__);
+	/* Decode the packet header */
+	if (pkt_len < CLD_PKT_FTR_LEN) {
+		HAIL_DEBUG(&sess->log, "%s: packet too short to have a "
+			   "well-formed footer", __func__);
 		return -EPROTO;
 	}
-
-	msglen = pkt_len - sizeof(*pkt) - SHA_DIGEST_LENGTH;
-
-	pkt_flags = le32_to_cpu(pkt->flags);
-	first_frag = pkt_flags & CPF_FIRST;
-	last_frag = pkt_flags & CPF_LAST;
-	have_get = first_frag && (msg->op == CMO_GET);
-	have_new_sess = first_frag && (msg->op == CMO_NEW_SESS);
-	no_seqid = first_frag && ((msg->op == CMO_NOT_MASTER) ||
-				  (msg->op == CMO_ACK_FRAG));
-
-	if (sess->log.verbose) {
-		if (have_get) {
-			struct cld_msg_get_resp *dp;
-			dp = (struct cld_msg_get_resp *) msg;
-			HAIL_DEBUG(&sess->log, "%s(len %u, op %s"
-				   ", seqid %llu, user %s, size %u)",
-				   __func__,
-				   (unsigned int) pkt_len,
-				   __cld_opstr(msg->op),
-				   (unsigned long long) le64_to_cpu(pkt->seqid),
-				   pkt->user,
-				   le32_to_cpu(dp->size));
-		} else if (have_new_sess) {
-			struct cld_msg_resp *dp;
-			dp = (struct cld_msg_resp *) msg;
-			HAIL_DEBUG(&sess->log, "%s(len %u, op %s"
-				   ", seqid %llu, user %s, xid_in %llu)",
-				   __func__,
-				   (unsigned int) pkt_len,
-				   __cld_opstr(msg->op),
-				   (unsigned long long) le64_to_cpu(pkt->seqid),
-				   pkt->user,
-				   (unsigned long long) le64_to_cpu(dp->xid_in));
-		} else {
-			HAIL_DEBUG(&sess->log, "%s(len %u, "
-				   "flags %s%s, op %s, seqid %llu, user %s)",
-				   __func__,
-				   (unsigned int) pkt_len,
-				   first_frag ? "F" : "",
-				   last_frag ? "L" : "",
-				   first_frag ? __cld_opstr(msg->op) : "n/a",
-				   (unsigned long long) le64_to_cpu(pkt->seqid),
-				   pkt->user);
-		}
+	xdrmem_create(&xdrs, (void *)pktbuf,
+			pkt_len - CLD_PKT_FTR_LEN, XDR_DECODE);
+	memset(&pkt, 0, sizeof(pkt));
+	if (!xdr_cld_pkt_hdr(&xdrs, &pkt)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to decode packet header",
+			   __func__);
+		xdr_destroy(&xdrs);
+		return -EPROTO;
 	}
-
-	if (memcmp(pkt->magic, CLD_PKT_MAGIC, sizeof(pkt->magic))) {
+	hdr_len = xdr_getpos(&xdrs);
+	xdr_destroy(&xdrs);
+	if (memcmp(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic))) {
 		HAIL_DEBUG(&sess->log, "%s: bad pkt magic", __func__);
 		return -EPROTO;
 	}
 
 	/* check HMAC signature */
-	secret_key = user_key(sess, pkt->user);
-	if (__cld_authcheck(&sess->log, secret_key,
-			    pkt, pkt_len - SHA_DIGEST_LENGTH,
-			    (uint8_t *)pkt + pkt_len - SHA_DIGEST_LENGTH)) {
-		HAIL_DEBUG(&sess->log, "%s: invalid auth", __func__);
+	foot = (const struct cld_pkt_ftr *)
+		(((char *)pktbuf) + (pkt_len - CLD_PKT_FTR_LEN));
+	ret = __cld_authcheck(&sess->log, pkt.user,
+		       pktbuf, pkt_len - SHA_DIGEST_LENGTH, foot->sha);
+	if (ret) {
+		HAIL_DEBUG(&sess->log, "%s: invalid auth (ret=%d)",
+			   __func__, ret);
 		return -EACCES;
 	}
 
@@ -434,48 +448,39 @@ int cldc_receive_pkt(struct cldc_session
 	if (current_time >= sess->msg_scan_time)
 		sess_expire_outmsg(sess, current_time);
 
-	if (first_frag)
-		sess->msg_buf_len = 0;
-
-	if ((sess->msg_buf_len + msglen) > CLD_MAX_MSG_SZ) {
-		HAIL_DEBUG(&sess->log, "%s: bad pkt length", __func__);
-		return -EPROTO;
+	if (pkt.mi.order & CLD_PKT_IS_FIRST) {
+		/* This packet begins a new message.
+		 * Determine the new message's type */
+		sess->msg_buf_type = pkt.mi.cld_pkt_msg_info_u.mi.type;
 	}
 
-	memcpy(sess->msg_buf + sess->msg_buf_len, msg, msglen);
-	sess->msg_buf_len += msglen;
-
-	/* verify (or set, for new-sess) sequence id */
-	seqid = le64_to_cpu(pkt->seqid);
-	if (have_new_sess) {
-		sess->next_seqid_in = seqid + 1;
-		sess->next_seqid_in_tr =
-			sess->next_seqid_in - CLDC_MSG_REMEMBER;
-
-		HAIL_DEBUG(&sess->log, "%s: "
-			   "setting next_seqid_in to %llu",
+	seqid = le64_to_cpu(foot->seqid);
+	ret = accept_seqid(sess, seqid, sess->msg_buf_type);
+	if (ret) {
+		HAIL_DEBUG(&sess->log, "%s: bad seqid %llu",
 			   __func__, (unsigned long long) seqid);
-	} else if (!no_seqid) {
-		if (seqid != sess->next_seqid_in) {
-			if (seqid_in_range(seqid,
-					   sess->next_seqid_in_tr,
-					   sess->next_seqid_in))
-				return ack_seqid(sess, pkt->seqid);
-
-			HAIL_DEBUG(&sess->log, "%s: bad seqid %llu",
-				   __func__, (unsigned long long) seqid);
-			return -EBADSLT;
-		}
-		sess->next_seqid_in++;
-		sess->next_seqid_in_tr++;
+		return ret;
 	}
 
+	if (pkt.mi.order & CLD_PKT_IS_FIRST)
+		sess->msg_buf_len = 0;
+	msg_len = pkt_len - hdr_len - CLD_PKT_FTR_LEN;
+	if ((sess->msg_buf_len + msg_len) > CLD_MAX_MSG_SZ) {
+		HAIL_DEBUG(&sess->log, "%s: message too long", __func__);
+		return -EPROTO;
+	}
+	memcpy(sess->msg_buf + sess->msg_buf_len, pktbuf + hdr_len, msg_len);
+	sess->msg_buf_len += msg_len;
 	sess->expire_time = current_time + CLDC_SESS_EXPIRE;
 
-	if (!last_frag)
-		return sess ? ack_seqid(sess, pkt->seqid) : 0;
-
-	return cldc_receive_msg(sess, pkt, pkt_len);
+	if (pkt.mi.order & CLD_PKT_IS_LAST) {
+		HAIL_DEBUG(&sess->log, "%s: receiving complete message of "
+			   "type %s", __func__,
+			   __cld_opstr(sess->msg_buf_type));
+		return rx_complete(sess, &pkt, foot);
+	} else {
+		return ack_seqid(sess, foot->seqid);
+	}
 }
 
 static void sess_next_seqid(struct cldc_session *sess, uint64_t *seqid)
@@ -484,69 +489,115 @@ static void sess_next_seqid(struct cldc_
 	*seqid = rc;
 }
 
+/**
+ * creates a new cldc_msg
+ *
+ * @param sess		The session
+ * @param copts		The call options
+ * @param type		The type of message to create
+ * @param xdrproc	The XDR function to use to create the message body
+ * @param data		The data to pass to xdrproc
+ *
+ * @return		The cldc message, or NULL on error,
+ */
 static struct cldc_msg *cldc_new_msg(struct cldc_session *sess,
 				     const struct cldc_call_opts *copts,
-				     enum cld_msg_ops op,
-				     size_t msg_len)
+				     enum cld_msg_op type,
+				     xdrproc_t xdrproc, const void *data)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_hdr *hdr;
 	struct timeval tv;
-	int i, data_left;
-	void *p;
+	size_t i, body_len, n_pkts;
+	char *body;
+	XDR xbdy;
+
+	/* Encode the message body */
+	body_len = xdr_sizeof(xdrproc, (void *)data);
+	body = alloca(body_len);
+	xdrmem_create(&xbdy, body, body_len, XDR_ENCODE);
+	if (!xdrproc(&xbdy, (void *)data)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to encode "
+			   "message", __func__);
+		xdr_destroy(&xbdy);
+		return NULL;
+	}
+	xdr_destroy(&xbdy);
 
-	gettimeofday(&tv, NULL);
+	if (body_len == 0)
+		/* Some packets (like ACKS) just have a header, and no message
+		 * body. */
+		n_pkts = 1;
+	else {
+		/* round up */
+		n_pkts = (body_len + CLD_MAX_PKT_MSG_SZ - 1) /
+			CLD_MAX_PKT_MSG_SZ;
+	}
 
-	msg = calloc(1, sizeof(*msg) + msg_len);
+	/* Create cldc_msg */
+	msg = calloc(1, sizeof(struct cldc_msg) +
+			(sizeof(struct cldc_pkt_info *) * n_pkts));
 	if (!msg)
 		return NULL;
 
+	msg->n_pkts = n_pkts;
 	__cld_rand64(&msg->xid);
-
+	msg->type = type;
 	msg->sess = sess;
-
 	if (copts)
 		memcpy(&msg->copts, copts, sizeof(msg->copts));
-
+	gettimeofday(&tv, NULL);
 	msg->expire_time = tv.tv_sec + CLDC_MSG_EXPIRE;
 
-	msg->data_len = msg_len;
-
-	msg->n_pkts = msg_len / CLD_MAX_PKT_MSG_SZ;
-	msg->n_pkts += ((msg_len % CLD_MAX_PKT_MSG_SZ) ? 1 : 0);
-
-	p = msg->data;
-	data_left = msg_len;
 	for (i = 0; i < msg->n_pkts; i++) {
+		XDR xhdr;
+		struct cld_pkt_hdr pkt;
 		struct cldc_pkt_info *pi;
-		int pkt_len;
+		int hdr_len, body_chunk_len, pkt_len;
 
-		pkt_len = MIN(data_left, CLD_MAX_PKT_MSG_SZ);
+		/* Set up packet header */
+		memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+		memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+		pkt.user = sess->user;
+		if (i == 0) {
+			if (i == (msg->n_pkts - 1))
+				pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_FIRST;
+			pkt.mi.cld_pkt_msg_info_u.mi.xid = msg->xid;
+			pkt.mi.cld_pkt_msg_info_u.mi.type = type;
+		} else {
+			if (i == (msg->n_pkts - 1))
+				pkt.mi.order = CLD_PKT_ORD_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_MID;
+		}
 
-		pi = calloc(1, sizeof(*pi) + pkt_len + SHA_DIGEST_LENGTH);
+		/* Allocate memory */
+		hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, &pkt);
+		body_chunk_len = MIN(body_len, CLD_MAX_PKT_MSG_SZ);
+		pkt_len = hdr_len + body_chunk_len + CLD_PKT_FTR_LEN;
+		pi = calloc(1, sizeof(*pi) + pkt_len);
 		if (!pi)
 			goto err_out;
-
 		pi->pkt_len = pkt_len;
+		msg->pkt_info[i] = pi;
+		strncpy(pi->user, sess->user, CLD_MAX_USERNAME - 1);
 
-		memcpy(pi->pkt.magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-		memcpy(pi->pkt.sid, sess->sid, CLD_SID_SZ);
-		strncpy(pi->pkt.user, sess->user, CLD_MAX_USERNAME - 1);
-
-		if (i == 0)
-			pi->pkt.flags |= cpu_to_le32(CPF_FIRST);
-		if (i == (msg->n_pkts - 1))
-			pi->pkt.flags |= cpu_to_le32(CPF_LAST);
+		/* Fill in the packet header */
+		xdrmem_create(&xhdr, (char *)pi->data, hdr_len, XDR_ENCODE);
+		if (!xdr_cld_pkt_hdr(&xhdr, &pkt)) {
+			HAIL_DEBUG(&sess->log, "%s: failed to encode header "
+				   "for packet %d", __func__, i);
+			xdr_destroy(&xhdr);
+			goto err_out;
+		}
 
-		msg->pkt_info[i] = pi;
-		data_left -= pkt_len;
+		/* Fill in the body */
+		memcpy(pi->data + hdr_len, body, body_chunk_len);
+		body += body_chunk_len;
+		body_len -= body_chunk_len;
 	}
 
-	hdr = (struct cld_msg_hdr *) &msg->data[0];
-	memcpy(&hdr->magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	hdr->op = op;
-	hdr->xid = msg->xid;
-
 	return msg;
 
 err_out:
@@ -564,7 +615,7 @@ static void sess_msg_drop(struct cldc_se
 		tmp = tmp->next;
 
 		if (!msg->done && msg->cb)
-			msg->cb(msg, NULL, 0, false);
+			msg->cb(msg, NULL, 0, CLE_TIMEOUT);
 
 		cldc_msg_free(msg);
 	}
@@ -585,32 +636,8 @@ static void sess_expire(struct cldc_sess
 }
 
 static int sess_send_pkt(struct cldc_session *sess,
-			 const struct cld_packet *pkt, size_t pkt_len)
+			 const void *pkt, size_t pkt_len)
 {
-	if (sess->log.verbose) {
-		uint32_t flags = le32_to_cpu(pkt->flags);
-		bool first = (flags & CPF_FIRST);
-		bool last = (flags & CPF_LAST);
-		uint8_t op = CMO_NOP;
-
-		if (first) {
-			struct cld_msg_hdr *hdr;
-
-			hdr = (struct cld_msg_hdr *) (pkt + 1);
-			op = hdr->op;
-		}
-
-		HAIL_DEBUG(&sess->log,
-			   "%s(len %zu, flags %s%s, "
-			   "op %s, seqid %llu)",
-			   __func__,
-			   pkt_len,
-			   first ? "F" : "",
-			   last ? "L" : "",
-			   first ? __cld_opstr(op) : "n/a",
-			   (unsigned long long) le64_to_cpu(pkt->seqid));
-	}
-
 	return sess->ops->pkt_send(sess->private,
 				   sess->addr, sess->addr_len,
 				   pkt, pkt_len);
@@ -639,18 +666,12 @@ static int sess_timer(struct cldc_sessio
 
 		for (i = 0; i < msg->n_pkts; i++) {
 			struct cldc_pkt_info *pi;
-			int total_pkt_len;
 
 			pi = msg->pkt_info[i];
 			if (!pi)
 				continue;
-
-			total_pkt_len = sizeof(struct cld_packet) +
-					pi->pkt_len + SHA_DIGEST_LENGTH;
-
 			pi->retries++;
-
-			sess_send_pkt(sess, &pi->pkt, total_pkt_len);
+			sess_send_pkt(sess, pi->data, pi->pkt_len);
 		}
 	}
 
@@ -661,38 +682,30 @@ static int sess_timer(struct cldc_sessio
 
 static int sess_send(struct cldc_session *sess, struct cldc_msg *msg)
 {
-	int i, data_left;
-	void *p;
-	const char *secret_key;
-
-	secret_key = user_key(sess, sess->user);
+	int ret, i;
 
-	p = msg->data;
-	data_left = msg->data_len;
 	for (i = 0; i < msg->n_pkts; i++) {
 		struct cldc_pkt_info *pi;
-		int total_pkt_len;
+		struct cld_pkt_ftr *foot;
 
 		pi = msg->pkt_info[i];
-		memcpy(pi->data, p, pi->pkt_len);
 
-		total_pkt_len = sizeof(struct cld_packet) +
-				pi->pkt_len + SHA_DIGEST_LENGTH;
-
-		sess_next_seqid(sess, &pi->pkt.seqid);
-
-		p += pi->pkt_len;
-		data_left -= pi->pkt_len;
-
-		if (__cld_authsign(&sess->log, secret_key,
-				   &pi->pkt, total_pkt_len - SHA_DIGEST_LENGTH,
-				   ((uint8_t *)&pi->pkt + total_pkt_len) -
-				    	SHA_DIGEST_LENGTH))
-			return -1;
+		/* Add the sequence number to the end of the packet */
+		foot = (struct cld_pkt_ftr *)
+			(pi->data + pi->pkt_len - CLD_PKT_FTR_LEN);
+		memset(foot, 0, CLD_PKT_FTR_LEN);
+		sess_next_seqid(sess, &foot->seqid);
+
+		/* Add the signature to the end of the packet */
+		ret = __cld_authsign(&sess->log, pi->user,
+				     pi->data,
+				     pi->pkt_len - SHA_DIGEST_LENGTH,foot->sha);
+		if (ret)
+			return ret;
 
 		/* attempt first send */
-		if (sess_send_pkt(sess, &pi->pkt, total_pkt_len) < 0)
-			return -1;
+		if (sess_send_pkt(sess, pi->data, pi->pkt_len) < 0)
+			return -EIO;
 	}
 
 	/* add to list of outgoing packets, waiting to be ack'd */
@@ -723,16 +736,8 @@ static void sess_free(struct cldc_sessio
 }
 
 static ssize_t end_sess_cb(struct cldc_msg *msg, const void *resp_p,
-			   size_t resp_len, bool ok)
+			   size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->code);
-
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
 
@@ -749,7 +754,7 @@ int cldc_end_sess(struct cldc_session *s
 
 	/* create END-SESS message */
 	msg = cldc_new_msg(sess, copts, CMO_END_SESS,
-			   sizeof(struct cld_msg_hdr));
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg)
 		return -ENOMEM;
 
@@ -759,19 +764,10 @@ int cldc_end_sess(struct cldc_session *s
 }
 
 static ssize_t new_sess_cb(struct cldc_msg *msg, const void *resp_p,
-			   size_t resp_len, bool ok)
+			   size_t resp_len, enum cle_err_codes resp_rc)
 {
-	struct cldc_session *sess = msg->sess;
-	const struct cld_msg_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->code);
-
 	if (resp_rc == CLE_OK)
-		sess->confirmed = true;
+		msg->sess->confirmed = true;
 
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
@@ -824,7 +820,7 @@ int cldc_new_sess(const struct cldc_ops 
 
 	/* create NEW-SESS message */
 	msg = cldc_new_msg(sess, copts, CMO_NEW_SESS,
-			   sizeof(struct cld_msg_hdr));
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg) {
 		sess_free(sess);
 		return -ENOMEM;
@@ -854,16 +850,8 @@ void cldc_kill_sess(struct cldc_session 
 }
 
 static ssize_t generic_end_cb(struct cldc_msg *msg, const void *resp_p,
-			      size_t resp_len, bool ok)
+			      size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->code);
-
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
 
@@ -878,7 +866,8 @@ int cldc_nop(struct cldc_session *sess, 
 		return -EINVAL;
 
 	/* create NOP message */
-	msg = cldc_new_msg(sess, copts, CMO_NOP, sizeof(struct cld_msg_hdr));
+	msg = cldc_new_msg(sess, copts, CMO_NOP,
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg)
 		return -ENOMEM;
 
@@ -891,8 +880,7 @@ int cldc_del(struct cldc_session *sess, 
 	     const char *pathname)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_del *del;
-	void *p;
+	struct cld_msg_del del;
 	size_t plen;
 
 	if (!sess->confirmed)
@@ -903,46 +891,37 @@ int cldc_del(struct cldc_session *sess, 
 		return -EINVAL;
 
 	plen = strlen(pathname);
-	if (plen > 65530)
+	if (plen > CLD_INODE_NAME_MAX)
 		return -EINVAL;
 
 	/* create DEL message */
+	del.inode_name = (char *)pathname;
 	msg = cldc_new_msg(sess, copts, CMO_DEL,
-			   sizeof(struct cld_msg_del) + strlen(pathname));
+			   (xdrproc_t)xdr_cld_msg_del, &del);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in DEL-specific name_len, name info */
-	del = (struct cld_msg_del *) msg->data;
-	del->name_len = cpu_to_le16(plen);
-	p = del;
-	p += sizeof(struct cld_msg_del);
-	memcpy(p, pathname, plen);
-
 	return sess_send(sess, msg);
 }
 
 static ssize_t open_end_cb(struct cldc_msg *msg, const void *resp_p,
-			   size_t resp_len, bool ok)
+			   size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_open_resp *resp = resp_p;
-	struct cldc_fh *fh = msg->cb_private;
-	enum cle_err_codes resp_rc = CLE_OK;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else {
-		if (resp_len < sizeof(resp->resp))
+	if (resp_rc == CLE_OK) {
+		struct cldc_fh *fh = msg->cb_private;
+		XDR xdrs;
+		struct cld_msg_open_resp resp;
+
+		xdrmem_create(&xdrs, (void *)resp_p, resp_len, XDR_DECODE);
+		memset(&resp, 0, sizeof(resp));
+		if (!xdr_cld_msg_open_resp(&xdrs, &resp)) {
+			xdr_destroy(&xdrs);
 			return -1009;
-		resp_rc = le32_to_cpu(resp->resp.code);
-	}
+		}
 
-	if (resp_rc == CLE_OK) {
-		if (resp_len < sizeof(*resp))
-			return -1010;
-		fh->fh_le = resp->fh;
+		fh->fh = resp.fh;
 		fh->valid = true;
 	}
 
@@ -958,9 +937,8 @@ int cldc_open(struct cldc_session *sess,
 	      uint32_t events, struct cldc_fh **fh_out)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_open *open;
+	struct cld_msg_open open;
 	struct cldc_fh fh, *fhtmp;
-	void *p;
 	size_t plen;
 	int fh_idx;
 
@@ -974,12 +952,15 @@ int cldc_open(struct cldc_session *sess,
 		return -EINVAL;
 
 	plen = strlen(pathname);
-	if (plen > 65530)
+	if (plen > CLD_INODE_NAME_MAX)
 		return -EINVAL;
 
 	/* create OPEN message */
+	open.mode = open_mode;
+	open.events = events;
+	open.inode_name = (char *)pathname;
 	msg = cldc_new_msg(sess, copts, CMO_OPEN,
-			   sizeof(struct cld_msg_open) + strlen(pathname));
+			   (xdrproc_t)xdr_cld_msg_open, &open);
 	if (!msg)
 		return -ENOMEM;
 
@@ -994,15 +975,6 @@ int cldc_open(struct cldc_session *sess,
 	msg->cb = open_end_cb;
 	msg->cb_private = fhtmp;
 
-	/* fill in OPEN-specific info */
-	open = (struct cld_msg_open *) msg->data;
-	open->mode = cpu_to_le32(open_mode);
-	open->events = cpu_to_le32(events);
-	open->name_len = cpu_to_le16(plen);
-	p = open;
-	p += sizeof(struct cld_msg_open);
-	memcpy(p, pathname, plen);
-
 	*fh_out = fhtmp;
 
 	return sess_send(sess, msg);
@@ -1012,7 +984,7 @@ int cldc_close(struct cldc_fh *fh, const
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_close *close_msg;
+	struct cld_msg_close close;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1020,8 +992,9 @@ int cldc_close(struct cldc_fh *fh, const
 	sess = fh->sess;
 
 	/* create CLOSE message */
+	close.fh = fh->fh;
 	msg = cldc_new_msg(sess, copts, CMO_CLOSE,
-			   sizeof(struct cld_msg_close));
+			   (xdrproc_t)xdr_cld_msg_close, &close);
 	if (!msg)
 		return -ENOMEM;
 
@@ -1030,10 +1003,6 @@ int cldc_close(struct cldc_fh *fh, const
 
 	msg->cb = generic_end_cb;
 
-	/* fill in CLOSE-specific fh info */
-	close_msg = (struct cld_msg_close *) msg->data;
-	close_msg->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
@@ -1042,7 +1011,7 @@ int cldc_lock(struct cldc_fh *fh, const 
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_lock *lock;
+	struct cld_msg_lock lock;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1050,19 +1019,16 @@ int cldc_lock(struct cldc_fh *fh, const 
 	sess = fh->sess;
 
 	/* create LOCK message */
+	lock.fh = fh->fh;
+	lock.flags = lock_flags;
 	msg = cldc_new_msg(sess, copts,
 			   wait_for_lock ? CMO_LOCK : CMO_TRYLOCK,
-			   sizeof(struct cld_msg_lock));
+			   (xdrproc_t)xdr_cld_msg_lock, &lock);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in LOCK-specific info */
-	lock = (struct cld_msg_lock *) msg->data;
-	lock->fh = fh->fh_le;
-	lock->flags = cpu_to_le32(lock_flags);
-
 	return sess_send(sess, msg);
 }
 
@@ -1070,7 +1036,7 @@ int cldc_unlock(struct cldc_fh *fh, cons
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_unlock *unlock;
+	struct cld_msg_unlock unlock;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1078,17 +1044,14 @@ int cldc_unlock(struct cldc_fh *fh, cons
 	sess = fh->sess;
 
 	/* create UNLOCK message */
+	unlock.fh = fh->fh;
 	msg = cldc_new_msg(sess, copts, CMO_UNLOCK,
-			   sizeof(struct cld_msg_unlock));
+			   (xdrproc_t)xdr_cld_msg_unlock, &unlock);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in UNLOCK-specific info */
-	unlock = (struct cld_msg_unlock *) msg->data;
-	unlock->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
@@ -1097,9 +1060,9 @@ int cldc_put(struct cldc_fh *fh, const s
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_put *put;
+	struct cld_msg_put put;
 
-	if (!data || !data_len || data_len > CLD_MAX_MSG_SZ)
+	if (!data || !data_len || data_len > CLD_MAX_PAYLOAD_SZ)
 		return -EINVAL;
 
 	if (!fh->valid)
@@ -1108,17 +1071,14 @@ int cldc_put(struct cldc_fh *fh, const s
 	sess = fh->sess;
 
 	/* create PUT message */
+	put.fh = fh->fh;
+	put.data.data_len = data_len;
+	put.data.data_val = (char *)data;
 	msg = cldc_new_msg(sess, copts, CMO_PUT,
-			   sizeof(struct cld_msg_put) + data_len);
+			   (xdrproc_t)xdr_cld_msg_put, &put);
 	if (!msg)
 		return -ENOMEM;
 
-	put = (struct cld_msg_put *) msg->data;
-	put->fh = fh->fh_le;
-	put->data_size = cpu_to_le32(data_len);
-
-	memcpy((put + 1), data, data_len);
-
 	msg->cb = generic_end_cb;
 
 	sess_send(sess, msg);
@@ -1126,73 +1086,39 @@ int cldc_put(struct cldc_fh *fh, const s
 	return 0;
 }
 
-#undef XC32
-#undef XC64
-#define XC32(name) \
-	o->name = le32_to_cpu(resp->name)
-#define XC64(name) \
-	o->name = le64_to_cpu(resp->name)
-
 static ssize_t get_end_cb(struct cldc_msg *msg, const void *resp_p,
-			  size_t resp_len, bool ok)
+			  size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_get_resp *resp = resp_p;
-	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_get_resp *o = NULL;
-
-	if (!ok)
-		resp_rc = CLE_TIMEOUT;
-	else
-		resp_rc = le32_to_cpu(resp->resp.code);
-
 	if (resp_rc == CLE_OK) {
-		bool get_body;
-
-		o = &msg->copts.u.get.resp;
+		XDR xin;
+		struct cld_msg_get_resp *resp = &msg->copts.resp;
 
-		get_body = (resp->resp.hdr.op == CMO_GET);
-		msg->copts.op = CMO_GET;
-
-		/* copy-and-swap */
-		XC64(inum);
-		XC32(ino_len);
-		XC32(size);
-		XC64(version);
-		XC64(time_create);
-		XC64(time_modify);
-		XC32(flags);
-
-		/* copy inode name */
-		if (o->ino_len <= CLD_INODE_NAME_MAX) {
-			size_t diffsz;
-			const void *p;
-
-			p = (resp + 1);
-			memcpy(&msg->copts.u.get.inode_name, p, o->ino_len);
-
-			p += o->ino_len;
-			diffsz = p - resp_p;
-
-			/* point to internal buffer holding GET data */
-			msg->copts.u.get.buf = msg->sess->msg_buf + diffsz;
-			msg->copts.u.get.size = msg->sess->msg_buf_len - diffsz;
-		} else {
-			o->ino_len = 0;		/* Probably full of garbage */
+		/* Parse GET response.
+		 * Avoid memory allocation in xdr_string by pointing
+		 * variable-length elements at static buffers. */
+		xdrmem_create(&xin, (void *)resp_p, resp_len, XDR_DECODE);
+		memset(resp, 0, sizeof(struct cld_msg_get_resp));
+		resp->inode_name = msg->sess->inode_name_temp;
+		resp->data.data_val = msg->sess->payload;
+		resp->data.data_len = 0;
+		if (!xdr_cld_msg_get_resp(&xin, resp)) {
+			xdr_destroy(&xin);
+			return -1009;
 		}
+		xdr_destroy(&xin);
 	}
 
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
 	return 0;
 }
-#undef XC
 
 int cldc_get(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 	     bool metadata_only)
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_get *get;
+	struct cld_msg_get get;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1200,17 +1126,14 @@ int cldc_get(struct cldc_fh *fh, const s
 	sess = fh->sess;
 
 	/* create GET message */
+	get.fh = fh->fh;
 	msg = cldc_new_msg(sess, copts, CMO_GET,
-			   sizeof(struct cld_msg_get));
+			   (xdrproc_t)xdr_cld_msg_get, &get);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = get_end_cb;
 
-	/* fill in GET-specific info */
-	get = (struct cld_msg_get *) msg->data;
-	get->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
Binary files cld/lib/cldc-dns.o and cld.rpcgen/lib/cldc-dns.o differ
Binary files cld/lib/cldc.o and cld.rpcgen/lib/cldc.o differ
Binary files cld/lib/cldc-udp.o and cld.rpcgen/lib/cldc-udp.o differ
diff -X /garz/tmp/dontdiff -urNp cld/lib/cld_msg_rpc.x cld.rpcgen/lib/cld_msg_rpc.x
--- cld/lib/cld_msg_rpc.x	1969-12-31 19:00:00.000000000 -0500
+++ cld.rpcgen/lib/cld_msg_rpc.x	2010-02-03 01:21:23.000000000 -0500
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010, Colin McCabe
+ *
+ * This program 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.
+ *
+ * This program 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 this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+const CLD_PKT_MAGIC = "CLDc1pkt";
+const CLD_SID_SZ = 8;
+
+const CLD_INODE_NAME_MAX = 256; /**< max total pathname len */
+
+const CLD_MAX_USERNAME = 32;
+
+const CLD_MAX_PKT_MSG_SZ = 1024; /**< The maximum number of message bytes we'll
+				   put in a single packet */
+
+const CLD_MAX_PAYLOAD_SZ = 131072; /**< Maximum length of the data that can be
+					sent with get or put. In some sense,
+					this is part of cld's API, and
+					shouldn't be changed lightly.  */
+
+const CLD_MAX_MSG_SZ = 196608; /**< Maximum size of a single message
+					including all packets. */
+
+const CLD_MAX_SECRET_KEY = 128; /**< includes req. nul */
+
+/** available RPC operations */
+enum cld_msg_op {
+	/* client -> server */
+	CMO_NOP			= 0,	/**< no op */
+	CMO_NEW_SESS		= 1,	/**< new session */
+	CMO_OPEN		= 2,	/**< open file */
+	CMO_GET_META		= 3,	/**< get metadata */
+	CMO_GET			= 4,	/**< get metadata + data */
+	CMO_PUT			= 6,	/**< put data */
+	CMO_CLOSE		= 7,	/**< close file */
+	CMO_DEL			= 8,	/**< delete file */
+	CMO_LOCK		= 9,	/**< lock */
+	CMO_UNLOCK		= 10,	/**< unlock */
+	CMO_TRYLOCK		= 11,	/**< trylock */
+	CMO_ACK			= 12,	/**< ack of seqid rx'd */
+	CMO_END_SESS		= 13,	/**< end session */
+
+	/* server -> client */
+	CMO_PING		= 14,	/**< server to client ping */
+	CMO_NOT_MASTER		= 15,	/**< I am not the master! */
+	CMO_EVENT		= 16,	/**< server->cli async event */
+	CMO_ACK_FRAG		= 17, 	/**< ack partial msg */
+
+	CMO_AFTER_LAST
+};
+
+/** CLD error codes */
+enum cle_err_codes {
+	CLE_OK			= 0,	/**< success / no error */
+	CLE_SESS_EXISTS		= 1,	/**< session exists */
+	CLE_SESS_INVAL		= 2,	/**< session doesn't exist */
+	CLE_DB_ERR		= 3,	/**< db error */
+	CLE_BAD_PKT		= 4,	/**< invalid/corrupted packet */
+	CLE_INODE_INVAL		= 5,	/**< inode doesn't exist */
+	CLE_NAME_INVAL		= 6,	/**< inode name invalid */
+	CLE_OOM			= 7,	/**< server out of memory */
+	CLE_FH_INVAL		= 8,	/**< file handle invalid */
+	CLE_DATA_INVAL		= 9,	/**< invalid data pkt */
+	CLE_LOCK_INVAL		= 10,	/**< invalid lock */
+	CLE_LOCK_CONFLICT 	= 11,	/**< conflicting lock held */
+	CLE_LOCK_PENDING	= 12,	/**< lock waiting to be acq. */
+	CLE_MODE_INVAL		= 13,	/**< op incompat. w/ file mode */
+	CLE_INODE_EXISTS	= 14,	/**< inode exists */
+	CLE_DIR_NOTEMPTY	= 15,	/**< dir not empty */
+	CLE_INTERNAL_ERR	= 16,	/**< nonspecific internal err */
+	CLE_TIMEOUT 		= 17,	/**< session timed out */
+	CLE_SIG_INVAL 		= 18	/**< HMAC sig bad / auth failed */
+};
+
+/** availble OPEN mode flags */
+enum cld_open_modes {
+	COM_READ		= 0x01,	/**< read */
+	COM_WRITE		= 0x02,	/**< write */
+	COM_LOCK		= 0x04,	/**< lock */
+	COM_ACL			= 0x08,	/**< ACL update */
+	COM_CREATE		= 0x10,	/**< create file, if not exist */
+	COM_EXCL		= 0x20,	/**< fail create if file exists */
+	COM_DIRECTORY		= 0x40	/**< operate on a directory */
+};
+
+/** potential events client may receive */
+enum cld_events {
+	CE_UPDATED		= 0x01,	/**< contents updated */
+	CE_DELETED		= 0x02,	/**< inode deleted */
+	CE_LOCKED		= 0x04,	/**< lock acquired */
+	CE_MASTER_FAILOVER	= 0x08,	/**< master failover */
+	CE_SESS_FAILED		= 0x10
+};
+
+/** LOCK flags */
+enum cld_lock_flags {
+	CLF_SHARED		= 0x01	/**< a shared (read) lock */
+};
+
+/** Describes whether a packet begins, continues, or ends a message. */
+enum cld_pkt_order_t {
+	CLD_PKT_ORD_MID = 0x0,
+	CLD_PKT_ORD_FIRST = 0x1,
+	CLD_PKT_ORD_LAST = 0x2,
+	CLD_PKT_ORD_FIRST_LAST = 0x3
+};
+const CLD_PKT_IS_FIRST = 0x1;
+const CLD_PKT_IS_LAST = 0x2;
+
+/** Information that appears only in the first packet */
+struct cld_pkt_msg_infos {
+	hyper			xid;		/**< opaque message id */
+	enum cld_msg_op	type;		/**< type of the message */
+};
+
+/** Information about the message contained in this packet */
+union cld_pkt_msg_info switch (enum cld_pkt_order_t order) {
+	case CLD_PKT_ORD_MID:
+	case CLD_PKT_ORD_LAST:
+		void;
+	case CLD_PKT_ORD_FIRST:
+	case CLD_PKT_ORD_FIRST_LAST:
+		struct cld_pkt_msg_infos mi;
+};
+
+/** header for each packet */
+struct cld_pkt_hdr {
+	hyper		magic;		/**< magic number; constant */
+	hyper		sid;		/**< client id */
+	string		user<CLD_MAX_USERNAME>;	/**< authenticated user */
+	struct cld_pkt_msg_info mi;
+};
+
+/** generic response for PUT, CLOSE, DEL, LOCK, UNLOCK */
+struct cld_msg_generic_resp {
+	enum cle_err_codes	code;		/**< error code, CLE_xxx */
+	hyper			xid_in;		/**< C->S xid */
+};
+
+/** ACK-FRAG message */
+struct cld_msg_ack_frag {
+	hyper			seqid;		/**< sequence id to ack */
+};
+
+/** OPEN message */
+struct cld_msg_open {
+	int			mode;		/**< open mode, COM_xxx */
+	int			events;		/**< events mask, CE_xxx */
+	string			inode_name<CLD_INODE_NAME_MAX>;
+};
+
+/** OPEN message response */
+struct cld_msg_open_resp {
+	struct cld_msg_generic_resp msg;
+	hyper			fh;		/**< handle opened */
+};
+
+/** GET message */
+struct cld_msg_get {
+	hyper			fh;		/**< open file handle */
+};
+
+/** GET message response */
+struct cld_msg_get_resp {
+	struct cld_msg_generic_resp msg;
+	hyper			inum;		/**< unique inode number */
+	hyper			vers;		/**< inode version */
+	hyper			time_create;	/**< creation time */
+	hyper			time_modify;	/**< last modification time */
+	int			flags;		/**< inode flags; CIFL_xxx */
+	string			inode_name<CLD_INODE_NAME_MAX>;
+	opaque			data<CLD_MAX_PAYLOAD_SZ>;
+};
+
+/** PUT message */
+struct cld_msg_put {
+	hyper			fh;		/**< open file handle */
+	opaque			data<CLD_MAX_PAYLOAD_SZ>;
+};
+
+/** CLOSE message */
+struct cld_msg_close {
+	hyper			fh;		/**< open file handle */
+};
+
+/** DEL message */
+struct cld_msg_del {
+	string			inode_name<CLD_INODE_NAME_MAX>;
+};
+
+/** UNLOCK message */
+struct cld_msg_unlock {
+	uint64_t		fh;		/**< open file handle */
+};
+
+/** LOCK message */
+struct cld_msg_lock {
+	hyper			fh;		/**< open file handle */
+	int			flags;		/**< CLF_xxx */
+};
+
+/** Server-to-client EVENT message */
+struct cld_msg_event {
+	hyper			fh;		/**< open file handle */
+	int			events;		/**< CE_xxx */
+};
Binary files cld/lib/cld_msg_rpc_xdr.o and cld.rpcgen/lib/cld_msg_rpc_xdr.o differ
diff -X /garz/tmp/dontdiff -urNp cld/lib/common.c cld.rpcgen/lib/common.c
--- cld/lib/common.c	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/lib/common.c	2010-01-22 18:15:07.000000000 -0500
@@ -26,7 +26,11 @@
 #include <errno.h>
 #include <glib.h>
 #include <cld-private.h>
-#include "cld_msg.h"
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include "cld_msg_rpc.h"
+#include <hail_log.h>
+#include <syslog.h>
 
 /* duplicated from tools/cldcli.c; put in common header somewhere? */
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
Binary files cld/lib/common.o and cld.rpcgen/lib/common.o differ
Binary files cld/lib/libcldc.a and cld.rpcgen/lib/libcldc.a differ
Binary files cld/lib/.libs/libcldc.a and cld.rpcgen/lib/.libs/libcldc.a differ
Binary files cld/lib/libtimer.a and cld.rpcgen/lib/libtimer.a differ
Binary files cld/lib/libtimer.o and cld.rpcgen/lib/libtimer.o differ
diff -X /garz/tmp/dontdiff -urNp cld/lib/Makefile.am cld.rpcgen/lib/Makefile.am
--- cld/lib/Makefile.am	2010-02-03 00:17:47.000000000 -0500
+++ cld.rpcgen/lib/Makefile.am	2010-02-02 23:10:51.000000000 -0500
@@ -1,10 +1,21 @@
 
+BUILT_SOURCES		= cld_msg_rpc.h
+
 EXTRA_DIST =
 	libcldc.pc.in libcldc-uninstalled.pc.in
 
 INCLUDES		= -I$(top_srcdir)/include	\
 			  @GLIB_CFLAGS@
 
+mostlyclean-local:
+	rm -f *_rpc.h *_rpc_xdr.c
+
+%_rpc.h: %_rpc.x
+	rpcgen -h $< > $@
+
+%_rpc_xdr.c: %_rpc.x
+	rpcgen -c $< > $@
+
 LINK = $(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -o $@
 
 lib_LTLIBRARIES		= libcldc.la
@@ -15,7 +26,8 @@ libcldc_la_SOURCES	=	\
 	cldc-dns.c		\
 	common.c		\
 	libtimer.c		\
-	pkt.c
+	pkt.c			\
+	cld_msg_rpc_xdr.c
 
 libcldc_la_LDFLAGS = \
 	-version-info $(LIBCLDC_CURRENT):$(LIBCLDC_REVISION):$(LIBCLDC_AGE) \
@@ -25,3 +37,4 @@ libcldc_la_LDFLAGS = \
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libcldc.pc
 
+include_HEADERS = cld_msg_rpc.h
diff -X /garz/tmp/dontdiff -urNp cld/lib/pkt.c cld.rpcgen/lib/pkt.c
--- cld/lib/pkt.c	2010-02-03 01:15:26.000000000 -0500
+++ cld.rpcgen/lib/pkt.c	2010-02-03 01:21:49.000000000 -0500
@@ -17,11 +17,16 @@
  */
 
 #include <string.h>
+#include <stdio.h>
 #include <errno.h>
+#include <glib.h>
 #include <syslog.h>
 #include <openssl/sha.h>
 #include <openssl/hmac.h>
-#include <cldc.h>
+#include <cld-private.h>
+#include "cld_pkt.h"
+#include "cld_msg_rpc.h"
+#include <hail_log.h>
 
 int __cld_authcheck(struct hail_log *log, const char *key,
 		    const void *buf, size_t buf_len, const void *sha)
@@ -68,9 +73,9 @@ int __cld_authsign(struct hail_log *log,
 	return 0;
 }
 
-const char *__cld_opstr(enum cld_msg_ops op)
+const char *__cld_opstr(enum cld_msg_op type)
 {
-	switch (op) {
+	switch (type) {
 	case CMO_NOP:		return "CMO_NOP";
 	case CMO_NEW_SESS:	return "CMO_NEW_SESS";
 	case CMO_OPEN:		return "CMO_OPEN";
@@ -92,3 +97,93 @@ const char *__cld_opstr(enum cld_msg_ops
 	}
 }
 
+const char *__cld_pkt_hdr_to_str(char *scratch,
+			   const char *pkt_hdr, size_t pkt_len)
+{
+	XDR xin;
+	struct cld_pkt_hdr pkt;
+	bool bad_magic;
+	char temp[50], temp2[50];
+	uint64_t seqid;
+	struct cld_pkt_ftr *foot;
+	size_t hdr_len;
+
+	temp[0] = '\0';
+	temp2[0] = '\0';
+	foot = (struct cld_pkt_ftr *)(pkt_hdr + pkt_len - CLD_PKT_FTR_LEN);
+	seqid = le64_to_cpu(foot->seqid);
+
+	if (pkt_len <= CLD_PKT_FTR_LEN) {
+		snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+			 "[MALFORMED: only %d bytes]", pkt_len);
+		return scratch;
+	}
+	xdrmem_create(&xin, (void *)pkt_hdr, pkt_len - CLD_PKT_FTR_LEN,
+		      XDR_DECODE);
+	memset(&pkt, 0, sizeof(pkt));
+	if (!xdr_cld_pkt_hdr(&xin, &pkt)) {
+		xdr_destroy(&xin);
+		snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+			 "[MALFORMED: can't parse]");
+		return scratch;
+	}
+	hdr_len = xdr_getpos(&xin);
+	xdr_destroy(&xin);
+
+	bad_magic = !!(memcmp(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic)));
+	if (pkt.mi.order & CLD_PKT_IS_FIRST) {
+		struct cld_pkt_msg_infos *infos =
+			&pkt.mi.cld_pkt_msg_info_u.mi;
+		snprintf(temp, sizeof(temp), "[TYPE:%s, XID:%llx]",
+			 __cld_opstr(infos->type), infos->xid);
+		switch (infos->type) {
+		case CMO_ACK_FRAG: {
+			XDR x;
+			struct cld_msg_ack_frag ack;
+			memset(&ack, 0, sizeof(ack));
+			xdrmem_create(&x, ((char *)pkt_hdr) + hdr_len,
+				      pkt_len - hdr_len - CLD_PKT_FTR_LEN,
+				      XDR_DECODE);
+			if (!xdr_cld_msg_ack_frag(&x, &ack)) {
+				xdr_destroy(&x);
+				snprintf(temp2, sizeof(temp2), "{MALFORMED}");
+				break;
+			}
+			snprintf(temp2, sizeof(temp2), "{seqid:%llx}",
+				ack.seqid);
+			xdr_destroy(&x);
+			break;
+		}
+		default:
+			break;
+		}
+	} else {
+		snprintf(temp, sizeof(temp), "[CONT]");
+	}
+
+	snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+		"<%s%s%s> "
+		"%s USER:'%s' SEQID:%llu %s",
+		((pkt.mi.order & CLD_PKT_IS_FIRST) ? "1st" : ""),
+		((pkt.mi.order & CLD_PKT_IS_LAST) ? "End" : ""),
+		(bad_magic ? "B" : ""),
+		temp, pkt.user, seqid, temp2);
+	xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+	return scratch;
+}
+
+void __cld_dump_buf(const void *buf, size_t len)
+{
+	const unsigned char *buff = buf;
+	size_t off = 0;
+	do {
+		int i;
+		for (i = 0; i < 8; i++) {
+			if (!len)
+				break;
+			printf("%02x ", buff[off++]);
+			len--;
+		}
+		printf("\n");
+	} while (len);
+}
Binary files cld/lib/pkt.o and cld.rpcgen/lib/pkt.o differ
Binary files cld/server/cld and cld.rpcgen/server/cld differ
Binary files cld/server/cldbadm and cld.rpcgen/server/cldbadm differ
Binary files cld/server/cldbadm.o and cld.rpcgen/server/cldbadm.o differ
diff -X /garz/tmp/dontdiff -urNp cld/server/cldb.h cld.rpcgen/server/cldb.h
--- cld/server/cldb.h	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/server/cldb.h	2010-01-22 18:29:11.000000000 -0500
@@ -23,7 +23,7 @@
 #include <stdbool.h>
 #include <db.h>
 #include <cld-private.h>
-#include <cld_msg.h>
+#include <cld_msg_rpc.h>
 
 typedef uint64_t cldino_t;
 
Binary files cld/server/cldb.o and cld.rpcgen/server/cldb.o differ
diff -X /garz/tmp/dontdiff -urNp cld/server/cld.h cld.rpcgen/server/cld.h
--- cld/server/cld.h	2010-02-03 01:02:41.000000000 -0500
+++ cld.rpcgen/server/cld.h	2010-02-03 01:23:09.000000000 -0500
@@ -25,7 +25,7 @@
 #include <poll.h>
 #include <glib.h>
 #include "cldb.h"
-#include <cld_msg.h>
+#include <cld_msg_rpc.h>
 #include <cld_common.h>
 #include <hail_log.h>
 
@@ -72,20 +72,12 @@ struct session {
 	bool			dead;		/* session has ended */
 
 	/* huge buffer should always come last */
+	enum cld_msg_op		msg_op;
+	uint64_t		msg_xid;
 	unsigned int		msg_buf_len;
 	char			msg_buf[CLD_MAX_MSG_SZ];
 };
 
-struct msg_params {
-	int			sock_fd;
-	const struct client	*cli;
-	struct session		*sess;
-
-	const struct cld_packet	*pkt;
-	const void		*msg;
-	size_t			msg_len;
-};
-
 struct server_stats {
 	unsigned long		poll;		/* num. polls */
 	unsigned long		event;		/* events dispatched */
@@ -122,29 +114,59 @@ struct server {
 	struct server_stats	stats;		/* global statistics */
 };
 
+struct pkt_info {
+	struct cld_pkt_hdr *pkt;
+	struct session *sess;
+	uint64_t seqid;
+	uint64_t xid;
+	enum cld_msg_op type;
+	size_t hdr_len;
+};
+
 /* msg.c */
 extern int inode_lock_rescan(DB_TXN *txn, cldino_t inum);
-extern void msg_open(struct msg_params *);
-extern void msg_put(struct msg_params *);
-extern void msg_close(struct msg_params *);
-extern void msg_del(struct msg_params *);
-extern void msg_unlock(struct msg_params *);
-extern void msg_lock(struct msg_params *, bool);
-extern void msg_ack(struct msg_params *);
-extern void msg_get(struct msg_params *, bool);
+extern void msg_get(struct session *sess, const void *v);
+extern void msg_open(struct session *sess, const void *v);
+extern void msg_put(struct session *sess, const void *v);
+extern void msg_close(struct session *sess, const void *v);
+extern void msg_del(struct session *sess, const void *v);
+extern void msg_unlock(struct session *sess, const void *v);
+extern void msg_lock(struct session *sess, const void *v);
+extern void msg_ack(struct session *sess, uint64_t seqid);
 
 /* session.c */
 extern uint64_t next_seqid_le(uint64_t *seq);
-extern void pkt_init_pkt(struct cld_packet *dest, const struct cld_packet *src);
 extern guint sess_hash(gconstpointer v);
 extern gboolean sess_equal(gconstpointer _a, gconstpointer _b);
-extern void msg_new_sess(struct msg_params *, const struct client *);
-extern void msg_end_sess(struct msg_params *, const struct client *);
+extern void msg_new_sess(int sock_fd, const struct client *cli,
+			const struct pkt_info *info);
+extern void msg_end_sess(struct session *sess, uint64_t xid);
 extern struct raw_session *session_new_raw(const struct session *sess);
 extern void sessions_free(void);
-extern bool sess_sendmsg(struct session *sess, const void *msg_, size_t msglen,
-		  void (*done_cb)(struct session_outpkt *),
-		  void *done_data);
+
+/** Send a message as part of a session.
+ *
+ * @param sess		The session
+ * @param xdrproc	The XDR function to use to serialize the data
+ * @param xdrdata	The message data
+ * @param type		The type of the message
+ * @param done_cb	The callback to call when the message has been acked
+ * @param done_data	The data to give to done_cb
+ *
+ * @return		true only if the message was sent
+ */
+extern bool sess_sendmsg(struct session *sess,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op type,
+	void (*done_cb)(struct session_outpkt *), void *done_data);
+
+/** Send a generic response message.
+ *
+ * @param sess		The session
+ * @param code		The error code to send
+ */
+extern void sess_sendresp_generic(struct session *sess,
+				  enum cle_err_codes code);
+
 extern int session_dispose(DB_TXN *txn, struct session *sess);
 extern int session_remove_locks(DB_TXN *txn, uint8_t *sid, uint64_t fh,
 				cldino_t inum, bool *waiter);
@@ -156,11 +178,25 @@ extern struct hail_log srv_log;
 extern struct timeval current_time;
 extern int udp_tx(int sock_fd, struct sockaddr *, socklen_t,
 	    const void *, size_t);
-extern void resp_copy(struct cld_msg_resp *resp, const struct cld_msg_hdr *src);
-extern void resp_err(struct session *sess,
-	      const struct cld_msg_hdr *src, enum cle_err_codes errcode);
-extern void resp_ok(struct session *sess, const struct cld_msg_hdr *src);
-extern const char *user_key(const char *user);
+
+/** Transmit a single packet.
+ *
+ * This function doesn't provide error-retransmission logic.
+ * It can't handle messages that are bigger than a single packet.
+ *
+ * @param fd		Socket to send the response on
+ * @param cli		Client address data
+ * @param sid		The session-id to use. Must be of length CLD_SID_SZ
+ * @param seqid		The sequence id to use
+ * @param xdrproc	The XDR function to use to serialize the data
+ * @param xdrdata	The message data
+ * @param type		The type of message to send
+ *
+ * @return		true only on success
+ */
+extern void simple_sendmsg(int fd, const struct client *cli,
+	uint64_t sid, const char *username, uint64_t seqid,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op type);
 
 /* util.c */
 extern int write_pid_file(const char *pid_fn);
Binary files cld/server/cld_msg_rpc_xdr.o and cld.rpcgen/server/cld_msg_rpc_xdr.o differ
Binary files cld/server/common.o and cld.rpcgen/server/common.o differ
Binary files cld/server/libtimer.o and cld.rpcgen/server/libtimer.o differ
diff -X /garz/tmp/dontdiff -urNp cld/server/Makefile.am cld.rpcgen/server/Makefile.am
--- cld/server/Makefile.am	2010-02-03 00:14:51.000000000 -0500
+++ cld.rpcgen/server/Makefile.am	2010-02-02 23:10:54.000000000 -0500
@@ -1,5 +1,6 @@
 
 INCLUDES	= -I$(top_srcdir)/include	\
+		  -I$(top_srcdir)/lib		\
 		  @GLIB_CFLAGS@			\
 		  -DCLD_LIBDIR=\""$(libdir)"\"	\
 		  -DCLD_LOCAL_STATE_DIR="\"$(localstatedir)\""
@@ -8,7 +9,8 @@ sbin_PROGRAMS	= cld cldbadm
 
 cld_SOURCES	= cldb.h cld.h \
 		  ../lib/common.c ../lib/libtimer.c ../lib/pkt.c \
-		  cldb.c msg.c server.c session.c util.c
+		  cldb.c msg.c server.c session.c util.c \
+		  ../lib/cld_msg_rpc_xdr.c
 cld_LDADD	= @CRYPTO_LIBS@ @GLIB_LIBS@ @DB4_LIBS@
 
 cldbadm_SOURCES	= cldb.h cldbadm.c
diff -X /garz/tmp/dontdiff -urNp cld/server/msg.c cld.rpcgen/server/msg.c
--- cld/server/msg.c	2010-02-03 01:15:17.000000000 -0500
+++ cld.rpcgen/server/msg.c	2010-02-03 01:23:17.000000000 -0500
@@ -25,6 +25,8 @@
 #include <syslog.h>
 #include <openssl/sha.h>
 #include <cld-private.h>
+#include <cld_common.h>
+#include <cld_msg_rpc.h>
 #include "cld.h"
 
 enum {
@@ -247,12 +249,11 @@ static int inode_notify(DB_TXN *txn, cld
 		}
 
 		memset(&me, 0, sizeof(me));
-		memcpy(me.hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-		me.hdr.op = CMO_EVENT;
-		me.fh = h.fh;
-		me.events = cpu_to_le32(deleted ? CE_DELETED : CE_UPDATED);
+		me.fh = le64_to_cpu(h.fh);
+		me.events = deleted ? CE_DELETED : CE_UPDATED;
 
-		if (!sess_sendmsg(sess, &me, sizeof(me), NULL, NULL))
+		if (!sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_event,
+				  (void *)&me, CMO_EVENT, NULL, NULL))
 			break;
 	}
 
@@ -293,7 +294,6 @@ int inode_lock_rescan(DB_TXN *txn, cldin
 	cldino_t inum_le = cldino_to_le(inum);
 	struct raw_lock lock;
 	uint32_t lflags;
-	struct cld_msg_event me;
 	struct session *sess;
 
 	rc = db_locks->cursor(db_locks, txn, &cur, 0);
@@ -313,15 +313,13 @@ int inode_lock_rescan(DB_TXN *txn, cldin
 	val.ulen = sizeof(lock);
 	val.flags = DB_DBT_USERMEM;
 
-	memset(&me, 0, sizeof(me));
-	memcpy(me.hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	me.hdr.op = CMO_EVENT;
-
 	/* loop through locks associated with this inode, searching
 	 * for pending locks that can be converted into acquired
 	 */
 	gflags = DB_SET | DB_RMW;
 	while (1) {
+		struct cld_msg_event me;
+
 		rc = cur->get(cur, &key, &val, gflags);
 		if (rc) {
 			/* no locks, or no next-dup */
@@ -377,10 +375,12 @@ int inode_lock_rescan(DB_TXN *txn, cldin
 			continue;
 		}
 
-		me.fh = lock.fh;
-		me.events = cpu_to_le32(CE_LOCKED);
+		memset(&me, 0, sizeof(me));
+		me.fh = le64_to_cpu(lock.fh);
+		me.events = CE_LOCKED;
 
-		if (!sess_sendmsg(sess, &me, sizeof(me), NULL, NULL))
+		if (!sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_event,
+				(void *)&me, CMO_EVENT, NULL, NULL))
 			break;
 	}
 
@@ -388,12 +388,10 @@ int inode_lock_rescan(DB_TXN *txn, cldin
 	return rc;
 }
 
-void msg_get(struct msg_params *mp, bool metadata_only)
+void msg_get(struct session *sess, const void *v)
 {
-	const struct cld_msg_get *msg = mp->msg;
-	struct cld_msg_get_resp *resp;
-	size_t resp_len;
-	uint64_t fh;
+	const struct cld_msg_get *get = v;
+	struct cld_msg_get_resp resp;
 	struct raw_handle *h = NULL;
 	struct raw_inode *inode = NULL;
 	enum cle_err_codes resp_rc = CLE_OK;
@@ -401,17 +399,10 @@ void msg_get(struct msg_params *mp, bool
 	uint32_t name_len, inode_size;
 	uint32_t omode;
 	int rc;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
-	void *p;
-
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	/* get filehandle from input msg */
-	fh = le64_to_cpu(msg->fh);
+	void *data_mem;
+	char *inode_name;
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -421,7 +412,7 @@ void msg_get(struct msg_params *mp, bool
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, get->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -442,42 +433,30 @@ void msg_get(struct msg_params *mp, bool
 		goto err_out;
 	}
 
-	name_len = le32_to_cpu(inode->ino_len);
 	inode_size = le32_to_cpu(inode->size);
-
-	resp_len = sizeof(*resp) + name_len +
-		   (metadata_only ? 0 : inode_size);
-	resp = alloca(resp_len);
-	if (!resp) {
-		resp_rc = CLE_OOM;
-		goto err_out;
-	}
-
-	HAIL_DEBUG(&srv_log, "%s: sizeof(resp) %zu, name_len %u, "
-		   "inode->size %u, resp_len %zu",
-		   __func__,
-		   sizeof(*resp), name_len,
-		   inode_size, resp_len);
+	HAIL_DEBUG(&srv_log, "GET-DEBUG: inode->size %u\n", inode_size);
 
 	/* return response containing inode metadata */
-	memset(resp, 0, resp_len);
-	resp_copy(&resp->resp, mp->msg);
-	resp->inum = inode->inum;
-	resp->ino_len = inode->ino_len;
-	resp->size = inode->size;
-	resp->version = inode->version;
-	resp->time_create = inode->time_create;
-	resp->time_modify = inode->time_modify;
-	resp->flags = inode->flags;
+	memset(&resp, 0, sizeof(resp));
+	resp.msg.code = CLE_OK;
+	resp.msg.xid_in = sess->msg_xid;
+	resp.inum = le64_to_cpu(inode->inum);
+	resp.vers = le64_to_cpu(inode->version);
+	resp.time_create = le64_to_cpu(inode->time_create);
+	resp.time_modify = le64_to_cpu(inode->time_modify);
+	resp.flags = le32_to_cpu(inode->flags);
 
-	p = (resp + 1);
-	memcpy(p, (inode + 1), name_len);
+	name_len = le32_to_cpu(inode->ino_len);
+	inode_name = alloca(name_len + 1);
+	snprintf(inode_name, name_len + 1, "%s", (char *)(inode + 1));
+	resp.inode_name = inode_name;
 
-	p += name_len;
+	resp.data.data_len = 0;
+	resp.data.data_val = NULL;
 
 	/* send data, if requested */
-	if (!metadata_only) {
-		void *data_mem;
+	data_mem = NULL;
+	if (sess->msg_op == CMO_GET) {
 		size_t data_mem_len;
 
 		rc = cldb_data_get(txn, inum, &data_mem, &data_mem_len,
@@ -486,22 +465,23 @@ void msg_get(struct msg_params *mp, bool
 		/* treat not-found as zero length file, as we may
 		 * not yet have created the data record
 		 */
-		if (rc == DB_NOTFOUND) {
-			resp->size = 0;
-			resp_len -= inode_size;
-		} else if (rc || (data_mem_len != inode_size)) {
-			if (!rc)
-				free(data_mem);
-			resp_rc = CLE_DB_ERR;
-			goto err_out;
-		} else {
-			memcpy(p, data_mem, data_mem_len);
-
-			free(data_mem);
+		if (rc != DB_NOTFOUND) {
+			if (rc || (data_mem_len != inode_size)) {
+				if (!rc)
+					free(data_mem);
+				resp_rc = CLE_DB_ERR;
+				goto err_out;
+			} else {
+				resp.data.data_len = data_mem_len;
+				resp.data.data_val = data_mem;
+			}
 		}
 	}
 
-	sess_sendmsg(sess, resp, resp_len, NULL, NULL);
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_get_resp,
+		     (void *)&resp, CMO_GET, NULL, NULL);
+	if (data_mem)
+		free(data_mem);
 
 	rc = txn->commit(txn, 0);
 	if (rc)
@@ -516,14 +496,14 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_get txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 	free(inode);
 }
 
-void msg_open(struct msg_params *mp)
+void msg_open(struct session *sess, const void *v)
 {
-	const struct cld_msg_open *msg = mp->msg;
+	const struct cld_msg_open *open = v;
 	struct cld_msg_open_resp resp;
 	const char *name;
 	struct raw_session *raw_sess = NULL;
@@ -534,24 +514,12 @@ void msg_open(struct msg_params *mp)
 	struct pathname_info pinfo;
 	void *parent_data = NULL;
 	size_t parent_len;
-	uint32_t msg_mode, msg_events;
 	uint64_t fh;
 	cldino_t inum;
 	enum cle_err_codes resp_rc = CLE_OK;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	msg_mode = le32_to_cpu(msg->mode);
-	msg_events = le32_to_cpu(msg->events);
-	name_len = le16_to_cpu(msg->name_len);
-
-	if (mp->msg_len < (sizeof(*msg) + name_len))
-		return;
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -559,11 +527,12 @@ void msg_open(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	name = mp->msg + sizeof(*msg);
+	name = open->inode_name;
+	name_len = strlen(name);
 
-	create = msg_mode & COM_CREATE;
-	excl = msg_mode & COM_EXCL;
-	do_dir = msg_mode & COM_DIRECTORY;
+	create = open->mode & COM_CREATE;
+	excl = open->mode & COM_EXCL;
+	do_dir = open->mode & COM_DIRECTORY;
 
 	if (!valid_inode_name(name, name_len) || (create && name_len < 2)) {
 		resp_rc = CLE_NAME_INVAL;
@@ -661,7 +630,7 @@ void msg_open(struct msg_params *mp)
 	inum = cldino_from_le(inode->inum);
 
 	/* alloc & init new handle; updates session's next_fh */
-	h = cldb_handle_new(mp->sess, inum, msg_mode, msg_events);
+	h = cldb_handle_new(sess, inum, open->mode, open->events);
 	if (!h) {
 		HAIL_CRIT(&srv_log, "cannot allocate handle");
 		resp_rc = CLE_OOM;
@@ -688,7 +657,7 @@ void msg_open(struct msg_params *mp)
 	}
 
 	/* encode in-memory session to raw database session struct */
-	raw_sess = session_new_raw(mp->sess);
+	raw_sess = session_new_raw(sess);
 
 	if (!raw_sess) {
 		HAIL_CRIT(&srv_log, "cannot allocate session");
@@ -716,11 +685,12 @@ void msg_open(struct msg_params *mp)
 	free(raw_sess);
 	free(h);
 
-	resp_copy(&resp.resp, mp->msg);
-	resp.resp.code = cpu_to_le32(CLE_OK);
-	resp.fh = cpu_to_le64(fh);
-	sess_sendmsg(mp->sess, &resp, sizeof(resp), NULL, NULL);
-
+	memset(&resp, 0, sizeof(resp));
+	resp.msg.xid_in = sess->msg_xid;
+	resp.msg.code = CLE_OK;
+	resp.fh = fh;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_open_resp,
+		     (void *)&resp, CMO_OPEN, NULL, NULL);
 	return;
 
 err_out:
@@ -728,7 +698,7 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_open txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(parent_data);
 	free(parent);
 	free(inode);
@@ -736,38 +706,18 @@ err_out_noabort:
 	free(h);
 }
 
-void msg_put(struct msg_params *mp)
+void msg_put(struct session *sess, const void *v)
 {
-	const struct cld_msg_put *msg = mp->msg;
-	struct session *sess = mp->sess;
-	uint64_t fh;
+	const struct cld_msg_put *put = v;
 	struct raw_handle *h = NULL;
 	struct raw_inode *inode = NULL;
 	enum cle_err_codes resp_rc = CLE_OK;
-	const void *mem;
 	int rc;
 	cldino_t inum;
-	uint32_t omode, data_size;
+	uint32_t omode;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as message header */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	/* make sure additional input data as large as expected */
-	data_size = le32_to_cpu(msg->data_size);
-	if (mp->msg_len != (data_size + sizeof(*msg))) {
-		HAIL_INFO(&srv_log, "PUT len mismatch: msg len %zu, "
-			  "wanted %zu + %u (== %zu)",
-			  mp->msg_len,
-			  sizeof(*msg), data_size, data_size + sizeof(*msg));
-		resp_rc = CLE_BAD_PKT;
-		goto err_out_noabort;
-	}
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -776,7 +726,7 @@ void msg_put(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, put->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -799,14 +749,14 @@ void msg_put(struct msg_params *mp)
 	}
 
 	/* store contig. data area in db */
-	mem = (msg + 1);
-	rc = cldb_data_put(txn, inum, mem, data_size, 0);
+	rc = cldb_data_put(txn, inum,
+			   put->data.data_val, put->data.data_len, 0);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
 	}
 
-	inode->size = cpu_to_le32(data_size);
+	inode->size = cpu_to_le32(put->data.data_len);
 
 	/* update inode */
 	rc = inode_touch(txn, inode);
@@ -822,7 +772,7 @@ void msg_put(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 
 	free(h);
 	free(inode);
@@ -833,31 +783,23 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_put txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 
 	free(h);
 	free(inode);
 }
 
-void msg_close(struct msg_params *mp)
+void msg_close(struct session *sess, const void *v)
 {
-	const struct cld_msg_close *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_close *close = v;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
 	struct raw_handle *h = NULL;
 	cldino_t lock_inum = 0;
 	bool waiter = false;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -866,7 +808,7 @@ void msg_close(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, DB_RMW);
+	rc = cldb_handle_get(txn, sess->sid, close->fh, &h, DB_RMW);
 	if (rc) {
 		if (rc == DB_NOTFOUND)
 			resp_rc = CLE_FH_INVAL;
@@ -879,14 +821,15 @@ void msg_close(struct msg_params *mp)
 		lock_inum = cldino_from_le(h->inum);
 
 	/* delete handle from db */
-	rc = cldb_handle_del(txn, sess->sid, fh);
+	rc = cldb_handle_del(txn, sess->sid, close->fh);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
 	}
 
 	/* remove locks, if any */
-	rc = session_remove_locks(txn, sess->sid, fh, lock_inum, &waiter);
+	rc = session_remove_locks(txn, sess->sid,
+				  close->fh, lock_inum, &waiter);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
@@ -908,7 +851,7 @@ void msg_close(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -917,16 +860,15 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_close txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
-void msg_del(struct msg_params *mp)
+void msg_del(struct session *sess, const void *v)
 {
-	const struct cld_msg_del *msg = mp->msg;
+	const struct cld_msg_del *del = v;
 	enum cle_err_codes resp_rc = CLE_OK;
 	int rc, name_len;
-	const char *name;
 	struct pathname_info pinfo;
 	struct raw_inode *parent = NULL, *ino = NULL;
 	void *parent_data = NULL;
@@ -939,23 +881,13 @@ void msg_del(struct msg_params *mp)
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	name_len = le16_to_cpu(msg->name_len);
-
-	if (mp->msg_len < (sizeof(*msg) + name_len))
-		return;
-
-	name = mp->msg + sizeof(*msg);
-
-	if (!valid_inode_name(name, name_len) || (name_len < 2)) {
+	name_len = strlen(del->inode_name);
+	if (!valid_inode_name(del->inode_name, name_len) || (name_len < 2)) {
 		resp_rc = CLE_NAME_INVAL;
 		goto err_out_noabort;
 	}
 
-	pathname_parse(name, name_len, &pinfo);
+	pathname_parse(del->inode_name, name_len, &pinfo);
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -981,7 +913,8 @@ void msg_del(struct msg_params *mp)
 	}
 
 	/* read inode to be deleted */
-	rc = cldb_inode_get_byname(txn, name, name_len, &ino, false, 0);
+	rc = cldb_inode_get_byname(txn, del->inode_name, name_len,
+				   &ino, false, 0);
 	if (rc) {
 		if (rc == DB_NOTFOUND)
 			resp_rc = CLE_NAME_INVAL;
@@ -1090,7 +1023,7 @@ void msg_del(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(mp->sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(ino);
 	free(parent);
 	free(parent_data);
@@ -1101,31 +1034,23 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_del txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(ino);
 	free(parent);
 	free(parent_data);
 }
 
-void msg_unlock(struct msg_params *mp)
+void msg_unlock(struct session *sess, const void *v)
 {
-	const struct cld_msg_unlock *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_unlock *unlock = v;
 	struct raw_handle *h = NULL;
 	cldino_t inum;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
 	uint32_t omode;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -1134,7 +1059,7 @@ void msg_unlock(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, unlock->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -1149,7 +1074,7 @@ void msg_unlock(struct msg_params *mp)
 	}
 
 	/* attempt to given lock on filehandle */
-	rc = cldb_lock_del(txn, sess->sid, fh, inum);
+	rc = cldb_lock_del(txn, sess->sid, unlock->fh, inum);
 	if (rc) {
 		resp_rc = CLE_LOCK_INVAL;
 		goto err_out;
@@ -1162,7 +1087,7 @@ void msg_unlock(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -1171,30 +1096,22 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_unlock txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
-void msg_lock(struct msg_params *mp, bool wait)
+void msg_lock(struct session *sess, const void *v)
 {
-	const struct cld_msg_lock *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_lock *lock = v;
+	bool wait = (sess->msg_op == CMO_LOCK);
 	struct raw_handle *h = NULL;
 	cldino_t inum;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
-	uint32_t lock_flags, omode;
+	uint32_t omode;
 	bool acquired = false;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
-	struct session *sess = mp->sess;
-
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-	lock_flags = le32_to_cpu(msg->flags);
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -1204,7 +1121,7 @@ void msg_lock(struct msg_params *mp, boo
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, lock->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -1219,8 +1136,8 @@ void msg_lock(struct msg_params *mp, boo
 	}
 
 	/* attempt to add lock */
-	rc = cldb_lock_add(txn, sess->sid, fh, inum,
-			   lock_flags & CLF_SHARED, wait, &acquired);
+	rc = cldb_lock_add(txn, sess->sid, lock->fh, inum,
+			   lock->flags & CLF_SHARED, wait, &acquired);
 	if (rc) {
 		if (rc == DB_KEYEXIST)
 			resp_rc = CLE_LOCK_CONFLICT;
@@ -1243,7 +1160,7 @@ void msg_lock(struct msg_params *mp, boo
 	}
 
 	/* lock was acquired immediately */
-	resp_ok(mp->sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -1252,7 +1169,7 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_lock txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
Binary files cld/server/msg.o and cld.rpcgen/server/msg.o differ
Binary files cld/server/pkt.o and cld.rpcgen/server/pkt.o differ
diff -X /garz/tmp/dontdiff -urNp cld/server/server.c cld.rpcgen/server/server.c
--- cld/server/server.c	2010-02-03 01:15:26.000000000 -0500
+++ cld.rpcgen/server/server.c	2010-02-03 01:23:24.000000000 -0500
@@ -37,6 +37,7 @@
 #include <openssl/hmac.h>
 #include <cld-private.h>
 #include "cld.h"
+#include <cld_pkt.h>
 
 #define PROGRAM_NAME "cld"
 
@@ -125,8 +126,6 @@ int udp_tx(int sock_fd, struct sockaddr 
 {
 	ssize_t src;
 
-	HAIL_DEBUG(&srv_log, "%s, fd %d", __func__, sock_fd);
-
 	src = sendto(sock_fd, data, data_len, 0, addr, addr_len);
 	if (src < 0 && errno != EAGAIN)
 		HAIL_ERR(&srv_log, "%s sendto (fd %d, data_len %u): %s",
@@ -139,308 +138,415 @@ int udp_tx(int sock_fd, struct sockaddr 
 	return 0;
 }
 
-void resp_copy(struct cld_msg_resp *resp, const struct cld_msg_hdr *src)
-{
-	memcpy(&resp->hdr, src, sizeof(*src));
-	resp->code = 0;
-	resp->rsv = 0;
-	resp->xid_in = src->xid;
+static int udp_rx_handle(struct session *sess,
+		void (*msg_handler)(struct session *sess, const void *),
+		xdrproc_t xdrproc, void *xdrdata)
+{
+	XDR xin;
+	xdrmem_create(&xin, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdrproc(&xin, xdrdata)) {
+		HAIL_DEBUG(&srv_log, "%s: couldn't parse message of type %s",
+			   __func__, __cld_opstr(sess->msg_op));
+		xdr_destroy(&xin);
+		return CLE_BAD_PKT;
+	}
+	msg_handler(sess, xdrdata);
+	xdr_free(xdrproc, xdrdata);
+	xdr_destroy(&xin);
+	return 0;
 }
 
-void resp_err(struct session *sess,
-	      const struct cld_msg_hdr *src, enum cle_err_codes errcode)
+/** Recieve a UDP packet
+ *
+ * @param sock_fd	The UDP socket we received the packet on
+ * @param cli		Client address data
+ * @param info		Packet information
+ * @param raw_pkt	The raw packet buffer
+ * @param raw_len	Length of the raw packet buffer
+ *
+ * @return		An error code if we should send an error message
+ *			response. CLE_OK if we are done.
+ */
+static enum cle_err_codes udp_rx(int sock_fd,
+	const struct client *cli, struct pkt_info *info,
+	const char *raw_pkt, size_t raw_len)
 {
-	struct cld_msg_resp resp;
+	struct cld_pkt_hdr *pkt = info->pkt;
+	struct session *sess = info->sess;
 
-	resp_copy(&resp, src);
-	__cld_rand64(&resp.hdr.xid);
-	resp.code = cpu_to_le32(errcode);
+	if (sess) {
+		size_t msg_len;
 
-	if (sess->sock_fd <= 0) {
-		HAIL_ERR(&srv_log, "Nul sock in response");
-		return;
-	}
+		/* advance sequence id's and update last-contact timestamp */
+		sess->last_contact = current_time.tv_sec;
+		sess->sock_fd = sock_fd;
 
-	sess_sendmsg(sess, &resp, sizeof(resp), NULL, NULL);
-}
+		if (info->type != CMO_ACK) {
+			/* received message - update session */
+			sess->next_seqid_in++;
+		}
 
-void resp_ok(struct session *sess, const struct cld_msg_hdr *src)
-{
-	resp_err(sess, src, CLE_OK);
-}
+		/* copy message fragment into reassembly buffer */
+		if (pkt->mi.order & CLD_PKT_IS_FIRST) {
+			sess->msg_op = info->type;
+			sess->msg_xid = info->xid;
+			sess->msg_buf_len = 0;
+		}
+		msg_len = raw_len - info->hdr_len - CLD_PKT_FTR_LEN;
+		if ((sess->msg_buf_len + msg_len) > CLD_MAX_MSG_SZ)
+			return CLE_BAD_PKT;
 
-const char *user_key(const char *user)
-{
-	/* TODO: better auth scheme.
-	 * for now, use simple username==password auth scheme
-	 */
-	if (!user || !*user ||
-	    (strnlen(user, 32) >= 32))
-		return NULL;
+		memcpy(sess->msg_buf + sess->msg_buf_len,
+			raw_pkt + info->hdr_len, msg_len);
+		sess->msg_buf_len += msg_len;
+	}
 
-	return user;	/* our secret key */
-}
+	if (!(pkt->mi.order & CLD_PKT_IS_LAST)) {
+		struct cld_msg_ack_frag ack;
+		ack.seqid = info->seqid;
 
-static void show_msg(const struct cld_msg_hdr *msg)
-{
-	switch (msg->op) {
-	case CMO_NOP:
-	case CMO_NEW_SESS:
-	case CMO_OPEN:
-	case CMO_GET_META:
+		/* transmit ack-partial-msg response (once, without retries) */
+		simple_sendmsg(sock_fd, cli, pkt->sid,
+			       pkt->user, 0xdeadbeef,
+			       (xdrproc_t)xdr_cld_msg_ack_frag, (void *)&ack,
+			       CMO_ACK_FRAG);
+		return CLE_OK;
+	}
+
+	/* Handle a complete message */
+	switch (info->type) {
 	case CMO_GET:
-	case CMO_PUT:
-	case CMO_CLOSE:
-	case CMO_DEL:
-	case CMO_LOCK:
-	case CMO_UNLOCK:
+		/* fall through */
+	case CMO_GET_META: {
+		struct cld_msg_get get = {0};
+		return udp_rx_handle(sess, msg_get,
+				     (xdrproc_t)xdr_cld_msg_get, &get);
+	}
+	case CMO_OPEN: {
+		struct cld_msg_open open = {0};
+		return udp_rx_handle(sess, msg_open,
+				     (xdrproc_t)xdr_cld_msg_open, &open);
+	}
+	case CMO_PUT: {
+		struct cld_msg_put put = {0};
+		return udp_rx_handle(sess, msg_put,
+				     (xdrproc_t)xdr_cld_msg_put, &put);
+	}
+	case CMO_CLOSE: {
+		struct cld_msg_close close = {0};
+		return udp_rx_handle(sess, msg_close,
+				     (xdrproc_t)xdr_cld_msg_close, &close);
+	}
+	case CMO_DEL: {
+		struct cld_msg_del del = {0};
+		return udp_rx_handle(sess, msg_del,
+				     (xdrproc_t)xdr_cld_msg_del, &del);
+	}
+	case CMO_UNLOCK: {
+		struct cld_msg_unlock unlock = {0};
+		return udp_rx_handle(sess, msg_unlock,
+				     (xdrproc_t)xdr_cld_msg_unlock, &unlock);
+	}
 	case CMO_TRYLOCK:
-	case CMO_ACK:
-	case CMO_END_SESS:
-	case CMO_PING:
-	case CMO_NOT_MASTER:
-	case CMO_EVENT:
-	case CMO_ACK_FRAG:
-		HAIL_DEBUG(&srv_log, "msg: op %s, xid %llu",
-			   __cld_opstr(msg->op),
-			   (unsigned long long) le64_to_cpu(msg->xid));
-		break;
+		/* fall through */
+	case CMO_LOCK: {
+		struct cld_msg_lock lock = {0};
+		return udp_rx_handle(sess, msg_lock,
+				     (xdrproc_t)xdr_cld_msg_lock, &lock);
 	}
-}
-
-static void udp_rx_msg(const struct client *cli, const struct cld_packet *pkt,
-		       const struct cld_msg_hdr *msg, struct msg_params *mp)
-{
-	struct session *sess = mp->sess;
-
-	if (srv_log.verbose)
-		show_msg(msg);
-
-	switch(msg->op) {
+	case CMO_ACK:
+		msg_ack(sess, info->seqid);
+		return 0;
 	case CMO_NOP:
-		resp_ok(sess, msg);
-		break;
-
-	case CMO_NEW_SESS:	msg_new_sess(mp, cli); break;
-	case CMO_END_SESS:	msg_end_sess(mp, cli); break;
-	case CMO_OPEN:		msg_open(mp); break;
-	case CMO_GET:		msg_get(mp, false); break;
-	case CMO_GET_META:	msg_get(mp, true); break;
-	case CMO_PUT:		msg_put(mp); break;
-	case CMO_CLOSE:		msg_close(mp); break;
-	case CMO_DEL:		msg_del(mp); break;
-	case CMO_UNLOCK:	msg_unlock(mp); break;
-	case CMO_LOCK:		msg_lock(mp, true); break;
-	case CMO_TRYLOCK:	msg_lock(mp, false); break;
-	case CMO_ACK:		msg_ack(mp); break;
-
+		sess_sendresp_generic(sess, CLE_OK);
+		return 0;
+	case CMO_NEW_SESS:
+		msg_new_sess(sock_fd, cli, info);
+		return 0;
+	case CMO_END_SESS:
+		msg_end_sess(sess, info->xid);
+		return 0;
 	default:
+		HAIL_DEBUG(&srv_log, "%s: unexpected packet of type %s",
+			   __func__, __cld_opstr(info->type));
 		/* do nothing */
-		break;
+		return 0;
 	}
 }
 
-static void pkt_ack_frag(int sock_fd,
-			 const struct client *cli,
-			 const struct cld_packet *pkt)
-{
-	size_t alloc_len;
-	struct cld_packet *outpkt;
-	struct cld_msg_ack_frag *ack_msg;
-	void *p;
-	const char *secret_key;
-
-	alloc_len = sizeof(*outpkt) + sizeof(*ack_msg) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	ack_msg = (struct cld_msg_ack_frag *) (outpkt + 1);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, pkt);
-
-	memcpy(ack_msg->hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	__cld_rand64(&ack_msg->hdr.xid);
-	ack_msg->hdr.op = CMO_ACK_FRAG;
-	ack_msg->seqid = pkt->seqid;
-
-	p = outpkt;
-	secret_key = user_key(outpkt->user);
-	__cld_authsign(&srv_log, secret_key, p, alloc_len - SHA_DIGEST_LENGTH,
-		       p + alloc_len - SHA_DIGEST_LENGTH);
-
-	HAIL_DEBUG(&srv_log, "%s: "
-		   "sid " SIDFMT ", op %s, seqid %llu",
-		   __func__,
-		   SIDARG(outpkt->sid), __cld_opstr(ack_msg->hdr.op),
-		   (unsigned long long) le64_to_cpu(outpkt->seqid));
-
-	/* transmit ack-partial-msg response (once, without retries) */
-	udp_tx(sock_fd, (struct sockaddr *) &cli->addr, cli->addr_len,
-	       outpkt, alloc_len);
-}
-
-static void udp_rx(int sock_fd,
-		   const struct client *cli,
-		   const void *raw_pkt, size_t pkt_len)
-{
-	const struct cld_packet *pkt = raw_pkt;
-	struct cld_packet *outpkt;
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-	struct session *sess = NULL;
-	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_resp *resp;
-	struct msg_params mp;
-	size_t alloc_len;
-	uint32_t pkt_flags;
-	bool first_frag, last_frag, have_new_sess, have_ack, have_put;
-	const char *secret_key;
-	int auth_rc;
-	void *p;
-
-	secret_key = user_key(pkt->user);
-
-	/* verify pkt data integrity and credentials via HMAC signature */
-	auth_rc = __cld_authcheck(&srv_log, secret_key, raw_pkt,
-				  pkt_len - SHA_DIGEST_LENGTH,
-				  raw_pkt + pkt_len - SHA_DIGEST_LENGTH);
-	if (auth_rc) {
-		HAIL_DEBUG(&srv_log, "auth failed, code %d", auth_rc);
-		resp_rc = CLE_SIG_INVAL;
-		goto err_out;
-	}
-
-	pkt_flags = le32_to_cpu(pkt->flags);
-	first_frag = pkt_flags & CPF_FIRST;
-	last_frag = pkt_flags & CPF_LAST;
-	have_new_sess = first_frag && (msg->op == CMO_NEW_SESS);
-	have_ack = first_frag && (msg->op == CMO_ACK);
-	have_put = first_frag && (msg->op == CMO_PUT);
-
-	/* look up client session, verify it matches IP and username */
-	sess = g_hash_table_lookup(cld_srv.sessions, pkt->sid);
-	if (sess &&
-	    ((sess->addr_len != cli->addr_len) ||
-	     memcmp(&sess->addr, &cli->addr, sess->addr_len) ||
-	     strncmp(pkt->user, sess->user, CLD_MAX_USERNAME) ||
-	     sess->dead)) {
-		resp_rc = CLE_SESS_INVAL;
-		goto err_out;
-	}
+/** Parse a packet's header. Verify that the magic number is correct.
+ *
+ * @param raw_pkt	Pointer to the packet data
+ * @param raw_len	Length of the raw data
+ * @param pkt		(out param) the packet header
+ * @param hdr_len	(out param) the length of the packet header
+ *
+ * @return		true on success; false if this packet is garbage
+ */
+static bool parse_pkt_header(const char *raw_pkt, int raw_len,
+			struct cld_pkt_hdr *pkt, ssize_t *hdr_len)
+{
+	XDR xin;
+	static const char * const magic = CLD_PKT_MAGIC;
 
-	mp.sock_fd = sock_fd;
-	mp.cli = cli;
-	mp.sess = sess;
-	mp.pkt = pkt;
-	mp.msg = msg;
-	mp.msg_len = pkt_len - sizeof(*pkt) - SHA_DIGEST_LENGTH;
-
-	HAIL_DEBUG(&srv_log, "%s pkt: len %zu, seqid %llu, sid " SIDFMT ", "
-		   "flags %s%s, user %s",
-		   __func__,
-		   pkt_len, (unsigned long long) le64_to_cpu(pkt->seqid),
-		   SIDARG(pkt->sid),
-		   first_frag ? "F" : "", last_frag ? "L" : "",
-		   pkt->user);
-
-	/* advance sequence id's and update last-contact timestamp */
-	if (!have_new_sess) {
-		if (!sess) {
-			resp_rc = CLE_SESS_INVAL;
-			goto err_out;
+	if (raw_len <= CLD_PKT_FTR_LEN) {
+		HAIL_DEBUG(&srv_log, "%s: packet is too short: only "
+			   "%d bytes", __func__, raw_len);
+		return false;
+	}
+	xdrmem_create(&xin, (void *)raw_pkt, raw_len - CLD_PKT_FTR_LEN,
+		      XDR_DECODE);
+	memset(pkt, 0, sizeof(*pkt));
+	if (!xdr_cld_pkt_hdr(&xin, pkt)) {
+		HAIL_DEBUG(&srv_log, "%s: couldn't parse packet header",
+			   __func__);
+		xdr_destroy(&xin);
+		return false;
+	}
+	*hdr_len = xdr_getpos(&xin);
+	xdr_destroy(&xin);
+
+	if (memcmp((void *)&pkt->magic, magic, sizeof(pkt->magic))) {
+		HAIL_DEBUG(&srv_log, "%s: bad magic number", __func__);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)pkt);
+		return false;
+	}
+
+	return true;
+}
+
+/** Look up some information about a packet, including its session and the
+ * type of message it carries.
+ *
+ * @param pkt		The packet's header
+ * @param raw_pkt	Pointer to the raw packet data
+ * @param raw_len	Length of the raw packet data
+ * @param info		(out param) Information about the packet
+ *
+ * @return		true on success; false if this packet is garbage
+ */
+static bool get_pkt_info(struct cld_pkt_hdr *pkt,
+			 const char *raw_pkt, size_t raw_len,
+			 size_t hdr_len, struct pkt_info *info)
+{
+	struct cld_pkt_ftr *foot;
+	struct session *s;
+
+	memset(info, 0, sizeof(info));
+	info->pkt = pkt;
+	info->sess = s = g_hash_table_lookup(cld_srv.sessions, &pkt->sid);
+	foot = (struct cld_pkt_ftr *)
+			(raw_pkt + (raw_len - CLD_PKT_FTR_LEN));
+	info->seqid = le64_to_cpu(foot->seqid);
+
+	if (pkt->mi.order & CLD_PKT_IS_FIRST) {
+		info->xid = pkt->mi.cld_pkt_msg_info_u.mi.xid;
+		info->type = pkt->mi.cld_pkt_msg_info_u.mi.type;
+	} else {
+		if (!s) {
+			HAIL_DEBUG(&srv_log, "%s: packet is not first, "
+				"but also not part of an existing session. "
+				"Protocol error.", __func__);
+			return false;
 		}
+		info->xid = s->msg_xid;
+		info->type = s->msg_op;
+	}
+	info->hdr_len = hdr_len;
+	return true;
+}
 
-		sess->last_contact = current_time.tv_sec;
-		sess->sock_fd = sock_fd;
-
-		if (!have_ack) {
-			/* eliminate duplicates; do not return any response */
-			if (le64_to_cpu(pkt->seqid) != sess->next_seqid_in) {
-				HAIL_DEBUG(&srv_log, "%s: dropping dup", __func__);
-				return;
-			}
+/** Verify that the client session matches IP and username
+ *
+ * @param info		Packet information
+ * @param cli		Client address data
+ *
+ * @return		0 on success; error code otherwise
+ */
+static enum cle_err_codes validate_pkt_session(const struct pkt_info *info,
+						const struct client *cli)
+{
+	struct session *sess = info->sess;
 
-			/* received message - update session */
-			sess->next_seqid_in++;
+	if (!sess) {
+		/* Packets that don't belong to a session must be new-session
+		 * packets attempting to establish a session. */
+		if (info->type != CMO_NEW_SESS) {
+			HAIL_DEBUG(&srv_log, "%s: packet doesn't belong to a "
+				   "session,but has type %d",
+				   __func__, info->type);
+			return CLE_SESS_INVAL;
 		}
-	} else {
-		if (sess) {
-			/* eliminate duplicates; do not return any response */
-			if (le64_to_cpu(pkt->seqid) != sess->next_seqid_in) {
-				HAIL_DEBUG(&srv_log, "%s: dropping dup", __func__);
-				return;
-			}
+		return 0;
+	}
 
-			resp_rc = CLE_SESS_EXISTS;
-			goto err_out;
-		}
+	if (info->type == CMO_NEW_SESS) {
+		HAIL_DEBUG(&srv_log, "%s: Tried to create a new session, "
+			   "but a session with that ID already exists.",
+			   __func__);
+		return CLE_SESS_EXISTS;
 	}
 
-	/* copy message fragment into reassembly buffer */
-	if (sess) {
-		if (first_frag)
-			sess->msg_buf_len = 0;
+	/* verify that client session matches IP */
+	if ((sess->addr_len != cli->addr_len) ||
+	     memcmp(&sess->addr, &cli->addr, sess->addr_len)) {
+		HAIL_DEBUG(&srv_log, "%s: sess->addr doesn't match packet "
+			   "addr", __func__);
+		return CLE_SESS_INVAL;
+	}
 
-		if ((sess->msg_buf_len + mp.msg_len) > CLD_MAX_MSG_SZ) {
-			resp_rc = CLE_BAD_PKT;
-			goto err_out;
-		}
+	/* verify that client session matches username */
+	if (strncmp(info->pkt->user, sess->user, CLD_MAX_USERNAME)) {
+		HAIL_DEBUG(&srv_log, "%s: session doesn't match packet's  "
+			   "username", __func__);
+		return CLE_SESS_INVAL;
+	}
 
-		memcpy(&sess->msg_buf[sess->msg_buf_len], msg, mp.msg_len);
-		sess->msg_buf_len += mp.msg_len;
+	if (sess->dead) {
+		HAIL_DEBUG(&srv_log, "%s: packet session is dead",
+			   __func__);
+		return CLE_SESS_INVAL;
+	}
 
-		if (!last_frag) {
-			pkt_ack_frag(sock_fd, cli, pkt);
-			return;
-		}
+	return 0;
+}
 
-		mp.msg = msg = (struct cld_msg_hdr *) sess->msg_buf;
-		mp.msg_len = sess->msg_buf_len;
+/** Check a packet's cryptographic signature
+ *
+ * @param raw_pkt	Pointer to the packet data
+ * @param raw_len	Length of the raw data
+ * @param pkt		the packet header
+ *
+ * @return		0 on success; error code otherwise
+ */
+static enum cle_err_codes validate_pkt_signature(const char *raw_pkt,
+		int raw_len, const struct cld_pkt_hdr *pkt)
+{
+	int ret;
+	struct cld_pkt_ftr *foot;
 
-		if ((srv_log.verbose > 1) && !first_frag)
-			HAIL_DEBUG(&srv_log, "    final message size %u",
-				   sess->msg_buf_len);
+	foot = (struct cld_pkt_ftr *)
+			(raw_pkt + (raw_len - CLD_PKT_FTR_LEN));
+	ret = __cld_authcheck(&srv_log, pkt->user,
+			raw_pkt, raw_len - SHA_DIGEST_LENGTH,
+			foot->sha);
+	if (ret) {
+		HAIL_DEBUG(&srv_log, "%s: bad HMAC signature on packet "
+			   "ret = %d", __func__, ret);
+		return CLE_SIG_INVAL;
 	}
 
-	if (last_frag)
-		udp_rx_msg(cli, pkt, msg, &mp);
-	return;
+	return 0;
+}
 
-err_out:
-	/* transmit error response (once, without retries) */
-	alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	resp = (struct cld_msg_resp *) (outpkt + 1);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, pkt);
-
-	resp_copy(resp, msg);
-	resp->code = cpu_to_le32(resp_rc);
-
-	p = outpkt;
-	secret_key = user_key(outpkt->user);
-	__cld_authsign(&srv_log, secret_key, p, alloc_len - SHA_DIGEST_LENGTH,
-		       p + alloc_len - SHA_DIGEST_LENGTH);
-
-	HAIL_DEBUG(&srv_log, "%s err: "
-		   "sid " SIDFMT ", op %s, seqid %llu, code %d",
-		   __func__,
-		   SIDARG(outpkt->sid), __cld_opstr(resp->hdr.op),
-		   (unsigned long long) le64_to_cpu(outpkt->seqid),
-		   resp_rc);
+/** Check if this packet is a duplicate
+ *
+ * @param info		Packet info
+ *
+ * @return		true only if the packet is a duplicate
+ */
+static bool packet_is_dupe(const struct pkt_info *info)
+{
+	if (!info->sess)
+		return false;
+	if (info->type == CMO_ACK)
+		return false;
+
+	/* Check sequence IDs to discover if this packet is a dupe */
+	if (info->seqid != info->sess->next_seqid_in) {
+		HAIL_DEBUG(&srv_log, "dropping dup with seqid %llu "
+			   "(expected %llu).",
+			   (unsigned long long) info->seqid,
+			   (unsigned long long) info->sess->next_seqid_in);
+		return true;
+	}
+
+	return false;
+}
+
+void simple_sendmsg(int fd, const struct client *cli,
+	uint64_t sid, const char *user, uint64_t seqid,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op type)
+{
+	XDR xhdr, xmsg;
+	struct cld_pkt_hdr pkt;
+	struct cld_pkt_msg_infos *infos;
+	struct cld_pkt_ftr *foot;
+	char *buf;
+	size_t msg_len, hdr_len, buf_len;
+
+	/* Set up the packet header */
+	memset(&pkt, 0, sizeof(cld_pkt_hdr));
+	memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+	pkt.sid = sid;
+	pkt.user = (char *)user;
+	pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+	infos = &pkt.mi.cld_pkt_msg_info_u.mi;
+	__cld_rand64(&infos->xid);
+	infos->type = type;
+
+	/* Determine sizes */
+	msg_len = xdr_sizeof(xdrproc, (void *)xdrdata);
+	if (msg_len > CLD_MAX_MSG_SZ) {
+		HAIL_ERR(&srv_log, "%s: tried to put %d message bytes in a "
+			 "single packet. Maximum message bytes per packet "
+			 "is %d", __func__, msg_len, CLD_MAX_PKT_MSG_SZ);
+		return;
+	}
+	hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, &pkt);
+	buf_len = msg_len + hdr_len + CLD_PKT_FTR_LEN;
+	buf = alloca(buf_len);
+
+	/* Serialize data */
+	xdrmem_create(&xhdr, buf, hdr_len, XDR_ENCODE);
+	if (!xdr_cld_pkt_hdr(&xhdr, &pkt)) {
+		xdr_destroy(&xhdr);
+		HAIL_ERR(&srv_log, "%s: xdr_cld_pkt_hdr failed",
+			 __func__);
+		return;
+	}
+	xdr_destroy(&xhdr);
+	xdrmem_create(&xmsg, buf + hdr_len, msg_len, XDR_ENCODE);
+	if (!xdrproc(&xmsg, (void *)xdrdata)) {
+		xdr_destroy(&xmsg);
+		HAIL_ERR(&srv_log, "%s: xdrproc failed", __func__);
+		return;
+	}
+	xdr_destroy(&xmsg);
 
-	udp_tx(sock_fd, (struct sockaddr *) &cli->addr, cli->addr_len,
-	       outpkt, alloc_len);
+	foot = (struct cld_pkt_ftr *)
+		(buf + (buf_len - SHA_DIGEST_LENGTH));
+	foot->seqid = cpu_to_le64(seqid);
+	__cld_authsign(&srv_log, pkt.user, buf, buf_len - SHA_DIGEST_LENGTH,
+		foot->sha);
+	udp_tx(fd, (struct sockaddr *) &cli->addr, cli->addr_len,
+		buf, buf_len);
+}
+
+static void simple_sendresp(int sock_fd, const struct client *cli,
+		    const struct pkt_info *info, enum cle_err_codes code)
+{
+	const struct cld_pkt_hdr *pkt = info->pkt;
+	struct cld_msg_generic_resp resp;
+	resp.code = code;
+	resp.xid_in = info->xid;
+
+	simple_sendmsg(sock_fd, cli, pkt->sid, pkt->user, info->seqid,
+		       (xdrproc_t)xdr_cld_msg_generic_resp, (void *)&resp,
+		       info->type);
 }
 
 static bool udp_srv_event(int fd, short events, void *userdata)
 {
 	struct client cli;
 	char host[64];
-	ssize_t rrc;
+	ssize_t rrc, hdr_len;
 	struct msghdr hdr;
 	struct iovec iov[2];
-	uint8_t raw_pkt[CLD_RAW_MSG_SZ], ctl_msg[CLD_RAW_MSG_SZ];
-	struct cld_packet *pkt = (struct cld_packet *) raw_pkt;
+	char raw_pkt[CLD_RAW_MSG_SZ], ctl_msg[CLD_RAW_MSG_SZ];
+	struct cld_pkt_hdr pkt;
+	struct pkt_info info;
+	enum cle_err_codes err;
 
 	memset(&cli, 0, sizeof(cli));
 
@@ -470,46 +576,52 @@ static bool udp_srv_event(int fd, short 
 
 	HAIL_DEBUG(&srv_log, "client %s message (%d bytes)", host, (int) rrc);
 
-	/* if it is complete garbage, drop immediately */
-	if ((rrc < (sizeof(*pkt) + SHA_DIGEST_LENGTH)) ||
-	    (memcmp(pkt->magic, CLD_PKT_MAGIC, sizeof(pkt->magic)))) {
+	if (!parse_pkt_header(raw_pkt, rrc, &pkt, &hdr_len)) {
 		cld_srv.stats.garbage++;
-		return true; /* continue main loop; do NOT terminate server */
+		return true;
+	}
+
+	if (!get_pkt_info(&pkt, raw_pkt, rrc, hdr_len, &info)) {
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		cld_srv.stats.garbage++;
+		return true;
+	}
+
+	if (packet_is_dupe(&info)) {
+		/* silently drop dupes */
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
 	}
 
-	if (cld_srv.cldb.is_master && cld_srv.cldb.up)
-		udp_rx(fd, &cli, raw_pkt, rrc);
+	err = validate_pkt_session(&info, &cli);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
 
-	else {
-		struct cld_packet *outpkt;
-		struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-		struct cld_msg_resp *resp;
-		size_t alloc_len;
-		const char *secret_key;
-		void *p;
-
-		alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-		outpkt = alloca(alloc_len);
-		memset(outpkt, 0, alloc_len);
-
-		pkt_init_pkt(outpkt, pkt);
-
-		/* transmit not-master error msg */
-		resp = (struct cld_msg_resp *) (outpkt + 1);
-		resp_copy(resp, msg);
-		resp->hdr.op = CMO_NOT_MASTER;
-
-		p = outpkt;
-		secret_key = user_key(outpkt->user);
-		__cld_authsign(&srv_log, secret_key, p,
-			       alloc_len - SHA_DIGEST_LENGTH,
-			       p + alloc_len - SHA_DIGEST_LENGTH);
+	err = validate_pkt_signature(raw_pkt, rrc, &pkt);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
 
-		udp_tx(fd, (struct sockaddr *) &cli.addr, cli.addr_len,
-		       outpkt, alloc_len);
+	if (!(cld_srv.cldb.is_master && cld_srv.cldb.up)) {
+		simple_sendmsg(fd, &cli, pkt.sid, pkt.user, 0xdeadbeef,
+			       (xdrproc_t)xdr_void, NULL, CMO_NOT_MASTER);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
 	}
 
-	return true;	/* continue main loop; do NOT terminate server */
+	err = udp_rx(fd, &cli, &info, raw_pkt, rrc);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
+	xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+	return true;
 }
 
 static void add_chkpt_timer(void)
Binary files cld/server/server.o and cld.rpcgen/server/server.o differ
diff -X /garz/tmp/dontdiff -urNp cld/server/session.c cld.rpcgen/server/session.c
--- cld/server/session.c	2010-02-03 01:15:00.000000000 -0500
+++ cld.rpcgen/server/session.c	2010-02-03 01:23:28.000000000 -0500
@@ -29,15 +29,15 @@
 #include <openssl/sha.h>
 #include <cld-private.h>
 #include "cld.h"
+#include <cld_pkt.h>
 
 struct session_outpkt {
 	struct session		*sess;
 
-	struct cld_packet	*pkt;
+	char			*pkt_data;
 	size_t			pkt_len;
 
 	uint64_t		next_retry;
-	uint64_t		src_seqid;
 	unsigned int		refs;
 
 	void			(*done_cb)(struct session_outpkt *);
@@ -60,26 +60,6 @@ uint64_t next_seqid_le(uint64_t *seq)
 	return rc;
 }
 
-void pkt_init_pkt(struct cld_packet *dest, const struct cld_packet *src)
-{
-	memset(dest, 0, sizeof(*dest));
-	memcpy(dest->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	dest->seqid = cpu_to_le64(0xdeadbeef);
-	memcpy(dest->sid, src->sid, CLD_SID_SZ);
-	dest->flags = cpu_to_le32(CPF_FIRST | CPF_LAST);
-	strncpy(dest->user, src->user, CLD_MAX_USERNAME - 1);
-}
-
-static void pkt_init_sess(struct cld_packet *dest, struct session *sess)
-{
-	memset(dest, 0, sizeof(*dest));
-	memcpy(dest->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	dest->seqid = next_seqid_le(&sess->next_seqid_out);
-	memcpy(dest->sid, sess->sid, CLD_SID_SZ);
-	dest->flags = 0;
-	strncpy(dest->user, sess->user, CLD_MAX_USERNAME - 1);
-}
-
 guint sess_hash(gconstpointer v)
 {
 	const struct session *sess = v;
@@ -397,20 +377,6 @@ static void session_ping_done(struct ses
 	outpkt->sess->ping_open = false;
 }
 
-static void session_ping(struct session *sess)
-{
-	struct cld_msg_hdr resp;
-
-	memset(&resp, 0, sizeof(resp));
-	memcpy(resp.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	__cld_rand64(&resp.xid);
-	resp.op = CMO_PING;
-
-	sess->ping_open = true;
-
-	sess_sendmsg(sess, &resp, sizeof(resp), session_ping_done, NULL);
-}
-
 static void session_timeout(struct cld_timer *timer)
 {
 	struct session *sess = timer->userdata;
@@ -424,8 +390,12 @@ static void session_timeout(struct cld_t
 	if (!sess->dead && (sess_expire > now)) {
 		if (!sess->ping_open &&
 		    (sess_expire > (sess->last_contact + (CLD_SESS_TIMEOUT / 2) &&
-		    (sess->sock_fd > 0))))
-			session_ping(sess);
+		    (sess->sock_fd > 0)))) {
+			sess->ping_open = true;
+			sess_sendmsg(sess,
+				     (xdrproc_t)xdr_void, NULL, CMO_PING,
+				     session_ping_done, NULL);
+		}
 
 		cld_timer_add(&cld_srv.timers, &sess->timer,
 			      now + ((sess_expire - now) / 2) + 1);
@@ -519,8 +489,8 @@ static struct session_outpkt *op_alloc(s
 	if (!op)
 		return NULL;
 
-	op->pkt = calloc(1, pkt_len);
-	if (!op->pkt) {
+	op->pkt_data = calloc(1, pkt_len);
+	if (!op->pkt_data) {
 		free(op);
 		return NULL;
 	}
@@ -542,7 +512,7 @@ static void op_unref(struct session_outp
 			return;
 	}
 
-	free(op->pkt);
+	free(op->pkt_data);
 	free(op);
 }
 
@@ -556,18 +526,10 @@ static int sess_retry_output(struct sess
 
 	tmp = sess->out_q;
 	while (tmp) {
-		struct cld_packet *outpkt;
-		struct cld_msg_hdr *outmsg;
 		struct session_outpkt *op;
-		GList *tmp1;
-
-		tmp1 = tmp;
+		op = tmp->data;
 		tmp = tmp->next;
 
-		op = tmp1->data;
-		outpkt = op->pkt;
-		outmsg = (struct cld_msg_hdr *) (outpkt + 1);
-
 		if (!next_retry || (op->next_retry < next_retry))
 			*next_retry_out = next_retry = op->next_retry;
 
@@ -575,15 +537,15 @@ static int sess_retry_output(struct sess
 			continue;
 
 		if (srv_log.verbose) {
-			HAIL_DEBUG(&srv_log, "%s: retrying: sid " SIDFMT ", "
-				   "op %s, seqid %llu",
+			char scratch[PKT_HDR_TO_STR_SCRATCH_LEN];
+			HAIL_DEBUG(&srv_log, "%s: retrying %s",
 				   __func__,
-				   SIDARG(outpkt->sid), __cld_opstr(outmsg->op),
-				   (unsigned long long) le64_to_cpu(outpkt->seqid));
+				   __cld_pkt_hdr_to_str(scratch, op->pkt_data,
+				  			op->pkt_len));
 		}
 
 		rc = udp_tx(sess->sock_fd, (struct sockaddr *) &sess->addr,
-			    sess->addr_len, op->pkt, op->pkt_len);
+			    sess->addr_len, op->pkt_data, op->pkt_len);
 		if (rc)
 			break;
 
@@ -616,149 +578,155 @@ static void session_outq(struct session 
 	sess->out_q = g_list_concat(sess->out_q, new_pkts);
 }
 
-bool sess_sendmsg(struct session *sess, const void *msg_, size_t msglen,
-		  void (*done_cb)(struct session_outpkt *),
-		  void *done_data)
-{
-	struct cld_packet *outpkt;
-	unsigned int n_pkts, i;
-	size_t pkt_len, msg_left = msglen;
-	struct session_outpkt *pkts[CLD_MAX_PKT_MSG], *op;
-	GList *tmp_root = NULL;
-	const void *p;
-	bool first_frag = true;
-
-	n_pkts = (msglen / CLD_MAX_PKT_MSG_SZ);
-	n_pkts += (msglen % CLD_MAX_PKT_MSG_SZ) ? 1 : 0;
-
-	if (srv_log.verbose) {
-		const struct cld_msg_hdr *hdr = msg_;
-		const struct cld_msg_resp *rsp;
-
-		switch (hdr->op) {
-		/* This is the command set that gets to cldc_rx_generic */
-		case CMO_NOP:
-		case CMO_CLOSE:
-		case CMO_DEL:
-		case CMO_LOCK:
-		case CMO_UNLOCK:
-		case CMO_TRYLOCK:
-		case CMO_PUT:
-		case CMO_NEW_SESS:
-		case CMO_END_SESS:
-		case CMO_OPEN:
-		case CMO_GET_META:
-		case CMO_GET:
-			rsp = (struct cld_msg_resp *) msg_;
-			HAIL_DEBUG(&srv_log, "%s: "
-				   "sid " SIDFMT ", op %s, msglen %u, code %u, "
-				   "xid %llu, xid_in %llu",
-				   __func__,
-				   SIDARG(sess->sid),
-				   __cld_opstr(hdr->op),
-				   (unsigned int) msglen,
-				   le32_to_cpu(rsp->code),
-				   (unsigned long long) le64_to_cpu(hdr->xid),
-				   (unsigned long long) le64_to_cpu(rsp->xid_in));
-			break;
-		default:
-			HAIL_DEBUG(&srv_log, "%s: "
-				   "sid " SIDFMT ", op %s, msglen %u",
-				   __func__,
-				   SIDARG(sess->sid),
-				   __cld_opstr(hdr->op),
-				   (unsigned int) msglen);
-		}
-	}
-
-	if (n_pkts > CLD_MAX_PKT_MSG)
+bool sess_sendmsg(struct session *sess,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op type,
+	void (*done_cb)(struct session_outpkt *), void *done_data)
+{
+	XDR xmsg;
+	size_t msg_rem, msg_len, msg_chunk_len;
+	char *msg_bytes, *msg_cur;
+	GList *tmp_list, *new_pkts = NULL;
+	int first, last;
+
+	/* Use XDR to serialize the message */
+	msg_len = xdr_sizeof(xdrproc, (void *)xdrdata);
+	if (msg_len > CLD_MAX_MSG_SZ)
+		return false;
+	msg_bytes = alloca(msg_len);
+	xdrmem_create(&xmsg, msg_bytes, msg_len, XDR_ENCODE);
+	if (!xdrproc(&xmsg, (void *)xdrdata)) {
+		xdr_destroy(&xmsg);
+		HAIL_ERR(&srv_log, "%s: xdrproc failed", __func__);
 		return false;
-
-	/* pass 1: perform allocations */
-	for (i = 0; i < n_pkts; i++) {
-		pkts[i] = op = op_alloc(sizeof(*outpkt) +
-					CLD_MAX_PKT_MSG_SZ +
-					SHA_DIGEST_LENGTH);
-		if (!op)
-			goto err_out;
-
-		tmp_root = g_list_append(tmp_root, op);
 	}
+	xdr_destroy(&xmsg);
 
-	/* pass 2: fill packets */
-	p = msg_;
-	for (i = 0; i < n_pkts; i++) {
-		struct cld_msg_hdr *outmsg;
-		void *outmsg_mem;
-		size_t copy_len;
-		void *out_p;
-		const char *secret_key;
-
-		op = pkts[i];
-
-		op->sess = sess;
-
-		outpkt = op->pkt;
-		pkt_len = op->pkt_len;
-
-		outmsg_mem = (outpkt + 1);
-		outmsg = outmsg_mem;
-
-		/* init packet header */
-		pkt_init_sess(outpkt, sess);
-
-		if (first_frag) {
-			first_frag = false;
-			outpkt->flags |= cpu_to_le32(CPF_FIRST);
-		}
-
-		copy_len = MIN(pkt_len - sizeof(*outpkt) - SHA_DIGEST_LENGTH,
-			       msg_left);
-		memcpy(outmsg_mem, p, copy_len);
-
-		p += copy_len;
-		msg_left -= copy_len;
-
-		op->pkt_len =
-		pkt_len = sizeof(*outpkt) + copy_len + SHA_DIGEST_LENGTH;
-
-		if (!msg_left) {
-			op->done_cb = done_cb;
-			op->done_data = done_data;
+	/* Break the message into packets */
+	first = 1;
+	msg_rem = msg_len;
+	msg_cur = msg_bytes;
+	do {
+		XDR xout;
+		struct cld_pkt_hdr pkt;
+		size_t hdr_len;
+		struct session_outpkt *op;
 
-			outpkt->flags |= cpu_to_le32(CPF_LAST);
+		if (msg_rem <= CLD_MAX_PKT_MSG_SZ) {
+			msg_chunk_len = msg_rem;
+			last = 1;
+		} else {
+			msg_chunk_len = CLD_MAX_PKT_MSG_SZ;
+			last = 0;
+		}
+
+		/* Set up packet header */
+		memset(&pkt, 0, sizeof(pkt));
+		memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+		memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+		pkt.user = sess->user;
+		if (first) {
+			struct cld_pkt_msg_infos *infos =
+				&pkt.mi.cld_pkt_msg_info_u.mi;
+			if (last)
+				pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_FIRST;
+			__cld_rand64(&infos->xid);
+			infos->type = type;
+		} else {
+			if (last)
+				pkt.mi.order = CLD_PKT_ORD_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_MID;
+		}
+
+		/* Allocate space and initialize session_outpkt structure */
+		hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, (void *)&pkt);
+		op = op_alloc(hdr_len + msg_chunk_len + CLD_PKT_FTR_LEN);
+		if (!op) {
+			HAIL_DEBUG(&srv_log, "%s: op_alloc failed",
+				   __func__);
+			goto err_out;
 		}
-
+		op->sess = sess;
 		op->next_retry = current_time.tv_sec + CLD_RETRY_START;
+		op->done_cb = done_cb;
+		op->done_data = done_data;
+		xdrmem_create(&xout, op->pkt_data, hdr_len, XDR_ENCODE);
+		if (!xdr_cld_pkt_hdr(&xout, &pkt)) {
+			xdr_destroy(&xout);
+			HAIL_ERR(&srv_log, "%s: xdr_cld_pkt_hdr failed",
+				  __func__);
+			goto err_out;
+		}
+		xdr_destroy(&xout);
 
-		out_p = outpkt;
-		secret_key = user_key(outpkt->user);
-		if (__cld_authsign(&srv_log, secret_key, out_p,
-				   pkt_len - SHA_DIGEST_LENGTH,
-				   out_p + pkt_len - SHA_DIGEST_LENGTH))
-			goto err_out;	/* FIXME: we free all pkts -- wrong! */
+		/* Fill in data */
+		memcpy(op->pkt_data + hdr_len, msg_cur, msg_chunk_len);
+		msg_cur += msg_chunk_len;
+		msg_rem -= msg_chunk_len;
+		first = 0;
+
+		new_pkts = g_list_prepend(new_pkts, op);
+	} while (!last);
+
+	/* add sequence IDs and SHAs */
+	new_pkts = g_list_reverse(new_pkts);
+	for (tmp_list = g_list_first(new_pkts);
+	     tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op =
+			(struct session_outpkt *) tmp_list->data;
+		struct cld_pkt_ftr *foot = (struct cld_pkt_ftr *)
+			(op->pkt_data + (op->pkt_len - CLD_PKT_FTR_LEN));
+		int ret;
+
+		foot->seqid = next_seqid_le(&sess->next_seqid_out);
+		ret = __cld_authsign(&srv_log, sess->user,
+				op->pkt_data, op->pkt_len - SHA_DIGEST_LENGTH,
+				foot->sha);
+		if (ret)
+			goto err_out;
+	}
 
+	/* send packets */
+	for (tmp_list = g_list_first(new_pkts);
+	     tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op =
+			(struct session_outpkt *) tmp_list->data;
 		udp_tx(sess->sock_fd, (struct sockaddr *) &sess->addr,
-		       sess->addr_len, outpkt, pkt_len);
+			sess->addr_len, op->pkt_data, op->pkt_len);
 	}
 
-	session_outq(sess, tmp_root);
+	session_outq(sess, new_pkts);
 
 	return true;
 
 err_out:
-	for (i = 0; i < n_pkts; i++)
-		op_unref(pkts[i]);
-	g_list_free(tmp_root);
+	for (tmp_list = g_list_first(new_pkts); tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op;
+		op = (struct session_outpkt *)tmp_list->data;
+		op_unref(op);
+	}
+	g_list_free(new_pkts);
 	return false;
 }
 
-void msg_ack(struct msg_params *mp)
+void sess_sendresp_generic(struct session *sess, enum cle_err_codes code)
+{
+	struct cld_msg_generic_resp resp;
+	resp.code = code;
+	resp.xid_in = sess->msg_xid;
+
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+		     (void *)&resp, sess->msg_op, NULL, NULL);
+}
+
+void msg_ack(struct session *sess, uint64_t seqid)
 {
-	struct cld_packet *outpkt;
-	struct cld_msg_hdr *outmsg;
 	GList *tmp, *tmp1;
-	struct session *sess = mp->sess;
 	struct session_outpkt *op;
 
 	if (!sess->out_q)
@@ -767,19 +735,22 @@ void msg_ack(struct msg_params *mp)
 	/* look through output queue */
 	tmp = sess->out_q;
 	while (tmp) {
+		uint64_t op_seqid;
+		struct cld_pkt_ftr *foot;
 		tmp1 = tmp;
 		tmp = tmp->next;
 
 		op = tmp1->data;
-		outpkt = op->pkt;
-		outmsg = (struct cld_msg_hdr *) (outpkt + 1);
+		foot = (struct cld_pkt_ftr *)
+			(op->pkt_data + (op->pkt_len - CLD_PKT_FTR_LEN));
+		op_seqid = le64_to_cpu(foot->seqid);
 
 		/* if matching seqid found, we ack'd a message in out_q */
-		if (mp->pkt->seqid != outpkt->seqid)
+		if (seqid != op_seqid)
 			continue;
 
 		HAIL_DEBUG(&srv_log, "    expiring seqid %llu",
-			   (unsigned long long) le64_to_cpu(outpkt->seqid));
+			   (unsigned long long) op_seqid);
 
 		/* remove and delete the ack'd msg; call ack'd callback */
 		sess->out_q = g_list_delete_link(sess->out_q, tmp1);
@@ -793,19 +764,17 @@ void msg_ack(struct msg_params *mp)
 		cld_timer_del(&cld_srv.timers, &sess->retry_timer);
 }
 
-void msg_new_sess(struct msg_params *mp, const struct client *cli)
+void msg_new_sess(int sock_fd, const struct client *cli,
+		  const struct pkt_info *info)
 {
+	const struct cld_pkt_hdr *pkt = info->pkt;
 	DB *db = cld_srv.cldb.sessions;
 	struct raw_session raw_sess;
 	struct session *sess = NULL;
 	DBT key, val;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_resp *resp;
-	struct cld_packet *outpkt;
-	size_t alloc_len;
-	const char *secret_key;
-	void *p;
+	struct cld_msg_generic_resp resp;
 
 	sess = session_new();
 	if (!sess) {
@@ -814,17 +783,17 @@ void msg_new_sess(struct msg_params *mp,
 	}
 
 	/* build raw_session database record */
-	memcpy(sess->sid, mp->pkt->sid, sizeof(sess->sid));
+	memcpy(sess->sid, &pkt->sid, sizeof(sess->sid));
 	memcpy(&sess->addr, &cli->addr, sizeof(sess->addr));
 
-	strncpy(sess->user, mp->pkt->user, sizeof(sess->user));
-	sess->user[sizeof(sess->user) - 1] = 0;
+	snprintf(sess->user, sizeof(sess->user), "%s",
+		pkt->user);
 
-	sess->sock_fd = mp->sock_fd;
+	sess->sock_fd = sock_fd;
 	sess->addr_len = cli->addr_len;
 	strncpy(sess->ipaddr, cli->addr_host, sizeof(sess->ipaddr));
 	sess->last_contact = current_time.tv_sec;
-	sess->next_seqid_in = le64_to_cpu(mp->pkt->seqid) + 1;
+	sess->next_seqid_in = info->seqid + 1;
 
 	session_encode(&raw_sess, sess);
 
@@ -861,34 +830,26 @@ void msg_new_sess(struct msg_params *mp,
 	cld_timer_add(&cld_srv.timers, &sess->timer,
 		      time(NULL) + (CLD_SESS_TIMEOUT / 2));
 
-	resp_ok(sess, mp->msg);
+	/* send new-sess reply */
+	resp.code = CLE_OK;
+	resp.xid_in = info->xid;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+		     (void *)&resp, CMO_NEW_SESS, NULL, NULL);
+
 	return;
 
 err_out:
 	session_free(sess, true);
 
-	alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, mp->pkt);
-
-	resp = (struct cld_msg_resp *) (outpkt + 1);
-	resp_copy(resp, mp->msg);
-	resp->code = cpu_to_le32(resp_rc);
-
-	p = outpkt;
-	secret_key = user_key(outpkt->user);
-	__cld_authsign(&srv_log, secret_key, p, alloc_len - SHA_DIGEST_LENGTH,
-		       p + alloc_len - SHA_DIGEST_LENGTH);
-
-	HAIL_DEBUG(&srv_log, "%s err: sid " SIDFMT ", op %s, seqid %llu",
+	HAIL_DEBUG(&srv_log, "%s err: sid " SIDFMT ", op %s",
 		   __func__,
-		   SIDARG(outpkt->sid), __cld_opstr(resp->hdr.op),
-		   (unsigned long long) le64_to_cpu(outpkt->seqid));
+		   pkt->sid, __cld_opstr(CMO_NEW_SESS));
 
-	udp_tx(mp->sock_fd, (struct sockaddr *) &mp->cli->addr,
-	       mp->cli->addr_len, outpkt, alloc_len);
+	resp.code = resp_rc;
+	resp.xid_in = info->xid;
+	simple_sendmsg(sock_fd, cli, pkt->sid, pkt->user, 0xdeadbeef,
+		       (xdrproc_t)xdr_cld_msg_generic_resp, (void *)&resp,
+		       CMO_NEW_SESS);
 
 	HAIL_DEBUG(&srv_log, "NEW-SESS failed: %d", resp_rc);
 }
@@ -898,18 +859,18 @@ static void end_sess_done(struct session
 	session_trash(outpkt->sess);
 }
 
-void msg_end_sess(struct msg_params *mp, const struct client *cli)
+void msg_end_sess(struct session *sess, uint64_t xid)
 {
-	struct session *sess = mp->sess;
-	struct cld_msg_resp resp;
+	struct cld_msg_generic_resp resp;
 
 	/* do nothing; let message acknowledgement via
 	 * end_sess_done mark session dead
 	 */
-
-	memset(&resp, 0, sizeof(resp));
-	resp_copy(&resp, mp->msg);
-	sess_sendmsg(sess, &resp, sizeof(resp), end_sess_done, NULL);
+	resp.code = CLE_OK;
+	resp.xid_in = xid;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+			&resp, CMO_END_SESS,
+			end_sess_done, NULL);
 }
 
 /*
Binary files cld/server/session.o and cld.rpcgen/server/session.o differ
Binary files cld/server/util.o and cld.rpcgen/server/util.o differ
Binary files cld/test/it-works and cld.rpcgen/test/it-works differ
Binary files cld/test/it-works.o and cld.rpcgen/test/it-works.o differ
Binary files cld/test/libtest.a and cld.rpcgen/test/libtest.a differ
Binary files cld/test/load-file-event and cld.rpcgen/test/load-file-event differ
diff -X /garz/tmp/dontdiff -urNp cld/test/load-file-event.c cld.rpcgen/test/load-file-event.c
--- cld/test/load-file-event.c	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/test/load-file-event.c	2010-02-02 22:53:27.000000000 -0500
@@ -33,6 +33,8 @@
 #include <cldc.h>
 #include "test.h"
 
+#include "cld_pkt.h"
+
 struct run {
 	struct cldc_udp *udp;
 	struct cld_timer_list tlist;
@@ -146,6 +148,8 @@ static int read_1_cb(struct cldc_call_op
 {
 	struct run *rp = coptarg->private;
 	struct cldc_call_opts copts;
+	char *data;
+	size_t data_len;
 	int rc;
 
 	if (errc != CLE_OK) {
@@ -153,12 +157,14 @@ static int read_1_cb(struct cldc_call_op
 		exit(1);
 	}
 
-	if (coptarg->u.get.size != TESTLEN) {
-		fprintf(stderr, "Bad CLD file length %d\n", coptarg->u.get.size);
+	cldc_call_opts_get_data(coptarg, &data, &data_len);
+
+	if (data_len != TESTLEN) {
+		fprintf(stderr, "Bad CLD file length %d\n", data_len);
 		exit(1);
 	}
 
-	if (memcmp(coptarg->u.get.buf, TESTSTR, TESTLEN)) {
+	if (memcmp(data, TESTSTR, TESTLEN)) {
 		fprintf(stderr, "Bad CLD file content\n");
 		exit(1);
 	}
Binary files cld/test/load-file-event.o and cld.rpcgen/test/load-file-event.o differ
Binary files cld/test/lock-file-event and cld.rpcgen/test/lock-file-event differ
Binary files cld/test/lock-file-event.o and cld.rpcgen/test/lock-file-event.o differ
diff -X /garz/tmp/dontdiff -urNp cld/test/Makefile.am cld.rpcgen/test/Makefile.am
--- cld/test/Makefile.am	2010-02-02 22:31:09.000000000 -0500
+++ cld.rpcgen/test/Makefile.am	2010-02-02 22:30:24.000000000 -0500
@@ -1,5 +1,6 @@
 
 INCLUDES	= -I$(top_srcdir)/include	\
+		  -I$(top_srcdir)/lib		\
 		  @GLIB_CFLAGS@
 
 EXTRA_DIST =			\
Binary files cld/test/save-file-event and cld.rpcgen/test/save-file-event differ
Binary files cld/test/save-file-event.o and cld.rpcgen/test/save-file-event.o differ
Binary files cld/test/util.o and cld.rpcgen/test/util.o differ
Binary files cld/tools/cldcli and cld.rpcgen/tools/cldcli differ
diff -X /garz/tmp/dontdiff -urNp cld/tools/cldcli.c cld.rpcgen/tools/cldcli.c
--- cld/tools/cldcli.c	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/tools/cldcli.c	2010-01-22 18:15:07.000000000 -0500
@@ -217,6 +217,8 @@ static int cb_ls_2(struct cldc_call_opts
 	struct cldc_call_opts copts = { NULL, };
 	struct cld_dirent_cur dc;
 	int rc, i;
+	char *data;
+	size_t data_len;
 	bool first = true;
 
 	if (errc != CLE_OK) {
@@ -224,8 +226,9 @@ static int cb_ls_2(struct cldc_call_opts
 		write_from_thread(&cresp, sizeof(cresp));
 		return 0;
 	}
+	cldc_call_opts_get_data(copts_in, &data, &data_len);
 
-	rc = cldc_dirent_count(copts_in->u.get.buf, copts_in->u.get.size);
+	rc = cldc_dirent_count(data, data_len);
 	if (rc < 0) {
 		write_from_thread(&cresp, sizeof(cresp));
 		return 0;
@@ -236,7 +239,7 @@ static int cb_ls_2(struct cldc_call_opts
 
 	write_from_thread(&cresp, sizeof(cresp));
 
-	cldc_dirent_cur_init(&dc, copts_in->u.get.buf, copts_in->u.get.size);
+	cldc_dirent_cur_init(&dc, data, data_len);
 
 	for (i = 0; i < rc; i++) {
 		struct ls_rec lsr;
@@ -293,6 +296,8 @@ static int cb_cat_2(struct cldc_call_opt
 {
 	struct cresp cresp = { .tcode = TC_FAILED, };
 	struct cldc_call_opts copts = { NULL, };
+	char *data;
+	size_t data_len;
 
 	if (errc != CLE_OK) {
 		errc_msg(&cresp, errc);
@@ -300,11 +305,13 @@ static int cb_cat_2(struct cldc_call_opt
 		return 0;
 	}
 
+	cldc_call_opts_get_data(copts_in, &data, &data_len);
+
 	cresp.tcode = TC_OK;
-	cresp.u.file_len = copts_in->u.get.size;
+	cresp.u.file_len = data_len;
 
 	write_from_thread(&cresp, sizeof(cresp));
-	write_from_thread(copts_in->u.get.buf, copts_in->u.get.size);
+	write_from_thread(data, data_len);
 
 	/* FIXME: race; should wait until close succeeds/fails before
 	 * returning any data.  'fh' may still be in use, otherwise.
@@ -337,6 +344,8 @@ static int cb_cp_cf_2(struct cldc_call_o
 {
 	struct cresp cresp = { .tcode = TC_FAILED, };
 	struct cldc_call_opts copts = { NULL, };
+	char *data;
+	size_t data_len;
 
 	if (errc != CLE_OK) {
 		errc_msg(&cresp, errc);
@@ -344,11 +353,12 @@ static int cb_cp_cf_2(struct cldc_call_o
 		return 0;
 	}
 
+	cldc_call_opts_get_data(copts_in, &data, &data_len);
 	cresp.tcode = TC_OK;
-	cresp.u.file_len = copts_in->u.get.size;
+	cresp.u.file_len = data_len;
 
 	write_from_thread(&cresp, sizeof(cresp));
-	write_from_thread(copts_in->u.get.buf, copts_in->u.get.size);
+	write_from_thread(data, data_len);
 
 	/* FIXME: race; should wait until close succeeds/fails before
 	 * returning any data.  'fh' may still be in use, otherwise.
Binary files cld/tools/cldcli.o and cld.rpcgen/tools/cldcli.o differ
diff -X /garz/tmp/dontdiff -urNp cld/tools/Makefile.am cld.rpcgen/tools/Makefile.am
--- cld/tools/Makefile.am	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/tools/Makefile.am	2010-01-22 18:28:08.000000000 -0500
@@ -1,5 +1,6 @@
 
 INCLUDES	= -I$(top_srcdir)/include	\
+		  -I$(top_srcdir)/lib		\
 		  @GLIB_CFLAGS@
 
 bin_PROGRAMS	= cldcli

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

* Re: [PATCH] Re: [PATCHv2] cld: use XDR for all messages
  2010-02-03  6:35               ` [PATCH] " Jeff Garzik
@ 2010-02-03 21:45                 ` Colin McCabe
  2010-02-05  9:51                   ` Jeff Garzik
  0 siblings, 1 reply; 25+ messages in thread
From: Colin McCabe @ 2010-02-03 21:45 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Project Hail List, Pete Zaitcev

On Tue, Feb 2, 2010 at 10:35 PM, Jeff Garzik <jeff@garzik.org> wrote:
> On 01/29/2010 08:01 PM, Colin McCabe wrote:
>>
>> That seems reasonable. All of those functions could be looked at as
>> "common."
>>
>> The intention was to group together a bunch of functions that were
>> useful for packet formatting. Although the bulk of the formatting is
>> done by XDR, there are some things that XDR doesn't do, like
>> generating and checking SHA sums. cld_dump_buf and cld_pkt_hdr_to_str
>> are purely for debugging purposes, but they seemed like something that
>> would be generally useful for developers making packet format changes.
>> I know that they were useful to me.
>
> I finally pushed out several changes split off from your main XDR patch, to
> the upstream cld git repo.  It took longer than expected because I would
> make changes of my own along the way, which, each time, necessitated a
> rediff+rebuild between "current cld" working tree and "cld + xdr" working
> tree.
>
> The attached patch is what remains to be split up and committed.  I have
> definitely whittled it down, and along the way, moved and renamed some
> things on my own.  With regards to cld_fmt.*, my main objection was to
> creating too many to-be-installed headers.  One more header can be a pain
> for us and for users, while one more source file in cld/lib/ is no big deal.
>
> Thus, the intention is to eliminate cld_fmt.h (newly renamed to cld_pkt.h)
> altogether, while keeping the arrangement you created in cld_fmt.c (newly
> renamed to lib/pkt.c).

Yeah, after I looked at the v2 patch I realized it wasn't separated
out well enough.
So I was working on a v3 patch series that had the following changes:

1. kill CLD_MAX_PKT_MSG and add CLD_MAX_PAYLOAD_SZ
2. reformat Makefile.am files (to one file per line for nicer diffs)
3. move cld_msg macros into cld_common
4. some style issues
5. add cld_fmt.c (now called cld_pkt.c)
6. create cldc_call_opts_get_data and switch to using it in test/ and tool/
7. the big XDR changes.

It looks like 2, 3, 4, 5 have already come to pass (thanks Jeff!)

I just sent out #1 and another patch that is related to CLD_INODE_NAME_MAX
I think I'll send out a patch for #6 soon.

> I will continue whittling down the patch until it just contains the XDR
> changes themselves.

In tools/Makefile.am, I don't think you need $(top_srcdir)/lib any
more, since cld_msg_rpc.h is now an installed header.

Colin

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-26 23:47         ` Colin McCabe
  2010-01-29  7:01           ` Jeff Garzik
@ 2010-02-05  0:59           ` Jeff Garzik
  2010-02-05  4:14             ` Pete Zaitcev
  1 sibling, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-02-05  0:59 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev

[-- Attachment #1: Type: text/plain, Size: 123 bytes --]

Attached is the XDR working tree, following
[master 2a73620] libcldc: cosmetic changes, preparing for upcoming XDR merge



[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 120156 bytes --]

diff -X /garz/tmp/dontdiff -urNp cld/.gitignore cld.rpcgen/.gitignore
--- cld/.gitignore	2010-02-04 01:20:51.000000000 -0500
+++ cld.rpcgen/.gitignore	2010-01-22 18:15:07.000000000 -0500
@@ -32,5 +32,9 @@ cld-config.h*
 cscope.*
 ncscope.*
 
+# XDR files
+*_rpc.h
+*_rpc_xdr.c
+
 # ignore Doxygen output directory
 gendoc
diff -X /garz/tmp/dontdiff -urNp cld/include/cldc.h cld.rpcgen/include/cldc.h
--- cld/include/cldc.h	2010-02-04 19:18:46.000000000 -0500
+++ cld.rpcgen/include/cldc.h	2010-02-04 19:07:36.000000000 -0500
@@ -22,7 +22,7 @@
 #include <sys/types.h>
 #include <stdbool.h>
 #include <glib.h>
-#include <cld_msg.h>
+#include <cld_msg_rpc.h>
 #include <cld_common.h>
 #include <hail_log.h>
 
@@ -35,23 +35,17 @@ struct cldc_call_opts {
 	void		*private;
 
 	/* private; lib-owned */
-	union {
-		struct {
-			const char *buf;
-			unsigned int size;
-			char inode_name[CLD_INODE_NAME_MAX + 1];
-		} get;
-	} u;
 	struct cld_msg_get_resp resp;
 };
 
 struct cldc_pkt_info {
 	int		pkt_len;
+	int		hdr_len;
 	int		retries;
+	char		user[CLD_MAX_USERNAME];
 
 	/* must be at end of struct */
-	struct cld_packet pkt;
-	uint8_t		data[0];
+	char		data[0];
 };
 
 /** an outgoing message, from client to server */
@@ -70,18 +64,15 @@ struct cldc_msg {
 
 	time_t		expire_time;
 
-	int		data_len;
 	int		n_pkts;
 
-	uint8_t		*data;
-
 	/* must be at end of struct */
 	struct cldc_pkt_info *pkt_info[0];
 };
 
 /** an open file handle associated with a session */
 struct cldc_fh {
-	uint64_t	fh_le;			/* fh id, LE */
+	uint64_t	fh;
 	struct cldc_session *sess;
 	bool		valid;
 };
@@ -128,8 +119,11 @@ struct cldc_session {
 
 	bool		confirmed;
 
+	enum cld_msg_op msg_buf_op;
 	unsigned int	msg_buf_len;
 	char		msg_buf[CLD_MAX_MSG_SZ];
+	char		payload[CLD_MAX_PAYLOAD_SZ];
+	char		inode_name_temp[CLD_INODE_NAME_MAX];
 };
 
 /** Information for a single CLD server host */
@@ -209,7 +203,7 @@ extern void cldc_dirent_cur_init(struct 
 extern void cldc_dirent_cur_fini(struct cld_dirent_cur *dc);
 extern char *cldc_dirent_name(struct cld_dirent_cur *dc);
 extern void cldc_call_opts_get_data(const struct cldc_call_opts *copts,
-				    const char **data, size_t *data_len);
+				    char **data, size_t *data_len);
 
 /* cldc-udp */
 extern void cldc_udp_free(struct cldc_udp *udp);
diff -X /garz/tmp/dontdiff -urNp cld/include/cld_common.h cld.rpcgen/include/cld_common.h
--- cld/include/cld_common.h	2010-02-04 19:09:35.000000000 -0500
+++ cld.rpcgen/include/cld_common.h	2010-02-04 19:09:56.000000000 -0500
@@ -24,6 +24,7 @@
 #include <stdbool.h>
 #include <string.h>
 #include <time.h>
+#include <cld_msg_rpc.h>
 
 #define CLD_ALIGN8(n) ((8 - ((n) & 7)) & 7)
 
diff -X /garz/tmp/dontdiff -urNp cld/include/cld_msg.h cld.rpcgen/include/cld_msg.h
--- cld/include/cld_msg.h	2010-02-04 19:08:09.000000000 -0500
+++ cld.rpcgen/include/cld_msg.h	1969-12-31 19:00:00.000000000 -0500
@@ -1,245 +0,0 @@
-#ifndef __CLD_MSG_H__
-#define __CLD_MSG_H__
-
-/*
- * Copyright 2009 Red Hat, Inc.
- *
- * This program 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.
- *
- * This program 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 this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <stdint.h>
-
-#define CLD_PKT_MAGIC	"CLDc1pkt"
-#define CLD_MSG_MAGIC	"CLDc1msg"
-
-enum {
-	CLD_MAGIC_SZ		= 8,		/**< length of magic number */
-	CLD_SID_SZ		= 8,		/**< length of session id */
-
-	CLD_INODE_NAME_MAX	= 256,		/**< max total pathname len */
-
-	CLD_MAX_USERNAME	= 32,		/**< includes req. nul */
-	CLD_MAX_SECRET_KEY	= 128,		/**< includes req. nul */
-
-	CLD_MAX_PKT_MSG_SZ	= 1024,
-	CLD_MAX_PAYLOAD_SZ	= 131072,	/**< maximum size of data that users
-						  can GET or PUT */
-	CLD_MAX_MSG_SZ		= 196608,	/**< maximum total
-						msg size, including all packets */
-};
-
-/** available RPC operations */
-enum cld_msg_op {
-	/* client -> server */
-	CMO_NOP			= 0,		/**< no op */
-	CMO_NEW_SESS		= 1,		/**< new session */
-	CMO_OPEN		= 2,		/**< open file */
-	CMO_GET_META		= 3,		/**< get metadata */
-	CMO_GET			= 4,		/**< get metadata + data */
-	CMO_PUT			= 6,		/**< put data */
-	CMO_CLOSE		= 7,		/**< close file */
-	CMO_DEL			= 8,		/**< delete file */
-	CMO_LOCK		= 9,		/**< lock */
-	CMO_UNLOCK		= 10,		/**< unlock */
-	CMO_TRYLOCK		= 11,		/**< trylock */
-	CMO_ACK			= 12,		/**< ack of seqid rx'd */
-	CMO_END_SESS		= 13,		/**< end session */
-
-	/* server -> client */
-	CMO_PING		= 30,		/**< server to client ping */
-	CMO_NOT_MASTER		= 31,		/**< I am not the master! */
-	CMO_EVENT		= 32,		/**< server->cli async event */
-	CMO_ACK_FRAG		= 33,		/**< ack partial msg */
-};
-
-/** CLD error codes */
-enum cle_err_codes {
-	CLE_OK			= 0,		/**< success / no error */
-	CLE_SESS_EXISTS		= 1,		/**< session exists */
-	CLE_SESS_INVAL		= 2,		/**< session doesn't exist */
-	CLE_DB_ERR		= 3,		/**< db error */
-	CLE_BAD_PKT		= 4,		/**< invalid/corrupted packet */
-	CLE_INODE_INVAL		= 5,		/**< inode doesn't exist */
-	CLE_NAME_INVAL		= 6,		/**< inode name invalid */
-	CLE_OOM			= 7,		/**< server out of memory */
-	CLE_FH_INVAL		= 8,		/**< file handle invalid */
-	CLE_DATA_INVAL		= 9,		/**< invalid data pkt */
-	CLE_LOCK_INVAL		= 10,		/**< invalid lock */
-	CLE_LOCK_CONFLICT	= 11,		/**< conflicting lock held */
-	CLE_LOCK_PENDING	= 12,		/**< lock waiting to be acq. */
-	CLE_MODE_INVAL		= 13,		/**< op incompat. w/ file mode */
-	CLE_INODE_EXISTS	= 14,		/**< inode exists */
-	CLE_DIR_NOTEMPTY	= 15,		/**< dir not empty */
-	CLE_INTERNAL_ERR	= 16,		/**< nonspecific internal err */
-	CLE_TIMEOUT		= 17,		/**< session timed out */
-	CLE_SIG_INVAL		= 18,		/**< HMAC sig bad / auth failed */
-};
-
-/** availble OPEN mode flags */
-enum cld_open_modes {
-	COM_READ		= (1 << 0),	/**< read */
-	COM_WRITE		= (1 << 1),	/**< write */
-	COM_LOCK		= (1 << 2),	/**< lock */
-	COM_ACL			= (1 << 3),	/**< ACL update */
-	COM_CREATE		= (1 << 4),	/**< create file, if not exist */
-	COM_EXCL		= (1 << 5),	/**< fail create if file exists */
-	COM_DIRECTORY		= (1 << 6),	/**< operate on a directory */
-};
-
-/** potential events client may receive */
-enum cld_events {
-	CE_UPDATED		= (1 << 0),	/**< contents updated */
-	CE_DELETED		= (1 << 1),	/**< inode deleted */
-	CE_LOCKED		= (1 << 2),	/**< lock acquired */
-	CE_MASTER_FAILOVER	= (1 << 3),	/**< master failover */
-	CE_SESS_FAILED		= (1 << 4),
-};
-
-/** LOCK flags */
-enum cld_lock_flags {
-	CLF_SHARED		= (1 << 0),	/**< a shared (read) lock */
-};
-
-/** CLD packet flags */
-enum cld_packet_flags {
-	CPF_FIRST		= (1 << 0),	/**< first fragment */
-	CPF_LAST		= (1 << 1),	/**< last fragment */
-};
-
-/** header for each packet */
-struct cld_packet {
-	uint8_t		magic[CLD_MAGIC_SZ];	/**< magic number; constant */
-	uint64_t	seqid;			/**< sequence id */
-	uint8_t		sid[CLD_SID_SZ];	/**< client id */
-	uint32_t	flags;			/**< CPF_xxx flags */
-	uint8_t		res[4];
-	char		user[CLD_MAX_USERNAME];	/**< authenticated user */
-};
-
-/** header for each message */
-struct cld_msg_hdr {
-	uint8_t		magic[CLD_MAGIC_SZ];	/**< magic number; constant */
-	uint64_t	xid;			/**< opaque message id */
-	uint8_t		op;			/**< operation code */
-	uint8_t		res1[7];
-};
-
-/** standard response for each message */
-struct cld_msg_resp {
-	struct cld_msg_hdr	hdr;
-
-	uint32_t		code;		/**< error code, CLE_xxx */
-	uint32_t		rsv;		/**< reserved */
-	uint64_t		xid_in;		/**< C->S xid */
-};
-
-/** ACK-FRAG message */
-struct cld_msg_ack_frag {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		seqid;		/**< sequence id to ack */
-};
-
-/** OPEN message */
-struct cld_msg_open {
-	struct cld_msg_hdr	hdr;
-
-	uint32_t		mode;		/**< open mode, COM_xxx */
-	uint32_t		events;		/**< events mask, CE_xxx */
-	uint16_t		name_len;	/**< length of file name */
-	uint8_t			res[6];
-	/* inode name */
-};
-
-/** OPEN message response */
-struct cld_msg_open_resp {
-	struct cld_msg_resp	resp;
-
-	uint64_t		fh;		/**< handle opened */
-};
-
-/** GET message */
-struct cld_msg_get {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** GET message response */
-struct cld_msg_get_resp {
-	struct cld_msg_resp	resp;
-
-	uint64_t		inum;		/**< unique inode number */
-	uint32_t		ino_len;	/**< inode name len */
-	uint32_t		size;		/**< data size */
-	uint64_t		version;	/**< inode version */
-	uint64_t		time_create;	/**< creation time */
-	uint64_t		time_modify;	/**< last modification time */
-	uint32_t		flags;		/**< inode flags; CIFL_xxx */
-	uint8_t			res[4];
-	/* inode name */
-};
-
-/** PUT message */
-struct cld_msg_put {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		data_size;	/**< total size of data */
-	uint8_t			res[4];
-};
-
-/** CLOSE message */
-struct cld_msg_close {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** DEL message */
-struct cld_msg_del {
-	struct cld_msg_hdr	hdr;
-
-	uint16_t		name_len;	/**< length of file name */
-	uint8_t			res[6];
-	/* inode name */
-};
-
-/** UNLOCK message */
-struct cld_msg_unlock {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-};
-
-/** LOCK message */
-struct cld_msg_lock {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		flags;		/**< CLF_xxx */
-	uint8_t			res[4];
-};
-
-/** Server-to-client EVENT message */
-struct cld_msg_event {
-	struct cld_msg_hdr	hdr;
-
-	uint64_t		fh;		/**< open file handle */
-	uint32_t		events;		/**< CE_xxx */
-	uint8_t			res[4];
-};
-
-#endif /* __CLD_MSG_H__ */
diff -X /garz/tmp/dontdiff -urNp cld/include/cld_pkt.h cld.rpcgen/include/cld_pkt.h
--- cld/include/cld_pkt.h	1969-12-31 19:00:00.000000000 -0500
+++ cld.rpcgen/include/cld_pkt.h	2010-02-03 00:12:58.000000000 -0500
@@ -0,0 +1,56 @@
+#ifndef __CLD_PKT_H__
+#define __CLD_PKT_H__
+
+/*
+ * Copyright 2010, Colin McCabe
+ *
+ * This program 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.
+ *
+ * This program 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 this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <openssl/sha.h>
+#include <cld_msg_rpc.h>
+#include <stdbool.h>
+
+/* @file	cld_pkt.h
+ *
+ * This file has some definitions and helper functions pertaining to the CLD
+ * network protocol. Unlike cld_msg.x, it's not an XDR file.
+ */
+
+/* Returns a string representation of a packet header
+ *
+ * @param scratch		(out param) buffer of length
+ *				PKT_HDR_TO_STR_SCRATCH_LEN
+ * @param pkt_hdr		packet header
+ * @param pkt_len		length of packet
+ *
+ * @return			pointer to 'scratch'
+ */
+const char *__cld_pkt_hdr_to_str(char *scratch,
+			   const char *pkt_hdr, size_t pkt_len);
+
+void __cld_dump_buf(const void *buf, size_t len);
+
+/** Footer that appears at the end of each packet */
+struct __attribute__((packed)) cld_pkt_ftr {
+	uint64_t seqid;				/**< packet sequence ID */
+	char sha[SHA_DIGEST_LENGTH];		/**< packet signature */
+};
+
+/** Length of the packet footer. This size is fixed */
+#define CLD_PKT_FTR_LEN sizeof(struct cld_pkt_ftr)
+
+#define PKT_HDR_TO_STR_SCRATCH_LEN 128
+
+#endif
diff -X /garz/tmp/dontdiff -urNp cld/include/Makefile.am cld.rpcgen/include/Makefile.am
--- cld/include/Makefile.am	2010-02-04 01:20:51.000000000 -0500
+++ cld.rpcgen/include/Makefile.am	2010-02-02 22:51:08.000000000 -0500
@@ -1,5 +1,5 @@
 
 EXTRA_DIST = cld-private.h
 
-include_HEADERS = cldc.h hail_log.h cld_common.h cld_msg.h
+include_HEADERS = cldc.h hail_log.h cld_common.h cld_pkt.h
 
diff -X /garz/tmp/dontdiff -urNp cld/lib/cldc.c cld.rpcgen/lib/cldc.c
--- cld/lib/cldc.c	2010-02-04 19:46:11.000000000 -0500
+++ cld.rpcgen/lib/cldc.c	2010-02-04 19:35:13.000000000 -0500
@@ -35,6 +35,8 @@
 #include <glib.h>
 #include <cld-private.h>
 #include <cldc.h>
+#include <cld_pkt.h>
+#include <cld_msg_rpc.h>
 #include <syslog.h>
 
 enum {
@@ -47,12 +49,7 @@ enum {
 
 static const char *user_key(struct cldc_session *sess, const char *user);
 static int sess_send_pkt(struct cldc_session *sess,
-			 const struct cld_packet *pkt, size_t pkt_len);
-
-static const struct cld_msg_hdr def_msg_ack = {
-	.magic		= CLD_MSG_MAGIC,
-	.op		= CMO_ACK,
-};
+			const void *pkt, size_t pkt_len);
 
 #ifndef HAVE_STRNLEN
 static size_t strnlen(const char *s, size_t maxlen)
@@ -81,10 +78,10 @@ static size_t strnlen(const char *s, siz
 #endif
 
 void cldc_call_opts_get_data(const struct cldc_call_opts *copts,
-			     const char **data, size_t *data_len)
+			     char **data, size_t *data_len)
 {
-	*data = copts->u.get.buf;
-	*data_len = copts->u.get.size;
+	*data = copts->resp.data.data_val;
+	*data_len = copts->resp.data.data_len;
 }
 
 static void cldc_errlog(int prio, const char *fmt, ...)
@@ -100,49 +97,72 @@ static void cldc_errlog(int prio, const 
 
 static int ack_seqid(struct cldc_session *sess, uint64_t seqid_le)
 {
-	struct cld_packet *pkt;
-	struct cld_msg_hdr *resp;
-	size_t pkt_len;
+	XDR xdrs;
+	size_t hdr_len, total_len;
+	char buf[CLD_MAX_PKT_MSG_SZ];
+	struct cld_pkt_hdr pkt;
+	struct cld_pkt_ftr *foot;
 	int ret;
+	static const char * const magic = CLD_PKT_MAGIC;
 	const char *secret_key;
 
-	pkt_len = sizeof(*pkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	pkt = alloca(pkt_len);
-	memset(pkt, 0, pkt_len);
-
 	/* Construct ACK packet */
-	memcpy(pkt->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	pkt->seqid = seqid_le;
-	memcpy(pkt->sid, sess->sid, CLD_SID_SZ);
-	pkt->flags = cpu_to_le32(CPF_FIRST | CPF_LAST);
-	strncpy(pkt->user, sess->user, CLD_MAX_USERNAME - 1);
+	memset(&pkt, 0, sizeof(struct cld_pkt_hdr));
+	memcpy(&pkt.magic, magic, sizeof(pkt.magic));
+	memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+	pkt.user = sess->user;
+	pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+	pkt.mi.cld_pkt_msg_info_u.mi.xid = 0;
+	pkt.mi.cld_pkt_msg_info_u.mi.op = CMO_ACK;
+
+	/* Serialize packet */
+	xdrmem_create(&xdrs, (char *)buf,
+		      sizeof(buf) - CLD_PKT_FTR_LEN, XDR_ENCODE);
+	if (!xdr_cld_pkt_hdr(&xdrs, &pkt)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to encode header "
+			   "for ack_seqid %llu",
+			   __func__,
+			   (unsigned long long) seqid_le);
+		xdr_destroy(&xdrs);
+		return -1009;
+	}
 
-	resp = (struct cld_msg_hdr *) (pkt + 1);
-	memcpy(resp, &def_msg_ack, sizeof(*resp));
+	/* Fill in footer */
+	hdr_len = xdr_getpos(&xdrs);
+	total_len = hdr_len + CLD_PKT_FTR_LEN;
+	foot = (struct cld_pkt_ftr *)(buf + hdr_len);
+	foot->seqid = seqid_le;
+	xdr_destroy(&xdrs);
 
 	secret_key = user_key(sess, sess->user);
 	ret = __cld_authsign(&sess->log, secret_key,
-			     pkt, pkt_len - SHA_DIGEST_LENGTH,
-			     (uint8_t *)pkt + pkt_len - SHA_DIGEST_LENGTH);
+			     buf, total_len - SHA_DIGEST_LENGTH, foot->sha);
 	if (ret) {
 		HAIL_ERR(&sess->log, "%s: authsign failed: %d",
 			 __func__, ret);
 		return ret;
 	}
 
-	return sess_send_pkt(sess, pkt, pkt_len);
+	return sess_send_pkt(sess, buf, total_len);
 }
 
 static int rxmsg_generic(struct cldc_session *sess,
-			 const struct cld_packet *pkt,
-			 const void *msgbuf, size_t buflen)
+			 const struct cld_pkt_hdr *pkt,
+			 const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_resp *resp = msgbuf;
+	XDR xdrs;
+	struct cld_msg_generic_resp resp;
 	struct cldc_msg *req = NULL;
 	GList *tmp;
 
-	if (buflen < sizeof(*resp))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdr_cld_msg_generic_resp(&xdrs, &resp)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to decode "
+			   "cld_msg_generic_resp", __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
 	/* Find out which outbound message this was a response to */
 	tmp = sess->out_msg;
@@ -152,10 +172,10 @@ static int rxmsg_generic(struct cldc_ses
 		HAIL_DEBUG(&sess->log, "%s: comparing req->xid (%llu) "
 			   "with resp.xid_in (%llu)",
 			   __func__,
-			   (unsigned long long) le64_to_cpu(req->xid),
-			   (unsigned long long) le64_to_cpu(resp->xid_in));
+			   (unsigned long long) req->xid,
+			   (unsigned long long) resp.xid_in);
 
-		if (req->xid == resp->xid_in)
+		if (req->xid == resp.xid_in)
 			break;
 		tmp = tmp->next;
 	}
@@ -163,7 +183,7 @@ static int rxmsg_generic(struct cldc_ses
 		HAIL_DEBUG(&sess->log, "%s: no match found with "
 			   "xid_in %llu",
 			   __func__,
-			   (unsigned long long) le64_to_cpu(resp->xid_in));
+			   (unsigned long long) resp.xid_in);
 		return -1005;
 	}
 
@@ -176,28 +196,37 @@ static int rxmsg_generic(struct cldc_ses
 		req->done = true;
 
 		if (req->cb) {
-			ssize_t rc = req->cb(req, msgbuf, buflen, resp->code);
+			ssize_t rc = req->cb(req, sess->msg_buf,
+					     sess->msg_buf_len, resp.code);
 			if (rc < 0)
 				return rc;
 		}
 	}
 
-	return ack_seqid(sess, pkt->seqid);
+	return ack_seqid(sess, foot->seqid);
 }
 
 static int rxmsg_ack_frag(struct cldc_session *sess,
-			  const struct cld_packet *pkt,
-			  const void *msgbuf, size_t buflen)
+			  const struct cld_pkt_hdr *pkt,
+			  const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_ack_frag *ack_msg = msgbuf;
+	XDR xdrs;
+	struct cld_msg_ack_frag ack_msg;
 	GList *tmp;
 
-	if (buflen < sizeof(*ack_msg))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	memset(&ack_msg, 0, sizeof(ack_msg));
+	if (!xdr_cld_msg_ack_frag(&xdrs, &ack_msg)) {
+		HAIL_INFO(&sess->log, "%s: failed to decode ack_msg",
+			  __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
 	HAIL_INFO(&sess->log, "%s: seqid %llu, want to ack",
 		  __func__,
-		  (unsigned long long) ack_msg->seqid);
+		  (unsigned long long) ack_msg.seqid);
 
 	tmp = sess->out_msg;
 	while (tmp) {
@@ -209,18 +238,21 @@ static int rxmsg_ack_frag(struct cldc_se
 
 		for (i = 0; i < req->n_pkts; i++) {
 			struct cldc_pkt_info *pi;
+			struct cld_pkt_ftr *f;
 			uint64_t seqid;
 
 			pi = req->pkt_info[i];
 			if (!pi)
 				continue;
-			seqid = pi->pkt.seqid;
-			if (seqid != ack_msg->seqid)
+			f = (struct cld_pkt_ftr *)
+				pi->data + (pi->pkt_len - CLD_PKT_FTR_LEN);
+			seqid = le64_to_cpu(f->seqid);
+			if (seqid != ack_msg.seqid)
 				continue;
 
 			HAIL_DEBUG(&sess->log, "%s: seqid %llu, expiring",
 				   __func__,
-				   (unsigned long long) ack_msg->seqid);
+				   (unsigned long long) ack_msg.seqid);
 
 			req->pkt_info[i] = NULL;
 			free(pi);
@@ -231,19 +263,26 @@ static int rxmsg_ack_frag(struct cldc_se
 }
 
 static int rxmsg_event(struct cldc_session *sess,
-		       const struct cld_packet *pkt,
-		       const void *msgbuf, size_t buflen)
+		       const struct cld_pkt_hdr *pkt,
+		       const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_event *ev = msgbuf;
+	XDR xdrs;
+	struct cld_msg_event ev;
 	struct cldc_fh *fh = NULL;
 	int i;
 
-	if (buflen < sizeof(*ev))
+	xdrmem_create(&xdrs, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdr_cld_msg_event(&xdrs, &ev)) {
+		HAIL_INFO(&sess->log, "%s: failed to decode cld_msg_event",
+			  __func__);
+		xdr_destroy(&xdrs);
 		return -1008;
+	}
+	xdr_destroy(&xdrs);
 
 	for (i = 0; i < sess->fh->len; i++) {
 		fh = &g_array_index(sess->fh, struct cldc_fh, i);
-		if (fh->fh_le == ev->fh)
+		if (fh->fh == ev.fh)
 			break;
 		else
 			fh = NULL;
@@ -252,7 +291,7 @@ static int rxmsg_event(struct cldc_sessi
 	if (!fh)
 		return -1011;
 
-	sess->ops->event(sess->private, sess, fh, le32_to_cpu(ev->events));
+	sess->ops->event(sess->private, sess, fh, ev.events);
 
 	return 0;
 }
@@ -264,8 +303,6 @@ static void cldc_msg_free(struct cldc_ms
 	if (!msg)
 		return;
 
-	free(msg->data);
-
 	for (i = 0; i < msg->n_pkts; i++)
 		free(msg->pkt_info[i]);
 
@@ -304,51 +341,27 @@ static const char *user_key(struct cldc_
 	return sess->secret_key;
 }
 
-static int cldc_receive_msg(struct cldc_session *sess,
-			    const struct cld_packet *pkt,
-			    size_t pkt_len)
+static int rx_complete(struct cldc_session *sess,
+		const struct cld_pkt_hdr *pkt,
+		const struct cld_pkt_ftr *foot)
 {
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) sess->msg_buf;
-	size_t msglen = sess->msg_buf_len;
-
-	if (memcmp(msg->magic, CLD_MSG_MAGIC, sizeof(msg->magic))) {
-		HAIL_DEBUG(&sess->log, "%s: bad msg magic", __func__);
-		return -EPROTO;
-	}
-
-	switch(msg->op) {
+	switch (sess->msg_buf_op) {
 	case CMO_ACK:
 		HAIL_INFO(&sess->log, "%s: received unexpected ACK", __func__);
 		return -EBADRQC;
 	case CMO_PING:
 		/* send out an ACK */
-		return ack_seqid(sess, pkt->seqid);
-	case CMO_NOP:
-	case CMO_CLOSE:
-	case CMO_DEL:
-	case CMO_LOCK:
-	case CMO_UNLOCK:
-	case CMO_TRYLOCK:
-	case CMO_PUT:
-	case CMO_NEW_SESS:
-	case CMO_END_SESS:
-	case CMO_OPEN:
-	case CMO_GET_META:
-	case CMO_GET:
-		return rxmsg_generic(sess, pkt, msg, msglen);
+		return ack_seqid(sess, foot->seqid);
 	case CMO_NOT_MASTER:
 		HAIL_ERR(&sess->log, "FIXME: not-master message received");
 		return -1055;	/* FIXME */
 	case CMO_EVENT:
-		return rxmsg_event(sess, pkt, msg, msglen);
+		return rxmsg_event(sess, pkt, foot);
 	case CMO_ACK_FRAG:
-		return rxmsg_ack_frag(sess, pkt, msg, msglen);
+		return rxmsg_ack_frag(sess, pkt, foot);
 	default:
-		break;
+		return rxmsg_generic(sess, pkt, foot);
 	}
-
-	/* unknown op code */
-	return -EBADRQC;
 }
 
 /** Accepts a packet's sequence ID.
@@ -362,11 +375,8 @@ static int cldc_receive_msg(struct cldc_
  * @return		0 on success; error code otherwise
  */
 static int accept_seqid(struct cldc_session *sess, uint64_t seqid,
-			enum cld_msg_op op, bool *dupe)
+			enum cld_msg_op op)
 {
-
-	*dupe = false;
-
 	switch (op) {
 	case CMO_NEW_SESS:
 		/* CMO_NEW_SESS initializes the session's sequence id */
@@ -393,7 +403,6 @@ static int accept_seqid(struct cldc_sess
 		if (seqid_in_range(seqid,
 				   sess->next_seqid_in_tr,
 				   sess->next_seqid_in)) {
-			*dupe = true;
 			return 0;
 		}
 
@@ -405,81 +414,47 @@ int cldc_receive_pkt(struct cldc_session
 		     const void *net_addr, size_t net_addrlen,
 		     const void *pktbuf, size_t pkt_len)
 {
-	const struct cld_packet *pkt = pktbuf;
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
 	const char *secret_key;
-	size_t msglen;
 	struct timeval tv;
 	time_t current_time;
+	struct cld_pkt_hdr pkt;
+	unsigned int hdr_len, msg_len;
+	const struct cld_pkt_ftr *foot;
 	uint64_t seqid;
-	uint32_t pkt_flags;
-	bool first_frag, last_frag;
-	bool dupe = false;
+	XDR xdrs;
 	int ret;
-	enum cld_msg_op msg_buf_op = CMO_ACK_FRAG;
 
 	gettimeofday(&tv, NULL);
 	current_time = tv.tv_sec;
 
-	if (pkt_len < (sizeof(*pkt) + SHA_DIGEST_LENGTH)) {
-		HAIL_DEBUG(&sess->log, "%s: msg too short", __func__);
+	/* Decode the packet header */
+	if (pkt_len < CLD_PKT_FTR_LEN) {
+		HAIL_DEBUG(&sess->log, "%s: packet too short to have a "
+			   "well-formed footer", __func__);
 		return -EPROTO;
 	}
-
-	msglen = pkt_len - sizeof(*pkt) - SHA_DIGEST_LENGTH;
-
-	pkt_flags = le32_to_cpu(pkt->flags);
-	first_frag = pkt_flags & CPF_FIRST;
-	last_frag = pkt_flags & CPF_LAST;
-	if (first_frag)
-		msg_buf_op = msg->op;
-
-	if (sess->log.verbose) {
-		if (msg_buf_op == CMO_GET) {
-			struct cld_msg_get_resp *dp;
-			dp = (struct cld_msg_get_resp *) msg;
-			HAIL_DEBUG(&sess->log, "%s(len %u, op %s"
-				   ", seqid %llu, user %s, size %u)",
-				   __func__,
-				   (unsigned int) pkt_len,
-				   __cld_opstr(msg->op),
-				   (unsigned long long) le64_to_cpu(pkt->seqid),
-				   pkt->user,
-				   le32_to_cpu(dp->size));
-		} else if (msg_buf_op == CMO_NEW_SESS) {
-			struct cld_msg_resp *dp;
-			dp = (struct cld_msg_resp *) msg;
-			HAIL_DEBUG(&sess->log, "%s(len %u, op %s"
-				   ", seqid %llu, user %s, xid_in %llu)",
-				   __func__,
-				   (unsigned int) pkt_len,
-				   __cld_opstr(msg->op),
-				   (unsigned long long) le64_to_cpu(pkt->seqid),
-				   pkt->user,
-				   (unsigned long long) le64_to_cpu(dp->xid_in));
-		} else {
-			HAIL_DEBUG(&sess->log, "%s(len %u, "
-				   "flags %s%s, op %s, seqid %llu, user %s)",
-				   __func__,
-				   (unsigned int) pkt_len,
-				   first_frag ? "F" : "",
-				   last_frag ? "L" : "",
-				   first_frag ? __cld_opstr(msg->op) : "n/a",
-				   (unsigned long long) le64_to_cpu(pkt->seqid),
-				   pkt->user);
-		}
+	xdrmem_create(&xdrs, (void *)pktbuf,
+			pkt_len - CLD_PKT_FTR_LEN, XDR_DECODE);
+	memset(&pkt, 0, sizeof(pkt));
+	if (!xdr_cld_pkt_hdr(&xdrs, &pkt)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to decode packet header",
+			   __func__);
+		xdr_destroy(&xdrs);
+		return -EPROTO;
 	}
-
-	if (memcmp(pkt->magic, CLD_PKT_MAGIC, sizeof(pkt->magic))) {
+	hdr_len = xdr_getpos(&xdrs);
+	xdr_destroy(&xdrs);
+	if (memcmp(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic))) {
 		HAIL_DEBUG(&sess->log, "%s: bad pkt magic", __func__);
 		return -EPROTO;
 	}
 
 	/* check HMAC signature */
-	secret_key = user_key(sess, pkt->user);
+	foot = (const struct cld_pkt_ftr *)
+		(((char *)pktbuf) + (pkt_len - CLD_PKT_FTR_LEN));
+	secret_key = user_key(sess, pkt.user);
 	ret = __cld_authcheck(&sess->log, secret_key,
-			      pkt, pkt_len - SHA_DIGEST_LENGTH,
-			      (uint8_t *)pkt + pkt_len - SHA_DIGEST_LENGTH);
+			      pktbuf, pkt_len - SHA_DIGEST_LENGTH, foot->sha);
 	if (ret) {
 		HAIL_DEBUG(&sess->log, "%s: invalid auth (ret=%d)",
 			   __func__, ret);
@@ -497,34 +472,40 @@ int cldc_receive_pkt(struct cldc_session
 	if (current_time >= sess->msg_scan_time)
 		sess_expire_outmsg(sess, current_time);
 
-	if (first_frag)
-		sess->msg_buf_len = 0;
-
-	if ((sess->msg_buf_len + msglen) > CLD_MAX_MSG_SZ) {
-		HAIL_DEBUG(&sess->log, "%s: bad pkt length", __func__);
-		return -EPROTO;
+	if (pkt.mi.order & CLD_PKT_IS_FIRST) {
+		/* This packet begins a new message.
+		 * Determine the new message's op */
+		sess->msg_buf_op = pkt.mi.cld_pkt_msg_info_u.mi.op;
 	}
 
-	memcpy(sess->msg_buf + sess->msg_buf_len, msg, msglen);
-	sess->msg_buf_len += msglen;
-
 	/* verify (or set, for new-sess) sequence id */
-	seqid = le64_to_cpu(pkt->seqid);
-	ret = accept_seqid(sess, seqid, msg_buf_op, &dupe);
+	seqid = le64_to_cpu(foot->seqid);
+	ret = accept_seqid(sess, seqid, sess->msg_buf_op);
 	if (ret) {
 		HAIL_DEBUG(&sess->log, "%s: bad seqid %llu",
 			   __func__, (unsigned long long) seqid);
 		return ret;
 	}
-	if (dupe)
-		return ack_seqid(sess, pkt->seqid);
 
+	if (pkt.mi.order & CLD_PKT_IS_FIRST)
+		sess->msg_buf_len = 0;
+	msg_len = pkt_len - hdr_len - CLD_PKT_FTR_LEN;
+	if ((sess->msg_buf_len + msg_len) > CLD_MAX_MSG_SZ) {
+		HAIL_DEBUG(&sess->log, "%s: message too long", __func__);
+		return -EPROTO;
+	}
+	memcpy(sess->msg_buf + sess->msg_buf_len, pktbuf + hdr_len, msg_len);
+	sess->msg_buf_len += msg_len;
 	sess->expire_time = current_time + CLDC_SESS_EXPIRE;
 
-	if (!last_frag)
-		return sess ? ack_seqid(sess, pkt->seqid) : 0;
-
-	return cldc_receive_msg(sess, pkt, pkt_len);
+	if (pkt.mi.order & CLD_PKT_IS_LAST) {
+		HAIL_DEBUG(&sess->log, "%s: receiving complete message of "
+			   "op %s", __func__,
+			   __cld_opstr(sess->msg_buf_op));
+		return rx_complete(sess, &pkt, foot);
+	} else {
+		return ack_seqid(sess, foot->seqid);
+	}
 }
 
 static void sess_next_seqid(struct cldc_session *sess, uint64_t *seqid)
@@ -533,19 +514,49 @@ static void sess_next_seqid(struct cldc_
 	*seqid = rc;
 }
 
+/**
+ * creates a new cldc_msg
+ *
+ * @param sess		The session
+ * @param copts		The call options
+ * @param op		The op of message to create
+ * @param xdrproc	The XDR function to use to create the message body
+ * @param data		The data to pass to xdrproc
+ *
+ * @return		The cldc message, or NULL on error,
+ */
 static struct cldc_msg *cldc_new_msg(struct cldc_session *sess,
 				     const struct cldc_call_opts *copts,
 				     enum cld_msg_op op,
-				     size_t msg_len)
+				     xdrproc_t xdrproc, const void *data)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_hdr *hdr;
 	struct timeval tv;
-	int n_pkts, i, data_left;
-	void *p;
+	size_t i, body_len, n_pkts;
+	char *body;
+	XDR xbdy;
+
+	/* Encode the message body */
+	body_len = xdr_sizeof(xdrproc, (void *)data);
+	body = alloca(body_len);
+	xdrmem_create(&xbdy, body, body_len, XDR_ENCODE);
+	if (!xdrproc(&xbdy, (void *)data)) {
+		HAIL_DEBUG(&sess->log, "%s: failed to encode "
+			   "message", __func__);
+		xdr_destroy(&xbdy);
+		return NULL;
+	}
+	xdr_destroy(&xbdy);
 
-	n_pkts = msg_len / CLD_MAX_PKT_MSG_SZ;
-	n_pkts += ((msg_len % CLD_MAX_PKT_MSG_SZ) ? 1 : 0);
+	if (body_len == 0)
+		/* Some packets (like ACKS) just have a header, and no message
+		 * body. */
+		n_pkts = 1;
+	else {
+		/* round up */
+		n_pkts = (body_len + CLD_MAX_PKT_MSG_SZ - 1) /
+			CLD_MAX_PKT_MSG_SZ;
+	}
 
 	/* Create cldc_msg */
 	msg = calloc(1, sizeof(*msg) +
@@ -553,57 +564,65 @@ static struct cldc_msg *cldc_new_msg(str
 	if (!msg)
 		return NULL;
 
-	msg->data = calloc(1, msg_len);
-	if (!msg->data) {
-		free(msg);
-		return NULL;
-	}
-
 	msg->n_pkts = n_pkts;
 	__cld_rand64(&msg->xid);
 	msg->op = op;
 	msg->sess = sess;
-
 	if (copts)
 		memcpy(&msg->copts, copts, sizeof(msg->copts));
-
 	gettimeofday(&tv, NULL);
 	msg->expire_time = tv.tv_sec + CLDC_MSG_EXPIRE;
 
-	msg->data_len = msg_len;
-
-	p = msg->data;
-	data_left = msg_len;
 	for (i = 0; i < msg->n_pkts; i++) {
+		XDR xhdr;
+		struct cld_pkt_hdr pkt;
 		struct cldc_pkt_info *pi;
-		int pkt_len;
+		int hdr_len, body_chunk_len, pkt_len;
 
-		pkt_len = MIN(data_left, CLD_MAX_PKT_MSG_SZ);
+		/* Set up packet header */
+		memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+		memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+		pkt.user = sess->user;
+		if (i == 0) {
+			if (i == (msg->n_pkts - 1))
+				pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_FIRST;
+			pkt.mi.cld_pkt_msg_info_u.mi.xid = msg->xid;
+			pkt.mi.cld_pkt_msg_info_u.mi.op = op;
+		} else {
+			if (i == (msg->n_pkts - 1))
+				pkt.mi.order = CLD_PKT_ORD_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_MID;
+		}
 
-		pi = calloc(1, sizeof(*pi) + pkt_len + SHA_DIGEST_LENGTH);
+		/* Allocate memory */
+		hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, &pkt);
+		body_chunk_len = MIN(body_len, CLD_MAX_PKT_MSG_SZ);
+		pkt_len = hdr_len + body_chunk_len + CLD_PKT_FTR_LEN;
+		pi = calloc(1, sizeof(*pi) + pkt_len);
 		if (!pi)
 			goto err_out;
-
 		pi->pkt_len = pkt_len;
+		msg->pkt_info[i] = pi;
+		strncpy(pi->user, sess->user, CLD_MAX_USERNAME - 1);
 
-		memcpy(pi->pkt.magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-		memcpy(pi->pkt.sid, sess->sid, CLD_SID_SZ);
-		strncpy(pi->pkt.user, sess->user, CLD_MAX_USERNAME - 1);
-
-		if (i == 0)
-			pi->pkt.flags |= cpu_to_le32(CPF_FIRST);
-		if (i == (msg->n_pkts - 1))
-			pi->pkt.flags |= cpu_to_le32(CPF_LAST);
+		/* Fill in the packet header */
+		xdrmem_create(&xhdr, (char *)pi->data, hdr_len, XDR_ENCODE);
+		if (!xdr_cld_pkt_hdr(&xhdr, &pkt)) {
+			HAIL_DEBUG(&sess->log, "%s: failed to encode header "
+				   "for packet %zu", __func__, i);
+			xdr_destroy(&xhdr);
+			goto err_out;
+		}
 
-		msg->pkt_info[i] = pi;
-		data_left -= pkt_len;
+		/* Fill in the body */
+		memcpy(pi->data + hdr_len, body, body_chunk_len);
+		body += body_chunk_len;
+		body_len -= body_chunk_len;
 	}
 
-	hdr = (struct cld_msg_hdr *) msg->data;
-	memcpy(&hdr->magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	hdr->op = op;
-	hdr->xid = msg->xid;
-
 	return msg;
 
 err_out:
@@ -642,32 +661,8 @@ static void sess_expire(struct cldc_sess
 }
 
 static int sess_send_pkt(struct cldc_session *sess,
-			 const struct cld_packet *pkt, size_t pkt_len)
+			 const void *pkt, size_t pkt_len)
 {
-	if (sess->log.verbose) {
-		uint32_t flags = le32_to_cpu(pkt->flags);
-		bool first = (flags & CPF_FIRST);
-		bool last = (flags & CPF_LAST);
-		uint8_t op = CMO_NOP;
-
-		if (first) {
-			struct cld_msg_hdr *hdr;
-
-			hdr = (struct cld_msg_hdr *) (pkt + 1);
-			op = hdr->op;
-		}
-
-		HAIL_DEBUG(&sess->log,
-			   "%s(len %zu, flags %s%s, "
-			   "op %s, seqid %llu)",
-			   __func__,
-			   pkt_len,
-			   first ? "F" : "",
-			   last ? "L" : "",
-			   first ? __cld_opstr(op) : "n/a",
-			   (unsigned long long) le64_to_cpu(pkt->seqid));
-	}
-
 	return sess->ops->pkt_send(sess->private,
 				   sess->addr, sess->addr_len,
 				   pkt, pkt_len);
@@ -696,18 +691,12 @@ static int sess_timer(struct cldc_sessio
 
 		for (i = 0; i < msg->n_pkts; i++) {
 			struct cldc_pkt_info *pi;
-			int total_pkt_len;
 
 			pi = msg->pkt_info[i];
 			if (!pi)
 				continue;
-
-			total_pkt_len = sizeof(struct cld_packet) +
-					pi->pkt_len + SHA_DIGEST_LENGTH;
-
 			pi->retries++;
-
-			sess_send_pkt(sess, &pi->pkt, total_pkt_len);
+			sess_send_pkt(sess, pi->data, pi->pkt_len);
 		}
 	}
 
@@ -719,40 +708,31 @@ static int sess_timer(struct cldc_sessio
 static int sess_send(struct cldc_session *sess, struct cldc_msg *msg)
 {
 	int ret, i;
-	int data_left;
-	void *p;
 	const char *secret_key;
 
 	secret_key = user_key(sess, sess->user);
 
-	p = msg->data;
-	data_left = msg->data_len;
 	for (i = 0; i < msg->n_pkts; i++) {
 		struct cldc_pkt_info *pi;
-		int total_pkt_len;
+		struct cld_pkt_ftr *foot;
 
 		pi = msg->pkt_info[i];
-		memcpy(pi->data, p, pi->pkt_len);
-
-		total_pkt_len = sizeof(struct cld_packet) +
-				pi->pkt_len + SHA_DIGEST_LENGTH;
 
 		/* Add the sequence number to the end of the packet */
-		sess_next_seqid(sess, &pi->pkt.seqid);
-
-		p += pi->pkt_len;
-		data_left -= pi->pkt_len;
+		foot = (struct cld_pkt_ftr *)
+			(pi->data + pi->pkt_len - CLD_PKT_FTR_LEN);
+		memset(foot, 0, CLD_PKT_FTR_LEN);
+		sess_next_seqid(sess, &foot->seqid);
 
 		/* Add the signature to the end of the packet */
 		ret = __cld_authsign(&sess->log, secret_key,
-				     &pi->pkt, total_pkt_len-SHA_DIGEST_LENGTH,
-				     ((uint8_t *)&pi->pkt + total_pkt_len) -
-				    	SHA_DIGEST_LENGTH);
+				     pi->data,
+				     pi->pkt_len - SHA_DIGEST_LENGTH,foot->sha);
 		if (ret)
 			return ret;
 
 		/* attempt first send */
-		if (sess_send_pkt(sess, &pi->pkt, total_pkt_len) < 0)
+		if (sess_send_pkt(sess, pi->data, pi->pkt_len) < 0)
 			return -EIO;
 	}
 
@@ -802,7 +782,7 @@ int cldc_end_sess(struct cldc_session *s
 
 	/* create END-SESS message */
 	msg = cldc_new_msg(sess, copts, CMO_END_SESS,
-			   sizeof(struct cld_msg_hdr));
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg)
 		return -ENOMEM;
 
@@ -814,10 +794,8 @@ int cldc_end_sess(struct cldc_session *s
 static ssize_t new_sess_cb(struct cldc_msg *msg, const void *resp_p,
 			   size_t resp_len, enum cle_err_codes resp_rc)
 {
-	struct cldc_session *sess = msg->sess;
-
 	if (resp_rc == CLE_OK)
-		sess->confirmed = true;
+		msg->sess->confirmed = true;
 
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
@@ -870,7 +848,7 @@ int cldc_new_sess(const struct cldc_ops 
 
 	/* create NEW-SESS message */
 	msg = cldc_new_msg(sess, copts, CMO_NEW_SESS,
-			   sizeof(struct cld_msg_hdr));
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg) {
 		sess_free(sess);
 		return -ENOMEM;
@@ -917,7 +895,7 @@ int cldc_nop(struct cldc_session *sess, 
 
 	/* create NOP message */
 	msg = cldc_new_msg(sess, copts, CMO_NOP,
-			   sizeof(struct cld_msg_hdr));
+			   (xdrproc_t)xdr_void, NULL);
 	if (!msg)
 		return -ENOMEM;
 
@@ -930,8 +908,7 @@ int cldc_del(struct cldc_session *sess, 
 	     const char *pathname)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_del *del;
-	void *p;
+	struct cld_msg_del del;
 	size_t plen;
 
 	if (!sess->confirmed)
@@ -946,33 +923,33 @@ int cldc_del(struct cldc_session *sess, 
 		return -EINVAL;
 
 	/* create DEL message */
+	del.inode_name = (char *)pathname;
 	msg = cldc_new_msg(sess, copts, CMO_DEL,
-			   sizeof(struct cld_msg_del) + strlen(pathname));
+			   (xdrproc_t)xdr_cld_msg_del, &del);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in DEL-specific name_len, name info */
-	del = (struct cld_msg_del *) msg->data;
-	del->name_len = cpu_to_le16(plen);
-	p = del;
-	p += sizeof(struct cld_msg_del);
-	memcpy(p, pathname, plen);
-
 	return sess_send(sess, msg);
 }
 
 static ssize_t open_end_cb(struct cldc_msg *msg, const void *resp_p,
 			   size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_open_resp *resp = resp_p;
-	struct cldc_fh *fh = msg->cb_private;
-
 	if (resp_rc == CLE_OK) {
-		if (resp_len < sizeof(*resp))
-			return -1010;
-		fh->fh_le = resp->fh;
+		struct cldc_fh *fh = msg->cb_private;
+		XDR xdrs;
+		struct cld_msg_open_resp resp;
+
+		xdrmem_create(&xdrs, (void *)resp_p, resp_len, XDR_DECODE);
+		memset(&resp, 0, sizeof(resp));
+		if (!xdr_cld_msg_open_resp(&xdrs, &resp)) {
+			xdr_destroy(&xdrs);
+			return -1009;
+		}
+
+		fh->fh = resp.fh;
 		fh->valid = true;
 	}
 
@@ -988,9 +965,8 @@ int cldc_open(struct cldc_session *sess,
 	      uint32_t events, struct cldc_fh **fh_out)
 {
 	struct cldc_msg *msg;
-	struct cld_msg_open *open;
+	struct cld_msg_open open;
 	struct cldc_fh fh, *fhtmp;
-	void *p;
 	size_t plen;
 	int fh_idx;
 
@@ -1008,8 +984,11 @@ int cldc_open(struct cldc_session *sess,
 		return -EINVAL;
 
 	/* create OPEN message */
+	open.mode = open_mode;
+	open.events = events;
+	open.inode_name = (char *)pathname;
 	msg = cldc_new_msg(sess, copts, CMO_OPEN,
-			   sizeof(struct cld_msg_open) + strlen(pathname));
+			   (xdrproc_t)xdr_cld_msg_open, &open);
 	if (!msg)
 		return -ENOMEM;
 
@@ -1024,15 +1003,6 @@ int cldc_open(struct cldc_session *sess,
 	msg->cb = open_end_cb;
 	msg->cb_private = fhtmp;
 
-	/* fill in OPEN-specific info */
-	open = (struct cld_msg_open *) msg->data;
-	open->mode = cpu_to_le32(open_mode);
-	open->events = cpu_to_le32(events);
-	open->name_len = cpu_to_le16(plen);
-	p = open;
-	p += sizeof(struct cld_msg_open);
-	memcpy(p, pathname, plen);
-
 	*fh_out = fhtmp;
 
 	return sess_send(sess, msg);
@@ -1042,7 +1012,7 @@ int cldc_close(struct cldc_fh *fh, const
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_close *close_msg;
+	struct cld_msg_close close_msg;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1050,8 +1020,9 @@ int cldc_close(struct cldc_fh *fh, const
 	sess = fh->sess;
 
 	/* create CLOSE message */
+	close_msg.fh = fh->fh;
 	msg = cldc_new_msg(sess, copts, CMO_CLOSE,
-			   sizeof(struct cld_msg_close));
+			   (xdrproc_t)xdr_cld_msg_close, &close_msg);
 	if (!msg)
 		return -ENOMEM;
 
@@ -1060,10 +1031,6 @@ int cldc_close(struct cldc_fh *fh, const
 
 	msg->cb = generic_end_cb;
 
-	/* fill in CLOSE-specific fh info */
-	close_msg = (struct cld_msg_close *) msg->data;
-	close_msg->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
@@ -1072,7 +1039,7 @@ int cldc_lock(struct cldc_fh *fh, const 
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_lock *lock;
+	struct cld_msg_lock lock;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1080,19 +1047,16 @@ int cldc_lock(struct cldc_fh *fh, const 
 	sess = fh->sess;
 
 	/* create LOCK message */
+	lock.fh = fh->fh;
+	lock.flags = lock_flags;
 	msg = cldc_new_msg(sess, copts,
 			   wait_for_lock ? CMO_LOCK : CMO_TRYLOCK,
-			   sizeof(struct cld_msg_lock));
+			   (xdrproc_t)xdr_cld_msg_lock, &lock);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in LOCK-specific info */
-	lock = (struct cld_msg_lock *) msg->data;
-	lock->fh = fh->fh_le;
-	lock->flags = cpu_to_le32(lock_flags);
-
 	return sess_send(sess, msg);
 }
 
@@ -1100,7 +1064,7 @@ int cldc_unlock(struct cldc_fh *fh, cons
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_unlock *unlock;
+	struct cld_msg_unlock unlock;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1108,17 +1072,14 @@ int cldc_unlock(struct cldc_fh *fh, cons
 	sess = fh->sess;
 
 	/* create UNLOCK message */
+	unlock.fh = fh->fh;
 	msg = cldc_new_msg(sess, copts, CMO_UNLOCK,
-			   sizeof(struct cld_msg_unlock));
+			   (xdrproc_t)xdr_cld_msg_unlock, &unlock);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = generic_end_cb;
 
-	/* fill in UNLOCK-specific info */
-	unlock = (struct cld_msg_unlock *) msg->data;
-	unlock->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
@@ -1127,7 +1088,7 @@ int cldc_put(struct cldc_fh *fh, const s
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_put *put;
+	struct cld_msg_put put;
 
 	if (!data || !data_len || data_len > CLD_MAX_PAYLOAD_SZ)
 		return -EINVAL;
@@ -1138,17 +1099,14 @@ int cldc_put(struct cldc_fh *fh, const s
 	sess = fh->sess;
 
 	/* create PUT message */
+	put.fh = fh->fh;
+	put.data.data_len = data_len;
+	put.data.data_val = (char *)data;
 	msg = cldc_new_msg(sess, copts, CMO_PUT,
-			   sizeof(struct cld_msg_put) + data_len);
+			   (xdrproc_t)xdr_cld_msg_put, &put);
 	if (!msg)
 		return -ENOMEM;
 
-	put = (struct cld_msg_put *) msg->data;
-	put->fh = fh->fh_le;
-	put->data_size = cpu_to_le32(data_len);
-
-	memcpy((put + 1), data, data_len);
-
 	msg->cb = generic_end_cb;
 
 	sess_send(sess, msg);
@@ -1156,67 +1114,39 @@ int cldc_put(struct cldc_fh *fh, const s
 	return 0;
 }
 
-#undef XC32
-#undef XC64
-#define XC32(name) \
-	o->name = le32_to_cpu(resp->name)
-#define XC64(name) \
-	o->name = le64_to_cpu(resp->name)
-
 static ssize_t get_end_cb(struct cldc_msg *msg, const void *resp_p,
 			  size_t resp_len, enum cle_err_codes resp_rc)
 {
-	const struct cld_msg_get_resp *resp = resp_p;
-	struct cld_msg_get_resp *o = NULL;
-
 	if (resp_rc == CLE_OK) {
-		bool get_body;
-
-		o = &msg->copts.resp;
-
-		get_body = (resp->resp.hdr.op == CMO_GET);
-		msg->op = CMO_GET;
+		XDR xin;
+		struct cld_msg_get_resp *resp = &msg->copts.resp;
 
-		/* copy-and-swap */
-		XC64(inum);
-		XC32(ino_len);
-		XC32(size);
-		XC64(version);
-		XC64(time_create);
-		XC64(time_modify);
-		XC32(flags);
-
-		/* copy inode name */
-		if (o->ino_len <= CLD_INODE_NAME_MAX) {
-			size_t diffsz;
-			const void *p;
-
-			p = (resp + 1);
-			memcpy(&msg->copts.u.get.inode_name, p, o->ino_len);
-
-			p += o->ino_len;
-			diffsz = p - resp_p;
-
-			/* point to internal buffer holding GET data */
-			msg->copts.u.get.buf = msg->sess->msg_buf + diffsz;
-			msg->copts.u.get.size = msg->sess->msg_buf_len - diffsz;
-		} else {
-			o->ino_len = 0;		/* Probably full of garbage */
+		/* Parse GET response.
+		 * Avoid memory allocation in xdr_string by pointing
+		 * variable-length elements at static buffers. */
+		xdrmem_create(&xin, (void *)resp_p, resp_len, XDR_DECODE);
+		memset(resp, 0, sizeof(struct cld_msg_get_resp));
+		resp->inode_name = msg->sess->inode_name_temp;
+		resp->data.data_val = msg->sess->payload;
+		resp->data.data_len = 0;
+		if (!xdr_cld_msg_get_resp(&xin, resp)) {
+			xdr_destroy(&xin);
+			return -1009;
 		}
+		xdr_destroy(&xin);
 	}
 
 	if (msg->copts.cb)
 		return msg->copts.cb(&msg->copts, resp_rc);
 	return 0;
 }
-#undef XC
 
 int cldc_get(struct cldc_fh *fh, const struct cldc_call_opts *copts,
 	     bool metadata_only)
 {
 	struct cldc_session *sess;
 	struct cldc_msg *msg;
-	struct cld_msg_get *get;
+	struct cld_msg_get get;
 
 	if (!fh->valid)
 		return -EINVAL;
@@ -1224,17 +1154,14 @@ int cldc_get(struct cldc_fh *fh, const s
 	sess = fh->sess;
 
 	/* create GET message */
+	get.fh = fh->fh;
 	msg = cldc_new_msg(sess, copts, CMO_GET,
-			   sizeof(struct cld_msg_get));
+			   (xdrproc_t)xdr_cld_msg_get, &get);
 	if (!msg)
 		return -ENOMEM;
 
 	msg->cb = get_end_cb;
 
-	/* fill in GET-specific info */
-	get = (struct cld_msg_get *) msg->data;
-	get->fh = fh->fh_le;
-
 	return sess_send(sess, msg);
 }
 
diff -X /garz/tmp/dontdiff -urNp cld/lib/cld_msg_rpc.x cld.rpcgen/lib/cld_msg_rpc.x
--- cld/lib/cld_msg_rpc.x	1969-12-31 19:00:00.000000000 -0500
+++ cld.rpcgen/lib/cld_msg_rpc.x	2010-02-03 16:03:36.000000000 -0500
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010, Colin McCabe
+ *
+ * This program 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.
+ *
+ * This program 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 this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+const CLD_PKT_MAGIC = "CLDc1pkt";
+const CLD_SID_SZ = 8;
+
+const CLD_INODE_NAME_MAX = 256; /**< max total pathname len */
+
+const CLD_MAX_USERNAME = 32;
+
+const CLD_MAX_PKT_MSG_SZ = 1024; /**< The maximum number of message bytes we'll
+				   put in a single packet */
+
+const CLD_MAX_PAYLOAD_SZ = 131072; /**< Maximum length of the data that can be
+					sent with get or put. In some sense,
+					this is part of cld's API, and
+					shouldn't be changed lightly.  */
+
+const CLD_MAX_MSG_SZ = 196608; /**< Maximum size of a single message
+					including all packets. */
+
+const CLD_MAX_SECRET_KEY = 128; /**< includes req. nul */
+
+/** available RPC operations */
+enum cld_msg_op {
+	/* client -> server */
+	CMO_NOP			= 0,	/**< no op */
+	CMO_NEW_SESS		= 1,	/**< new session */
+	CMO_OPEN		= 2,	/**< open file */
+	CMO_GET_META		= 3,	/**< get metadata */
+	CMO_GET			= 4,	/**< get metadata + data */
+	CMO_PUT			= 6,	/**< put data */
+	CMO_CLOSE		= 7,	/**< close file */
+	CMO_DEL			= 8,	/**< delete file */
+	CMO_LOCK		= 9,	/**< lock */
+	CMO_UNLOCK		= 10,	/**< unlock */
+	CMO_TRYLOCK		= 11,	/**< trylock */
+	CMO_ACK			= 12,	/**< ack of seqid rx'd */
+	CMO_END_SESS		= 13,	/**< end session */
+
+	/* server -> client */
+	CMO_PING		= 14,	/**< server to client ping */
+	CMO_NOT_MASTER		= 15,	/**< I am not the master! */
+	CMO_EVENT		= 16,	/**< server->cli async event */
+	CMO_ACK_FRAG		= 17, 	/**< ack partial msg */
+
+	CMO_AFTER_LAST
+};
+
+/** CLD error codes */
+enum cle_err_codes {
+	CLE_OK			= 0,	/**< success / no error */
+	CLE_SESS_EXISTS		= 1,	/**< session exists */
+	CLE_SESS_INVAL		= 2,	/**< session doesn't exist */
+	CLE_DB_ERR		= 3,	/**< db error */
+	CLE_BAD_PKT		= 4,	/**< invalid/corrupted packet */
+	CLE_INODE_INVAL		= 5,	/**< inode doesn't exist */
+	CLE_NAME_INVAL		= 6,	/**< inode name invalid */
+	CLE_OOM			= 7,	/**< server out of memory */
+	CLE_FH_INVAL		= 8,	/**< file handle invalid */
+	CLE_DATA_INVAL		= 9,	/**< invalid data pkt */
+	CLE_LOCK_INVAL		= 10,	/**< invalid lock */
+	CLE_LOCK_CONFLICT 	= 11,	/**< conflicting lock held */
+	CLE_LOCK_PENDING	= 12,	/**< lock waiting to be acq. */
+	CLE_MODE_INVAL		= 13,	/**< op incompat. w/ file mode */
+	CLE_INODE_EXISTS	= 14,	/**< inode exists */
+	CLE_DIR_NOTEMPTY	= 15,	/**< dir not empty */
+	CLE_INTERNAL_ERR	= 16,	/**< nonspecific internal err */
+	CLE_TIMEOUT 		= 17,	/**< session timed out */
+	CLE_SIG_INVAL 		= 18	/**< HMAC sig bad / auth failed */
+};
+
+/** availble OPEN mode flags */
+enum cld_open_modes {
+	COM_READ		= 0x01,	/**< read */
+	COM_WRITE		= 0x02,	/**< write */
+	COM_LOCK		= 0x04,	/**< lock */
+	COM_ACL			= 0x08,	/**< ACL update */
+	COM_CREATE		= 0x10,	/**< create file, if not exist */
+	COM_EXCL		= 0x20,	/**< fail create if file exists */
+	COM_DIRECTORY		= 0x40	/**< operate on a directory */
+};
+
+/** potential events client may receive */
+enum cld_events {
+	CE_UPDATED		= 0x01,	/**< contents updated */
+	CE_DELETED		= 0x02,	/**< inode deleted */
+	CE_LOCKED		= 0x04,	/**< lock acquired */
+	CE_MASTER_FAILOVER	= 0x08,	/**< master failover */
+	CE_SESS_FAILED		= 0x10
+};
+
+/** LOCK flags */
+enum cld_lock_flags {
+	CLF_SHARED		= 0x01	/**< a shared (read) lock */
+};
+
+/** Describes whether a packet begins, continues, or ends a message. */
+enum cld_pkt_order_t {
+	CLD_PKT_ORD_MID = 0x0,
+	CLD_PKT_ORD_FIRST = 0x1,
+	CLD_PKT_ORD_LAST = 0x2,
+	CLD_PKT_ORD_FIRST_LAST = 0x3
+};
+const CLD_PKT_IS_FIRST = 0x1;
+const CLD_PKT_IS_LAST = 0x2;
+
+/** Information that appears only in the first packet */
+struct cld_pkt_msg_infos {
+	hyper			xid;		/**< opaque message id */
+	enum cld_msg_op		op;		/**< message operation */
+};
+
+/** Information about the message contained in this packet */
+union cld_pkt_msg_info switch (enum cld_pkt_order_t order) {
+	case CLD_PKT_ORD_MID:
+	case CLD_PKT_ORD_LAST:
+		void;
+	case CLD_PKT_ORD_FIRST:
+	case CLD_PKT_ORD_FIRST_LAST:
+		struct cld_pkt_msg_infos mi;
+};
+
+/** header for each packet */
+struct cld_pkt_hdr {
+	hyper		magic;		/**< magic number; constant */
+	hyper		sid;		/**< client id */
+	string		user<CLD_MAX_USERNAME>;	/**< authenticated user */
+	struct cld_pkt_msg_info mi;
+};
+
+/** generic response for PUT, CLOSE, DEL, LOCK, UNLOCK */
+struct cld_msg_generic_resp {
+	enum cle_err_codes	code;		/**< error code, CLE_xxx */
+	hyper			xid_in;		/**< C->S xid */
+};
+
+/** ACK-FRAG message */
+struct cld_msg_ack_frag {
+	hyper			seqid;		/**< sequence id to ack */
+};
+
+/** OPEN message */
+struct cld_msg_open {
+	int			mode;		/**< open mode, COM_xxx */
+	int			events;		/**< events mask, CE_xxx */
+	string			inode_name<CLD_INODE_NAME_MAX>;
+};
+
+/** OPEN message response */
+struct cld_msg_open_resp {
+	struct cld_msg_generic_resp msg;
+	hyper			fh;		/**< handle opened */
+};
+
+/** GET message */
+struct cld_msg_get {
+	hyper			fh;		/**< open file handle */
+};
+
+/** GET message response */
+struct cld_msg_get_resp {
+	struct cld_msg_generic_resp msg;
+	hyper			inum;		/**< unique inode number */
+	hyper			vers;		/**< inode version */
+	hyper			time_create;	/**< creation time */
+	hyper			time_modify;	/**< last modification time */
+	int			flags;		/**< inode flags; CIFL_xxx */
+	string			inode_name<CLD_INODE_NAME_MAX>;
+	opaque			data<CLD_MAX_PAYLOAD_SZ>;
+};
+
+/** PUT message */
+struct cld_msg_put {
+	hyper			fh;		/**< open file handle */
+	opaque			data<CLD_MAX_PAYLOAD_SZ>;
+};
+
+/** CLOSE message */
+struct cld_msg_close {
+	hyper			fh;		/**< open file handle */
+};
+
+/** DEL message */
+struct cld_msg_del {
+	string			inode_name<CLD_INODE_NAME_MAX>;
+};
+
+/** UNLOCK message */
+struct cld_msg_unlock {
+	uint64_t		fh;		/**< open file handle */
+};
+
+/** LOCK message */
+struct cld_msg_lock {
+	hyper			fh;		/**< open file handle */
+	int			flags;		/**< CLF_xxx */
+};
+
+/** Server-to-client EVENT message */
+struct cld_msg_event {
+	hyper			fh;		/**< open file handle */
+	int			events;		/**< CE_xxx */
+};
diff -X /garz/tmp/dontdiff -urNp cld/lib/common.c cld.rpcgen/lib/common.c
--- cld/lib/common.c	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/lib/common.c	2010-01-22 18:15:07.000000000 -0500
@@ -26,7 +26,11 @@
 #include <errno.h>
 #include <glib.h>
 #include <cld-private.h>
-#include "cld_msg.h"
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include "cld_msg_rpc.h"
+#include <hail_log.h>
+#include <syslog.h>
 
 /* duplicated from tools/cldcli.c; put in common header somewhere? */
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
diff -X /garz/tmp/dontdiff -urNp cld/lib/Makefile.am cld.rpcgen/lib/Makefile.am
--- cld/lib/Makefile.am	2010-02-04 01:20:51.000000000 -0500
+++ cld.rpcgen/lib/Makefile.am	2010-02-02 23:10:51.000000000 -0500
@@ -1,10 +1,21 @@
 
+BUILT_SOURCES		= cld_msg_rpc.h
+
 EXTRA_DIST =
 	libcldc.pc.in libcldc-uninstalled.pc.in
 
 INCLUDES		= -I$(top_srcdir)/include	\
 			  @GLIB_CFLAGS@
 
+mostlyclean-local:
+	rm -f *_rpc.h *_rpc_xdr.c
+
+%_rpc.h: %_rpc.x
+	rpcgen -h $< > $@
+
+%_rpc_xdr.c: %_rpc.x
+	rpcgen -c $< > $@
+
 LINK = $(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(LDFLAGS) -o $@
 
 lib_LTLIBRARIES		= libcldc.la
@@ -15,7 +26,8 @@ libcldc_la_SOURCES	=	\
 	cldc-dns.c		\
 	common.c		\
 	libtimer.c		\
-	pkt.c
+	pkt.c			\
+	cld_msg_rpc_xdr.c
 
 libcldc_la_LDFLAGS = \
 	-version-info $(LIBCLDC_CURRENT):$(LIBCLDC_REVISION):$(LIBCLDC_AGE) \
@@ -25,3 +37,4 @@ libcldc_la_LDFLAGS = \
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libcldc.pc
 
+include_HEADERS = cld_msg_rpc.h
diff -X /garz/tmp/dontdiff -urNp cld/lib/pkt.c cld.rpcgen/lib/pkt.c
--- cld/lib/pkt.c	2010-02-04 19:08:09.000000000 -0500
+++ cld.rpcgen/lib/pkt.c	2010-02-03 16:27:08.000000000 -0500
@@ -17,11 +17,16 @@
  */
 
 #include <string.h>
+#include <stdio.h>
 #include <errno.h>
+#include <glib.h>
 #include <syslog.h>
 #include <openssl/sha.h>
 #include <openssl/hmac.h>
-#include <cldc.h>
+#include <cld-private.h>
+#include "cld_pkt.h"
+#include "cld_msg_rpc.h"
+#include <hail_log.h>
 
 int __cld_authcheck(struct hail_log *log, const char *key,
 		    const void *buf, size_t buf_len, const void *sha)
@@ -92,3 +97,96 @@ const char *__cld_opstr(enum cld_msg_op 
 	}
 }
 
+const char *__cld_pkt_hdr_to_str(char *scratch,
+			   const char *pkt_hdr, size_t pkt_len)
+{
+	XDR xin;
+	struct cld_pkt_hdr pkt;
+	bool bad_magic;
+	char temp[50], temp2[50];
+	uint64_t seqid;
+	struct cld_pkt_ftr *foot;
+	size_t hdr_len;
+
+	temp[0] = '\0';
+	temp2[0] = '\0';
+	foot = (struct cld_pkt_ftr *)(pkt_hdr + pkt_len - CLD_PKT_FTR_LEN);
+	seqid = le64_to_cpu(foot->seqid);
+
+	if (pkt_len <= CLD_PKT_FTR_LEN) {
+		snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+			 "[MALFORMED: only %zu bytes]", pkt_len);
+		return scratch;
+	}
+	xdrmem_create(&xin, (void *)pkt_hdr, pkt_len - CLD_PKT_FTR_LEN,
+		      XDR_DECODE);
+	memset(&pkt, 0, sizeof(pkt));
+	if (!xdr_cld_pkt_hdr(&xin, &pkt)) {
+		xdr_destroy(&xin);
+		snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+			 "[MALFORMED: can't parse]");
+		return scratch;
+	}
+	hdr_len = xdr_getpos(&xin);
+	xdr_destroy(&xin);
+
+	bad_magic = !!(memcmp(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic)));
+	if (pkt.mi.order & CLD_PKT_IS_FIRST) {
+		struct cld_pkt_msg_infos *infos =
+			&pkt.mi.cld_pkt_msg_info_u.mi;
+		snprintf(temp, sizeof(temp), "[TYPE:%s, XID:%llx]",
+			 __cld_opstr(infos->op),
+			 (unsigned long long) infos->xid);
+		switch (infos->op) {
+		case CMO_ACK_FRAG: {
+			XDR x;
+			struct cld_msg_ack_frag ack;
+			memset(&ack, 0, sizeof(ack));
+			xdrmem_create(&x, ((char *)pkt_hdr) + hdr_len,
+				      pkt_len - hdr_len - CLD_PKT_FTR_LEN,
+				      XDR_DECODE);
+			if (!xdr_cld_msg_ack_frag(&x, &ack)) {
+				xdr_destroy(&x);
+				snprintf(temp2, sizeof(temp2), "{MALFORMED}");
+				break;
+			}
+			snprintf(temp2, sizeof(temp2), "{seqid:%llx}",
+				 (unsigned long long) ack.seqid);
+			xdr_destroy(&x);
+			break;
+		}
+		default:
+			break;
+		}
+	} else {
+		snprintf(temp, sizeof(temp), "[CONT]");
+	}
+
+	snprintf(scratch, PKT_HDR_TO_STR_SCRATCH_LEN,
+		"<%s%s%s> "
+		"%s USER:'%s' SEQID:%llx %s",
+		((pkt.mi.order & CLD_PKT_IS_FIRST) ? "1st" : ""),
+		((pkt.mi.order & CLD_PKT_IS_LAST) ? "End" : ""),
+		(bad_magic ? "B" : ""),
+		temp, pkt.user,
+		(unsigned long long) seqid,
+		temp2);
+	xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+	return scratch;
+}
+
+void __cld_dump_buf(const void *buf, size_t len)
+{
+	const unsigned char *buff = buf;
+	size_t off = 0;
+	do {
+		int i;
+		for (i = 0; i < 8; i++) {
+			if (!len)
+				break;
+			printf("%02x ", buff[off++]);
+			len--;
+		}
+		printf("\n");
+	} while (len);
+}
diff -X /garz/tmp/dontdiff -urNp cld/server/cldb.h cld.rpcgen/server/cldb.h
--- cld/server/cldb.h	2010-02-04 01:54:16.000000000 -0500
+++ cld.rpcgen/server/cldb.h	2010-01-22 18:29:11.000000000 -0500
@@ -23,7 +23,7 @@
 #include <stdbool.h>
 #include <db.h>
 #include <cld-private.h>
-#include <cld_msg.h>
+#include <cld_msg_rpc.h>
 
 typedef uint64_t cldino_t;
 
diff -X /garz/tmp/dontdiff -urNp cld/server/cld.h cld.rpcgen/server/cld.h
--- cld/server/cld.h	2010-02-04 01:54:16.000000000 -0500
+++ cld.rpcgen/server/cld.h	2010-02-03 16:46:41.000000000 -0500
@@ -25,7 +25,7 @@
 #include <poll.h>
 #include <glib.h>
 #include "cldb.h"
-#include <cld_msg.h>
+#include <cld_msg_rpc.h>
 #include <cld_common.h>
 #include <hail_log.h>
 
@@ -72,20 +72,12 @@ struct session {
 	bool			dead;		/* session has ended */
 
 	/* huge buffer should always come last */
+	enum cld_msg_op		msg_op;
+	uint64_t		msg_xid;
 	unsigned int		msg_buf_len;
 	char			msg_buf[CLD_MAX_MSG_SZ];
 };
 
-struct msg_params {
-	int			sock_fd;
-	const struct client	*cli;
-	struct session		*sess;
-
-	const struct cld_packet	*pkt;
-	const void		*msg;
-	size_t			msg_len;
-};
-
 struct server_stats {
 	unsigned long		poll;		/* num. polls */
 	unsigned long		event;		/* events dispatched */
@@ -122,29 +114,59 @@ struct server {
 	struct server_stats	stats;		/* global statistics */
 };
 
+struct pkt_info {
+	struct cld_pkt_hdr *pkt;
+	struct session *sess;
+	uint64_t seqid;
+	uint64_t xid;
+	enum cld_msg_op op;
+	size_t hdr_len;
+};
+
 /* msg.c */
 extern int inode_lock_rescan(DB_TXN *txn, cldino_t inum);
-extern void msg_open(struct msg_params *);
-extern void msg_put(struct msg_params *);
-extern void msg_close(struct msg_params *);
-extern void msg_del(struct msg_params *);
-extern void msg_unlock(struct msg_params *);
-extern void msg_lock(struct msg_params *, bool);
-extern void msg_ack(struct msg_params *);
-extern void msg_get(struct msg_params *, bool);
+extern void msg_get(struct session *sess, const void *v);
+extern void msg_open(struct session *sess, const void *v);
+extern void msg_put(struct session *sess, const void *v);
+extern void msg_close(struct session *sess, const void *v);
+extern void msg_del(struct session *sess, const void *v);
+extern void msg_unlock(struct session *sess, const void *v);
+extern void msg_lock(struct session *sess, const void *v);
+extern void msg_ack(struct session *sess, uint64_t seqid);
 
 /* session.c */
 extern uint64_t next_seqid_le(uint64_t *seq);
-extern void pkt_init_pkt(struct cld_packet *dest, const struct cld_packet *src);
 extern guint sess_hash(gconstpointer v);
 extern gboolean sess_equal(gconstpointer _a, gconstpointer _b);
-extern void msg_new_sess(struct msg_params *, const struct client *);
-extern void msg_end_sess(struct msg_params *, const struct client *);
+extern void msg_new_sess(int sock_fd, const struct client *cli,
+			const struct pkt_info *info);
+extern void msg_end_sess(struct session *sess, uint64_t xid);
 extern struct raw_session *session_new_raw(const struct session *sess);
 extern void sessions_free(void);
-extern bool sess_sendmsg(struct session *sess, const void *msg_, size_t msglen,
-		  void (*done_cb)(struct session_outpkt *),
-		  void *done_data);
+
+/** Send a message as part of a session.
+ *
+ * @param sess		The session
+ * @param xdrproc	The XDR function to use to serialize the data
+ * @param xdrdata	The message data
+ * @param op		The op of the message
+ * @param done_cb	The callback to call when the message has been acked
+ * @param done_data	The data to give to done_cb
+ *
+ * @return		true only if the message was sent
+ */
+extern bool sess_sendmsg(struct session *sess,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op op,
+	void (*done_cb)(struct session_outpkt *), void *done_data);
+
+/** Send a generic response message.
+ *
+ * @param sess		The session
+ * @param code		The error code to send
+ */
+extern void sess_sendresp_generic(struct session *sess,
+				  enum cle_err_codes code);
+
 extern int session_dispose(DB_TXN *txn, struct session *sess);
 extern int session_remove_locks(DB_TXN *txn, uint8_t *sid, uint64_t fh,
 				cldino_t inum, bool *waiter);
@@ -156,12 +178,27 @@ extern struct hail_log srv_log;
 extern struct timeval current_time;
 extern int udp_tx(int sock_fd, struct sockaddr *, socklen_t,
 	    const void *, size_t);
-extern void resp_copy(struct cld_msg_resp *resp, const struct cld_msg_hdr *src);
-extern void resp_err(struct session *sess,
-	      const struct cld_msg_hdr *src, enum cle_err_codes errcode);
-extern void resp_ok(struct session *sess, const struct cld_msg_hdr *src);
 extern const char *user_key(const char *user);
 
+/** Transmit a single packet.
+ *
+ * This function doesn't provide error-retransmission logic.
+ * It can't handle messages that are bigger than a single packet.
+ *
+ * @param fd		Socket to send the response on
+ * @param cli		Client address data
+ * @param sid		The session-id to use. Must be of length CLD_SID_SZ
+ * @param seqid		The sequence id to use
+ * @param xdrproc	The XDR function to use to serialize the data
+ * @param xdrdata	The message data
+ * @param op		The op of message to send
+ *
+ * @return		true only on success
+ */
+extern void simple_sendmsg(int fd, const struct client *cli,
+	uint64_t sid, const char *username, uint64_t seqid,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op op);
+
 /* util.c */
 extern int write_pid_file(const char *pid_fn);
 extern void syslogerr(const char *prefix);
diff -X /garz/tmp/dontdiff -urNp cld/server/Makefile.am cld.rpcgen/server/Makefile.am
--- cld/server/Makefile.am	2010-02-04 01:20:51.000000000 -0500
+++ cld.rpcgen/server/Makefile.am	2010-02-02 23:10:54.000000000 -0500
@@ -1,5 +1,6 @@
 
 INCLUDES	= -I$(top_srcdir)/include	\
+		  -I$(top_srcdir)/lib		\
 		  @GLIB_CFLAGS@			\
 		  -DCLD_LIBDIR=\""$(libdir)"\"	\
 		  -DCLD_LOCAL_STATE_DIR="\"$(localstatedir)\""
@@ -8,7 +9,8 @@ sbin_PROGRAMS	= cld cldbadm
 
 cld_SOURCES	= cldb.h cld.h \
 		  ../lib/common.c ../lib/libtimer.c ../lib/pkt.c \
-		  cldb.c msg.c server.c session.c util.c
+		  cldb.c msg.c server.c session.c util.c \
+		  ../lib/cld_msg_rpc_xdr.c
 cld_LDADD	= @CRYPTO_LIBS@ @GLIB_LIBS@ @DB4_LIBS@
 
 cldbadm_SOURCES	= cldb.h cldbadm.c
diff -X /garz/tmp/dontdiff -urNp cld/server/msg.c cld.rpcgen/server/msg.c
--- cld/server/msg.c	2010-02-04 01:20:51.000000000 -0500
+++ cld.rpcgen/server/msg.c	2010-02-03 01:23:17.000000000 -0500
@@ -25,6 +25,8 @@
 #include <syslog.h>
 #include <openssl/sha.h>
 #include <cld-private.h>
+#include <cld_common.h>
+#include <cld_msg_rpc.h>
 #include "cld.h"
 
 enum {
@@ -247,12 +249,11 @@ static int inode_notify(DB_TXN *txn, cld
 		}
 
 		memset(&me, 0, sizeof(me));
-		memcpy(me.hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-		me.hdr.op = CMO_EVENT;
-		me.fh = h.fh;
-		me.events = cpu_to_le32(deleted ? CE_DELETED : CE_UPDATED);
+		me.fh = le64_to_cpu(h.fh);
+		me.events = deleted ? CE_DELETED : CE_UPDATED;
 
-		if (!sess_sendmsg(sess, &me, sizeof(me), NULL, NULL))
+		if (!sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_event,
+				  (void *)&me, CMO_EVENT, NULL, NULL))
 			break;
 	}
 
@@ -375,12 +376,11 @@ int inode_lock_rescan(DB_TXN *txn, cldin
 		}
 
 		memset(&me, 0, sizeof(me));
-		memcpy(me.hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-		me.hdr.op = CMO_EVENT;
-		me.fh = lock.fh;
-		me.events = cpu_to_le32(CE_LOCKED);
+		me.fh = le64_to_cpu(lock.fh);
+		me.events = CE_LOCKED;
 
-		if (!sess_sendmsg(sess, &me, sizeof(me), NULL, NULL))
+		if (!sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_event,
+				(void *)&me, CMO_EVENT, NULL, NULL))
 			break;
 	}
 
@@ -388,12 +388,10 @@ int inode_lock_rescan(DB_TXN *txn, cldin
 	return rc;
 }
 
-void msg_get(struct msg_params *mp, bool metadata_only)
+void msg_get(struct session *sess, const void *v)
 {
-	const struct cld_msg_get *msg = mp->msg;
-	struct cld_msg_get_resp *resp;
-	size_t resp_len;
-	uint64_t fh;
+	const struct cld_msg_get *get = v;
+	struct cld_msg_get_resp resp;
 	struct raw_handle *h = NULL;
 	struct raw_inode *inode = NULL;
 	enum cle_err_codes resp_rc = CLE_OK;
@@ -401,17 +399,10 @@ void msg_get(struct msg_params *mp, bool
 	uint32_t name_len, inode_size;
 	uint32_t omode;
 	int rc;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
-	void *p;
-
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	/* get filehandle from input msg */
-	fh = le64_to_cpu(msg->fh);
+	void *data_mem;
+	char *inode_name;
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -421,7 +412,7 @@ void msg_get(struct msg_params *mp, bool
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, get->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -442,42 +433,30 @@ void msg_get(struct msg_params *mp, bool
 		goto err_out;
 	}
 
-	name_len = le32_to_cpu(inode->ino_len);
 	inode_size = le32_to_cpu(inode->size);
-
-	resp_len = sizeof(*resp) + name_len +
-		   (metadata_only ? 0 : inode_size);
-	resp = alloca(resp_len);
-	if (!resp) {
-		resp_rc = CLE_OOM;
-		goto err_out;
-	}
-
-	HAIL_DEBUG(&srv_log, "%s: sizeof(resp) %zu, name_len %u, "
-		   "inode->size %u, resp_len %zu",
-		   __func__,
-		   sizeof(*resp), name_len,
-		   inode_size, resp_len);
+	HAIL_DEBUG(&srv_log, "GET-DEBUG: inode->size %u\n", inode_size);
 
 	/* return response containing inode metadata */
-	memset(resp, 0, resp_len);
-	resp_copy(&resp->resp, mp->msg);
-	resp->inum = inode->inum;
-	resp->ino_len = inode->ino_len;
-	resp->size = inode->size;
-	resp->version = inode->version;
-	resp->time_create = inode->time_create;
-	resp->time_modify = inode->time_modify;
-	resp->flags = inode->flags;
+	memset(&resp, 0, sizeof(resp));
+	resp.msg.code = CLE_OK;
+	resp.msg.xid_in = sess->msg_xid;
+	resp.inum = le64_to_cpu(inode->inum);
+	resp.vers = le64_to_cpu(inode->version);
+	resp.time_create = le64_to_cpu(inode->time_create);
+	resp.time_modify = le64_to_cpu(inode->time_modify);
+	resp.flags = le32_to_cpu(inode->flags);
 
-	p = (resp + 1);
-	memcpy(p, (inode + 1), name_len);
+	name_len = le32_to_cpu(inode->ino_len);
+	inode_name = alloca(name_len + 1);
+	snprintf(inode_name, name_len + 1, "%s", (char *)(inode + 1));
+	resp.inode_name = inode_name;
 
-	p += name_len;
+	resp.data.data_len = 0;
+	resp.data.data_val = NULL;
 
 	/* send data, if requested */
-	if (!metadata_only) {
-		void *data_mem;
+	data_mem = NULL;
+	if (sess->msg_op == CMO_GET) {
 		size_t data_mem_len;
 
 		rc = cldb_data_get(txn, inum, &data_mem, &data_mem_len,
@@ -486,22 +465,23 @@ void msg_get(struct msg_params *mp, bool
 		/* treat not-found as zero length file, as we may
 		 * not yet have created the data record
 		 */
-		if (rc == DB_NOTFOUND) {
-			resp->size = 0;
-			resp_len -= inode_size;
-		} else if (rc || (data_mem_len != inode_size)) {
-			if (!rc)
-				free(data_mem);
-			resp_rc = CLE_DB_ERR;
-			goto err_out;
-		} else {
-			memcpy(p, data_mem, data_mem_len);
-
-			free(data_mem);
+		if (rc != DB_NOTFOUND) {
+			if (rc || (data_mem_len != inode_size)) {
+				if (!rc)
+					free(data_mem);
+				resp_rc = CLE_DB_ERR;
+				goto err_out;
+			} else {
+				resp.data.data_len = data_mem_len;
+				resp.data.data_val = data_mem;
+			}
 		}
 	}
 
-	sess_sendmsg(sess, resp, resp_len, NULL, NULL);
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_get_resp,
+		     (void *)&resp, CMO_GET, NULL, NULL);
+	if (data_mem)
+		free(data_mem);
 
 	rc = txn->commit(txn, 0);
 	if (rc)
@@ -516,15 +496,14 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_get txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 	free(inode);
 }
 
-void msg_open(struct msg_params *mp)
+void msg_open(struct session *sess, const void *v)
 {
-	const struct cld_msg_open *msg = mp->msg;
-	struct session *sess = mp->sess;
+	const struct cld_msg_open *open = v;
 	struct cld_msg_open_resp resp;
 	const char *name;
 	struct raw_session *raw_sess = NULL;
@@ -535,24 +514,12 @@ void msg_open(struct msg_params *mp)
 	struct pathname_info pinfo;
 	void *parent_data = NULL;
 	size_t parent_len;
-	uint32_t msg_mode, msg_events;
 	uint64_t fh;
 	cldino_t inum;
 	enum cle_err_codes resp_rc = CLE_OK;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	msg_mode = le32_to_cpu(msg->mode);
-	msg_events = le32_to_cpu(msg->events);
-	name_len = le16_to_cpu(msg->name_len);
-
-	if (mp->msg_len < (sizeof(*msg) + name_len))
-		return;
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -560,11 +527,12 @@ void msg_open(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	name = mp->msg + sizeof(*msg);
+	name = open->inode_name;
+	name_len = strlen(name);
 
-	create = msg_mode & COM_CREATE;
-	excl = msg_mode & COM_EXCL;
-	do_dir = msg_mode & COM_DIRECTORY;
+	create = open->mode & COM_CREATE;
+	excl = open->mode & COM_EXCL;
+	do_dir = open->mode & COM_DIRECTORY;
 
 	if (!valid_inode_name(name, name_len) || (create && name_len < 2)) {
 		resp_rc = CLE_NAME_INVAL;
@@ -662,7 +630,7 @@ void msg_open(struct msg_params *mp)
 	inum = cldino_from_le(inode->inum);
 
 	/* alloc & init new handle; updates session's next_fh */
-	h = cldb_handle_new(sess, inum, msg_mode, msg_events);
+	h = cldb_handle_new(sess, inum, open->mode, open->events);
 	if (!h) {
 		HAIL_CRIT(&srv_log, "cannot allocate handle");
 		resp_rc = CLE_OOM;
@@ -717,11 +685,12 @@ void msg_open(struct msg_params *mp)
 	free(raw_sess);
 	free(h);
 
-	resp_copy(&resp.resp, mp->msg);
-	resp.resp.code = cpu_to_le32(CLE_OK);
-	resp.fh = cpu_to_le64(fh);
-	sess_sendmsg(sess, &resp, sizeof(resp), NULL, NULL);
-
+	memset(&resp, 0, sizeof(resp));
+	resp.msg.xid_in = sess->msg_xid;
+	resp.msg.code = CLE_OK;
+	resp.fh = fh;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_open_resp,
+		     (void *)&resp, CMO_OPEN, NULL, NULL);
 	return;
 
 err_out:
@@ -729,7 +698,7 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_open txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(parent_data);
 	free(parent);
 	free(inode);
@@ -737,45 +706,18 @@ err_out_noabort:
 	free(h);
 }
 
-void msg_put(struct msg_params *mp)
+void msg_put(struct session *sess, const void *v)
 {
-	const struct cld_msg_put *msg = mp->msg;
-	struct session *sess = mp->sess;
-	uint64_t fh;
+	const struct cld_msg_put *put = v;
 	struct raw_handle *h = NULL;
 	struct raw_inode *inode = NULL;
 	enum cle_err_codes resp_rc = CLE_OK;
-	const void *mem;
 	int rc;
 	cldino_t inum;
-	uint32_t omode, data_size;
+	uint32_t omode;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as message header */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	/* make sure additional input data as large as expected */
-	data_size = le32_to_cpu(msg->data_size);
-	if (data_size > CLD_MAX_PAYLOAD_SZ) {
-		HAIL_ERR(&srv_log, "%s: can't PUT %d bytes of data: "
-			"%d is the maximum.\n",
-			__func__, data_size, CLD_MAX_PAYLOAD_SZ);
-		resp_rc = CLE_BAD_PKT;
-		goto err_out_noabort;
-	}
-	if (mp->msg_len != (data_size + sizeof(*msg))) {
-		HAIL_INFO(&srv_log, "PUT len mismatch: msg len %zu, "
-			  "wanted %zu + %u (== %zu)",
-			  mp->msg_len,
-			  sizeof(*msg), data_size, data_size + sizeof(*msg));
-		resp_rc = CLE_BAD_PKT;
-		goto err_out_noabort;
-	}
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -784,7 +726,7 @@ void msg_put(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, put->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -807,15 +749,14 @@ void msg_put(struct msg_params *mp)
 	}
 
 	/* store contig. data area in db */
-	mem = (msg + 1);
 	rc = cldb_data_put(txn, inum,
-			   mem, data_size, 0);
+			   put->data.data_val, put->data.data_len, 0);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
 	}
 
-	inode->size = cpu_to_le32(data_size);
+	inode->size = cpu_to_le32(put->data.data_len);
 
 	/* update inode */
 	rc = inode_touch(txn, inode);
@@ -831,7 +772,7 @@ void msg_put(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 
 	free(h);
 	free(inode);
@@ -842,31 +783,23 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_put txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 
 	free(h);
 	free(inode);
 }
 
-void msg_close(struct msg_params *mp)
+void msg_close(struct session *sess, const void *v)
 {
-	const struct cld_msg_close *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_close *close = v;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
 	struct raw_handle *h = NULL;
 	cldino_t lock_inum = 0;
 	bool waiter = false;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -875,7 +808,7 @@ void msg_close(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, DB_RMW);
+	rc = cldb_handle_get(txn, sess->sid, close->fh, &h, DB_RMW);
 	if (rc) {
 		if (rc == DB_NOTFOUND)
 			resp_rc = CLE_FH_INVAL;
@@ -888,7 +821,7 @@ void msg_close(struct msg_params *mp)
 		lock_inum = cldino_from_le(h->inum);
 
 	/* delete handle from db */
-	rc = cldb_handle_del(txn, sess->sid, fh);
+	rc = cldb_handle_del(txn, sess->sid, close->fh);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
@@ -896,7 +829,7 @@ void msg_close(struct msg_params *mp)
 
 	/* remove locks, if any */
 	rc = session_remove_locks(txn, sess->sid,
-				  fh, lock_inum, &waiter);
+				  close->fh, lock_inum, &waiter);
 	if (rc) {
 		resp_rc = CLE_DB_ERR;
 		goto err_out;
@@ -918,7 +851,7 @@ void msg_close(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -927,16 +860,15 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_close txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
-void msg_del(struct msg_params *mp)
+void msg_del(struct session *sess, const void *v)
 {
-	const struct cld_msg_del *msg = mp->msg;
+	const struct cld_msg_del *del = v;
 	enum cle_err_codes resp_rc = CLE_OK;
 	int rc, name_len;
-	const char *name;
 	struct pathname_info pinfo;
 	struct raw_inode *parent = NULL, *ino = NULL;
 	void *parent_data = NULL;
@@ -949,23 +881,13 @@ void msg_del(struct msg_params *mp)
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	name_len = le16_to_cpu(msg->name_len);
-
-	if (mp->msg_len < (sizeof(*msg) + name_len))
-		return;
-
-	name = mp->msg + sizeof(*msg);
-
-	if (!valid_inode_name(name, name_len) || (name_len < 2)) {
+	name_len = strlen(del->inode_name);
+	if (!valid_inode_name(del->inode_name, name_len) || (name_len < 2)) {
 		resp_rc = CLE_NAME_INVAL;
 		goto err_out_noabort;
 	}
 
-	pathname_parse(name, name_len, &pinfo);
+	pathname_parse(del->inode_name, name_len, &pinfo);
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -991,7 +913,8 @@ void msg_del(struct msg_params *mp)
 	}
 
 	/* read inode to be deleted */
-	rc = cldb_inode_get_byname(txn, name, name_len, &ino, false, 0);
+	rc = cldb_inode_get_byname(txn, del->inode_name, name_len,
+				   &ino, false, 0);
 	if (rc) {
 		if (rc == DB_NOTFOUND)
 			resp_rc = CLE_NAME_INVAL;
@@ -1100,7 +1023,7 @@ void msg_del(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(mp->sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(ino);
 	free(parent);
 	free(parent_data);
@@ -1111,31 +1034,23 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_del txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(ino);
 	free(parent);
 	free(parent_data);
 }
 
-void msg_unlock(struct msg_params *mp)
+void msg_unlock(struct session *sess, const void *v)
 {
-	const struct cld_msg_unlock *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_unlock *unlock = v;
 	struct raw_handle *h = NULL;
 	cldino_t inum;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
 	uint32_t omode;
-	struct session *sess = mp->sess;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
 
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
 		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
@@ -1144,7 +1059,7 @@ void msg_unlock(struct msg_params *mp)
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, unlock->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -1159,7 +1074,7 @@ void msg_unlock(struct msg_params *mp)
 	}
 
 	/* attempt to given lock on filehandle */
-	rc = cldb_lock_del(txn, sess->sid, fh, inum);
+	rc = cldb_lock_del(txn, sess->sid, unlock->fh, inum);
 	if (rc) {
 		resp_rc = CLE_LOCK_INVAL;
 		goto err_out;
@@ -1172,7 +1087,7 @@ void msg_unlock(struct msg_params *mp)
 		goto err_out_noabort;
 	}
 
-	resp_ok(sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -1181,30 +1096,22 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_unlock txn abort");
 err_out_noabort:
-	resp_err(sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
-void msg_lock(struct msg_params *mp, bool wait)
+void msg_lock(struct session *sess, const void *v)
 {
-	const struct cld_msg_lock *msg = mp->msg;
-	uint64_t fh;
+	const struct cld_msg_lock *lock = v;
+	bool wait = (sess->msg_op == CMO_LOCK);
 	struct raw_handle *h = NULL;
 	cldino_t inum;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
-	uint32_t lock_flags, omode;
+	uint32_t omode;
 	bool acquired = false;
 	DB_ENV *dbenv = cld_srv.cldb.env;
 	DB_TXN *txn;
-	struct session *sess = mp->sess;
-
-	/* make sure input data as large as expected */
-	if (mp->msg_len < sizeof(*msg))
-		return;
-
-	fh = le64_to_cpu(msg->fh);
-	lock_flags = le32_to_cpu(msg->flags);
 
 	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
 	if (rc) {
@@ -1214,7 +1121,7 @@ void msg_lock(struct msg_params *mp, boo
 	}
 
 	/* read handle from db */
-	rc = cldb_handle_get(txn, sess->sid, fh, &h, 0);
+	rc = cldb_handle_get(txn, sess->sid, lock->fh, &h, 0);
 	if (rc) {
 		resp_rc = CLE_FH_INVAL;
 		goto err_out;
@@ -1229,8 +1136,8 @@ void msg_lock(struct msg_params *mp, boo
 	}
 
 	/* attempt to add lock */
-	rc = cldb_lock_add(txn, sess->sid, fh, inum,
-			   lock_flags & CLF_SHARED, wait, &acquired);
+	rc = cldb_lock_add(txn, sess->sid, lock->fh, inum,
+			   lock->flags & CLF_SHARED, wait, &acquired);
 	if (rc) {
 		if (rc == DB_KEYEXIST)
 			resp_rc = CLE_LOCK_CONFLICT;
@@ -1253,7 +1160,7 @@ void msg_lock(struct msg_params *mp, boo
 	}
 
 	/* lock was acquired immediately */
-	resp_ok(mp->sess, mp->msg);
+	sess_sendresp_generic(sess, CLE_OK);
 	free(h);
 	return;
 
@@ -1262,7 +1169,7 @@ err_out:
 	if (rc)
 		dbenv->err(dbenv, rc, "msg_lock txn abort");
 err_out_noabort:
-	resp_err(mp->sess, mp->msg, resp_rc);
+	sess_sendresp_generic(sess, resp_rc);
 	free(h);
 }
 
diff -X /garz/tmp/dontdiff -urNp cld/server/server.c cld.rpcgen/server/server.c
--- cld/server/server.c	2010-02-04 01:54:16.000000000 -0500
+++ cld.rpcgen/server/server.c	2010-02-03 16:49:16.000000000 -0500
@@ -37,6 +37,7 @@
 #include <openssl/hmac.h>
 #include <cld-private.h>
 #include "cld.h"
+#include <cld_pkt.h>
 
 #define PROGRAM_NAME "cld"
 
@@ -125,8 +126,6 @@ int udp_tx(int sock_fd, struct sockaddr 
 {
 	ssize_t src;
 
-	HAIL_DEBUG(&srv_log, "%s, fd %d", __func__, sock_fd);
-
 	src = sendto(sock_fd, data, data_len, 0, addr, addr_len);
 	if (src < 0 && errno != EAGAIN)
 		HAIL_ERR(&srv_log, "%s sendto (fd %d, data_len %u): %s",
@@ -139,36 +138,6 @@ int udp_tx(int sock_fd, struct sockaddr 
 	return 0;
 }
 
-void resp_copy(struct cld_msg_resp *resp, const struct cld_msg_hdr *src)
-{
-	memcpy(&resp->hdr, src, sizeof(*src));
-	resp->code = 0;
-	resp->rsv = 0;
-	resp->xid_in = src->xid;
-}
-
-void resp_err(struct session *sess,
-	      const struct cld_msg_hdr *src, enum cle_err_codes errcode)
-{
-	struct cld_msg_resp resp;
-
-	resp_copy(&resp, src);
-	__cld_rand64(&resp.hdr.xid);
-	resp.code = cpu_to_le32(errcode);
-
-	if (sess->sock_fd <= 0) {
-		HAIL_ERR(&srv_log, "Nul sock in response");
-		return;
-	}
-
-	sess_sendmsg(sess, &resp, sizeof(resp), NULL, NULL);
-}
-
-void resp_ok(struct session *sess, const struct cld_msg_hdr *src)
-{
-	resp_err(sess, src, CLE_OK);
-}
-
 const char *user_key(const char *user)
 {
 	/* TODO: better auth scheme.
@@ -181,266 +150,426 @@ const char *user_key(const char *user)
 	return user;	/* our secret key */
 }
 
-static void show_msg(const struct cld_msg_hdr *msg)
+static int udp_rx_handle(struct session *sess,
+		void (*msg_handler)(struct session *sess, const void *),
+		xdrproc_t xdrproc, void *xdrdata)
+{
+	XDR xin;
+	xdrmem_create(&xin, sess->msg_buf, sess->msg_buf_len, XDR_DECODE);
+	if (!xdrproc(&xin, xdrdata)) {
+		HAIL_DEBUG(&srv_log, "%s: couldn't parse %s message",
+			   __func__, __cld_opstr(sess->msg_op));
+		xdr_destroy(&xin);
+		return CLE_BAD_PKT;
+	}
+	msg_handler(sess, xdrdata);
+	xdr_free(xdrproc, xdrdata);
+	xdr_destroy(&xin);
+	return 0;
+}
+
+/** Recieve a UDP packet
+ *
+ * @param sock_fd	The UDP socket we received the packet on
+ * @param cli		Client address data
+ * @param info		Packet information
+ * @param raw_pkt	The raw packet buffer
+ * @param raw_len	Length of the raw packet buffer
+ *
+ * @return		An error code if we should send an error message
+ *			response. CLE_OK if we are done.
+ */
+static enum cle_err_codes udp_rx(int sock_fd,
+	const struct client *cli, struct pkt_info *info,
+	const char *raw_pkt, size_t raw_len)
 {
-	switch (msg->op) {
-	case CMO_NOP:
-	case CMO_NEW_SESS:
-	case CMO_OPEN:
-	case CMO_GET_META:
+	struct cld_pkt_hdr *pkt = info->pkt;
+	struct session *sess = info->sess;
+
+	if (sess) {
+		size_t msg_len;
+
+		/* advance sequence id's and update last-contact timestamp */
+		sess->last_contact = current_time.tv_sec;
+		sess->sock_fd = sock_fd;
+
+		if (info->op != CMO_ACK) {
+			/* received message - update session */
+			sess->next_seqid_in++;
+		}
+
+		/* copy message fragment into reassembly buffer */
+		if (pkt->mi.order & CLD_PKT_IS_FIRST) {
+			sess->msg_op = info->op;
+			sess->msg_xid = info->xid;
+			sess->msg_buf_len = 0;
+		}
+		msg_len = raw_len - info->hdr_len - CLD_PKT_FTR_LEN;
+		if ((sess->msg_buf_len + msg_len) > CLD_MAX_MSG_SZ)
+			return CLE_BAD_PKT;
+
+		memcpy(sess->msg_buf + sess->msg_buf_len,
+			raw_pkt + info->hdr_len, msg_len);
+		sess->msg_buf_len += msg_len;
+	}
+
+	if (!(pkt->mi.order & CLD_PKT_IS_LAST)) {
+		struct cld_msg_ack_frag ack;
+		ack.seqid = info->seqid;
+
+		/* transmit ack-partial-msg response (once, without retries) */
+		simple_sendmsg(sock_fd, cli, pkt->sid,
+			       pkt->user, 0xdeadbeef,
+			       (xdrproc_t)xdr_cld_msg_ack_frag, (void *)&ack,
+			       CMO_ACK_FRAG);
+		return CLE_OK;
+	}
+
+	/* Handle a complete message */
+	switch (info->op) {
 	case CMO_GET:
-	case CMO_PUT:
-	case CMO_CLOSE:
-	case CMO_DEL:
-	case CMO_LOCK:
-	case CMO_UNLOCK:
+		/* fall through */
+	case CMO_GET_META: {
+		struct cld_msg_get get = {0};
+		return udp_rx_handle(sess, msg_get,
+				     (xdrproc_t)xdr_cld_msg_get, &get);
+	}
+	case CMO_OPEN: {
+		struct cld_msg_open open = {0};
+		return udp_rx_handle(sess, msg_open,
+				     (xdrproc_t)xdr_cld_msg_open, &open);
+	}
+	case CMO_PUT: {
+		struct cld_msg_put put = {0};
+		return udp_rx_handle(sess, msg_put,
+				     (xdrproc_t)xdr_cld_msg_put, &put);
+	}
+	case CMO_CLOSE: {
+		struct cld_msg_close close = {0};
+		return udp_rx_handle(sess, msg_close,
+				     (xdrproc_t)xdr_cld_msg_close, &close);
+	}
+	case CMO_DEL: {
+		struct cld_msg_del del = {0};
+		return udp_rx_handle(sess, msg_del,
+				     (xdrproc_t)xdr_cld_msg_del, &del);
+	}
+	case CMO_UNLOCK: {
+		struct cld_msg_unlock unlock = {0};
+		return udp_rx_handle(sess, msg_unlock,
+				     (xdrproc_t)xdr_cld_msg_unlock, &unlock);
+	}
 	case CMO_TRYLOCK:
+		/* fall through */
+	case CMO_LOCK: {
+		struct cld_msg_lock lock = {0};
+		return udp_rx_handle(sess, msg_lock,
+				     (xdrproc_t)xdr_cld_msg_lock, &lock);
+	}
 	case CMO_ACK:
+		msg_ack(sess, info->seqid);
+		return 0;
+	case CMO_NOP:
+		sess_sendresp_generic(sess, CLE_OK);
+		return 0;
+	case CMO_NEW_SESS:
+		msg_new_sess(sock_fd, cli, info);
+		return 0;
 	case CMO_END_SESS:
-	case CMO_PING:
-	case CMO_NOT_MASTER:
-	case CMO_EVENT:
-	case CMO_ACK_FRAG:
-		HAIL_DEBUG(&srv_log, "msg: op %s, xid %llu",
-			   __cld_opstr(msg->op),
-			   (unsigned long long) le64_to_cpu(msg->xid));
-		break;
+		msg_end_sess(sess, info->xid);
+		return 0;
+	default:
+		HAIL_DEBUG(&srv_log, "%s: unexpected %s packet",
+			   __func__, __cld_opstr(info->op));
+		/* do nothing */
+		return 0;
 	}
 }
 
-static void udp_rx_msg(const struct client *cli, const struct cld_packet *pkt,
-		       const struct cld_msg_hdr *msg, struct msg_params *mp)
+/** Parse a packet's header. Verify that the magic number is correct.
+ *
+ * @param raw_pkt	Pointer to the packet data
+ * @param raw_len	Length of the raw data
+ * @param pkt		(out param) the packet header
+ * @param hdr_len	(out param) the length of the packet header
+ *
+ * @return		true on success; false if this packet is garbage
+ */
+static bool parse_pkt_header(const char *raw_pkt, int raw_len,
+			struct cld_pkt_hdr *pkt, ssize_t *hdr_len)
 {
-	struct session *sess = mp->sess;
+	XDR xin;
+	static const char * const magic = CLD_PKT_MAGIC;
 
-	if (srv_log.verbose)
-		show_msg(msg);
+	if (raw_len <= CLD_PKT_FTR_LEN) {
+		HAIL_DEBUG(&srv_log, "%s: packet is too short: only "
+			   "%d bytes", __func__, raw_len);
+		return false;
+	}
+	xdrmem_create(&xin, (void *)raw_pkt, raw_len - CLD_PKT_FTR_LEN,
+		      XDR_DECODE);
+	memset(pkt, 0, sizeof(*pkt));
+	if (!xdr_cld_pkt_hdr(&xin, pkt)) {
+		HAIL_DEBUG(&srv_log, "%s: couldn't parse packet header",
+			   __func__);
+		xdr_destroy(&xin);
+		return false;
+	}
+	*hdr_len = xdr_getpos(&xin);
+	xdr_destroy(&xin);
+
+	if (memcmp((void *)&pkt->magic, magic, sizeof(pkt->magic))) {
+		HAIL_DEBUG(&srv_log, "%s: bad magic number", __func__);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)pkt);
+		return false;
+	}
+
+	return true;
+}
+
+/** Look up some information about a packet, including its session and the
+ * type of message it carries.
+ *
+ * @param pkt		The packet's header
+ * @param raw_pkt	Pointer to the raw packet data
+ * @param raw_len	Length of the raw packet data
+ * @param info		(out param) Information about the packet
+ *
+ * @return		true on success; false if this packet is garbage
+ */
+static bool get_pkt_info(struct cld_pkt_hdr *pkt,
+			 const char *raw_pkt, size_t raw_len,
+			 size_t hdr_len, struct pkt_info *info)
+{
+	struct cld_pkt_ftr *foot;
+	struct session *s;
+
+	memset(info, 0, sizeof(info));
+	info->pkt = pkt;
+	info->sess = s = g_hash_table_lookup(cld_srv.sessions, &pkt->sid);
+	foot = (struct cld_pkt_ftr *)
+			(raw_pkt + (raw_len - CLD_PKT_FTR_LEN));
+	info->seqid = le64_to_cpu(foot->seqid);
+
+	if (pkt->mi.order & CLD_PKT_IS_FIRST) {
+		info->xid = pkt->mi.cld_pkt_msg_info_u.mi.xid;
+		info->op = pkt->mi.cld_pkt_msg_info_u.mi.op;
+	} else {
+		if (!s) {
+			HAIL_DEBUG(&srv_log, "%s: packet is not first, "
+				"but also not part of an existing session. "
+				"Protocol error.", __func__);
+			return false;
+		}
+		info->xid = s->msg_xid;
+		info->op = s->msg_op;
+	}
+	info->hdr_len = hdr_len;
+	return true;
+}
 
-	switch(msg->op) {
-	case CMO_NOP:
-		resp_ok(sess, msg);
-		break;
+/** Verify that the client session matches IP and username
+ *
+ * @param info		Packet information
+ * @param cli		Client address data
+ *
+ * @return		0 on success; error code otherwise
+ */
+static enum cle_err_codes validate_pkt_session(const struct pkt_info *info,
+						const struct client *cli)
+{
+	struct session *sess = info->sess;
 
-	case CMO_NEW_SESS:	msg_new_sess(mp, cli); break;
-	case CMO_END_SESS:	msg_end_sess(mp, cli); break;
-	case CMO_OPEN:		msg_open(mp); break;
-	case CMO_GET:		msg_get(mp, false); break;
-	case CMO_GET_META:	msg_get(mp, true); break;
-	case CMO_PUT:		msg_put(mp); break;
-	case CMO_CLOSE:		msg_close(mp); break;
-	case CMO_DEL:		msg_del(mp); break;
-	case CMO_UNLOCK:	msg_unlock(mp); break;
-	case CMO_LOCK:		msg_lock(mp, true); break;
-	case CMO_TRYLOCK:	msg_lock(mp, false); break;
-	case CMO_ACK:		msg_ack(mp); break;
+	if (!sess) {
+		/* Packets that don't belong to a session must be new-session
+		 * packets attempting to establish a session. */
+		if (info->op != CMO_NEW_SESS) {
+			HAIL_DEBUG(&srv_log, "%s: packet doesn't belong to a "
+				   "session,but has type %d",
+				   __func__, info->op);
+			return CLE_SESS_INVAL;
+		}
+		return 0;
+	}
 
-	default:
-		/* do nothing */
-		break;
+	if (info->op == CMO_NEW_SESS) {
+		HAIL_DEBUG(&srv_log, "%s: Tried to create a new session, "
+			   "but a session with that ID already exists.",
+			   __func__);
+		return CLE_SESS_EXISTS;
 	}
-}
 
-static void pkt_ack_frag(int sock_fd,
-			 const struct client *cli,
-			 const struct cld_packet *pkt)
-{
-	size_t alloc_len;
-	struct cld_packet *outpkt;
-	struct cld_msg_ack_frag *ack_msg;
-	void *p;
-	const char *secret_key;
+	/* verify that client session matches IP */
+	if ((sess->addr_len != cli->addr_len) ||
+	     memcmp(&sess->addr, &cli->addr, sess->addr_len)) {
+		HAIL_DEBUG(&srv_log, "%s: sess->addr doesn't match packet "
+			   "addr", __func__);
+		return CLE_SESS_INVAL;
+	}
+
+	/* verify that client session matches username */
+	if (strncmp(info->pkt->user, sess->user, CLD_MAX_USERNAME)) {
+		HAIL_DEBUG(&srv_log, "%s: session doesn't match packet's  "
+			   "username", __func__);
+		return CLE_SESS_INVAL;
+	}
+
+	if (sess->dead) {
+		HAIL_DEBUG(&srv_log, "%s: packet session is dead",
+			   __func__);
+		return CLE_SESS_INVAL;
+	}
+
+	return 0;
+}
 
-	alloc_len = sizeof(*outpkt) + sizeof(*ack_msg) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	ack_msg = (struct cld_msg_ack_frag *) (outpkt + 1);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, pkt);
-
-	memcpy(ack_msg->hdr.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	__cld_rand64(&ack_msg->hdr.xid);
-	ack_msg->hdr.op = CMO_ACK_FRAG;
-	ack_msg->seqid = pkt->seqid;
-
-	p = outpkt;
-	secret_key = user_key(outpkt->user);
-	__cld_authsign(&srv_log, secret_key, p, alloc_len - SHA_DIGEST_LENGTH,
-		       p + alloc_len - SHA_DIGEST_LENGTH);
-
-	HAIL_DEBUG(&srv_log, "%s: "
-		   "sid " SIDFMT ", op %s, seqid %llu",
-		   __func__,
-		   SIDARG(outpkt->sid), __cld_opstr(ack_msg->hdr.op),
-		   (unsigned long long) le64_to_cpu(outpkt->seqid));
-
-	/* transmit ack-partial-msg response (once, without retries) */
-	udp_tx(sock_fd, (struct sockaddr *) &cli->addr, cli->addr_len,
-	       outpkt, alloc_len);
-}
-
-static void udp_rx(int sock_fd,
-		   const struct client *cli,
-		   const void *raw_pkt, size_t pkt_len)
-{
-	const struct cld_packet *pkt = raw_pkt;
-	struct cld_packet *outpkt;
-	const struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-	struct session *sess = NULL;
-	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_resp *resp;
-	struct msg_params mp;
-	size_t alloc_len;
-	uint32_t pkt_flags;
-	bool first_frag, last_frag, have_new_sess, have_ack, have_put;
+/** Check a packet's cryptographic signature
+ *
+ * @param raw_pkt	Pointer to the packet data
+ * @param raw_len	Length of the raw data
+ * @param pkt		the packet header
+ *
+ * @return		0 on success; error code otherwise
+ */
+static enum cle_err_codes validate_pkt_signature(const char *raw_pkt,
+		int raw_len, const struct cld_pkt_hdr *pkt)
+{
+	struct cld_pkt_ftr *foot;
 	const char *secret_key;
 	int auth_rc;
-	void *p;
 
+	foot = (struct cld_pkt_ftr *)
+			(raw_pkt + (raw_len - CLD_PKT_FTR_LEN));
 	secret_key = user_key(pkt->user);
 
-	/* verify pkt data integrity and credentials via HMAC signature */
 	auth_rc = __cld_authcheck(&srv_log, secret_key, raw_pkt,
-				  pkt_len - SHA_DIGEST_LENGTH,
-				  raw_pkt + pkt_len - SHA_DIGEST_LENGTH);
+				  raw_len - SHA_DIGEST_LENGTH,
+				  foot->sha);
 	if (auth_rc) {
 		HAIL_DEBUG(&srv_log, "auth failed, code %d", auth_rc);
-		resp_rc = CLE_SIG_INVAL;
-		goto err_out;
-	}
-
-	pkt_flags = le32_to_cpu(pkt->flags);
-	first_frag = pkt_flags & CPF_FIRST;
-	last_frag = pkt_flags & CPF_LAST;
-	have_new_sess = first_frag && (msg->op == CMO_NEW_SESS);
-	have_ack = first_frag && (msg->op == CMO_ACK);
-	have_put = first_frag && (msg->op == CMO_PUT);
-
-	/* look up client session, verify it matches IP and username */
-	sess = g_hash_table_lookup(cld_srv.sessions, pkt->sid);
-	if (sess &&
-	    ((sess->addr_len != cli->addr_len) ||
-	     memcmp(&sess->addr, &cli->addr, sess->addr_len) ||
-	     strncmp(pkt->user, sess->user, CLD_MAX_USERNAME) ||
-	     sess->dead)) {
-		resp_rc = CLE_SESS_INVAL;
-		goto err_out;
+		return CLE_SIG_INVAL;
 	}
 
-	mp.sock_fd = sock_fd;
-	mp.cli = cli;
-	mp.sess = sess;
-	mp.pkt = pkt;
-	mp.msg = msg;
-	mp.msg_len = pkt_len - sizeof(*pkt) - SHA_DIGEST_LENGTH;
-
-	HAIL_DEBUG(&srv_log, "%s pkt: len %zu, seqid %llu, sid " SIDFMT ", "
-		   "flags %s%s, user %s",
-		   __func__,
-		   pkt_len, (unsigned long long) le64_to_cpu(pkt->seqid),
-		   SIDARG(pkt->sid),
-		   first_frag ? "F" : "", last_frag ? "L" : "",
-		   pkt->user);
-
-	/* advance sequence id's and update last-contact timestamp */
-	if (!have_new_sess) {
-		if (!sess) {
-			resp_rc = CLE_SESS_INVAL;
-			goto err_out;
-		}
-
-		sess->last_contact = current_time.tv_sec;
-		sess->sock_fd = sock_fd;
-
-		if (!have_ack) {
-			/* eliminate duplicates; do not return any response */
-			if (le64_to_cpu(pkt->seqid) != sess->next_seqid_in) {
-				HAIL_DEBUG(&srv_log, "%s: dropping dup", __func__);
-				return;
-			}
+	return 0;
+}
 
-			/* received message - update session */
-			sess->next_seqid_in++;
-		}
-	} else {
-		if (sess) {
-			/* eliminate duplicates; do not return any response */
-			if (le64_to_cpu(pkt->seqid) != sess->next_seqid_in) {
-				HAIL_DEBUG(&srv_log, "%s: dropping dup", __func__);
-				return;
-			}
+/** Check if this packet is a duplicate
+ *
+ * @param info		Packet info
+ *
+ * @return		true only if the packet is a duplicate
+ */
+static bool packet_is_dupe(const struct pkt_info *info)
+{
+	if (!info->sess)
+		return false;
+	if (info->op == CMO_ACK)
+		return false;
 
-			resp_rc = CLE_SESS_EXISTS;
-			goto err_out;
-		}
+	/* Check sequence IDs to discover if this packet is a dupe */
+	if (info->seqid != info->sess->next_seqid_in) {
+		HAIL_DEBUG(&srv_log, "dropping dup with seqid %llu "
+			   "(expected %llu).",
+			   (unsigned long long) info->seqid,
+			   (unsigned long long) info->sess->next_seqid_in);
+		return true;
 	}
 
-	/* copy message fragment into reassembly buffer */
-	if (sess) {
-		if (first_frag)
-			sess->msg_buf_len = 0;
-
-		if ((sess->msg_buf_len + mp.msg_len) > CLD_MAX_MSG_SZ) {
-			resp_rc = CLE_BAD_PKT;
-			goto err_out;
-		}
-
-		memcpy(&sess->msg_buf[sess->msg_buf_len], msg, mp.msg_len);
-		sess->msg_buf_len += mp.msg_len;
-
-		if (!last_frag) {
-			pkt_ack_frag(sock_fd, cli, pkt);
-			return;
-		}
+	return false;
+}
 
-		mp.msg = msg = (struct cld_msg_hdr *) sess->msg_buf;
-		mp.msg_len = sess->msg_buf_len;
+void simple_sendmsg(int fd, const struct client *cli,
+		    uint64_t sid, const char *user, uint64_t seqid,
+		    xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op op)
+{
+	XDR xhdr, xmsg;
+	struct cld_pkt_hdr pkt;
+	struct cld_pkt_msg_infos *infos;
+	struct cld_pkt_ftr *foot;
+	const char *secret_key;
+	char *buf;
+	size_t msg_len, hdr_len, buf_len;
+	int auth_rc;
 
-		if ((srv_log.verbose > 1) && !first_frag)
-			HAIL_DEBUG(&srv_log, "    final message size %u",
-				   sess->msg_buf_len);
+	/* Set up the packet header */
+	memset(&pkt, 0, sizeof(cld_pkt_hdr));
+	memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+	pkt.sid = sid;
+	pkt.user = (char *)user;
+	pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+	infos = &pkt.mi.cld_pkt_msg_info_u.mi;
+	__cld_rand64(&infos->xid);
+	infos->op = op;
+
+	/* Determine sizes */
+	msg_len = xdr_sizeof(xdrproc, (void *)xdrdata);
+	if (msg_len > CLD_MAX_MSG_SZ) {
+		HAIL_ERR(&srv_log, "%s: tried to put %d message bytes in a "
+			 "single packet. Maximum message bytes per packet "
+			 "is %d", __func__, msg_len, CLD_MAX_PKT_MSG_SZ);
+		return;
 	}
+	hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, &pkt);
+	buf_len = msg_len + hdr_len + CLD_PKT_FTR_LEN;
+	buf = alloca(buf_len);
+
+	/* Serialize data */
+	xdrmem_create(&xhdr, buf, hdr_len, XDR_ENCODE);
+	if (!xdr_cld_pkt_hdr(&xhdr, &pkt)) {
+		xdr_destroy(&xhdr);
+		HAIL_ERR(&srv_log, "%s: xdr_cld_pkt_hdr failed",
+			 __func__);
+		return;
+	}
+	xdr_destroy(&xhdr);
+	xdrmem_create(&xmsg, buf + hdr_len, msg_len, XDR_ENCODE);
+	if (!xdrproc(&xmsg, (void *)xdrdata)) {
+		xdr_destroy(&xmsg);
+		HAIL_ERR(&srv_log, "%s: xdrproc failed", __func__);
+		return;
+	}
+	xdr_destroy(&xmsg);
 
-	if (last_frag)
-		udp_rx_msg(cli, pkt, msg, &mp);
-	return;
-
-err_out:
-	/* transmit error response (once, without retries) */
-	alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	resp = (struct cld_msg_resp *) (outpkt + 1);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, pkt);
-
-	resp_copy(resp, msg);
-	resp->code = cpu_to_le32(resp_rc);
-
-	p = outpkt;
-	secret_key = user_key(outpkt->user);
-	__cld_authsign(&srv_log, secret_key, p, alloc_len - SHA_DIGEST_LENGTH,
-		       p + alloc_len - SHA_DIGEST_LENGTH);
-
-	HAIL_DEBUG(&srv_log, "%s err: "
-		   "sid " SIDFMT ", op %s, seqid %llu, code %d",
-		   __func__,
-		   SIDARG(outpkt->sid), __cld_opstr(resp->hdr.op),
-		   (unsigned long long) le64_to_cpu(outpkt->seqid),
-		   resp_rc);
-
-	udp_tx(sock_fd, (struct sockaddr *) &cli->addr, cli->addr_len,
-	       outpkt, alloc_len);
+	foot = (struct cld_pkt_ftr *)
+		(buf + (buf_len - SHA_DIGEST_LENGTH));
+	foot->seqid = cpu_to_le64(seqid);
+	secret_key = user_key(user);
+
+	auth_rc =__cld_authsign(&srv_log, secret_key, buf,
+				buf_len - SHA_DIGEST_LENGTH,
+				foot->sha);
+	if (auth_rc)
+		HAIL_ERR(&srv_log, "%s: authsign failed: %d",
+			 __func__, auth_rc);
+
+	udp_tx(fd, (struct sockaddr *) &cli->addr, cli->addr_len,
+		buf, buf_len);
+}
+
+static void simple_sendresp(int sock_fd, const struct client *cli,
+		    const struct pkt_info *info, enum cle_err_codes code)
+{
+	const struct cld_pkt_hdr *pkt = info->pkt;
+	struct cld_msg_generic_resp resp;
+	resp.code = code;
+	resp.xid_in = info->xid;
+
+	simple_sendmsg(sock_fd, cli, pkt->sid, pkt->user, info->seqid,
+		       (xdrproc_t)xdr_cld_msg_generic_resp, (void *)&resp,
+		       info->op);
 }
 
 static bool udp_srv_event(int fd, short events, void *userdata)
 {
 	struct client cli;
 	char host[64];
-	ssize_t rrc;
+	ssize_t rrc, hdr_len;
 	struct msghdr hdr;
 	struct iovec iov[2];
-	uint8_t raw_pkt[CLD_RAW_MSG_SZ], ctl_msg[CLD_RAW_MSG_SZ];
-	struct cld_packet *pkt = (struct cld_packet *) raw_pkt;
+	char raw_pkt[CLD_RAW_MSG_SZ], ctl_msg[CLD_RAW_MSG_SZ];
+	struct cld_pkt_hdr pkt;
+	struct pkt_info info;
+	enum cle_err_codes err;
 
 	memset(&cli, 0, sizeof(cli));
 
@@ -470,46 +599,52 @@ static bool udp_srv_event(int fd, short 
 
 	HAIL_DEBUG(&srv_log, "client %s message (%d bytes)", host, (int) rrc);
 
-	/* if it is complete garbage, drop immediately */
-	if ((rrc < (sizeof(*pkt) + SHA_DIGEST_LENGTH)) ||
-	    (memcmp(pkt->magic, CLD_PKT_MAGIC, sizeof(pkt->magic)))) {
+	if (!parse_pkt_header(raw_pkt, rrc, &pkt, &hdr_len)) {
 		cld_srv.stats.garbage++;
-		return true; /* continue main loop; do NOT terminate server */
+		return true;
 	}
 
-	if (cld_srv.cldb.is_master && cld_srv.cldb.up)
-		udp_rx(fd, &cli, raw_pkt, rrc);
+	if (!get_pkt_info(&pkt, raw_pkt, rrc, hdr_len, &info)) {
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		cld_srv.stats.garbage++;
+		return true;
+	}
+
+	if (packet_is_dupe(&info)) {
+		/* silently drop dupes */
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
 
-	else {
-		struct cld_packet *outpkt;
-		struct cld_msg_hdr *msg = (struct cld_msg_hdr *) (pkt + 1);
-		struct cld_msg_resp *resp;
-		size_t alloc_len;
-		const char *secret_key;
-		void *p;
-
-		alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-		outpkt = alloca(alloc_len);
-		memset(outpkt, 0, alloc_len);
-
-		pkt_init_pkt(outpkt, pkt);
-
-		/* transmit not-master error msg */
-		resp = (struct cld_msg_resp *) (outpkt + 1);
-		resp_copy(resp, msg);
-		resp->hdr.op = CMO_NOT_MASTER;
-
-		p = outpkt;
-		secret_key = user_key(outpkt->user);
-		__cld_authsign(&srv_log, secret_key, p,
-			       alloc_len - SHA_DIGEST_LENGTH,
-			       p + alloc_len - SHA_DIGEST_LENGTH);
+	err = validate_pkt_session(&info, &cli);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
 
-		udp_tx(fd, (struct sockaddr *) &cli.addr, cli.addr_len,
-		       outpkt, alloc_len);
+	err = validate_pkt_signature(raw_pkt, rrc, &pkt);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
 	}
 
-	return true;	/* continue main loop; do NOT terminate server */
+	if (!(cld_srv.cldb.is_master && cld_srv.cldb.up)) {
+		simple_sendmsg(fd, &cli, pkt.sid, pkt.user, 0xdeadbeef,
+			       (xdrproc_t)xdr_void, NULL, CMO_NOT_MASTER);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
+
+	err = udp_rx(fd, &cli, &info, raw_pkt, rrc);
+	if (err) {
+		simple_sendresp(fd, &cli, &info, err);
+		xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+		return true;
+	}
+	xdr_free((xdrproc_t)xdr_cld_pkt_hdr, (char *)&pkt);
+	return true;
 }
 
 static void add_chkpt_timer(void)
diff -X /garz/tmp/dontdiff -urNp cld/server/session.c cld.rpcgen/server/session.c
--- cld/server/session.c	2010-02-04 01:20:51.000000000 -0500
+++ cld.rpcgen/server/session.c	2010-02-03 16:49:55.000000000 -0500
@@ -29,15 +29,15 @@
 #include <openssl/sha.h>
 #include <cld-private.h>
 #include "cld.h"
+#include <cld_pkt.h>
 
 struct session_outpkt {
 	struct session		*sess;
 
-	struct cld_packet	*pkt;
+	char			*pkt_data;
 	size_t			pkt_len;
 
 	uint64_t		next_retry;
-	uint64_t		src_seqid;
 	unsigned int		refs;
 
 	void			(*done_cb)(struct session_outpkt *);
@@ -60,26 +60,6 @@ uint64_t next_seqid_le(uint64_t *seq)
 	return rc;
 }
 
-void pkt_init_pkt(struct cld_packet *dest, const struct cld_packet *src)
-{
-	memset(dest, 0, sizeof(*dest));
-	memcpy(dest->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	dest->seqid = cpu_to_le64(0xdeadbeef);
-	memcpy(dest->sid, src->sid, CLD_SID_SZ);
-	dest->flags = cpu_to_le32(CPF_FIRST | CPF_LAST);
-	strncpy(dest->user, src->user, CLD_MAX_USERNAME - 1);
-}
-
-static void pkt_init_sess(struct cld_packet *dest, struct session *sess)
-{
-	memset(dest, 0, sizeof(*dest));
-	memcpy(dest->magic, CLD_PKT_MAGIC, CLD_MAGIC_SZ);
-	dest->seqid = next_seqid_le(&sess->next_seqid_out);
-	memcpy(dest->sid, sess->sid, CLD_SID_SZ);
-	dest->flags = 0;
-	strncpy(dest->user, sess->user, CLD_MAX_USERNAME - 1);
-}
-
 guint sess_hash(gconstpointer v)
 {
 	const struct session *sess = v;
@@ -397,20 +377,6 @@ static void session_ping_done(struct ses
 	outpkt->sess->ping_open = false;
 }
 
-static void session_ping(struct session *sess)
-{
-	struct cld_msg_hdr resp;
-
-	memset(&resp, 0, sizeof(resp));
-	memcpy(resp.magic, CLD_MSG_MAGIC, CLD_MAGIC_SZ);
-	__cld_rand64(&resp.xid);
-	resp.op = CMO_PING;
-
-	sess->ping_open = true;
-
-	sess_sendmsg(sess, &resp, sizeof(resp), session_ping_done, NULL);
-}
-
 static void session_timeout(struct cld_timer *timer)
 {
 	struct session *sess = timer->userdata;
@@ -424,8 +390,12 @@ static void session_timeout(struct cld_t
 	if (!sess->dead && (sess_expire > now)) {
 		if (!sess->ping_open &&
 		    (sess_expire > (sess->last_contact + (CLD_SESS_TIMEOUT / 2) &&
-		    (sess->sock_fd > 0))))
-			session_ping(sess);
+		    (sess->sock_fd > 0)))) {
+			sess->ping_open = true;
+			sess_sendmsg(sess,
+				     (xdrproc_t)xdr_void, NULL, CMO_PING,
+				     session_ping_done, NULL);
+		}
 
 		cld_timer_add(&cld_srv.timers, &sess->timer,
 			      now + ((sess_expire - now) / 2) + 1);
@@ -519,8 +489,8 @@ static struct session_outpkt *op_alloc(s
 	if (!op)
 		return NULL;
 
-	op->pkt = calloc(1, pkt_len);
-	if (!op->pkt) {
+	op->pkt_data = calloc(1, pkt_len);
+	if (!op->pkt_data) {
 		free(op);
 		return NULL;
 	}
@@ -542,7 +512,7 @@ static void op_unref(struct session_outp
 			return;
 	}
 
-	free(op->pkt);
+	free(op->pkt_data);
 	free(op);
 }
 
@@ -556,18 +526,10 @@ static int sess_retry_output(struct sess
 
 	tmp = sess->out_q;
 	while (tmp) {
-		struct cld_packet *outpkt;
-		struct cld_msg_hdr *outmsg;
 		struct session_outpkt *op;
-		GList *tmp1;
-
-		tmp1 = tmp;
+		op = tmp->data;
 		tmp = tmp->next;
 
-		op = tmp1->data;
-		outpkt = op->pkt;
-		outmsg = (struct cld_msg_hdr *) (outpkt + 1);
-
 		if (!next_retry || (op->next_retry < next_retry))
 			*next_retry_out = next_retry = op->next_retry;
 
@@ -575,15 +537,15 @@ static int sess_retry_output(struct sess
 			continue;
 
 		if (srv_log.verbose) {
-			HAIL_DEBUG(&srv_log, "%s: retrying: sid " SIDFMT ", "
-				   "op %s, seqid %llu",
+			char scratch[PKT_HDR_TO_STR_SCRATCH_LEN];
+			HAIL_DEBUG(&srv_log, "%s: retrying %s",
 				   __func__,
-				   SIDARG(outpkt->sid), __cld_opstr(outmsg->op),
-				   (unsigned long long) le64_to_cpu(outpkt->seqid));
+				   __cld_pkt_hdr_to_str(scratch, op->pkt_data,
+				  			op->pkt_len));
 		}
 
 		rc = udp_tx(sess->sock_fd, (struct sockaddr *) &sess->addr,
-			    sess->addr_len, op->pkt, op->pkt_len);
+			    sess->addr_len, op->pkt_data, op->pkt_len);
 		if (rc)
 			break;
 
@@ -616,153 +578,161 @@ static void session_outq(struct session 
 	sess->out_q = g_list_concat(sess->out_q, new_pkts);
 }
 
-bool sess_sendmsg(struct session *sess, const void *msg_, size_t msglen,
-		  void (*done_cb)(struct session_outpkt *),
-		  void *done_data)
-{
-	struct cld_packet *outpkt;
-	unsigned int n_pkts, i;
-	size_t pkt_len, msg_left = msglen;
-	struct session_outpkt **pkts, *op;
-	GList *tmp_root = NULL;
-	const void *p;
-	bool first_frag = true;
-
-	if (msglen > CLD_MAX_MSG_SZ) {
-		HAIL_ERR(&srv_log, "%s: message too big (%zu bytes)\n",
-			__func__, msglen);
-		return false;
-	}
-
-	n_pkts = (msglen / CLD_MAX_PKT_MSG_SZ);
-	n_pkts += (msglen % CLD_MAX_PKT_MSG_SZ) ? 1 : 0;
-	pkts = alloca(sizeof(struct session_outpkt *) * n_pkts);
-
-	if (srv_log.verbose) {
-		const struct cld_msg_hdr *hdr = msg_;
-		const struct cld_msg_resp *rsp;
-
-		switch (hdr->op) {
-		/* This is the command set that gets to cldc_rx_generic */
-		case CMO_NOP:
-		case CMO_CLOSE:
-		case CMO_DEL:
-		case CMO_LOCK:
-		case CMO_UNLOCK:
-		case CMO_TRYLOCK:
-		case CMO_PUT:
-		case CMO_NEW_SESS:
-		case CMO_END_SESS:
-		case CMO_OPEN:
-		case CMO_GET_META:
-		case CMO_GET:
-			rsp = (struct cld_msg_resp *) msg_;
-			HAIL_DEBUG(&srv_log, "%s: "
-				   "sid " SIDFMT ", op %s, msglen %u, code %u, "
-				   "xid %llu, xid_in %llu",
-				   __func__,
-				   SIDARG(sess->sid),
-				   __cld_opstr(hdr->op),
-				   (unsigned int) msglen,
-				   le32_to_cpu(rsp->code),
-				   (unsigned long long) le64_to_cpu(hdr->xid),
-				   (unsigned long long) le64_to_cpu(rsp->xid_in));
-			break;
-		default:
-			HAIL_DEBUG(&srv_log, "%s: "
-				   "sid " SIDFMT ", op %s, msglen %u",
-				   __func__,
-				   SIDARG(sess->sid),
-				   __cld_opstr(hdr->op),
-				   (unsigned int) msglen);
-		}
-	}
+bool sess_sendmsg(struct session *sess,
+	xdrproc_t xdrproc, const void *xdrdata, enum cld_msg_op msg_op,
+	void (*done_cb)(struct session_outpkt *), void *done_data)
+{
+	XDR xmsg;
+	size_t msg_rem, msg_len, msg_chunk_len;
+	char *msg_bytes, *msg_cur;
+	GList *tmp_list, *new_pkts = NULL;
+	int first, last;
+	const char *secret_key;
 
-	/* pass 1: perform allocations */
-	for (i = 0; i < n_pkts; i++) {
-		pkts[i] = op = op_alloc(sizeof(*outpkt) +
-					CLD_MAX_PKT_MSG_SZ +
-					SHA_DIGEST_LENGTH);
-		if (!op)
-			goto err_out;
+	secret_key = user_key(sess->user);
 
-		tmp_root = g_list_append(tmp_root, op);
+	/* Use XDR to serialize the message */
+	msg_len = xdr_sizeof(xdrproc, (void *)xdrdata);
+	if (msg_len > CLD_MAX_MSG_SZ)
+		return false;
+	msg_bytes = alloca(msg_len);
+	xdrmem_create(&xmsg, msg_bytes, msg_len, XDR_ENCODE);
+	if (!xdrproc(&xmsg, (void *)xdrdata)) {
+		xdr_destroy(&xmsg);
+		HAIL_ERR(&srv_log, "%s: xdrproc failed", __func__);
+		return false;
 	}
+	xdr_destroy(&xmsg);
 
-	/* pass 2: fill packets */
-	p = msg_;
-	for (i = 0; i < n_pkts; i++) {
-		struct cld_msg_hdr *outmsg;
-		void *outmsg_mem;
-		size_t copy_len;
-		void *out_p;
-		const char *secret_key;
-
-		op = pkts[i];
+	/* Break the message into packets */
+	first = 1;
+	msg_rem = msg_len;
+	msg_cur = msg_bytes;
+	do {
+		XDR xout;
+		struct cld_pkt_hdr pkt;
+		size_t hdr_len;
+		struct session_outpkt *op;
 
+		if (msg_rem <= CLD_MAX_PKT_MSG_SZ) {
+			msg_chunk_len = msg_rem;
+			last = 1;
+		} else {
+			msg_chunk_len = CLD_MAX_PKT_MSG_SZ;
+			last = 0;
+		}
+
+		/* Set up packet header */
+		memset(&pkt, 0, sizeof(pkt));
+		memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
+		memcpy(&pkt.sid, sess->sid, CLD_SID_SZ);
+		pkt.user = sess->user;
+		if (first) {
+			struct cld_pkt_msg_infos *infos =
+				&pkt.mi.cld_pkt_msg_info_u.mi;
+			if (last)
+				pkt.mi.order = CLD_PKT_ORD_FIRST_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_FIRST;
+			__cld_rand64(&infos->xid);
+			infos->op = msg_op;
+		} else {
+			if (last)
+				pkt.mi.order = CLD_PKT_ORD_LAST;
+			else
+				pkt.mi.order = CLD_PKT_ORD_MID;
+		}
+
+		/* Allocate space and initialize session_outpkt structure */
+		hdr_len = xdr_sizeof((xdrproc_t)xdr_cld_pkt_hdr, (void *)&pkt);
+		op = op_alloc(hdr_len + msg_chunk_len + CLD_PKT_FTR_LEN);
+		if (!op) {
+			HAIL_DEBUG(&srv_log, "%s: op_alloc failed",
+				   __func__);
+			goto err_out;
+		}
 		op->sess = sess;
-
-		outpkt = op->pkt;
-		pkt_len = op->pkt_len;
-
-		outmsg_mem = (outpkt + 1);
-		outmsg = outmsg_mem;
-
-		/* init packet header */
-		pkt_init_sess(outpkt, sess);
-
-		if (first_frag) {
-			first_frag = false;
-			outpkt->flags |= cpu_to_le32(CPF_FIRST);
+		op->next_retry = current_time.tv_sec + CLD_RETRY_START;
+		op->done_cb = done_cb;
+		op->done_data = done_data;
+		xdrmem_create(&xout, op->pkt_data, hdr_len, XDR_ENCODE);
+		if (!xdr_cld_pkt_hdr(&xout, &pkt)) {
+			xdr_destroy(&xout);
+			HAIL_ERR(&srv_log, "%s: xdr_cld_pkt_hdr failed",
+				  __func__);
+			goto err_out;
 		}
+		xdr_destroy(&xout);
 
-		copy_len = MIN(pkt_len - sizeof(*outpkt) - SHA_DIGEST_LENGTH,
-			       msg_left);
-		memcpy(outmsg_mem, p, copy_len);
-
-		p += copy_len;
-		msg_left -= copy_len;
-
-		op->pkt_len =
-		pkt_len = sizeof(*outpkt) + copy_len + SHA_DIGEST_LENGTH;
-
-		if (!msg_left) {
-			op->done_cb = done_cb;
-			op->done_data = done_data;
-
-			outpkt->flags |= cpu_to_le32(CPF_LAST);
+		/* Fill in data */
+		memcpy(op->pkt_data + hdr_len, msg_cur, msg_chunk_len);
+		msg_cur += msg_chunk_len;
+		msg_rem -= msg_chunk_len;
+		first = 0;
+
+		new_pkts = g_list_prepend(new_pkts, op);
+	} while (!last);
+
+	/* add sequence IDs and SHAs */
+	new_pkts = g_list_reverse(new_pkts);
+	for (tmp_list = g_list_first(new_pkts);
+	     tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op =
+			(struct session_outpkt *) tmp_list->data;
+		struct cld_pkt_ftr *foot = (struct cld_pkt_ftr *)
+			(op->pkt_data + (op->pkt_len - CLD_PKT_FTR_LEN));
+		int ret;
+
+		foot->seqid = next_seqid_le(&sess->next_seqid_out);
+		ret = __cld_authsign(&srv_log, secret_key,
+				op->pkt_data, op->pkt_len - SHA_DIGEST_LENGTH,
+				foot->sha);
+		if (ret) {
+			HAIL_ERR(&srv_log, "%s: authsign failed: %d",
+				 __func__, ret);
+			goto err_out;
 		}
+	}
 
-		op->next_retry = current_time.tv_sec + CLD_RETRY_START;
-
-		out_p = outpkt;
-		secret_key = user_key(outpkt->user);
-		if (__cld_authsign(&srv_log, secret_key, out_p,
-				   pkt_len - SHA_DIGEST_LENGTH,
-				   out_p + pkt_len - SHA_DIGEST_LENGTH))
-			goto err_out;	/* FIXME: we free all pkts -- wrong! */
-
+	/* send packets */
+	for (tmp_list = g_list_first(new_pkts);
+	     tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op =
+			(struct session_outpkt *) tmp_list->data;
 		udp_tx(sess->sock_fd, (struct sockaddr *) &sess->addr,
-		       sess->addr_len, outpkt, pkt_len);
+			sess->addr_len, op->pkt_data, op->pkt_len);
 	}
 
-	session_outq(sess, tmp_root);
+	session_outq(sess, new_pkts);
 
 	return true;
 
 err_out:
-	for (i = 0; i < n_pkts; i++)
-		op_unref(pkts[i]);
-	g_list_free(tmp_root);
+	for (tmp_list = g_list_first(new_pkts); tmp_list;
+	     tmp_list = g_list_next(tmp_list)) {
+		struct session_outpkt *op;
+		op = (struct session_outpkt *)tmp_list->data;
+		op_unref(op);
+	}
+	g_list_free(new_pkts);
 	return false;
 }
 
-void msg_ack(struct msg_params *mp)
+void sess_sendresp_generic(struct session *sess, enum cle_err_codes code)
+{
+	struct cld_msg_generic_resp resp;
+	resp.code = code;
+	resp.xid_in = sess->msg_xid;
+
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+		     (void *)&resp, sess->msg_op, NULL, NULL);
+}
+
+void msg_ack(struct session *sess, uint64_t seqid)
 {
-	struct cld_packet *outpkt;
-	struct cld_msg_hdr *outmsg;
 	GList *tmp, *tmp1;
-	struct session *sess = mp->sess;
 	struct session_outpkt *op;
 
 	if (!sess->out_q)
@@ -771,19 +741,22 @@ void msg_ack(struct msg_params *mp)
 	/* look through output queue */
 	tmp = sess->out_q;
 	while (tmp) {
+		uint64_t op_seqid;
+		struct cld_pkt_ftr *foot;
 		tmp1 = tmp;
 		tmp = tmp->next;
 
 		op = tmp1->data;
-		outpkt = op->pkt;
-		outmsg = (struct cld_msg_hdr *) (outpkt + 1);
+		foot = (struct cld_pkt_ftr *)
+			(op->pkt_data + (op->pkt_len - CLD_PKT_FTR_LEN));
+		op_seqid = le64_to_cpu(foot->seqid);
 
 		/* if matching seqid found, we ack'd a message in out_q */
-		if (mp->pkt->seqid != outpkt->seqid)
+		if (seqid != op_seqid)
 			continue;
 
 		HAIL_DEBUG(&srv_log, "    expiring seqid %llu",
-			   (unsigned long long) le64_to_cpu(outpkt->seqid));
+			   (unsigned long long) op_seqid);
 
 		/* remove and delete the ack'd msg; call ack'd callback */
 		sess->out_q = g_list_delete_link(sess->out_q, tmp1);
@@ -797,19 +770,17 @@ void msg_ack(struct msg_params *mp)
 		cld_timer_del(&cld_srv.timers, &sess->retry_timer);
 }
 
-void msg_new_sess(struct msg_params *mp, const struct client *cli)
+void msg_new_sess(int sock_fd, const struct client *cli,
+		  const struct pkt_info *info)
 {
+	const struct cld_pkt_hdr *pkt = info->pkt;
 	DB *db = cld_srv.cldb.sessions;
 	struct raw_session raw_sess;
 	struct session *sess = NULL;
 	DBT key, val;
 	int rc;
 	enum cle_err_codes resp_rc = CLE_OK;
-	struct cld_msg_resp *resp;
-	struct cld_packet *outpkt;
-	size_t alloc_len;
-	const char *secret_key;
-	void *p;
+	struct cld_msg_generic_resp resp;
 
 	sess = session_new();
 	if (!sess) {
@@ -818,17 +789,17 @@ void msg_new_sess(struct msg_params *mp,
 	}
 
 	/* build raw_session database record */
-	memcpy(sess->sid, mp->pkt->sid, sizeof(sess->sid));
+	memcpy(sess->sid, &pkt->sid, sizeof(sess->sid));
 	memcpy(&sess->addr, &cli->addr, sizeof(sess->addr));
 
-	strncpy(sess->user, mp->pkt->user, sizeof(sess->user));
-	sess->user[sizeof(sess->user) - 1] = 0;
+	snprintf(sess->user, sizeof(sess->user), "%s",
+		pkt->user);
 
-	sess->sock_fd = mp->sock_fd;
+	sess->sock_fd = sock_fd;
 	sess->addr_len = cli->addr_len;
 	strncpy(sess->ipaddr, cli->addr_host, sizeof(sess->ipaddr));
 	sess->last_contact = current_time.tv_sec;
-	sess->next_seqid_in = le64_to_cpu(mp->pkt->seqid) + 1;
+	sess->next_seqid_in = info->seqid + 1;
 
 	session_encode(&raw_sess, sess);
 
@@ -865,34 +836,26 @@ void msg_new_sess(struct msg_params *mp,
 	cld_timer_add(&cld_srv.timers, &sess->timer,
 		      time(NULL) + (CLD_SESS_TIMEOUT / 2));
 
-	resp_ok(sess, mp->msg);
+	/* send new-sess reply */
+	resp.code = CLE_OK;
+	resp.xid_in = info->xid;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+		     (void *)&resp, CMO_NEW_SESS, NULL, NULL);
+
 	return;
 
 err_out:
 	session_free(sess, true);
 
-	alloc_len = sizeof(*outpkt) + sizeof(*resp) + SHA_DIGEST_LENGTH;
-	outpkt = alloca(alloc_len);
-	memset(outpkt, 0, alloc_len);
-
-	pkt_init_pkt(outpkt, mp->pkt);
-
-	resp = (struct cld_msg_resp *) (outpkt + 1);
-	resp_copy(resp, mp->msg);
-	resp->code = cpu_to_le32(resp_rc);
-
-	p = outpkt;
-	secret_key = user_key(outpkt->user);
-	__cld_authsign(&srv_log, secret_key, p, alloc_len - SHA_DIGEST_LENGTH,
-		       p + alloc_len - SHA_DIGEST_LENGTH);
-
-	HAIL_DEBUG(&srv_log, "%s err: sid " SIDFMT ", op %s, seqid %llu",
+	HAIL_DEBUG(&srv_log, "%s err: sid " SIDFMT ", op %s",
 		   __func__,
-		   SIDARG(outpkt->sid), __cld_opstr(resp->hdr.op),
-		   (unsigned long long) le64_to_cpu(outpkt->seqid));
+		   pkt->sid, __cld_opstr(CMO_NEW_SESS));
 
-	udp_tx(mp->sock_fd, (struct sockaddr *) &mp->cli->addr,
-	       mp->cli->addr_len, outpkt, alloc_len);
+	resp.code = resp_rc;
+	resp.xid_in = info->xid;
+	simple_sendmsg(sock_fd, cli, pkt->sid, pkt->user, 0xdeadbeef,
+		       (xdrproc_t)xdr_cld_msg_generic_resp, (void *)&resp,
+		       CMO_NEW_SESS);
 
 	HAIL_DEBUG(&srv_log, "NEW-SESS failed: %d", resp_rc);
 }
@@ -902,18 +865,18 @@ static void end_sess_done(struct session
 	session_trash(outpkt->sess);
 }
 
-void msg_end_sess(struct msg_params *mp, const struct client *cli)
+void msg_end_sess(struct session *sess, uint64_t xid)
 {
-	struct session *sess = mp->sess;
-	struct cld_msg_resp resp;
+	struct cld_msg_generic_resp resp;
 
 	/* do nothing; let message acknowledgement via
 	 * end_sess_done mark session dead
 	 */
-
-	memset(&resp, 0, sizeof(resp));
-	resp_copy(&resp, mp->msg);
-	sess_sendmsg(sess, &resp, sizeof(resp), end_sess_done, NULL);
+	resp.code = CLE_OK;
+	resp.xid_in = xid;
+	sess_sendmsg(sess, (xdrproc_t)xdr_cld_msg_generic_resp,
+			&resp, CMO_END_SESS,
+			end_sess_done, NULL);
 }
 
 /*
diff -X /garz/tmp/dontdiff -urNp cld/test/load-file-event.c cld.rpcgen/test/load-file-event.c
--- cld/test/load-file-event.c	2010-02-04 18:44:07.000000000 -0500
+++ cld.rpcgen/test/load-file-event.c	2010-02-04 18:45:07.000000000 -0500
@@ -146,7 +146,7 @@ static int read_1_cb(struct cldc_call_op
 {
 	struct run *rp = coptarg->private;
 	struct cldc_call_opts copts;
-	const char *data;
+	char *data;
 	size_t data_len;
 	int rc;
 
diff -X /garz/tmp/dontdiff -urNp cld/test/Makefile.am cld.rpcgen/test/Makefile.am
--- cld/test/Makefile.am	2010-02-04 01:20:51.000000000 -0500
+++ cld.rpcgen/test/Makefile.am	2010-02-02 22:30:24.000000000 -0500
@@ -1,5 +1,6 @@
 
 INCLUDES	= -I$(top_srcdir)/include	\
+		  -I$(top_srcdir)/lib		\
 		  @GLIB_CFLAGS@
 
 EXTRA_DIST =			\
diff -X /garz/tmp/dontdiff -urNp cld/tools/cldcli.c cld.rpcgen/tools/cldcli.c
--- cld/tools/cldcli.c	2010-02-04 18:44:18.000000000 -0500
+++ cld.rpcgen/tools/cldcli.c	2010-02-03 17:23:17.000000000 -0500
@@ -217,7 +217,7 @@ static int cb_ls_2(struct cldc_call_opts
 	struct cldc_call_opts copts = { NULL, };
 	struct cld_dirent_cur dc;
 	int rc, i;
-	const char *data;
+	char *data;
 	size_t data_len;
 	bool first = true;
 
@@ -297,7 +297,7 @@ static int cb_cat_2(struct cldc_call_opt
 {
 	struct cresp cresp = { .tcode = TC_FAILED, };
 	struct cldc_call_opts copts = { NULL, };
-	const char *data;
+	char *data;
 	size_t data_len;
 
 	if (errc != CLE_OK) {
@@ -345,7 +345,7 @@ static int cb_cp_cf_2(struct cldc_call_o
 {
 	struct cresp cresp = { .tcode = TC_FAILED, };
 	struct cldc_call_opts copts = { NULL, };
-	const char *data;
+	char *data;
 	size_t data_len;
 
 	if (errc != CLE_OK) {
diff -X /garz/tmp/dontdiff -urNp cld/tools/Makefile.am cld.rpcgen/tools/Makefile.am
--- cld/tools/Makefile.am	2010-01-29 00:36:25.000000000 -0500
+++ cld.rpcgen/tools/Makefile.am	2010-01-22 18:28:08.000000000 -0500
@@ -1,5 +1,6 @@
 
 INCLUDES	= -I$(top_srcdir)/include	\
+		  -I$(top_srcdir)/lib		\
 		  @GLIB_CFLAGS@
 
 bin_PROGRAMS	= cldcli

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-05  0:59           ` Jeff Garzik
@ 2010-02-05  4:14             ` Pete Zaitcev
  2010-02-05  6:06               ` Jeff Garzik
  0 siblings, 1 reply; 25+ messages in thread
From: Pete Zaitcev @ 2010-02-05  4:14 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Colin McCabe, Project Hail List

On Thu, 04 Feb 2010 19:59:45 -0500
Jeff Garzik <jeff@garzik.org> wrote:

> Attached is the XDR working tree, following
> [master 2a73620] libcldc: cosmetic changes, preparing for upcoming XDR merge

Looks ok, if it works. Some strange casts and formatting.

-- Pete

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-05  4:14             ` Pete Zaitcev
@ 2010-02-05  6:06               ` Jeff Garzik
  2010-02-05  7:16                 ` Pete Zaitcev
  0 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-02-05  6:06 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: Colin McCabe, Project Hail List

On 02/04/2010 11:14 PM, Pete Zaitcev wrote:
> On Thu, 04 Feb 2010 19:59:45 -0500
> Jeff Garzik<jeff@garzik.org>  wrote:
>
>> Attached is the XDR working tree, following
>> [master 2a73620] libcldc: cosmetic changes, preparing for upcoming XDR merge
>
> Looks ok, if it works. Some strange casts and formatting.

Which one... commit 2a73620, or the attached XDR working patch?

The XDR working tree definitely works.  While creating and committing 
the split-up patches, I have been bouncing between $current and $xdr, 
making changes to both, and making sure both continued to pass 'make check'.

	Jeff


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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-05  6:06               ` Jeff Garzik
@ 2010-02-05  7:16                 ` Pete Zaitcev
  0 siblings, 0 replies; 25+ messages in thread
From: Pete Zaitcev @ 2010-02-05  7:16 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Colin McCabe, Project Hail List

On Fri, 05 Feb 2010 01:06:32 -0500
Jeff Garzik <jeff@garzik.org> wrote:

> >> Attached is the XDR working tree, following
> >> [master 2a73620] libcldc: cosmetic changes, preparing for upcoming XDR merge
> >
> > Looks ok, if it works. Some strange casts and formatting.
> 
> Which one... commit 2a73620, or the attached XDR working patch?

I looked at the attached patch.

-- Pete

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-15 17:49 [PATCHv2] cld: use XDR for all messages Colin McCabe
  2010-01-21  1:38 ` Jeff Garzik
@ 2010-02-05  9:48 ` Jeff Garzik
  2010-02-05 18:49   ` Colin McCabe
  2010-02-09  9:20 ` Jeff Garzik
  2 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-02-05  9:48 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev

On 01/15/2010 12:49 PM, Colin McCabe wrote:
> This patch moves CLD from using manual data serialization to using XDR (via
> rpcgen). Both the packet header and the message body are now serialized and
> deserialized using XDR. This makes it easy to have a variable-length packet
> header, as well as a variable-length message body.
>
> This patch introduces a minor libcldc API change in struct cldc_call_opts.

> Signed-off-by: Colin McCabe<cmccabe@alumni.cmu.edu>

applied

I made yet more changes, and fixed a few minor bugs.

It passes 'make check' and 'make distcheck' without warnings.  We can 
pound out any rough edges via normal patches, from now on.

Thanks for your efforts, this definitely moves CLD forward in a positive 
direction.

	Jeff


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

* Re: [PATCH] Re: [PATCHv2] cld: use XDR for all messages
  2010-02-03 21:45                 ` Colin McCabe
@ 2010-02-05  9:51                   ` Jeff Garzik
  0 siblings, 0 replies; 25+ messages in thread
From: Jeff Garzik @ 2010-02-05  9:51 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev

On 02/03/2010 04:45 PM, Colin McCabe wrote:
> On Tue, Feb 2, 2010 at 10:35 PM, Jeff Garzik<jeff@garzik.org>  wrote:
>> I will continue whittling down the patch until it just contains the XDR
>> changes themselves.
>
> In tools/Makefile.am, I don't think you need $(top_srcdir)/lib any
> more, since cld_msg_rpc.h is now an installed header.


Build environment should always refer to the current in-tree header, not 
the installed header.  The header might not be installed (yet, at build 
time), or it might be an older version of the header you need.  Who 
knows what lives in system headers :)

	Jeff


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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-05  9:48 ` Jeff Garzik
@ 2010-02-05 18:49   ` Colin McCabe
  0 siblings, 0 replies; 25+ messages in thread
From: Colin McCabe @ 2010-02-05 18:49 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Project Hail List, Pete Zaitcev

Great news!

And thanks for your help in splitting what admittedly was a monster
patch into something more manageable. :)

Colin


On Fri, Feb 5, 2010 at 1:48 AM, Jeff Garzik <jeff@garzik.org> wrote:
> On 01/15/2010 12:49 PM, Colin McCabe wrote:
>>
>> This patch moves CLD from using manual data serialization to using XDR
>> (via
>> rpcgen). Both the packet header and the message body are now serialized
>> and
>> deserialized using XDR. This makes it easy to have a variable-length
>> packet
>> header, as well as a variable-length message body.
>>
>> This patch introduces a minor libcldc API change in struct cldc_call_opts.
>
>> Signed-off-by: Colin McCabe<cmccabe@alumni.cmu.edu>
>
> applied
>
> I made yet more changes, and fixed a few minor bugs.
>
> It passes 'make check' and 'make distcheck' without warnings.  We can pound
> out any rough edges via normal patches, from now on.
>
> Thanks for your efforts, this definitely moves CLD forward in a positive
> direction.
>
>        Jeff
>
>
>

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-01-15 17:49 [PATCHv2] cld: use XDR for all messages Colin McCabe
  2010-01-21  1:38 ` Jeff Garzik
  2010-02-05  9:48 ` Jeff Garzik
@ 2010-02-09  9:20 ` Jeff Garzik
  2010-02-09  9:38   ` Jeff Garzik
  2 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-02-09  9:20 UTC (permalink / raw)
  To: Colin McCabe; +Cc: Project Hail List, Pete Zaitcev


Useful per-packet cld [the daemon] verbose output seems to have 
disappeared?  Or did I miss a knob somewhere?

The only call I see to __cld_pkt_hdr_to_str() is in server/session.c, 
when retrying packets.  That does not cover incoming packets at all, 
which would be a regression from the pre-XDR codebase.

	Jeff




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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-09  9:20 ` Jeff Garzik
@ 2010-02-09  9:38   ` Jeff Garzik
  2010-02-09 15:47     ` Pete Zaitcev
  2010-02-09 23:13     ` Pete Zaitcev
  0 siblings, 2 replies; 25+ messages in thread
From: Jeff Garzik @ 2010-02-09  9:38 UTC (permalink / raw)
  To: Colin McCabe, Pete Zaitcev; +Cc: Project Hail List

On 02/09/2010 04:20 AM, Jeff Garzik wrote:
> Useful per-packet cld [the daemon] verbose output seems to have
> disappeared? Or did I miss a knob somewhere?

cldcli no longers dumps per-packet debug info at maximum verbosity, 
either.  Our built-in debug facilities have definitely regressed.

	Jeff



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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-09  9:38   ` Jeff Garzik
@ 2010-02-09 15:47     ` Pete Zaitcev
  2010-02-09 23:13     ` Pete Zaitcev
  1 sibling, 0 replies; 25+ messages in thread
From: Pete Zaitcev @ 2010-02-09 15:47 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Colin McCabe, Project Hail List

On Tue, 09 Feb 2010 04:38:21 -0500
Jeff Garzik <jeff@garzik.org> wrote:
> On 02/09/2010 04:20 AM, Jeff Garzik wrote:

> > Useful per-packet cld [the daemon] verbose output seems to have
> > disappeared? Or did I miss a knob somewhere?
> 
> cldcli no longers dumps per-packet debug info at maximum verbosity, 
> either.  Our built-in debug facilities have definitely regressed.

The cldcli may be ncld's fault, let me look. I left the assignment of
->verbose at least in one place. I did not touch anything at all on
the daemon side though.

-- Pete

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-09  9:38   ` Jeff Garzik
  2010-02-09 15:47     ` Pete Zaitcev
@ 2010-02-09 23:13     ` Pete Zaitcev
  2010-02-09 23:21       ` Jeff Garzik
  1 sibling, 1 reply; 25+ messages in thread
From: Pete Zaitcev @ 2010-02-09 23:13 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Colin McCabe, Project Hail List

On Tue, 09 Feb 2010 04:38:21 -0500
Jeff Garzik <jeff@garzik.org> wrote:
> On 02/09/2010 04:20 AM, Jeff Garzik wrote:

> > Useful per-packet cld [the daemon] verbose output seems to have
> > disappeared? Or did I miss a knob somewhere?
> 
> cldcli no longers dumps per-packet debug info at maximum verbosity, 
> either.  Our built-in debug facilities have definitely regressed.

I posted the patch for both server and client, which should fix it.
Looks like we became confused about what the exact meaning for
log.verbose is, since historically we had 2: one means
"enable LOG_DEBUG" and the other "enable per-packet dumping"
(which is really verbose in normal operation). So the patch
splits the two, thus permitting cldcli to control verbosity
precisely. And no more commented-out assignments.

-- Pete

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-09 23:13     ` Pete Zaitcev
@ 2010-02-09 23:21       ` Jeff Garzik
  2010-02-10  0:21         ` Pete Zaitcev
  0 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-02-09 23:21 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: Colin McCabe, Project Hail List

On 02/09/2010 06:13 PM, Pete Zaitcev wrote:
> On Tue, 09 Feb 2010 04:38:21 -0500
> Jeff Garzik<jeff@garzik.org>  wrote:
>> On 02/09/2010 04:20 AM, Jeff Garzik wrote:
>
>>> Useful per-packet cld [the daemon] verbose output seems to have
>>> disappeared? Or did I miss a knob somewhere?
>>
>> cldcli no longers dumps per-packet debug info at maximum verbosity,
>> either.  Our built-in debug facilities have definitely regressed.
>
> I posted the patch for both server and client, which should fix it.
> Looks like we became confused about what the exact meaning for
> log.verbose is, since historically we had 2: one means
> "enable LOG_DEBUG" and the other "enable per-packet dumping"
> (which is really verbose in normal operation). So the patch
> splits the two, thus permitting cldcli to control verbosity
> precisely. And no more commented-out assignments.

Actually it should be as presented in "cld --help" and "cldcli --help": 
  an integer, whose increasing value indicates increasing verbosity. 
Higher values enable really verbose output, such as per-packet output.

So, the programs themselves have it right -- debug/verbose is an integer 
not a bool.  The internal libraries get that wrong sometimes, though.

Having separate knobs for 'debug' and 'verbose' will be more confusing 
than a single control, IMO.

	Jeff



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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-09 23:21       ` Jeff Garzik
@ 2010-02-10  0:21         ` Pete Zaitcev
  2010-02-10  1:33           ` Jeff Garzik
  0 siblings, 1 reply; 25+ messages in thread
From: Pete Zaitcev @ 2010-02-10  0:21 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Colin McCabe, Project Hail List

On Tue, 09 Feb 2010 18:21:02 -0500
Jeff Garzik <jeff@garzik.org> wrote:

> So, the programs themselves have it right -- debug/verbose is an integer 
> not a bool.  The internal libraries get that wrong sometimes, though.
> 
> Having separate knobs for 'debug' and 'verbose' will be more confusing 
> than a single control, IMO.

I dunno, I think bigger packages tend to drift towards bitmasks.
But I don't have a strong opinion. I just wanted to restore the
per-packet output that you wanted. If you think that an integer is
better, that's fine. The newer code should is more regular and easier
to mold in any desired way.

Actually, I think the whole idea of debug levels is flawed.
We should work on adding a CLD protocol dissector to Wireshark
instead, now that with Colin's help we've settled to a protocol.

-- Pete

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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-10  0:21         ` Pete Zaitcev
@ 2010-02-10  1:33           ` Jeff Garzik
  2010-02-10  2:27             ` Colin McCabe
  0 siblings, 1 reply; 25+ messages in thread
From: Jeff Garzik @ 2010-02-10  1:33 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: Colin McCabe, Project Hail List

On 02/09/2010 07:21 PM, Pete Zaitcev wrote:
> On Tue, 09 Feb 2010 18:21:02 -0500
> Jeff Garzik<jeff@garzik.org>  wrote:
>
>> So, the programs themselves have it right -- debug/verbose is an integer
>> not a bool.  The internal libraries get that wrong sometimes, though.
>>
>> Having separate knobs for 'debug' and 'verbose' will be more confusing
>> than a single control, IMO.
>
> I dunno, I think bigger packages tend to drift towards bitmasks.
> But I don't have a strong opinion. I just wanted to restore the
> per-packet output that you wanted. If you think that an integer is
> better, that's fine. The newer code should is more regular and easier
> to mold in any desired way.

Integer or bitmask, either one is fine with me.  Anything but two 
separate variables, one for 'debug' and one for 'verbose'.  Programmers 
tend to like bitmasks, and users tend to -not- like them if forced to 
perform binary math.


> Actually, I think the whole idea of debug levels is flawed.
> We should work on adding a CLD protocol dissector to Wireshark
> instead, now that with Colin's help we've settled to a protocol.

Long term, I agree.  Creating a wireshark patch is definitely a to-do item.

For now, while a wireshark patch would be welcome, we are still changing 
the network protocol.  In particular, replication will _definitely_ 
change the protocol.  I was waiting on the XDR patch before making any 
more protocol changes.

That means we would either need to have locally patches wireshark 
packages, or maintain an out-of-tree wireshark patch in rawhide for a 
while, until the protocol settles down.

	Jeff



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

* Re: [PATCHv2] cld: use XDR for all messages
  2010-02-10  1:33           ` Jeff Garzik
@ 2010-02-10  2:27             ` Colin McCabe
  0 siblings, 0 replies; 25+ messages in thread
From: Colin McCabe @ 2010-02-10  2:27 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Pete Zaitcev, Project Hail List

On Tue, Feb 9, 2010 at 5:33 PM, Jeff Garzik <jeff@garzik.org> wrote:
> On 02/09/2010 07:21 PM, Pete Zaitcev wrote:
>>
>> On Tue, 09 Feb 2010 18:21:02 -0500
>> Jeff Garzik<jeff@garzik.org>  wrote:
>>
>>> So, the programs themselves have it right -- debug/verbose is an integer
>>> not a bool.  The internal libraries get that wrong sometimes, though.
>>>
>>> Having separate knobs for 'debug' and 'verbose' will be more confusing
>>> than a single control, IMO.
>>
>> I dunno, I think bigger packages tend to drift towards bitmasks.
>> But I don't have a strong opinion. I just wanted to restore the
>> per-packet output that you wanted. If you think that an integer is
>> better, that's fine. The newer code should is more regular and easier
>> to mold in any desired way.
>
> Integer or bitmask, either one is fine with me.  Anything but two separate
> variables, one for 'debug' and one for 'verbose'.  Programmers tend to like
> bitmasks, and users tend to -not- like them if forced to perform binary
> math.

I like bitmasks personally. If something is breaking, and you think
it's in the foo subsystem, you can tell users to turn on debugging for
just the foo system.

Even better is the ability to set the logging level after the program
has been started. Sometimes you just run into these scenarios where
the process has been running for a week, and it suddenly flakes out.

I wrote a logging system once where the daemon listened on a unix
domain socket. By sending messages to that socket, you could change
the log level on the fly.

Of course, there's a lot of other ways of getting the same
information, like using gdb to read variables, or wireshark. When I
was working on the XDR patch, I just hacked in a bunch of calls to
print the packet header in various places.

Colin

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

end of thread, other threads:[~2010-02-10  2:27 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-01-15 17:49 [PATCHv2] cld: use XDR for all messages Colin McCabe
2010-01-21  1:38 ` Jeff Garzik
2010-01-21  2:38   ` Colin McCabe
2010-01-26 21:22     ` Colin McCabe
2010-01-26 22:11       ` Jeff Garzik
2010-01-26 23:47         ` Colin McCabe
2010-01-29  7:01           ` Jeff Garzik
2010-01-30  1:01             ` Colin McCabe
2010-02-03  6:35               ` [PATCH] " Jeff Garzik
2010-02-03 21:45                 ` Colin McCabe
2010-02-05  9:51                   ` Jeff Garzik
2010-02-05  0:59           ` Jeff Garzik
2010-02-05  4:14             ` Pete Zaitcev
2010-02-05  6:06               ` Jeff Garzik
2010-02-05  7:16                 ` Pete Zaitcev
2010-02-05  9:48 ` Jeff Garzik
2010-02-05 18:49   ` Colin McCabe
2010-02-09  9:20 ` Jeff Garzik
2010-02-09  9:38   ` Jeff Garzik
2010-02-09 15:47     ` Pete Zaitcev
2010-02-09 23:13     ` Pete Zaitcev
2010-02-09 23:21       ` Jeff Garzik
2010-02-10  0:21         ` Pete Zaitcev
2010-02-10  1:33           ` Jeff Garzik
2010-02-10  2:27             ` Colin McCabe

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.