[libvirt] [PATCH 5/8] Extend RPC protocol to allow FD passing

Daniel P. Berrange berrange at redhat.com
Tue Oct 25 16:03:45 UTC 2011


From: "Daniel P. Berrange" <berrange at redhat.com>

Define two new RPC message types VIR_NET_CALL_WITH_FDS and
VIR_NET_REPLY_WITH_FDS. These message types are equivalent
to VIR_NET_CALL and VIR_NET_REPLY, except that between the
message header, and payload there is a 32-bit integer field
specifying how many file descriptors have been passed.

The actual file descriptors are sent/recv'd out of band.

* src/rpc/virnetmessage.c, src/rpc/virnetmessage.h,
  src/libvirt_private.syms: Add support for handling
  passed file descriptors
* src/rpc/virnetprotocol.x: Extend protocol for FD
  passing
---
 docs/internals/rpc.html.in |   33 +++++++++++++
 src/libvirt_private.syms   |    2 +
 src/rpc/virnetmessage.c    |  111 +++++++++++++++++++++++++++++++++++++++++++-
 src/rpc/virnetmessage.h    |    9 ++++
 src/rpc/virnetprotocol.x   |   23 +++++++++-
 src/virnetprotocol-structs |    2 +
 6 files changed, 177 insertions(+), 3 deletions(-)

diff --git a/docs/internals/rpc.html.in b/docs/internals/rpc.html.in
index 2c0c983..e746431 100644
--- a/docs/internals/rpc.html.in
+++ b/docs/internals/rpc.html.in
@@ -163,8 +163,11 @@
 
     <ul>
       <li>type=call: the in parameters for the method call, XDR encoded</li>
+      <li>type=call-with-fds: number of file handles, then the in parameters for the method call, XDR encoded, followed by the file handles</li>
       <li>type=reply+status=ok: the return value and/or out parameters for the method call, XDR encoded</li>
       <li>type=reply+status=error: the error information for the method, a virErrorPtr XDR encoded</li>
+      <li>type=reply-with-fds+status=ok: number of file handles, the return value and/or out parameters for the method call, XDR encoded, followed by the file handles</li>
+      <li>type=reply-with-fds+status=error: number of file handles, the error information for the method, a virErrorPtr XDR encoded, followed by the file handles</li>
       <li>type=event: the parameters for the event, XDR encoded</li>
       <li>type=stream+status=ok: no payload</li>
       <li>type=stream+status=error: the error information for the method, a virErrorPtr XDR encoded</li>
@@ -172,6 +175,15 @@
     </ul>
 
     <p>
+      With the two packet types that support passing file descriptors, in
+      between the header and the payload there will be a 4-byte integer
+      specifying the number of file descriptors which are being sent.
+      The actual file handles are sent after the payload has been sent.
+      Each file handle has a single dummy byte transmitted as a carrier
+      for the out of band file descriptor.
+    </p>
+
+    <p>
       For the exact payload information for each procedure, consult the XDR protocol
       definition for the program+version in question
     </p>
@@ -339,6 +351,27 @@
           +--+-----------------------+--------+
     </pre>
 
+    <h4><a name="wireexamplescallfd">Method call with passed FD</a></h4>
+
+    <p>
+      A single method call with 2 passed file descriptors and successful
+      reply, for a program=8, version=1, procedure=3, which 10 bytes worth
+      of input args, and 4 bytes worth of return values. The number of
+      file descriptors is encoded as a 32-bit int. Each file descriptor
+      then has a 1 byte dummy payload. The overall input
+      packet length is 4 + 24 + 4 + 2 + 10 == 44, and output packet length 32.
+    </p>
+
+    <pre>
+          +--+-----------------------+---------------+-------+
+   C -->  |44| 8 | 1 | 3 | 0 | 1 | 0 | 2 | .o.oOo.o. | 0 | 0 |  --> S  (call)
+          +--+-----------------------+---------------+-------+
+
+          +--+-----------------------+--------+
+   C <--  |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo |  <-- S  (reply)
+          +--+-----------------------+--------+
+    </pre>
+
 
     <h2><a name="security">RPC security</a></h2>
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 6a42d1b..924c2c3 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1185,8 +1185,10 @@ virFileFdopen;
 
 # virnetmessage.h
 virNetMessageClear;
