1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "error_messages.hpp" 19 #include "node.hpp" 20 21 namespace redfish { 22 23 enum NetworkProtocolUnitStructFields { 24 NET_PROTO_UNIT_NAME, 25 NET_PROTO_UNIT_DESC, 26 NET_PROTO_UNIT_LOAD_STATE, 27 NET_PROTO_UNIT_ACTIVE_STATE, 28 NET_PROTO_UNIT_SUB_STATE, 29 NET_PROTO_UNIT_DEVICE, 30 NET_PROTO_UNIT_OBJ_PATH, 31 NET_PROTO_UNIT_ALWAYS_0, 32 NET_PROTO_UNIT_ALWAYS_EMPTY, 33 NET_PROTO_UNIT_ALWAYS_ROOT_PATH 34 }; 35 36 enum NetworkProtocolListenResponseElements { 37 NET_PROTO_LISTEN_TYPE, 38 NET_PROTO_LISTEN_STREAM 39 }; 40 41 /** 42 * @brief D-Bus Unit structure returned in array from ListUnits Method 43 */ 44 using UnitStruct = 45 std::tuple<std::string, std::string, std::string, std::string, std::string, 46 std::string, sdbusplus::message::object_path, uint32_t, 47 std::string, sdbusplus::message::object_path>; 48 49 struct ServiceConfiguration { 50 std::string serviceName; 51 std::string socketPath; 52 }; 53 54 class OnDemandNetworkProtocolProvider { 55 public: 56 template <typename CallbackFunc> 57 static void getServices(CallbackFunc&& callback) { 58 crow::connections::systemBus->async_method_call( 59 [callback{std::move(callback)}](const boost::system::error_code ec, 60 const std::vector<UnitStruct>& resp) { 61 if (ec) { 62 callback(false, resp); 63 } else { 64 callback(true, resp); 65 } 66 }, 67 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 68 "org.freedesktop.systemd1.Manager", "ListUnits"); 69 } 70 71 template <typename CallbackFunc> 72 static void getSocketListenPort(const std::string& path, 73 CallbackFunc&& callback) { 74 crow::connections::systemBus->async_method_call( 75 [callback{std::move(callback)}]( 76 const boost::system::error_code ec, 77 const sdbusplus::message::variant< 78 std::vector<std::tuple<std::string, std::string>>>& resp) { 79 if (ec) { 80 callback(false, false, 0); 81 } else { 82 auto responsePtr = mapbox::getPtr< 83 const std::vector<std::tuple<std::string, std::string>>>(resp); 84 85 std::string listenStream = 86 std::get<NET_PROTO_LISTEN_STREAM>((*responsePtr)[0]); 87 auto lastColonPos = listenStream.rfind(":"); 88 if (lastColonPos != std::string::npos) { 89 std::string portStr = listenStream.substr(lastColonPos + 1); 90 char* endPtr; 91 // Use strtol instead of stroi to avoid exceptions 92 long port = std::strtol(portStr.c_str(), &endPtr, 10); 93 94 if (*endPtr != '\0' || portStr.empty()) { 95 // Invalid value 96 callback(true, false, 0); 97 } else { 98 // Everything OK 99 callback(true, true, port); 100 } 101 } else { 102 // Not a port 103 callback(true, false, 0); 104 } 105 } 106 }, 107 "org.freedesktop.systemd1", path, "org.freedesktop.DBus.Properties", 108 "Get", "org.freedesktop.systemd1.Socket", "Listen"); 109 } 110 }; 111 112 class NetworkProtocol : public Node { 113 public: 114 NetworkProtocol(CrowApp& app) 115 : Node(app, "/redfish/v1/Managers/openbmc/NetworkProtocol") { 116 Node::json["@odata.type"] = 117 "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"; 118 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/NetworkProtocol"; 119 Node::json["@odata.context"] = 120 "/redfish/v1/$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"; 121 Node::json["Id"] = "NetworkProtocol"; 122 Node::json["Name"] = "Manager Network Protocol"; 123 Node::json["Description"] = "Manager Network Service"; 124 Node::json["Status"]["Health"] = "OK"; 125 Node::json["Status"]["HealthRollup"] = "OK"; 126 Node::json["Status"]["State"] = "Enabled"; 127 128 for (auto& protocol : protocolToDBus) { 129 Node::json[protocol.first]["ProtocolEnabled"] = false; 130 } 131 132 entityPrivileges = { 133 {boost::beast::http::verb::get, {{"Login"}}}, 134 {boost::beast::http::verb::head, {{"Login"}}}, 135 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 136 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 137 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 138 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 139 } 140 141 private: 142 void doGet(crow::Response& res, const crow::Request& req, 143 const std::vector<std::string>& params) override { 144 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 145 146 getData(asyncResp); 147 } 148 149 std::string getHostName() const { 150 std::string hostName; 151 152 std::array<char, HOST_NAME_MAX> hostNameCStr; 153 if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) { 154 hostName = hostNameCStr.data(); 155 } 156 return hostName; 157 } 158 159 void getData(const std::shared_ptr<AsyncResp>& asyncResp) { 160 Node::json["HostName"] = getHostName(); 161 asyncResp->res.jsonValue = Node::json; 162 163 OnDemandNetworkProtocolProvider::getServices( 164 [&, asyncResp](const bool success, 165 const std::vector<UnitStruct>& resp) { 166 if (!success) { 167 asyncResp->res.jsonValue = nlohmann::json::object(); 168 messages::addMessageToErrorJson(asyncResp->res.jsonValue, 169 messages::internalError()); 170 asyncResp->res.result( 171 boost::beast::http::status::internal_server_error); 172 } 173 174 for (auto& unit : resp) { 175 for (auto& kv : protocolToDBus) { 176 if (kv.second.serviceName == 177 std::get<NET_PROTO_UNIT_NAME>(unit)) { 178 std::string service = kv.first; 179 180 // Process state 181 if (std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == "running") { 182 asyncResp->res.jsonValue[service]["ProtocolEnabled"] = true; 183 } else { 184 asyncResp->res.jsonValue[service]["ProtocolEnabled"] = false; 185 } 186 187 // Process port 188 OnDemandNetworkProtocolProvider::getSocketListenPort( 189 kv.second.socketPath, 190 [&, asyncResp, service{std::move(service)} ]( 191 const bool fetchSuccess, const bool portAvailable, 192 const unsigned long port) { 193 if (fetchSuccess) { 194 if (portAvailable) { 195 asyncResp->res.jsonValue[service]["Port"] = port; 196 } else { 197 asyncResp->res.jsonValue[service]["Port"] = nullptr; 198 } 199 } else { 200 messages::addMessageToJson(asyncResp->res.jsonValue, 201 messages::internalError(), 202 "/" + service); 203 } 204 }); 205 break; 206 } 207 } 208 } 209 }); 210 } 211 212 boost::container::flat_map<std::string, ServiceConfiguration> protocolToDBus{ 213 {"SSH", 214 {"dropbear.service", 215 "/org/freedesktop/systemd1/unit/dropbear_2esocket"}}, 216 {"HTTPS", 217 {"phosphor-gevent.service", 218 "/org/freedesktop/systemd1/unit/phosphor_2dgevent_2esocket"}}, 219 {"IPMI", 220 {"phosphor-ipmi-net.service", 221 "/org/freedesktop/systemd1/unit/phosphor_2dipmi_2dnet_2esocket"}}}; 222 }; 223 224 } // namespace redfish 225