1 #include "channel.hpp"
2 #include "types.hpp"
3 #include "transporthandler.hpp"
4 #include "utils.hpp"
5 
6 #include <string>
7 #include <arpa/inet.h>
8 
9 #include <phosphor-logging/log.hpp>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include "xyz/openbmc_project/Common/error.hpp"
12 
13 extern struct ChannelConfig_t channelConfig;
14 
15 constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
16 
17 using namespace phosphor::logging;
18 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
19 
20 /** @struct GetChannelAccessRequest
21  *
22  *  IPMI payload for Get Channel access command request.
23  */
24 struct GetChannelAccessRequest
25 {
26     uint8_t channelNumber;      //!< Channel number.
27     uint8_t volatileSetting;    //!< Get non-volatile or the volatile setting.
28 } __attribute__((packed));
29 
30 /** @struct GetChannelAccessResponse
31  *
32  *  IPMI payload for Get Channel access command response.
33  */
34 struct GetChannelAccessResponse
35 {
36     uint8_t settings;          //!< Channel settings.
37     uint8_t privilegeLimit;    //!< Channel privilege level limit.
38 } __attribute__((packed));
39 
40 ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn,
41                                    ipmi_cmd_t cmd,
42                                    ipmi_request_t request,
43                                    ipmi_response_t response,
44                                    ipmi_data_len_t data_len,
45                                    ipmi_context_t context)
46 {
47     ipmi_ret_t rc = IPMI_CC_OK;
48 
49     std::string ipaddress;
50     std::string gateway;
51     uint8_t prefix {};
52     uint32_t vlanID {};
53     std::string networkInterfacePath;
54     ipmi::DbusObjectInfo ipObject;
55     ipmi::DbusObjectInfo systemObject;
56 
57     // Todo: parse the request data if needed.
58     // Using Set Channel cmd to apply changes of Set Lan Cmd.
59     try
60     {
61         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
62 
63         log<level::INFO>("Network data from Cache",
64                          entry("PREFIX=%s", channelConfig.netmask.c_str()),
65                          entry("ADDRESS=%s", channelConfig.ipaddr.c_str()),
66                          entry("GATEWAY=%s", channelConfig.gateway.c_str()),
67                          entry("VLAN=%d", channelConfig.vlanID),
68                          entry("IPSRC=%d", channelConfig.ipsrc));
69 
70         if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
71         {
72             //get the first twelve bits which is vlan id
73             //not interested in rest of the bits.
74             channelConfig.vlanID = le32toh(channelConfig.vlanID);
75             vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
76         }
77 
78         // if the asked ip src is DHCP then not interested in
79         // any given data except vlan.
80         if (channelConfig.ipsrc != ipmi::network::IPOrigin::DHCP)
81         {
82             // always get the system object
83             systemObject = ipmi::getDbusObject(
84                     bus,
85                     ipmi::network::SYSTEMCONFIG_INTERFACE,
86                     ipmi::network::ROOT);
87 
88             // the below code is to determine the mode of the interface
89             // as the handling is same, if the system is configured with
90             // DHCP or user has given all the data.
91             try
92             {
93                 ipmi::ObjectTree ancestorMap;
94 
95                 ipmi::InterfaceList interfaces {
96                     ipmi::network::ETHERNET_INTERFACE };
97 
98                 // if the system is having ip object,then
99                 // get the IP object.
100                 ipObject = ipmi::getDbusObject(bus,
101                                                ipmi::network::IP_INTERFACE,
102                                                ipmi::network::ROOT,
103                                                ipmi::network::IP_TYPE);
104 
105                 // Get the parent interface of the IP object.
106                 try
107                 {
108                     ancestorMap = ipmi::getAllAncestors(bus,
109                                                         ipObject.first,
110                                                         std::move(interfaces));
111                 }
112                 catch (InternalFailure& e)
113                 {
114                     // if unable to get the parent interface
115                     // then commit the error and return.
116                     log<level::ERR>("Unable to get the parent interface",
117                                     entry("PATH=%s", ipObject.first.c_str()),
118                                     entry("INTERFACE=%s",
119                                           ipmi::network::ETHERNET_INTERFACE));
120                     commit<InternalFailure>();
121                     rc = IPMI_CC_UNSPECIFIED_ERROR;
122                     channelConfig.clear();
123                     return rc;
124                 }
125 
126                 networkInterfacePath = ancestorMap.begin()->first;
127             }
128             catch (InternalFailure& e)
129             {
130                 // TODO Currently IPMI supports single interface,need to handle
131                 // Multiple interface through
132                 // https://github.com/openbmc/openbmc/issues/2138
133 
134                 // if there is no ip configured on the system,then
135                 // get the network interface object.
136                 auto networkInterfaceObject = ipmi::getDbusObject(
137                         bus,
138                         ipmi::network::ETHERNET_INTERFACE,
139                         ipmi::network::ROOT,
140                         ipmi::network::INTERFACE);
141 
142                 networkInterfacePath = std::move(networkInterfaceObject.first);
143             }
144 
145             // get the configured mode on the system.
146             auto enableDHCP = ipmi::getDbusProperty(
147                     bus,
148                     ipmi::network::SERVICE,
149                     networkInterfacePath,
150                     ipmi::network::ETHERNET_INTERFACE,
151                     "DHCPEnabled").get<bool>();
152 
153             // check whether user has given all the data
154             // or the configured system interface is dhcp enabled,
155             // in both of the cases get the values from the cache.
156             if ((!channelConfig.ipaddr.empty() &&
157                  !channelConfig.netmask.empty() &&
158                  !channelConfig.gateway.empty()) ||
159                 (enableDHCP)) // configured system interface mode = DHCP
160             {
161                 //convert mask into prefix
162                 ipaddress = channelConfig.ipaddr;
163                 prefix = ipmi::network::toPrefix(AF_INET, channelConfig.netmask);
164                 gateway = channelConfig.gateway;
165                 if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK)
166                 {
167                     //get the first twelve bits which is vlan id
168                     //not interested in rest of the bits.
169                     channelConfig.vlanID = le32toh(channelConfig.vlanID);
170                     vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK;
171                 }
172                 else
173                 {
174                     vlanID = ipmi::network::getVLAN(networkInterfacePath);
175                 }
176 
177             }
178             else // asked ip src = static and configured system src = static
179                 // or partially given data.
180             {
181                 // We have partial filled cache so get the remaining
182                 // info from the system.
183 
184                 // Get the network data from the system as user has
185                 // not given all the data then use the data fetched from the
186                 // system but it is implementation dependent,IPMI spec doesn't
187                 // force it.
188 
189                 // if system is not having any ip object don't throw error,
190                 try
191                 {
192                     auto properties = ipmi::getAllDbusProperties(
193                             bus,
194                             ipObject.second,
195                             ipObject.first,
196                             ipmi::network::IP_INTERFACE);
197 
198                     ipaddress = channelConfig.ipaddr.empty() ?
199                                 ipmi::getIPAddress(bus,
200                                                    ipmi::network::IP_INTERFACE,
201                                                    ipmi::network::ROOT,
202                                                    ipmi::network::IP_TYPE) :
203                                 channelConfig.ipaddr;
204 
205                     prefix = channelConfig.netmask.empty() ?
206                         properties["PrefixLength"].get<uint8_t>() :
207                         ipmi::network::toPrefix(AF_INET,
208                                 channelConfig.netmask);
209 
210                 }
211                 catch (InternalFailure& e)
212                 {
213                     log<level::INFO>("Failed to get IP object which matches",
214                             entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
215                             entry("MATCH=%s", ipmi::network::IP_TYPE));
216                 }
217 
218                 auto systemProperties = ipmi::getAllDbusProperties(
219                         bus,
220                         systemObject.second,
221                         systemObject.first,
222                         ipmi::network::SYSTEMCONFIG_INTERFACE);
223 
224                 gateway = channelConfig.gateway.empty() ?
225                         systemProperties["DefaultGateway"].get<std::string>() :
226                         channelConfig.gateway;
227 
228             }
229         }
230 
231         // Currently network manager doesn't support purging of all the
232         // ip addresses and the vlan interfaces from the parent interface,
233         // TODO once the support is there, will make the change here.
234         // https://github.com/openbmc/openbmc/issues/2141.
235 
236         // TODO Currently IPMI supports single interface,need to handle
237         // Multiple interface through
238         // https://github.com/openbmc/openbmc/issues/2138
239 
240         // instead of deleting all the vlan interfaces and
241         // all the ipv4 address,we will call reset method.
242         //delete all the vlan interfaces
243 
244         ipmi::deleteAllDbusObjects(bus,
245                                    ipmi::network::ROOT,
246                                    ipmi::network::VLAN_INTERFACE);
247 
248         // set the interface mode  to static
249         auto networkInterfaceObject = ipmi::getDbusObject(
250                 bus,
251                 ipmi::network::ETHERNET_INTERFACE,
252                 ipmi::network::ROOT,
253                 ipmi::network::INTERFACE);
254 
255         // setting the physical interface mode to static.
256         ipmi::setDbusProperty(bus,
257                               ipmi::network::SERVICE,
258                               networkInterfaceObject.first,
259                               ipmi::network::ETHERNET_INTERFACE,
260                               "DHCPEnabled",
261                               false);
262 
263         networkInterfacePath = networkInterfaceObject.first;
264 
265         //delete all the ipv4 addresses
266         ipmi::deleteAllDbusObjects(bus,
267                                    ipmi::network::ROOT,
268                                    ipmi::network::IP_INTERFACE,
269                                    ipmi::network::IP_TYPE);
270 
271         if (vlanID)
272         {
273             ipmi::network::createVLAN(bus,
274                                       ipmi::network::SERVICE,
275                                       ipmi::network::ROOT,
276                                       ipmi::network::INTERFACE,
277                                       vlanID);
278 
279             auto networkInterfaceObject = ipmi::getDbusObject(
280                     bus,
281                     ipmi::network::VLAN_INTERFACE,
282                     ipmi::network::ROOT);
283 
284            networkInterfacePath = networkInterfaceObject.first;
285         }
286 
287         if (channelConfig.ipsrc == ipmi::network::IPOrigin::DHCP)
288         {
289             ipmi::setDbusProperty(bus,
290                                   ipmi::network::SERVICE,
291                                   networkInterfacePath,
292                                   ipmi::network::ETHERNET_INTERFACE,
293                                   "DHCPEnabled",
294                                   true);
295         }
296         else
297         {
298             //change the mode to static
299             ipmi::setDbusProperty(bus,
300                                   ipmi::network::SERVICE,
301                                   networkInterfacePath,
302                                   ipmi::network::ETHERNET_INTERFACE,
303                                   "DHCPEnabled",
304                                   false);
305 
306             if (!ipaddress.empty())
307             {
308                 ipmi::network::createIP(bus,
309                                         ipmi::network::SERVICE,
310                                         networkInterfacePath,
311                                         ipv4Protocol,
312                                         ipaddress,
313                                         prefix);
314             }
315 
316             if (!gateway.empty())
317             {
318                 ipmi::setDbusProperty(bus,
319                                       systemObject.second,
320                                       systemObject.first,
321                                       ipmi::network::SYSTEMCONFIG_INTERFACE,
322                                       "DefaultGateway",
323                                       std::string(gateway));
324             }
325         }
326 
327     }
328     catch (InternalFailure& e)
329     {
330         log<level::ERR>("Failed to set network data",
331                         entry("PREFIX=%d", prefix),
332                         entry("ADDRESS=%s", ipaddress.c_str()),
333                         entry("GATEWAY=%s", gateway.c_str()),
334                         entry("VLANID=%d", vlanID),
335                         entry("IPSRC=%d", channelConfig.ipsrc));
336 
337         commit<InternalFailure>();
338         rc = IPMI_CC_UNSPECIFIED_ERROR;
339     }
340 
341     channelConfig.clear();
342     return rc;
343 }
344 
345 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
346                             ipmi_request_t request, ipmi_response_t response,
347                             ipmi_data_len_t data_len, ipmi_context_t context)
348 {
349     auto requestData = reinterpret_cast<const GetChannelAccessRequest*>
350                    (request);
351     std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
352     auto responseData = reinterpret_cast<GetChannelAccessResponse*>
353             (outPayload.data());
354 
355     // Channel 1 is arbitrarily assigned to ETH0 channel
356     constexpr auto channelOne = 0x01;
357 
358     /*
359      * The value Eh is used as a way to identify the current channel that
360      * the command is being received from.
361      */
362     constexpr auto channelE = 0x0E;
363 
364     if (requestData->channelNumber != channelOne &&
365         requestData->channelNumber != channelE)
366     {
367         *data_len = 0;
368         return IPMI_CC_INVALID_FIELD_REQUEST;
369     }
370 
371     /*
372      * [7:6] - reserved
373      * [5]   - 1b = Alerting disabled
374      * [4]   - 1b = per message authentication disabled
375      * [3]   - 0b = User level authentication enabled
376      * [2:0] - 2h = always available
377      */
378     constexpr auto channelSetting = 0x32;
379 
380     responseData->settings = channelSetting;
381     //Defaulting the channel privilege to administrator level.
382     responseData->privilegeLimit = PRIVILEGE_ADMIN;
383 
384     *data_len = outPayload.size();
385     memcpy(response, outPayload.data(), *data_len);
386 
387     return IPMI_CC_OK;
388 }
389 
390 // ATTENTION: This ipmi function is very hardcoded on purpose
391 // OpenBMC does not fully support IPMI.  This command is useful
392 // to have around because it enables testing of interfaces with
393 // the IPMI tool.
394 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
395 // IPMI Table 6-2
396 #define IPMI_CHANNEL_TYPE_IPMB 1
397 // IPMI Table 6-3
398 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
399 
400 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
401                              ipmi_request_t request, ipmi_response_t response,
402                              ipmi_data_len_t data_len, ipmi_context_t context)
403 {
404     ipmi_ret_t rc = IPMI_CC_OK;
405     uint8_t resp[] = {
406         1,
407         IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
408         IPMI_CHANNEL_TYPE_IPMB,
409         1,0x41,0xA7,0x00,0,0};
410     uint8_t *p = (uint8_t*) request;
411 
412     printf("IPMI APP GET CHANNEL INFO\n");
413 
414     // The supported channels numbers are 1 and 8.
415     // Channel Number E is used as way to identify the current channel
416     // that the command is being is received from.
417     if (*p == 0xe || *p == 1 || *p == 8) {
418 
419         *data_len = sizeof(resp);
420         memcpy(response, resp, *data_len);
421 
422     } else {
423         rc = IPMI_CC_PARM_OUT_OF_RANGE;
424         *data_len = 0;
425     }
426 
427     return rc;
428 }
429