+virNetMessageDecodeNumFDs;
 virNetMessageEncodeHeader;
 virNetMessageEncodePayload;
+virNetMessageEncodeNumFDs;
 virNetMessageFree;
 virNetMessageNew;
 virNetMessageQueuePush;
diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c
index a1ae9c4..a833c90 100644
--- a/src/rpc/virnetmessage.c
+++ b/src/rpc/virnetmessage.c
@@ -21,11 +21,13 @@
 #include <config.h>
 
 #include <stdlib.h>
+#include <unistd.h>
 
 #include "virnetmessage.h"
 #include "memory.h"
 #include "virterror_internal.h"
 #include "logging.h"
+#include "virfile.h"
 
 #define VIR_FROM_THIS VIR_FROM_RPC
 #define virNetError(code, ...)                                    \
@@ -51,6 +53,13 @@ virNetMessagePtr virNetMessageNew(bool tracked)
 void virNetMessageClear(virNetMessagePtr msg)
 {
     bool tracked = msg->tracked;
+    size_t i;
+
+    VIR_DEBUG("msg=%p nfds=%zu", msg, msg->nfds);
+
+    for (i = 0 ; i < msg->nfds ; i++)
+        VIR_FORCE_CLOSE(msg->fds[i]);
+    VIR_FREE(msg->fds);
     memset(msg, 0, sizeof(*msg));
     msg->tracked = tracked;
 }
@@ -58,14 +67,18 @@ void virNetMessageClear(virNetMessagePtr msg)
 
 void virNetMessageFree(virNetMessagePtr msg)
 {
+    size_t i;
     if (!msg)
         return;
 
+    VIR_DEBUG("msg=%p nfds=%zu cb=%p", msg, msg->nfds, msg->cb);
+
     if (msg->cb)
         msg->cb(msg, msg->opaque);
 
-    VIR_DEBUG("msg=%p", msg);
-
+    for (i = 0 ; i < msg->nfds ; i++)
+        VIR_FORCE_CLOSE(msg->fds[i]);
+    VIR_FREE(msg->fds);
     VIR_FREE(msg);
 }
 
@@ -239,6 +252,78 @@ cleanup:
 }
 
 
+int virNetMessageEncodeNumFDs(virNetMessagePtr msg)
+{
+    XDR xdr;
+    unsigned int numFDs = msg->nfds;
+    int ret = -1;
+
+    xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset, XDR_ENCODE);
+
+    if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) {
+        virNetError(VIR_ERR_RPC,
+                    _("Too many FDs to send %d, expected %d maximum"),
+                    numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX);
+        goto cleanup;
+    }
+
+    if (!xdr_u_int(&xdr, &numFDs)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to encode number of FDs"));
+        goto cleanup;
+    }
+    msg->bufferOffset += xdr_getpos(&xdr);
+
+    VIR_DEBUG("Send %zu FDs to peer", msg->nfds);
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
+int virNetMessageDecodeNumFDs(virNetMessagePtr msg)
+{
+    XDR xdr;
+    unsigned int numFDs;
+    int ret = -1;
+    size_t i;
+
+    xdrmem_create(&xdr, msg->buffer + msg->bufferOffset,
+                  msg->bufferLength - msg->bufferOffset, XDR_DECODE);
+    if (!xdr_u_int(&xdr, &numFDs)) {
+        virNetError(VIR_ERR_RPC, "%s", _("Unable to decode number of FDs"));
+        goto cleanup;
+    }
+    msg->bufferOffset += xdr_getpos(&xdr);
+
+    if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) {
+        virNetError(VIR_ERR_RPC,
+                    _("Received too many FDs %d, expected %d maximum"),
+                    numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX);
+        goto cleanup;
+    }
+
+    msg->nfds = numFDs;
+    if (VIR_ALLOC_N(msg->fds, msg->nfds) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    for (i = 0 ; i < msg->nfds ; i++)
+        msg->fds[i] = -1;
+
+    VIR_DEBUG("Got %zu FDs from peer", msg->nfds);
+
+    ret = 0;
+
+cleanup:
+    xdr_destroy(&xdr);
+    return ret;
+}
+
+
 int virNetMessageEncodePayload(virNetMessagePtr msg,
                                xdrproc_t filter,
                                void *data)
@@ -403,3 +488,25 @@ void virNetMessageSaveError(virNetMessageErrorPtr rerr)
         rerr->level = VIR_ERR_ERROR;
     }
 }
