[Libvir] Preliminary patch to support remote driver / libvirtd

Mark McLoughlin markmc at redhat.com
Wed Jan 31 19:07:13 UTC 2007


Hey,

On Tue, 2007-01-30 at 17:42 +0000, Richard W.M. Jones wrote:
> This patch is just for discussion.  It's not in a state to be applied, 
> even if it were accepted (which is a long-shot at present anyway).

	Cool stuff on getting this far.

	Okay, some comments to start with:

  - From past dealings with SunRPC and CORBA and the like, the choice 
    of SunRPC makes me pretty nervous. The amount of code we'd be 
    adding to libvirt to support SunRPC over the different transports 
    makes me even more nervous. I wouldn't fancy having to debug 
    problems there, or add another transport, for example.

    I'm pretty jaded of RPC systems in general, though. So, I'd suggest 
    either a homegrown binary protocol like we're using now or, if it 
    turns out to be fairly simple, XML RPC.

  - Wrt. the TLS support - as recommended by the RFC AFAIR, new 
    protocols are supposed to negotiate TLS at connection time rather
    than having a separate port for TLS (as Dan does with qemud)

  - There doesn't seem to be any authentication with TLS support. 
    That's definitely necessary, even if it's just cert based.

  - If libvirtd is going to be a pure proxy, I don't think the UNIX 
    transport is going useful.

  - Also, if it's just a proxy, couldn't this be launched by xinetd?

  - I'd advocate merging qemud into libvirtd, but you could have a long 
    discussion either way on that one.

  - The ssh and ext transports just seem like hacks, especially if the 
    ssh transport won't support authentication (both ways). If people 
    want to use SSH, they can just set up their own tunnel.

  - Wrt. coding style - libvirt has a *fairly* consistent coding style 
    throughout and I think that helps with the maintainability of any 
    project. Deviations are already starting to creep in (e.g. not 
    using studlyCaps in places) ... but we should try and stem that 
    tide IMHO (even though the current coding style wouldn't be my 
    favourite)

>   * SunRPC is stateless so we need to hand out a cookie to
>     represent the virConnectPtr handle on the server side.
>     However if the client dies without explicitly calling
>     close, we have no way to know, and so the cookie/handle
>     on the server side lives forever.

	The server side context should be the file descriptor - i.e. there
should be only a single open() call per fd, and so only a single
virConnectPtr per fd ... so if socket is closed by the remote side, we
know we can call close() on the virConnectPtr. There shouldn't be a need
for cookies, IMHO.

	(Perhaps this "single open() call per connection" implies that there
shouldn't be an open() RPC, but rather that happens as part of the
initial negotiation stage)
 
> +    /* driver private data
> +     * (Ought to replace the above ad-hoc Xen data, IMHO anyway.
> +     * Currently only the 'remote' driver uses this.
> +     *     - RWMJ).
> +     */
> +    void *private;
> +

	Yep, that'd be a nice cleanup - e.g. I have this patch to make qemud
and xen not share the "handle" member:

  http://www.gnome.org/~markmc/code/libvirt-networking/libvirt-qemu-dont-share-cnx-handle-with-xen.patch



> +// See: http://people.redhat.com/drepper/userapi-ipv6.html
> +static int
> +make_sockets (int *fds, int max_fds, int *nfds_r, const char *service)
> +{
> +  struct addrinfo *ai;
> +  struct addrinfo hints;
> +  memset (&hints, 0, sizeof hints);
> +  hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
> +  hints.ai_socktype = SOCK_STREAM;
> +
> +  int e = getaddrinfo (NULL, service, &hints, &ai);
> +  if (e != 0) {
> +    fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (e));
> +    return -1;
> +  }
> +
> +  struct addrinfo *runp = ai;
> +  while (runp && *nfds_r < max_fds) {
> +    fds[*nfds_r] = socket (runp->ai_family, runp->ai_socktype,
> +			   runp->ai_protocol);
> +    if (fds[*nfds_r] == -1) {
> +      perror ("socket");
> +      return -1;
> +    }
> +
> +    int opt = 1;
> +    setsockopt (fds[*nfds_r], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
> +
> +    if (bind (fds[*nfds_r], runp->ai_addr, runp->ai_addrlen) == -1) {
> +      if (errno != EADDRINUSE) {
> +	perror ("bind");
> +	return -1;
> +      }
> +      close (fds[*nfds_r]);
> +    }
> +    else {
> +      if (listen (fds[*nfds_r], SOMAXCONN) == -1) {
> +	perror ("listen");
> +	return -1;
> +      }
> +      ++*nfds_r;
> +    }
> +    runp = runp->ai_next;
> +  }
> +
> +  freeaddrinfo (ai);

	I'm really not a big fan of this way of doing IPv6 support because:

  - getaddrinfo() is a pretty complicated function, so the code above 
    is pretty obtuse without a thorough read of the getaddrinfo() man
    page

  - you really want to end up with *either* a single IPv6 socket or and 
    IPv4 socket ... the above code can end up with multiple sockets. 
    Here's what I prefer to do:

---
    struct sockaddr_in addr_in;
    struct sockaddr *addr;
    socklen_t addrlen;

#ifdef ENABLE_IPV6
    struct sockaddr_in6 addr_in6;

    memset(&addr_in6, 0, sizeof(addr_in6));
    addr_in6.sin6_family = AF_INET6;
    addr_in6.sin6_port = htons(port);
    addr_in6.sin6_addr = localOnly ? in6addr_loopback : in6addr_any;

    addr = (struct sockaddr *)&addr_in6;
    addrlen = sizeof(addr_in6);

    sock = socket(AF_INET6, SOCK_STREAM, 0);
#endif

    if (sock < 0) {
        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            return -1;

        memset(&addr_in, 0, sizeof(addr_in));
        addr_in.sin_family = AF_INET;
        addr_in.sin_port = htons(port);
        addr_in.sin_addr.s_addr = localOnly ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY);

        addr = (struct sockaddr *)&addr_in;
        addrlen = sizeof(addr_in);
    }

    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                   (char *)&one, sizeof(one)) < 0) {
        close(sock);
        return -1;
    }
    if (bind(sock, addr, addrlen) < 0) {
        close(sock);
        return -1;
    }
----


> +static int
> +remote_open (virConnectPtr conn, const char *name,
> +	     int flags __attribute__((unused)))

	This function is huge, and extremely hard to verify. Recommend
splitting it up a lot. Perhaps when you use libxml's URI parsing etc. it
won't be so bad, though.

	Also, libvirt has ATTRIBUTE_UNUSED.

> +  switch (proto) {
> +  case proto_unknown:
> +    abort ();			/* Internal error in this function. */
> +
> +  case proto_tls:
> +  case proto_tcp: {
....
> +	proto == proto_tls
> +	? clntgnutls_create (r->ai_family, r->ai_addr, r->ai_addrlen, xcred,
> +			     LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
> +			     &private.sock, 0, 0)
> +	: clnttcp2_create (r->ai_family, r->ai_addr, r->ai_addrlen,
> +			   LIBVIRTREMOTE, LIBVIRTREMOTE_VERS1,
> +			   &private.sock, 0, 0);
> +      if (private.cl)
> +	goto tcp_connected;
> +    }
> +
> +    freeaddrinfo (res);
> +    error (conn, VIR_ERR_RPC,
> +	   clnt_spcreateerror ("could not create SunRPC client"));
> +    goto failed;
> +
> +  tcp_connected:
> +    freeaddrinfo (res);

	A goto in a switch() statement? ... that's a whole litter of cute
little puppies you've killed, right there! :-)

HTH,
Mark.




More information about the libvir-list mailing list