Blame SOURCES/kvm-io-Reply-to-ping-frames.patch

4a2fec
From 9fb29da8bb32eeead15ac4038cb9cbfca11066f5 Mon Sep 17 00:00:00 2001
4a2fec
From: "Daniel P. Berrange" <berrange@redhat.com>
4a2fec
Date: Wed, 20 Dec 2017 17:56:51 +0100
4a2fec
Subject: [PATCH 11/42] io: Reply to ping frames
4a2fec
4a2fec
RH-Author: Daniel P. Berrange <berrange@redhat.com>
4a2fec
Message-id: <20171220175702.29663-10-berrange@redhat.com>
4a2fec
Patchwork-id: 78462
4a2fec
O-Subject: [RHV-7.5 qemu-kvm-rhev PATCH v2 09/20] io: Reply to ping frames
4a2fec
Bugzilla: 1518649
4a2fec
RH-Acked-by: John Snow <jsnow@redhat.com>
4a2fec
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
4a2fec
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
4a2fec
4a2fec
From: Brandon Carpenter <brandon.carpenter@cypherpath.com>
4a2fec
4a2fec
Add an immediate ping reply (pong) to the outgoing stream when a ping
4a2fec
is received. Unsolicited pongs are ignored.
4a2fec
4a2fec
Signed-off-by: Brandon Carpenter <brandon.carpenter@cypherpath.com>
4a2fec
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
4a2fec
(cherry picked from commit 268a53f50de795481dd73ffd0e0c1339ad3dc44b)
4a2fec
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
4a2fec
---
4a2fec
 include/io/channel-websock.h |  1 +
4a2fec
 io/channel-websock.c         | 66 +++++++++++++++++++++++++++++---------------
4a2fec
 2 files changed, 45 insertions(+), 22 deletions(-)
4a2fec
4a2fec
diff --git a/include/io/channel-websock.h b/include/io/channel-websock.h
4a2fec
index 7c89655..ff32d86 100644
4a2fec
--- a/include/io/channel-websock.h
4a2fec
+++ b/include/io/channel-websock.h
4a2fec
@@ -60,6 +60,7 @@ struct QIOChannelWebsock {
4a2fec
     Buffer encoutput;
4a2fec
     Buffer rawinput;
4a2fec
     Buffer rawoutput;
4a2fec
+    Buffer ping_reply;
4a2fec
     size_t payload_remain;
4a2fec
     QIOChannelWebsockMask mask;
4a2fec
     guint io_tag;
4a2fec
diff --git a/io/channel-websock.c b/io/channel-websock.c
4a2fec
index bfe4008..b6fc0c9 100644
4a2fec
--- a/io/channel-websock.c
4a2fec
+++ b/io/channel-websock.c
4a2fec
@@ -573,7 +573,8 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
4a2fec
 }
4a2fec
 
4a2fec
 