+
+
+int virNetMessageDupFD(virNetMessagePtr msg,
+                       size_t slot)
+{
+    int fd;
+
+    if (slot >= msg->nfds) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("No FD available at slot %zu"), slot);
+        return -1;
+    }
+
+    if ((fd = dup(msg->fds[slot])) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to duplicate FD %d"),
+                             msg->fds[slot]);
+        return -1;
+    }
+
+    return fd;
+}
diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h
index 307a041..ad63409 100644
--- a/src/rpc/virnetmessage.h
+++ b/src/rpc/virnetmessage.h
@@ -46,6 +46,9 @@ struct _virNetMessage {
     virNetMessageFreeCallback cb;
     void *opaque;
 
+    size_t nfds;
+    int *fds;
+
     virNetMessagePtr next;
 };
 
@@ -78,6 +81,9 @@ int virNetMessageDecodePayload(virNetMessagePtr msg,
                                void *data)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
 
+int virNetMessageEncodeNumFDs(virNetMessagePtr msg);
+int virNetMessageDecodeNumFDs(virNetMessagePtr msg);
+
 int virNetMessageEncodePayloadRaw(virNetMessagePtr msg,
                                   const char *buf,
                                   size_t len)
@@ -88,4 +94,7 @@ int virNetMessageEncodePayloadEmpty(virNetMessagePtr msg)
 void virNetMessageSaveError(virNetMessageErrorPtr rerr)
     ATTRIBUTE_NONNULL(1);
 
+int virNetMessageDupFD(virNetMessagePtr msg,
+                       size_t slot);
+
 #endif /* __VIR_NET_MESSAGE_H__ */
diff --git a/src/rpc/virnetprotocol.x b/src/rpc/virnetprotocol.x
index 306fafa..4520e38 100644
--- a/src/rpc/virnetprotocol.x
+++ b/src/rpc/virnetprotocol.x
@@ -62,6 +62,11 @@ const VIR_NET_MESSAGE_LEN_MAX = 4;
  */
 const VIR_NET_MESSAGE_STRING_MAX = 65536;
 
+/* Limit on number of File Descriptors allowed to be
+ * passed per message
+ */
+const VIR_NET_MESSAGE_NUM_FDS_MAX = 32;
+
 /*
  * RPC wire format
  *
@@ -126,6 +131,18 @@ const VIR_NET_MESSAGE_STRING_MAX = 65536;
  *          remote_error error information
  *     * status == VIR_NET_OK
  *          <empty>
+ *
+ *  - type == VIR_NET_CALL_WITH_FDS
+ *          int8 - number of FDs
+ *          XXX_args  for procedure
+ *
+ *  - type == VIR_NET_REPLY_WITH_FDS
+ *          int8 - number of FDs
+ *     * status == VIR_NET_OK
+ *          XXX_ret         for procedure
+ *     * status == VIR_NET_ERROR
+ *          remote_error    Error information
+ *
  */
 enum virNetMessageType {
     /* client -> server. args from a method call */
@@ -135,7 +152,11 @@ enum virNetMessageType {
     /* either direction. async notification */
     VIR_NET_MESSAGE = 2,
     /* either direction. stream data packet */
-    VIR_NET_STREAM = 3
+    VIR_NET_STREAM = 3,
+    /* client -> server. args from a method call, with passed FDs */
+    VIR_NET_CALL_WITH_FDS = 4,
+    /* server -> client. reply/error from a method call, with passed FDs */
+    VIR_NET_REPLY_WITH_FDS = 5
 };
 
 enum virNetMessageStatus {
diff --git a/src/virnetprotocol-structs b/src/virnetprotocol-structs
index 4d97e9c..af4526c 100644
--- a/src/virnetprotocol-structs
+++ b/src/virnetprotocol-structs
@@ -4,6 +4,8 @@ enum virNetMessageType {
         VIR_NET_REPLY = 1,
         VIR_NET_MESSAGE = 2,
         VIR_NET_STREAM = 3,
+        VIR_NET_CALL_WITH_FDS = 4,
+        VIR_NET_REPLY_WITH_FDS = 5,
 };
 enum virNetMessageStatus {
         VIR_NET_OK = 0,
-- 
1.7.6.4




More information about the libvir-list mailing list