mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-22 04:25:18 +00:00
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:
parent
018044c89f
commit
b0f996a6b1
@ -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 --> |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>
|
||||
|
||||
|
@ -1186,8 +1186,10 @@ virFileRewrite;
|
||||
|
||||
# virnetmessage.h
|
||||
virNetMessageClear;
|
||||
virNetMessageDecodeNumFDs;
|
||||
virNetMessageEncodeHeader;
|
||||
virNetMessageEncodePayload;
|
||||
virNetMessageEncodeNumFDs;
|
||||
virNetMessageFree;
|
||||
virNetMessageNew;
|
||||
virNetMessageQueuePush;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user