On Fri, Apr 11, 2025 at 5:47 PM Daniel P. Berrangé <berra...@redhat.com>
wrote:

> On Tue, Apr 08, 2025 at 10:27:51AM +0800, yong.hu...@smartx.com wrote:
> > From: Hyman Huang <yong.hu...@smartx.com>
> >
> > As advised by the GNU TLS, the caller should attempt again
> > if the gnutls_record_{recv,send} return EAGAIN or EINTR;
> > check the following link to view the details:
> >
> https://www.gnutls.org/manual/html_node/Data-transfer-and-termination.html
> >
> > Add the retry parameter for virNetTLSSession{Read,Write}
> > functions in accordance with this guideline.
> >
> > This prevents the upper application from encountering the
> > following error message when it calls the virConnectOpenAuth API:
> > Unable to read TLS confirmation: Resource temporarily unavailable
> >
> > Signed-off-by: Hyman Huang <yong.hu...@smartx.com>
> > ---
> >  src/rpc/virnetclient.c     |  2 +-
> >  src/rpc/virnetsocket.c     |  4 ++--
> >  src/rpc/virnettlscontext.c | 28 ++++++++++++++++++++++++++--
> >  src/rpc/virnettlscontext.h |  6 ++++--
> >  4 files changed, 33 insertions(+), 7 deletions(-)
> >
> > diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c
> > index 92933220e2..5340db4211 100644
> > --- a/src/rpc/virnetclient.c
> > +++ b/src/rpc/virnetclient.c
> > @@ -1003,7 +1003,7 @@ int virNetClientSetTLSSession(virNetClient *client,
> >      ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL));
> >  #endif /* !WIN32 */
> >
> > -    len = virNetTLSSessionRead(client->tls, buf, 1);
> > +    len = virNetTLSSessionRead(client->tls, buf, 1, true);
> >      if (len < 0 && errno != ENOMSG) {
> >          virReportSystemError(errno, "%s",
> >                               _("Unable to read TLS confirmation"));
> > diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
> > index e8fc2d5f7d..6774dd4a4b 100644
> > --- a/src/rpc/virnetsocket.c
> > +++ b/src/rpc/virnetsocket.c
> > @@ -1739,7 +1739,7 @@ static ssize_t virNetSocketReadWire(virNetSocket
> *sock, char *buf, size_t len)
> >      if (sock->tlsSession &&
> >          virNetTLSSessionGetHandshakeStatus(sock->tlsSession) ==
> >          VIR_NET_TLS_HANDSHAKE_COMPLETE) {
> > -        ret = virNetTLSSessionRead(sock->tlsSession, buf, len);
> > +        ret = virNetTLSSessionRead(sock->tlsSession, buf, len, false);
> >      } else {
> >          ret = read(sock->fd, buf, len);
> >      }
> > @@ -1807,7 +1807,7 @@ static ssize_t virNetSocketWriteWire(virNetSocket
> *sock, const char *buf, size_t
> >      if (sock->tlsSession &&
> >          virNetTLSSessionGetHandshakeStatus(sock->tlsSession) ==
> >          VIR_NET_TLS_HANDSHAKE_COMPLETE) {
> > -        ret = virNetTLSSessionWrite(sock->tlsSession, buf, len);
> > +        ret = virNetTLSSessionWrite(sock->tlsSession, buf, len, false);
> >      } else {
> >          ret = write(sock->fd, buf, len); /* sc_avoid_write */
> >      }
> > diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c
> > index e8023133b4..92d909c0b7 100644
> > --- a/src/rpc/virnettlscontext.c
> > +++ b/src/rpc/virnettlscontext.c
> > @@ -679,16 +679,28 @@ void
> virNetTLSSessionSetIOCallbacks(virNetTLSSession *sess,
> >
> >
> >  ssize_t virNetTLSSessionWrite(virNetTLSSession *sess,
> > -                              const char *buf, size_t len)
> > +                              const char *buf, size_t len,
> > +                              bool retry)
> >  {
> >      ssize_t ret;
> >
> > + rewrite:
> >      virObjectLock(sess);
> >      ret = gnutls_record_send(sess->session, buf, len);
> >
> >      if (ret >= 0)
> >          goto cleanup;
> >
> > +    if (retry && (ret == GNUTLS_E_AGAIN || ret ==
> GNUTLS_E_INTERRUPTED)) {
> > +        /*
> > +         * GNU TLS advises calling the function again to obtain the
> data if EAGAIN is returned.
> > +         * See reference:
> https://www.gnutls.org/manual/html_node/Data-transfer-and-termination.html
> > +         * */
>
> To repeat my feedback on v1.
>
> This would surely be wrong for E_AGAIN - if the socket is blocking we must
> return to the main loop and wait for it to indicate that the socket has
> new data. If we don't wait in the main loop, this code will busy-loop on

a non-blocking socket


Agree, for the EAGAIN error, trying to repoll could be a solution like the
following pieces of code?

 repoll:
    source = virEventGLibAddSocketWatch(virNetSocketGetFD(client->sock),
                                        G_IO_IN,
                                        client->eventCtx,
                                        virNetClientIOEventTLSConfirm,
client, NULL);

    g_main_loop_run(client->eventLoop);

 retry:
    len = virNetTLSSessionRead(client->tls, buf, 1, true);
    if (len < 0 && errno == EINTR) {
        goto retry;
    }
    if (len < 0 && errno == EAGAIN) {
        goto repoll;
    }


>
>
> The GNUTLS docs do NOT say you must call it again /immediately/.


> A retry on E_INTERRUPTED is ok, but E_AGAIN must wait for the
> socket to be readable. This is something higher level libvirt
> code should already be doing correctly. Do you have any real
> world example of a problem being hit ?
>

Yes, when our higher management app try to connect Libvirtd using
uri: qemu+tls://10.111.222.11/system?no_verify=1 with the following
python code:

def libvirt_connection(uri=None, auth_info=None):
    conn = None
    _auth_info = auth_info

    def _request_cred(credentials, user_data):
        if not _auth_info:
            return 0

        for credential in credentials:
            if credential[0] == libvirt.VIR_CRED_AUTHNAME:
                credential[4] = _auth_info["username"]
            elif credential[0] == libvirt.VIR_CRED_PASSPHRASE:
                credential[4] = _auth_info["password"]
        return 0

    auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE],
_request_cred, None]

    if uri and "no_verify" not in uri:
        uri = "{}?no_verify=1".format(uri)

    try:
        conn = libvirt.openAuth(uri or "qemu:///system", auth)
        yield conn
    except libvirt.libvirtError as e:
        logging.exception("libvirtError: %s" % str(e))
        raise
    finally:
        if conn:
          conn.close()

We encountered the following error message:

[2025-04-01 12:54:42,339: ERROR/MainProcess
trace_id=79a42e2d-9ee1-4381-9a76-c96a05c88a66] libvirtError: Unable to read
TLS confirmation: Resource temporarily unavailable Traceback (most recent
call last): File
"/usr/local/venv/job-center/lib/python3.10/site-packages/smartx_app/elf/common/utils/libvirt_driver.py",
line 58, in libvirt_connection conn = libvirt.openAuth(uri or
"qemu:///system", auth) File
"/usr/local/venv/job-center/lib/python3.10/site-packages/libvirt.py", line
148, in openAuth raise libvirtError('virConnectOpenAuth() failed')
libvirt.libvirtError: Unable to read TLS confirmation: Resource temporarily
unavailable



> > +        VIR_DEBUG("Try writing data from the TLS session again");
> > +        virObjectUnlock(sess);
> > +        goto rewrite;
> > +    }
> > +
>
> With regards,
> Daniel
> --
> |: https://berrange.com      -o-
> https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-
> https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-
> https://www.instagram.com/dberrange :|
>
>
Thanks for the comments.

Yong
-- 
Best regards

Reply via email to