diff options
author | Ed Tanous <ed.tanous@intel.com> | 2018-12-20 12:30:45 -0800 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2019-02-21 01:45:55 +0000 |
commit | 3eb2f35f28249b9b5dc2159a44ca75a0fa7677a5 (patch) | |
tree | fdd26d7d50088bdef022f1d58de8e38458ef6552 /include/kvm_websocket.hpp | |
parent | 2f1ebcd18ca79f4bf19a0924a0b26a8436f24f6c (diff) | |
download | bmcweb-3eb2f35f28249b9b5dc2159a44ca75a0fa7677a5.tar.gz bmcweb-3eb2f35f28249b9b5dc2159a44ca75a0fa7677a5.zip |
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.hpp | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/include/kvm_websocket.hpp b/include/kvm_websocket.hpp new file mode 100644 index 0000000..aa2eaec --- /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 |