Extend RPC protocol to allow FD passing

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
This commit is contained in:
Daniel P. Berrange 2011-10-21 11:30:12 +01:00
parent 018044c89f
commit b0f996a6b1
6 changed files with 186 additions and 3 deletions

View File

@ -163,14 +163,28 @@
<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>
<li>type=stream+status=continue: the raw bytes of data for the stream. No XDR encoding</li>
</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. While the sender should always
send '\0' as the dummy byte value, the receiver ought to ignore the
value for the sake of robustness.
</p>
<p>
For the exact payload information for each procedure, consult the XDR protocol
definition for the program+version in question
@ -339,6 +353,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 --&gt; |44| 8 | 1 | 3 | 0 | 1 | 0 | 2 | .o.oOo.o. | 0 | 0 | --&gt; S (call)
+--+-----------------------+---------------+-------+
+--+-----------------------+--------+
C &lt;-- |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo | &lt;-- S (reply)
+--+-----------------------+--------+
</pre>
<h2><a name="security">RPC security</a></h2>

View File

@ -1186,8 +1186,10 @@ virFileRewrite;
# virnetmessage.h
virNetMessageClear;
virNetMessageDecodeNumFDs;
virNetMessageEncodeHeader;
virNetMessageEncodePayload;
virNetMessageEncodeNumFDs;
virNetMessageFree;
virNetMessageNew;
virNetMessageQueuePush;

View File

@ -21,11 +21,14 @@
#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"
#include "util.h"
#define VIR_FROM_THIS VIR_FROM_RPC
#define virNetError(code, ...) \
@ -51,6 +54,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 +68,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 +253,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 +489,31 @@ 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;
}
if (virSetInherit(fd, false) < 0) {
VIR_FORCE_CLOSE(fd);
virReportSystemError(errno,
_("Cannot set close-on-exec %d"),
fd);
return -1;
}
return fd;
}

View File

@ -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__ */

View File

@ -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 {

View File

@ -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,