xref: /openbmc/bmcweb/features/redfish/lib/network_protocol.hpp (revision 3a8a0088db901136584da9775acf76af0467f046)
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