4a2fec
-static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
4a2fec
+static void qio_channel_websock_encode_buffer(Buffer *output,
4a2fec
+                                              uint8_t opcode, Buffer *buffer)
4a2fec
 {
4a2fec
     size_t header_size;
4a2fec
     union {
4a2fec
@@ -581,33 +582,37 @@ static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
4a2fec
         QIOChannelWebsockHeader ws;
4a2fec
     } header;
4a2fec
 
4a2fec
-    if (!ioc->rawoutput.offset) {
4a2fec
-        return;
4a2fec
-    }
4a2fec
-
4a2fec
     header.ws.b0 = QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN |
4a2fec
-        (QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME &
4a2fec
-         QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE);
4a2fec
-    if (ioc->rawoutput.offset <
4a2fec
-        QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT) {
4a2fec
-        header.ws.b1 = (uint8_t)ioc->rawoutput.offset;
4a2fec
+        (opcode & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE);
4a2fec
+    if (buffer->offset < QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT) {
4a2fec
+        header.ws.b1 = (uint8_t)buffer->offset;
4a2fec
         header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT;
4a2fec
-    } else if (ioc->rawoutput.offset <
4a2fec
+    } else if (buffer->offset <
4a2fec
                QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT) {
4a2fec
         header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT;
4a2fec
-        header.ws.u.s16.l16 = cpu_to_be16((uint16_t)ioc->rawoutput.offset);
4a2fec
+        header.ws.u.s16.l16 = cpu_to_be16((uint16_t)buffer->offset);
4a2fec
         header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT;
4a2fec
     } else {
4a2fec
         header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT;
4a2fec
-        header.ws.u.s64.l64 = cpu_to_be64(ioc->rawoutput.offset);
4a2fec
+        header.ws.u.s64.l64 = cpu_to_be64(buffer->offset);
4a2fec
         header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT;
4a2fec
     }
4a2fec
     header_size -= QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK;
4a2fec
 
4a2fec
-    buffer_reserve(&ioc->encoutput, header_size + ioc->rawoutput.offset);
4a2fec
-    buffer_append(&ioc->encoutput, header.buf, header_size);
4a2fec
-    buffer_append(&ioc->encoutput, ioc->rawoutput.buffer,
4a2fec
-                  ioc->rawoutput.offset);
4a2fec
+    buffer_reserve(output, header_size + buffer->offset);
4a2fec
+    buffer_append(output, header.buf, header_size);
4a2fec
+    buffer_append(output, buffer->buffer, buffer->offset);
4a2fec
+}
4a2fec
+
4a2fec
+
4a2fec
+static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
4a2fec
+{
4a2fec
+    if (!ioc->rawoutput.offset) {
4a2fec
+        return;
4a2fec
+    }
4a2fec
+    qio_channel_websock_encode_buffer(
4a2fec
+        &ioc->encoutput, QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME,
4a2fec
+        &ioc->rawoutput);
4a2fec
     buffer_reset(&ioc->rawoutput);
4a2fec
 }
4a2fec
 
4a2fec
@@ -652,7 +657,7 @@ static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
4a2fec
     /* Websocket frame sanity check:
4a2fec
      * * Fragmentation is only supported for binary frames.
4a2fec
      * * All frames sent by a client MUST be masked.
4a2fec
-     * * Only binary encoding is supported.
4a2fec
+     * * Only binary and ping/pong encoding is supported.
4a2fec
      */
4a2fec
     if (!fin) {
4a2fec
         if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
4a2fec
@@ -713,6 +718,11 @@ static int qio_channel_websock_decode_payload(QIOChannelWebsock *ioc,
4a2fec
          * for purpose of unmasking, except at end of payload
4a2fec
          */
4a2fec
         if (ioc->encinput.offset < ioc->payload_remain) {
4a2fec
+            /* Wait for the entire payload before processing control frames
4a2fec
+             * because the payload will most likely be echoed back. */
4a2fec
+            if (ioc->opcode & QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK) {
4a2fec
+                return QIO_CHANNEL_ERR_BLOCK;
4a2fec
+            }
4a2fec
             payload_len = ioc->encinput.offset - (ioc->encinput.offset % 4);
4a2fec
         } else {
4a2fec
             payload_len = ioc->payload_remain;
4a2fec
@@ -735,13 +745,18 @@ static int qio_channel_websock_decode_payload(QIOChannelWebsock *ioc,
4a2fec
         }
4a2fec
     }
4a2fec
 
4a2fec
-    /* Drop the payload of ping/pong packets */
4a2fec
     if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
4a2fec
         if (payload_len) {
4a2fec
+            /* binary frames are passed on */
4a2fec
             buffer_reserve(&ioc->rawinput, payload_len);
4a2fec
             buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
4a2fec
         }
4a2fec
-    }
4a2fec
+    } else if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_PING) {
4a2fec
+        /* ping frames produce an immediate reply */
4a2fec
+        buffer_reset(&ioc->ping_reply);
4a2fec
+        qio_channel_websock_encode_buffer(
4a2fec
+            &ioc->ping_reply, QIO_CHANNEL_WEBSOCK_OPCODE_PONG, &ioc->encinput);
4a2fec
+    }   /* pong frames are ignored */
4a2fec
 
4a2fec
     if (payload_len) {
4a2fec
         buffer_advance(&ioc->encinput, payload_len);
4a2fec
@@ -799,6 +814,7 @@ static void qio_channel_websock_finalize(Object *obj)
4a2fec
     buffer_free(&ioc->encoutput);
4a2fec
     buffer_free(&ioc->rawinput);
4a2fec
     buffer_free(&ioc->rawoutput);
4a2fec
+    buffer_free(&ioc->ping_reply);
4a2fec
     object_unref(OBJECT(ioc->master));
4a2fec
     if (ioc->io_tag) {
4a2fec
         g_source_remove(ioc->io_tag);
4a2fec
@@ -855,7 +871,13 @@ static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *ioc,
4a2fec
 {
4a2fec
     ssize_t ret;
4a2fec
     ssize_t done = 0;
4a2fec
-    qio_channel_websock_encode(ioc);
4a2fec
+
4a2fec
+    /* ping replies take priority over binary data */
4a2fec
+    if (!ioc->ping_reply.offset) {
4a2fec
+        qio_channel_websock_encode(ioc);
4a2fec
+    } else if (!ioc->encoutput.offset) {
4a2fec
+        buffer_move_empty(&ioc->encoutput, &ioc->ping_reply);
4a2fec
+    }
4a2fec
 
4a2fec
     while (ioc->encoutput.offset > 0) {
4a2fec
         ret = qio_channel_write(ioc->master,
4a2fec
@@ -930,7 +952,7 @@ static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc)
4a2fec
         return;
4a2fec
     }
4a2fec
 
4a2fec
-    if (ioc->encoutput.offset) {
4a2fec
+    if (ioc->encoutput.offset || ioc->ping_reply.offset) {
4a2fec
         cond |= G_IO_OUT;
4a2fec
     }
4a2fec
     if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
4a2fec
-- 
4a2fec
1.8.3.1
4a2fec