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 #include <variant> 22 23 namespace redfish 24 { 25 26 enum NetworkProtocolUnitStructFields 27 { 28 NET_PROTO_UNIT_NAME, 29 NET_PROTO_UNIT_DESC, 30 NET_PROTO_UNIT_LOAD_STATE, 31 NET_PROTO_UNIT_ACTIVE_STATE, 32 NET_PROTO_UNIT_SUB_STATE, 33 NET_PROTO_UNIT_DEVICE, 34 NET_PROTO_UNIT_OBJ_PATH, 35 NET_PROTO_UNIT_ALWAYS_0, 36 NET_PROTO_UNIT_ALWAYS_EMPTY, 37 NET_PROTO_UNIT_ALWAYS_ROOT_PATH 38 }; 39 40 enum NetworkProtocolListenResponseElements 41 { 42 NET_PROTO_LISTEN_TYPE, 43 NET_PROTO_LISTEN_STREAM 44 }; 45 46 /** 47 * @brief D-Bus Unit structure returned in array from ListUnits Method 48 */ 49 using UnitStruct = 50 std::tuple<std::string, std::string, std::string, std::string, std::string, 51 std::string, sdbusplus::message::object_path, uint32_t, 52 std::string, sdbusplus::message::object_path>; 53 54 struct ServiceConfiguration 55 { 56 const char* serviceName; 57 const char* socketPath; 58 }; 59 60 const static boost::container::flat_map<const char*, ServiceConfiguration> 61 protocolToDBus{ 62 {"SSH", 63 {"dropbear.socket", 64 "/org/freedesktop/systemd1/unit/dropbear_2esocket"}}, 65 {"HTTPS", 66 {"bmcweb.service", 67 "/org/freedesktop/systemd1/unit/" 68 "bmcweb_2esocket"}}, //"/org/freedesktop/systemd1/unit/phosphor_2dgevent_2esocket"}}, 69 {"IPMI", 70 {"phosphor-ipmi-net.socket", "/org/freedesktop/systemd1/unit/" 71 "phosphor_2dipmi_2dnet_2esocket"}}}; 72 73 class NetworkProtocol : public Node 74 { 75 public: 76 NetworkProtocol(CrowApp& app) : 77 Node(app, "/redfish/v1/Managers/bmc/NetworkProtocol") 78 { 79 entityPrivileges = { 80 {boost::beast::http::verb::get, {{"Login"}}}, 81 {boost::beast::http::verb::head, {{"Login"}}}, 82 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 83 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 84 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 85 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 86 } 87 88 private: 89 void doGet(crow::Response& res, const crow::Request& req, 90 const std::vector<std::string>& params) override 91 { 92 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 93 94 getData(asyncResp); 95 } 96 97 std::string getHostName() const 98 { 99 std::string hostName; 100 101 std::array<char, HOST_NAME_MAX> hostNameCStr; 102 if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) 103 { 104 hostName = hostNameCStr.data(); 105 } 106 return hostName; 107 } 108 109 void getData(const std::shared_ptr<AsyncResp>& asyncResp) 110 { 111 asyncResp->res.jsonValue["@odata.type"] = 112 "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol"; 113 asyncResp->res.jsonValue["@odata.id"] = 114 "/redfish/v1/Managers/bmc/NetworkProtocol"; 115 asyncResp->res.jsonValue["@odata.context"] = 116 "/redfish/v1/" 117 "$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"; 118 asyncResp->res.jsonValue["Id"] = "NetworkProtocol"; 119 asyncResp->res.jsonValue["Name"] = "Manager Network Protocol"; 120 asyncResp->res.jsonValue["Description"] = "Manager Network Service"; 121 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 122 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK"; 123 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 124 125 for (auto& protocol : protocolToDBus) 126 { 127 asyncResp->res.jsonValue[protocol.first]["ProtocolEnabled"] = false; 128 } 129 130 asyncResp->res.jsonValue["HostName"] = getHostName(); 131 132 crow::connections::systemBus->async_method_call( 133 [asyncResp](const boost::system::error_code ec, 134 const std::vector<UnitStruct>& resp) { 135 if (ec) 136 { 137 asyncResp->res.jsonValue = nlohmann::json::object(); 138 messages::internalError(asyncResp->res); 139 return; 140 } 141 142 for (auto& unit : resp) 143 { 144 for (auto& kv : protocolToDBus) 145 { 146 if (kv.second.serviceName != 147 std::get<NET_PROTO_UNIT_NAME>(unit)) 148 { 149 continue; 150 } 151 const char* service = kv.first; 152 const char* socketPath = kv.second.socketPath; 153 154 asyncResp->res.jsonValue[service]["ProtocolEnabled"] = 155 (std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == 156 "running") || 157 (std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == 158 "listening"); 159 160 crow::connections::systemBus->async_method_call( 161 [asyncResp, service{std::string(service)}]( 162 const boost::system::error_code ec, 163 const std::variant<std::vector<std::tuple< 164 std::string, std::string>>>& resp) { 165 if (ec) 166 { 167 messages::internalError(asyncResp->res); 168 return; 169 } 170 const std::vector< 171 std::tuple<std::string, std::string>>* 172 responsePtr = std::get_if<std::vector< 173 std::tuple<std::string, std::string>>>( 174 &resp); 175 if (responsePtr == nullptr || 176 responsePtr->size() < 1) 177 { 178 return; 179 } 180 181 const std::string& listenStream = 182 std::get<NET_PROTO_LISTEN_STREAM>( 183 (*responsePtr)[0]); 184 std::size_t lastColonPos = 185 listenStream.rfind(":"); 186 if (lastColonPos == std::string::npos) 187 { 188 // Not a port 189 return; 190 } 191 std::string portStr = 192 listenStream.substr(lastColonPos + 1); 193 char* endPtr = nullptr; 194 // Use strtol instead of stroi to avoid 195 // exceptions 196 long port = 197 std::strtol(portStr.c_str(), &endPtr, 10); 198 199 if (*endPtr != '\0' || portStr.empty()) 200 { 201 // Invalid value 202 asyncResp->res.jsonValue[service]["Port"] = 203 nullptr; 204 } 205 else 206 { 207 // Everything OK 208 asyncResp->res.jsonValue[service]["Port"] = 209 port; 210 } 211 }, 212 "org.freedesktop.systemd1", socketPath, 213 "org.freedesktop.DBus.Properties", "Get", 214 "org.freedesktop.systemd1.Socket", "Listen"); 215 } 216 } 217 }, 218 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 219 "org.freedesktop.systemd1.Manager", "ListUnits"); 220 } 221 222 void handleHostnamePatch(const std::string& hostName, 223 const std::shared_ptr<AsyncResp>& asyncResp) 224 { 225 crow::connections::systemBus->async_method_call( 226 [asyncResp](const boost::system::error_code ec) { 227 if (ec) 228 { 229 messages::internalError(asyncResp->res); 230 return; 231 } 232 messages::success(asyncResp->res); 233 }, 234 "xyz.openbmc_project.Network", 235 "/xyz/openbmc_project/network/config", 236 "org.freedesktop.DBus.Properties", "Set", 237 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 238 std::variant<std::string>(hostName)); 239 } 240 241 void doPatch(crow::Response& res, const crow::Request& req, 242 const std::vector<std::string>& params) override 243 { 244 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 245 std::optional<std::string> newHostName; 246 247 if (!json_util::readJson(req, res, "HostName", newHostName)) 248 { 249 return; 250 } 251 if (newHostName) 252 { 253 handleHostnamePatch(*newHostName, asyncResp); 254 return; 255 } 256 } 257 }; 258 259 } // namespace redfish 260