xref: /openbmc/bmcweb/features/redfish/lib/network_protocol.hpp (revision 3a8a0088db901136584da9775acf76af0467f046)
170141561SBorawski.Lukasz /*
270141561SBorawski.Lukasz // Copyright (c) 2018 Intel Corporation
370141561SBorawski.Lukasz //
470141561SBorawski.Lukasz // Licensed under the Apache License, Version 2.0 (the "License");
570141561SBorawski.Lukasz // you may not use this file except in compliance with the License.
670141561SBorawski.Lukasz // You may obtain a copy of the License at
770141561SBorawski.Lukasz //
870141561SBorawski.Lukasz //      http://www.apache.org/licenses/LICENSE-2.0
970141561SBorawski.Lukasz //
1070141561SBorawski.Lukasz // Unless required by applicable law or agreed to in writing, software
1170141561SBorawski.Lukasz // distributed under the License is distributed on an "AS IS" BASIS,
1270141561SBorawski.Lukasz // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1370141561SBorawski.Lukasz // See the License for the specific language governing permissions and
1470141561SBorawski.Lukasz // limitations under the License.
1570141561SBorawski.Lukasz */
1670141561SBorawski.Lukasz #pragma once
1770141561SBorawski.Lukasz 
18*3a8a0088SKowalski, Kamil #include "error_messages.hpp"
1970141561SBorawski.Lukasz #include "node.hpp"
2070141561SBorawski.Lukasz 
2170141561SBorawski.Lukasz namespace redfish {
2270141561SBorawski.Lukasz 
23*3a8a0088SKowalski, Kamil enum NetworkProtocolUnitStructFields {
24*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_NAME,
25*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_DESC,
26*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_LOAD_STATE,
27*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_ACTIVE_STATE,
28*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_SUB_STATE,
29*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_DEVICE,
30*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_OBJ_PATH,
31*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_ALWAYS_0,
32*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_ALWAYS_EMPTY,
33*3a8a0088SKowalski, Kamil   NET_PROTO_UNIT_ALWAYS_ROOT_PATH
34*3a8a0088SKowalski, Kamil };
35*3a8a0088SKowalski, Kamil 
36*3a8a0088SKowalski, Kamil enum NetworkProtocolListenResponseElements {
37*3a8a0088SKowalski, Kamil   NET_PROTO_LISTEN_TYPE,
38*3a8a0088SKowalski, Kamil   NET_PROTO_LISTEN_STREAM
39*3a8a0088SKowalski, Kamil };
40*3a8a0088SKowalski, Kamil 
41*3a8a0088SKowalski, Kamil /**
42*3a8a0088SKowalski, Kamil  * @brief D-Bus Unit structure returned in array from ListUnits Method
43*3a8a0088SKowalski, Kamil  */
44*3a8a0088SKowalski, Kamil using UnitStruct =
45*3a8a0088SKowalski, Kamil     std::tuple<std::string, std::string, std::string, std::string, std::string,
46*3a8a0088SKowalski, Kamil                std::string, sdbusplus::message::object_path, uint32_t,
47*3a8a0088SKowalski, Kamil                std::string, sdbusplus::message::object_path>;
48*3a8a0088SKowalski, Kamil 
49*3a8a0088SKowalski, Kamil struct ServiceConfiguration {
50*3a8a0088SKowalski, Kamil   std::string serviceName;
51*3a8a0088SKowalski, Kamil   std::string socketPath;
52*3a8a0088SKowalski, Kamil };
53*3a8a0088SKowalski, Kamil 
54*3a8a0088SKowalski, Kamil class OnDemandNetworkProtocolProvider {
55*3a8a0088SKowalski, Kamil  public:
56*3a8a0088SKowalski, Kamil   template <typename CallbackFunc>
57*3a8a0088SKowalski, Kamil   static void getServices(CallbackFunc&& callback) {
58*3a8a0088SKowalski, Kamil     crow::connections::systemBus->async_method_call(
59*3a8a0088SKowalski, Kamil         [callback{std::move(callback)}](const boost::system::error_code ec,
60*3a8a0088SKowalski, Kamil                                         const std::vector<UnitStruct>& resp) {
61*3a8a0088SKowalski, Kamil           if (ec) {
62*3a8a0088SKowalski, Kamil             callback(false, resp);
63*3a8a0088SKowalski, Kamil           } else {
64*3a8a0088SKowalski, Kamil             callback(true, resp);
65*3a8a0088SKowalski, Kamil           }
66*3a8a0088SKowalski, Kamil         },
67*3a8a0088SKowalski, Kamil         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
68*3a8a0088SKowalski, Kamil         "org.freedesktop.systemd1.Manager", "ListUnits");
69*3a8a0088SKowalski, Kamil   }
70*3a8a0088SKowalski, Kamil 
71*3a8a0088SKowalski, Kamil   template <typename CallbackFunc>
72*3a8a0088SKowalski, Kamil   static void getSocketListenPort(const std::string& path,
73*3a8a0088SKowalski, Kamil                                   CallbackFunc&& callback) {
74*3a8a0088SKowalski, Kamil     crow::connections::systemBus->async_method_call(
75*3a8a0088SKowalski, Kamil         [callback{std::move(callback)}](
76*3a8a0088SKowalski, Kamil             const boost::system::error_code ec,
77*3a8a0088SKowalski, Kamil             const sdbusplus::message::variant<
78*3a8a0088SKowalski, Kamil                 std::vector<std::tuple<std::string, std::string>>>& resp) {
79*3a8a0088SKowalski, Kamil           if (ec) {
80*3a8a0088SKowalski, Kamil             callback(false, false, 0);
81*3a8a0088SKowalski, Kamil           } else {
82*3a8a0088SKowalski, Kamil             auto responsePtr = mapbox::getPtr<
83*3a8a0088SKowalski, Kamil                 const std::vector<std::tuple<std::string, std::string>>>(resp);
84*3a8a0088SKowalski, Kamil 
85*3a8a0088SKowalski, Kamil             std::string listenStream =
86*3a8a0088SKowalski, Kamil                 std::get<NET_PROTO_LISTEN_STREAM>((*responsePtr)[0]);
87*3a8a0088SKowalski, Kamil             auto lastColonPos = listenStream.rfind(":");
88*3a8a0088SKowalski, Kamil             if (lastColonPos != std::string::npos) {
89*3a8a0088SKowalski, Kamil               std::string portStr = listenStream.substr(lastColonPos + 1);
90*3a8a0088SKowalski, Kamil               char* endPtr;
91*3a8a0088SKowalski, Kamil               // Use strtol instead of stroi to avoid exceptions
92*3a8a0088SKowalski, Kamil               long port = std::strtol(portStr.c_str(), &endPtr, 10);
93*3a8a0088SKowalski, Kamil 
94*3a8a0088SKowalski, Kamil               if (*endPtr != '\0' || portStr.empty()) {
95*3a8a0088SKowalski, Kamil                 // Invalid value
96*3a8a0088SKowalski, Kamil                 callback(true, false, 0);
97*3a8a0088SKowalski, Kamil               } else {
98*3a8a0088SKowalski, Kamil                 // Everything OK
99*3a8a0088SKowalski, Kamil                 callback(true, true, port);
100*3a8a0088SKowalski, Kamil               }
101*3a8a0088SKowalski, Kamil             } else {
102*3a8a0088SKowalski, Kamil               // Not a port
103*3a8a0088SKowalski, Kamil               callback(true, false, 0);
104*3a8a0088SKowalski, Kamil             }
105*3a8a0088SKowalski, Kamil           }
106*3a8a0088SKowalski, Kamil         },
107*3a8a0088SKowalski, Kamil         "org.freedesktop.systemd1", path, "org.freedesktop.DBus.Properties",
108*3a8a0088SKowalski, Kamil         "Get", "org.freedesktop.systemd1.Socket", "Listen");
109*3a8a0088SKowalski, Kamil   }
110*3a8a0088SKowalski, Kamil };
111*3a8a0088SKowalski, Kamil 
11270141561SBorawski.Lukasz class NetworkProtocol : public Node {
11370141561SBorawski.Lukasz  public:
11470141561SBorawski.Lukasz   NetworkProtocol(CrowApp& app)
1154b1b8683SBorawski.Lukasz       : Node(app, "/redfish/v1/Managers/openbmc/NetworkProtocol") {
11670141561SBorawski.Lukasz     Node::json["@odata.type"] =
11770141561SBorawski.Lukasz         "#ManagerNetworkProtocol.v1_1_0.ManagerNetworkProtocol";
11870141561SBorawski.Lukasz     Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/NetworkProtocol";
11970141561SBorawski.Lukasz     Node::json["@odata.context"] =
12070141561SBorawski.Lukasz         "/redfish/v1/$metadata#ManagerNetworkProtocol.ManagerNetworkProtocol";
12170141561SBorawski.Lukasz     Node::json["Id"] = "NetworkProtocol";
12270141561SBorawski.Lukasz     Node::json["Name"] = "Manager Network Protocol";
12370141561SBorawski.Lukasz     Node::json["Description"] = "Manager Network Service";
12470141561SBorawski.Lukasz     Node::json["Status"]["Health"] = "OK";
12570141561SBorawski.Lukasz     Node::json["Status"]["HealthRollup"] = "OK";
12670141561SBorawski.Lukasz     Node::json["Status"]["State"] = "Enabled";
1273ebd75f7SEd Tanous 
128*3a8a0088SKowalski, Kamil     for (auto& protocol : protocolToDBus) {
129*3a8a0088SKowalski, Kamil       Node::json[protocol.first]["ProtocolEnabled"] = false;
130*3a8a0088SKowalski, Kamil     }
131*3a8a0088SKowalski, Kamil 
1324b1b8683SBorawski.Lukasz     entityPrivileges = {
1334b1b8683SBorawski.Lukasz         {boost::beast::http::verb::get, {{"Login"}}},
134e0d918bcSEd Tanous         {boost::beast::http::verb::head, {{"Login"}}},
135e0d918bcSEd Tanous         {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
136e0d918bcSEd Tanous         {boost::beast::http::verb::put, {{"ConfigureManager"}}},
137e0d918bcSEd Tanous         {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
138e0d918bcSEd Tanous         {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
13970141561SBorawski.Lukasz   }
14070141561SBorawski.Lukasz 
14170141561SBorawski.Lukasz  private:
14255c7b7a2SEd Tanous   void doGet(crow::Response& res, const crow::Request& req,
14370141561SBorawski.Lukasz              const std::vector<std::string>& params) override {
144*3a8a0088SKowalski, Kamil     std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
145*3a8a0088SKowalski, Kamil 
146*3a8a0088SKowalski, Kamil     getData(asyncResp);
14770141561SBorawski.Lukasz   }
14870141561SBorawski.Lukasz 
14970141561SBorawski.Lukasz   std::string getHostName() const {
15070141561SBorawski.Lukasz     std::string hostName;
15170141561SBorawski.Lukasz 
15270141561SBorawski.Lukasz     std::array<char, HOST_NAME_MAX> hostNameCStr;
15370141561SBorawski.Lukasz     if (gethostname(hostNameCStr.data(), hostNameCStr.size()) == 0) {
15470141561SBorawski.Lukasz       hostName = hostNameCStr.data();
15570141561SBorawski.Lukasz     }
15670141561SBorawski.Lukasz     return hostName;
15770141561SBorawski.Lukasz   }
15870141561SBorawski.Lukasz 
159*3a8a0088SKowalski, Kamil   void getData(const std::shared_ptr<AsyncResp>& asyncResp) {
160*3a8a0088SKowalski, Kamil     Node::json["HostName"] = getHostName();
161*3a8a0088SKowalski, Kamil     asyncResp->res.jsonValue = Node::json;
162*3a8a0088SKowalski, Kamil 
163*3a8a0088SKowalski, Kamil     OnDemandNetworkProtocolProvider::getServices(
164*3a8a0088SKowalski, Kamil         [&, asyncResp](const bool success,
165*3a8a0088SKowalski, Kamil                        const std::vector<UnitStruct>& resp) {
166*3a8a0088SKowalski, Kamil           if (!success) {
167*3a8a0088SKowalski, Kamil             asyncResp->res.jsonValue = nlohmann::json::object();
168*3a8a0088SKowalski, Kamil             messages::addMessageToErrorJson(asyncResp->res.jsonValue,
169*3a8a0088SKowalski, Kamil                                             messages::internalError());
170*3a8a0088SKowalski, Kamil             asyncResp->res.result(
171*3a8a0088SKowalski, Kamil                 boost::beast::http::status::internal_server_error);
172*3a8a0088SKowalski, Kamil           }
173*3a8a0088SKowalski, Kamil 
174*3a8a0088SKowalski, Kamil           for (auto& unit : resp) {
175*3a8a0088SKowalski, Kamil             for (auto& kv : protocolToDBus) {
176*3a8a0088SKowalski, Kamil               if (kv.second.serviceName ==
177*3a8a0088SKowalski, Kamil                   std::get<NET_PROTO_UNIT_NAME>(unit)) {
178*3a8a0088SKowalski, Kamil                 std::string service = kv.first;
179*3a8a0088SKowalski, Kamil 
180*3a8a0088SKowalski, Kamil                 // Process state
181*3a8a0088SKowalski, Kamil                 if (std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == "running") {
182*3a8a0088SKowalski, Kamil                   asyncResp->res.jsonValue[service]["ProtocolEnabled"] = true;
18370141561SBorawski.Lukasz                 } else {
184*3a8a0088SKowalski, Kamil                   asyncResp->res.jsonValue[service]["ProtocolEnabled"] = false;
18570141561SBorawski.Lukasz                 }
18670141561SBorawski.Lukasz 
187*3a8a0088SKowalski, Kamil                 // Process port
188*3a8a0088SKowalski, Kamil                 OnDemandNetworkProtocolProvider::getSocketListenPort(
189*3a8a0088SKowalski, Kamil                     kv.second.socketPath,
190*3a8a0088SKowalski, Kamil                     [&, asyncResp, service{std::move(service)} ](
191*3a8a0088SKowalski, Kamil                         const bool fetchSuccess, const bool portAvailable,
192*3a8a0088SKowalski, Kamil                         const unsigned long port) {
193*3a8a0088SKowalski, Kamil                       if (fetchSuccess) {
194*3a8a0088SKowalski, Kamil                         if (portAvailable) {
195*3a8a0088SKowalski, Kamil                           asyncResp->res.jsonValue[service]["Port"] = port;
196*3a8a0088SKowalski, Kamil                         } else {
197*3a8a0088SKowalski, Kamil                           asyncResp->res.jsonValue[service]["Port"] = nullptr;
198*3a8a0088SKowalski, Kamil                         }
199*3a8a0088SKowalski, Kamil                       } else {
200*3a8a0088SKowalski, Kamil                         messages::addMessageToJson(asyncResp->res.jsonValue,
201*3a8a0088SKowalski, Kamil                                                    messages::internalError(),
202*3a8a0088SKowalski, Kamil                                                    "/" + service);
203*3a8a0088SKowalski, Kamil                       }
204*3a8a0088SKowalski, Kamil                     });
205*3a8a0088SKowalski, Kamil                 break;
20670141561SBorawski.Lukasz               }
20770141561SBorawski.Lukasz             }
20870141561SBorawski.Lukasz           }
209*3a8a0088SKowalski, Kamil         });
21070141561SBorawski.Lukasz   }
21170141561SBorawski.Lukasz 
212*3a8a0088SKowalski, Kamil   boost::container::flat_map<std::string, ServiceConfiguration> protocolToDBus{
213*3a8a0088SKowalski, Kamil       {"SSH",
214*3a8a0088SKowalski, Kamil        {"dropbear.service",
215*3a8a0088SKowalski, Kamil         "/org/freedesktop/systemd1/unit/dropbear_2esocket"}},
216*3a8a0088SKowalski, Kamil       {"HTTPS",
217*3a8a0088SKowalski, Kamil        {"phosphor-gevent.service",
218*3a8a0088SKowalski, Kamil         "/org/freedesktop/systemd1/unit/phosphor_2dgevent_2esocket"}},
219*3a8a0088SKowalski, Kamil       {"IPMI",
220*3a8a0088SKowalski, Kamil        {"phosphor-ipmi-net.service",
221*3a8a0088SKowalski, Kamil         "/org/freedesktop/systemd1/unit/phosphor_2dipmi_2dnet_2esocket"}}};
22270141561SBorawski.Lukasz };
22370141561SBorawski.Lukasz 
22470141561SBorawski.Lukasz }  // namespace redfish
225