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