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