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 <optional> 22 #include <utils/json_utils.hpp> 23 #include <variant> 24 namespace redfish 25 { 26 27 enum NetworkProtocolUnitStructFields 28 { 29 NET_PROTO_UNIT_NAME, 30 NET_PROTO_UNIT_DESC, 31 NET_PROTO_UNIT_LOAD_STATE, 32 NET_PROTO_UNIT_ACTIVE_STATE, 33 NET_PROTO_UNIT_SUB_STATE, 34 NET_PROTO_UNIT_DEVICE, 35 NET_PROTO_UNIT_OBJ_PATH, 36 NET_PROTO_UNIT_ALWAYS_0, 37 NET_PROTO_UNIT_ALWAYS_EMPTY, 38 NET_PROTO_UNIT_ALWAYS_ROOT_PATH 39 }; 40 41 enum NetworkProtocolListenResponseElements 42 { 43 NET_PROTO_LISTEN_TYPE, 44 NET_PROTO_LISTEN_STREAM 45 }; 46 47 /** 48 * @brief D-Bus Unit structure returned in array from ListUnits Method 49 */ 50 using UnitStruct = 51 std::tuple<std::string, std::string, std::string, std::string, std::string, 52 std::string, sdbusplus::message::object_path, uint32_t, 53 std::string, sdbusplus::message::object_path>; 54 55 struct ServiceConfiguration 56 { 57 const char* serviceName; 58 const char* socketPath; 59 }; 60 61 const static boost::container::flat_map<const char*, ServiceConfiguration> 62 protocolToDBus{ 63 {"SSH", 64 {"dropbear.socket", 65 "/org/freedesktop/systemd1/unit/dropbear_2esocket"}}, 66 {"HTTPS", 67 {"bmcweb.service", 68 "/org/freedesktop/systemd1/unit/" 69 "bmcweb_2esocket"}}, //"/org/freedesktop/systemd1/unit/phosphor_2dgevent_2esocket"}}, 70 {"IPMI", 71 {"phosphor-ipmi-net.socket", "/org/freedesktop/systemd1/unit/" 72 "phosphor_2dipmi_2dnet_2esocket"}}}; 73 74 inline void extractNTPServersData(const GetManagedObjects& dbus_data, 75 std::vector<std::string>& ntpData) 76 { 77 for (const auto& obj : dbus_data) 78 { 79 for (const auto& ifacePair : obj.second) 80 { 81 if (obj.first == "/xyz/openbmc_project/network/eth0") 82 { 83 if (ifacePair.first == 84 "xyz.openbmc_project.Network.EthernetInterface") 85 { 86 for (const auto& propertyPair : ifacePair.second) 87 { 88 if (propertyPair.first == "NTPServers") 89 { 90 const std::vector<std::string>* ntpServers = 91 sdbusplus::message::variant_ns::get_if< 92 std::vector<std::string>>( 93 &propertyPair.second); 94 if (ntpServers != nullptr) 95 { 96 ntpData = std::move(*ntpServers); 97 } 98 } 99 } 100 } 101 } 102 } 103 } 104 } 105 106 template <typename CallbackFunc> 107 void getEthernetIfaceData(CallbackFunc&& callback) 108 { 109 crow::connections::systemBus->async_method_call( 110 [callback{std::move(callback)}]( 111 const boost::system::error_code error_code, 112 const GetManagedObjects& dbus_data) { 113 std::vector<std::string> ntpServers; 114 115 if (error_code) 116 { 117 callback(false, ntpServers); 118 return; 119 } 120 121 extractNTPServersData(dbus_data, ntpServers); 122 123 callback(true, ntpServers); 124 }, 125 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 126 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 127 }; 128 129 class NetworkProtocol : public Node 130 { 131 public: 132 NetworkProtocol(CrowApp& app) : 133 Node(app, "/redfish/v1/Managers/bmc/NetworkProtocol") 134 { 135 entityPrivileges = { 136 {boost::beast::http::verb::get, {{"Login"}}}, 137 {boost::beast::http::verb::head, {{"Login"}}}, 138 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 139 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 140 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 141 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 142 } 143 144 private: 145 void doGet(crow::Response& res, const crow::Request& req, 146 const std::vector<std::string>& params) override 147 { 148 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 149 150 getData(asyncResp); 151 } 152 153 std::string getHostName() const 154 { 155 std::string hostName; 156 157 std::array<char, HOST_NAME_MAX> hostNameCStr; 158 if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) 159 { 160 hostName = hostNameCStr.data(); 161 } 162 return hostName; 163 } 164 165 void getNTPProtocolEnabled(const std::shared_ptr<AsyncResp>& asyncResp) 166 { 167 crow::connections::systemBus->async_method_call( 168 [asyncResp](const boost::system::error_code error_code, 169 const std::variant<std::string>& timeSyncMethod) { 170 const std::string* s = 171 std::get_if<std::string>(&timeSyncMethod); 172 173 if (*s == "xyz.openbmc_project.Time.Synchronization.Method.NTP") 174 { 175 asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = true; 176 } 177 else if (*s == "xyz.openbmc_project.Time.Synchronization." 178 "Method.Manual") 179 { 180 asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = false; 181 } 182 }, 183 "xyz.openbmc_project.Settings", 184 "/xyz/openbmc_project/time/sync_method", 185 "org.freedesktop.DBus.Properties", "Get", 186 "xyz.openbmc_project.Time.Synchronization", "TimeSyncMethod"); 187 } 188 189 void getData(const std::shared_ptr<AsyncResp>& asyncResp) 190 { 191 asyncResp->res.jsonValue["@odata.type"] = 192 "#ManagerNetworkProtocol.v1_4_0.ManagerNetworkProtocol"; 193 asyncResp->res.jsonValue["@odata.id"] = 194 "/redfish/v1/Managers/bmc/NetworkProtocol"; 195 asyncResp->res.jsonValue["@odata.context"] = 196 "/redfish/v1/" 197 "$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol"; 198 asyncResp->res.jsonValue["Id"] = "NetworkProtocol"; 199 asyncResp->res.jsonValue["Name"] = "Manager Network Protocol"; 200 asyncResp->res.jsonValue["Description"] = "Manager Network Service"; 201 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 202 asyncResp->res.jsonValue["Status"]["HealthRollup"] = "OK"; 203 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 204 205 for (auto& protocol : protocolToDBus) 206 { 207 asyncResp->res.jsonValue[protocol.first]["ProtocolEnabled"] = false; 208 } 209 210 asyncResp->res.jsonValue["HostName"] = getHostName(); 211 212 getNTPProtocolEnabled(asyncResp); 213 214 // TODO Get eth0 interface data, and call the below callback for JSON 215 // preparation 216 getEthernetIfaceData( 217 [this, asyncResp](const bool& success, 218 const std::vector<std::string>& ntpServers) { 219 if (!success) 220 { 221 messages::resourceNotFound(asyncResp->res, 222 "EthernetInterface", "eth0"); 223 return; 224 } 225 asyncResp->res.jsonValue["NTP"]["NTPServers"] = ntpServers; 226 }); 227 228 crow::connections::systemBus->async_method_call( 229 [asyncResp](const boost::system::error_code ec, 230 const std::vector<UnitStruct>& resp) { 231 if (ec) 232 { 233 asyncResp->res.jsonValue = nlohmann::json::object(); 234 messages::internalError(asyncResp->res); 235 return; 236 } 237 238 for (auto& unit : resp) 239 { 240 for (auto& kv : protocolToDBus) 241 { 242 if (kv.second.serviceName != 243 std::get<NET_PROTO_UNIT_NAME>(unit)) 244 { 245 continue; 246 } 247 const char* service = kv.first; 248 const char* socketPath = kv.second.socketPath; 249 250 asyncResp->res.jsonValue[service]["ProtocolEnabled"] = 251 (std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == 252 "running") || 253 (std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == 254 "listening"); 255 256 crow::connections::systemBus->async_method_call( 257 [asyncResp, service{std::string(service)}]( 258 const boost::system::error_code ec, 259 const std::variant<std::vector<std::tuple< 260 std::string, std::string>>>& resp) { 261 if (ec) 262 { 263 messages::internalError(asyncResp->res); 264 return; 265 } 266 const std::vector< 267 std::tuple<std::string, std::string>>* 268 responsePtr = std::get_if<std::vector< 269 std::tuple<std::string, std::string>>>( 270 &resp); 271 if (responsePtr == nullptr || 272 responsePtr->size() < 1) 273 { 274 return; 275 } 276 277 const std::string& listenStream = 278 std::get<NET_PROTO_LISTEN_STREAM>( 279 (*responsePtr)[0]); 280 std::size_t lastColonPos = 281 listenStream.rfind(":"); 282 if (lastColonPos == std::string::npos) 283 { 284 // Not a port 285 return; 286 } 287 std::string portStr = 288 listenStream.substr(lastColonPos + 1); 289 char* endPtr = nullptr; 290 // Use strtol instead of stroi to avoid 291 // exceptions 292 long port = 293 std::strtol(portStr.c_str(), &endPtr, 10); 294 295 if (*endPtr != '\0' || portStr.empty()) 296 { 297 // Invalid value 298 asyncResp->res.jsonValue[service]["Port"] = 299 nullptr; 300 } 301 else 302 { 303 // Everything OK 304 asyncResp->res.jsonValue[service]["Port"] = 305 port; 306 } 307 }, 308 "org.freedesktop.systemd1", socketPath, 309 "org.freedesktop.DBus.Properties", "Get", 310 "org.freedesktop.systemd1.Socket", "Listen"); 311 } 312 } 313 }, 314 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 315 "org.freedesktop.systemd1.Manager", "ListUnits"); 316 } 317 318 void handleHostnamePatch(const std::string& hostName, 319 const std::shared_ptr<AsyncResp>& asyncResp) 320 { 321 crow::connections::systemBus->async_method_call( 322 [asyncResp](const boost::system::error_code ec) { 323 if (ec) 324 { 325 messages::internalError(asyncResp->res); 326 return; 327 } 328 messages::success(asyncResp->res); 329 }, 330 "xyz.openbmc_project.Network", 331 "/xyz/openbmc_project/network/config", 332 "org.freedesktop.DBus.Properties", "Set", 333 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 334 std::variant<std::string>(hostName)); 335 } 336 337 void handleNTPProtocolEnabled(const bool& ntpEnabled, 338 const std::shared_ptr<AsyncResp>& asyncResp) 339 { 340 std::string timeSyncMethod; 341 if (ntpEnabled) 342 { 343 timeSyncMethod = 344 "xyz.openbmc_project.Time.Synchronization.Method.NTP"; 345 } 346 else 347 { 348 timeSyncMethod = 349 "xyz.openbmc_project.Time.Synchronization.Method.Manual"; 350 } 351 352 crow::connections::systemBus->async_method_call( 353 [asyncResp, 354 ntpEnabled](const boost::system::error_code error_code) { 355 asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = ntpEnabled; 356 }, 357 "xyz.openbmc_project.Settings", 358 "/xyz/openbmc_project/time/sync_method", 359 "org.freedesktop.DBus.Properties", "Set", 360 "xyz.openbmc_project.Time.Synchronization", "TimeSyncMethod", 361 std::variant<std::string>{timeSyncMethod}); 362 } 363 364 void handleNTPServersPatch(const std::vector<std::string>& ntpServers, 365 const std::shared_ptr<AsyncResp>& asyncResp) 366 { 367 crow::connections::systemBus->async_method_call( 368 [asyncResp, ntpServers](const boost::system::error_code ec) { 369 if (ec) 370 { 371 messages::internalError(asyncResp->res); 372 return; 373 } 374 asyncResp->res.jsonValue["NTP"]["NTPServers"] = 375 std::move(ntpServers); 376 }, 377 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network/eth0", 378 "org.freedesktop.DBus.Properties", "Set", 379 "xyz.openbmc_project.Network.EthernetInterface", "NTPServers", 380 std::variant<std::vector<std::string>>{ntpServers}); 381 } 382 383 void doPatch(crow::Response& res, const crow::Request& req, 384 const std::vector<std::string>& params) override 385 { 386 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 387 std::optional<std::string> newHostName; 388 std::optional<std::vector<std::string>> ntpServers; 389 std::optional<bool> ntpEnabled; 390 391 if (!json_util::readJson(req, res, "HostName", newHostName, 392 "NTPServers", ntpServers, "NTPEnabled", 393 ntpEnabled)) 394 { 395 return; 396 } 397 if (newHostName) 398 { 399 handleHostnamePatch(*newHostName, asyncResp); 400 return; 401 } 402 if (ntpEnabled) 403 { 404 handleNTPProtocolEnabled(*ntpEnabled, asyncResp); 405 } 406 if (ntpServers) 407 { 408 handleNTPServersPatch(*ntpServers, asyncResp); 409 } 410 } 411 }; 412 413 } // namespace redfish 414