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