From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list