summaryrefslogtreecommitdiff
path: root/include/kvm_websocket.hpp
diff options
context:
space:
mode:
authorEd Tanous <ed.tanous@intel.com>2018-12-20 23:30:45 +0300
committerEd Tanous <ed.tanous@intel.com>2019-02-21 04:45:55 +0300
commit3eb2f35f28249b9b5dc2159a44ca75a0fa7677a5 (patch)
treefdd26d7d50088bdef022f1d58de8e38458ef6552 /include/kvm_websocket.hpp
parent2f1ebcd18ca79f4bf19a0924a0b26a8436f24f6c (diff)
downloadbmcweb-3eb2f35f28249b9b5dc2159a44ca75a0fa7677a5.tar.xz
Implement KVM websocket proxy in bmcweb
This patchset implements a KVM websocket proxy designed to interoperate with phosphor-webui and KVM. in short, IP address 127.0.0.1:5900 is proxied to the websocket. This allows someone to connect from a browser session. Requires patchset here for the phosphor-webui side: https://gerrit.openbmc-project.xyz/#/c/openbmc/phosphor-webui/+/10268/ and requires the kvm patches here: https://gerrit.openbmc-project.xyz/#/c/openbmc/meta-phosphor/+/13536/ Tested By: Launched webui, observed KVM. Moved mouse, and typed on keyboard, changes appeared on host system. Change-Id: I407488f4b16be208b188a0abc19954a0243af173 Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Diffstat (limited to 'include/kvm_websocket.hpp')
-rw-r--r--include/kvm_websocket.hpp173
1 files changed, 173 insertions, 0 deletions
diff --git a/include/kvm_websocket.hpp b/include/kvm_websocket.hpp
new file mode 100644
index 0000000000..aa2eaecc32
--- /dev/null
+++ b/include/kvm_websocket.hpp
@@ -0,0 +1,173 @@
+#pragma once
+#include <crow/app.h>
+#include <crow/websocket.h>
+#include <sys/socket.h>
+
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <webserver_common.hpp>
+
+namespace crow
+{
+namespace obmc_kvm
+{
+
+static std::unique_ptr<boost::asio::ip::tcp::socket> hostSocket;
+
+// TODO(ed) validate that these buffer sizes are sane
+static boost::beast::flat_static_buffer<1024U * 50U> outputBuffer;
+static boost::beast::flat_static_buffer<1024U> inputBuffer;
+
+static crow::websocket::Connection* session = nullptr;
+
+static bool doingWrite = false;
+
+inline void doWrite()
+{
+ if (doingWrite)
+ {
+ BMCWEB_LOG_DEBUG << "Already writing. Bailing out";
+ return;
+ }
+ if (inputBuffer.size() == 0)
+ {
+ BMCWEB_LOG_DEBUG << "inputBuffer empty. Bailing out";
+ return;
+ }
+
+ doingWrite = true;
+ hostSocket->async_write_some(
+ inputBuffer.data(),
+ [](boost::beast::error_code ec, std::size_t bytes_written) {
+ BMCWEB_LOG_DEBUG << "Wrote " << bytes_written << "bytes";
+ doingWrite = false;
+ inputBuffer.consume(bytes_written);
+
+ if (session == nullptr)
+ {
+ return;
+ }
+ if (ec == boost::asio::error::eof)
+ {
+ session->close("KVM socket port closed");
+ return;
+ }
+ if (ec)
+ {
+ session->close("Error in reading to host port");
+ BMCWEB_LOG_ERROR << "Error in KVM socket write " << ec;
+ return;
+ }
+ doWrite();
+ });
+}
+
+inline void doRead();
+
+inline void readDone(const boost::system::error_code& ec, std::size_t bytesRead)
+{
+ outputBuffer.commit(bytesRead);
+ BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes";
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Couldn't read from KVM socket port: " << ec;
+ if (session != nullptr)
+ {
+ session->close("Error in connecting to KVM port");
+ }
+ return;
+ }
+ if (session == nullptr)
+ {
+ return;
+ }
+
+ boost::beast::string_view payload(
+ static_cast<const char*>(outputBuffer.data().data()), bytesRead);
+ BMCWEB_LOG_DEBUG << "Sending payload size " << payload.size();
+ session->sendBinary(payload);
+ outputBuffer.consume(bytesRead);
+
+ doRead();
+}
+
+inline void doRead()
+{
+ std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
+ BMCWEB_LOG_DEBUG << "Reading " << bytes << " from kvm socket";
+ hostSocket->async_read_some(
+ outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
+ readDone);
+}
+
+inline void connectHandler(const boost::system::error_code& ec)
+{
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "Couldn't connect to KVM socket port: " << ec;
+ if (session != nullptr)
+ {
+ session->close("Error in connecting to KVM port");
+ }
+ return;
+ }
+
+ doWrite();
+ doRead();
+}
+
+inline void requestRoutes(CrowApp& app)
+{
+ BMCWEB_ROUTE(app, "/kvm/0")
+ .websocket()
+ .onopen([](crow::websocket::Connection& conn) {
+ BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
+
+ if (session != nullptr)
+ {
+ conn.close("User already connected");
+ return;
+ }
+
+ session = &conn;
+ if (hostSocket == nullptr)
+ {
+ boost::asio::ip::tcp::endpoint endpoint(
+ boost::asio::ip::address::from_string("127.0.0.1"), 5900);
+
+ hostSocket = std::make_unique<boost::asio::ip::tcp::socket>(
+ conn.get_io_context());
+ hostSocket->async_connect(endpoint, connectHandler);
+ }
+ })
+ .onclose(
+ [](crow::websocket::Connection& conn, const std::string& reason) {
+ session = nullptr;
+ hostSocket = nullptr;
+ inputBuffer.reset();
+ outputBuffer.reset();
+ })
+ .onmessage([](crow::websocket::Connection& conn,
+ const std::string& data, bool is_binary) {
+ if (data.length() > inputBuffer.capacity())
+ {
+ BMCWEB_LOG_ERROR << "Buffer overrun when writing "
+ << data.length() << " bytes";
+ conn.close("Buffer overrun");
+ return;
+ }
+
+ BMCWEB_LOG_DEBUG << "Read " << data.size()
+ << " bytes from websocket";
+ boost::asio::buffer_copy(inputBuffer.prepare(data.size()),
+ boost::asio::buffer(data));
+ BMCWEB_LOG_DEBUG << "commiting " << data.size()
+ << " bytes from websocket";
+ inputBuffer.commit(data.size());
+
+ BMCWEB_LOG_DEBUG << "inputbuffer size " << inputBuffer.size();
+ doWrite();
+ });
+}
+} // namespace obmc_kvm
+} // namespace crow