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             // check whether user has given all the data
180             // or the configured system interface is dhcp enabled,
181             // in both of the cases get the values from the cache.
182             if ((!channelConf->ipaddr.empty() &&
183                  !channelConf->netmask.empty() &&
184                  !channelConf->gateway.empty()) ||
185                 (enableDHCP)) // configured system interface mode = DHCP
186             {
187                 //convert mask into prefix
188                 ipaddress = channelConf->ipaddr;
189                 prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
190                 gateway = channelConf->gateway;
191                 if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
192                 {
193                     //get the first twelve bits which is vlan id
194                     //not interested in rest of the bits.
195                     channelConf->vlanID = le32toh(channelConf->vlanID);
196                     vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
197                 }
198                 else
199                 {
200                     vlanID = ipmi::network::getVLAN(networkInterfacePath);
201                 }
202 
203             }
204             else // asked ip src = static and configured system src = static
205                 // or partially given data.
206             {
207                 // We have partial filled cache so get the remaining
208                 // info from the system.
209 
210                 // Get the network data from the system as user has
211                 // not given all the data then use the data fetched from the
212                 // system but it is implementation dependent,IPMI spec doesn't
213                 // force it.
214 
215                 // if system is not having any ip object don't throw error,
216                 try
217                 {
218                     auto properties = ipmi::getAllDbusProperties(
219                             bus,
220                             ipObject.second,
221                             ipObject.first,
222                             ipmi::network::IP_INTERFACE);
223 
224                     ipaddress = channelConf->ipaddr.empty() ?
225                         properties["Address"].get<std::string>() :
226                         channelConf->ipaddr;
227 
228                     prefix = channelConf->netmask.empty() ?
229                         properties["PrefixLength"].get<uint8_t>() :
230                         ipmi::network::toPrefix(AF_INET,
231                                 channelConf->netmask);
232                 }
233                 catch (InternalFailure& e)
234                 {
235                     log<level::INFO>("Failed to get IP object which matches",
236                             entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
237                             entry("MATCH=%s", ethIp));
238                 }
239 
240                 auto systemProperties = ipmi::getAllDbusProperties(
241                         bus,
242                         systemObject.second,
243                         systemObject.first,
244                         ipmi::network::SYSTEMCONFIG_INTERFACE);
245 
246                 gateway = channelConf->gateway.empty() ?
247                         systemProperties["DefaultGateway"].get<std::string>() :
248                         channelConf->gateway;
249             }
250         }
251 
252         // Currently network manager doesn't support purging of all the
253         // ip addresses and the vlan interfaces from the parent interface,
254         // TODO once the support is there, will make the change here.
255         // https://github.com/openbmc/openbmc/issues/2141.
256 
257         // TODO Currently IPMI supports single interface,need to handle
258         // Multiple interface through
259         // https://github.com/openbmc/openbmc/issues/2138
260 
261         // instead of deleting all the vlan interfaces and
262         // all the ipv4 address,we will call reset method.
263         //delete all the vlan interfaces
264 
265         ipmi::deleteAllDbusObjects(bus,
266                                    ipmi::network::ROOT,
267                                    ipmi::network::VLAN_INTERFACE);
268 
269         // set the interface mode  to static
270         auto networkInterfaceObject = ipmi::getDbusObject(
271                 bus,
272                 ipmi::network::ETHERNET_INTERFACE,
273                 ipmi::network::ROOT,
274                 ethdevice);
275 
276         // setting the physical interface mode to static.
277         ipmi::setDbusProperty(bus,
278                               ipmi::network::SERVICE,
279                               networkInterfaceObject.first,
280                               ipmi::network::ETHERNET_INTERFACE,
281                               "DHCPEnabled",
282                               false);
283 
284         networkInterfacePath = networkInterfaceObject.first;
285 
286         //delete all the ipv4 addresses
287         ipmi::deleteAllDbusObjects(bus,
288                                    ipmi::network::ROOT,
289                                    ipmi::network::IP_INTERFACE,
290                                    ethIp);
291 
292         if (vlanID)
293         {
294             ipmi::network::createVLAN(bus,
295                                       ipmi::network::SERVICE,
296                                       ipmi::network::ROOT,
297                                       ethdevice,
298                                       vlanID);
299 
300             auto networkInterfaceObject = ipmi::getDbusObject(
301                     bus,
302                     ipmi::network::VLAN_INTERFACE,
303                     ipmi::network::ROOT);
304 
305            networkInterfacePath = networkInterfaceObject.first;
306         }
307 
308         if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
309         {
310             ipmi::setDbusProperty(bus,
311                                   ipmi::network::SERVICE,
312                                   networkInterfacePath,
313                                   ipmi::network::ETHERNET_INTERFACE,
314                                   "DHCPEnabled",
315                                   true);
316         }
317         else
318         {
319             //change the mode to static
320             ipmi::setDbusProperty(bus,
321                                   ipmi::network::SERVICE,
322                                   networkInterfacePath,
323                                   ipmi::network::ETHERNET_INTERFACE,
324                                   "DHCPEnabled",
325                                   false);
326 
327             if (!ipaddress.empty())
328             {
329                 ipmi::network::createIP(bus,
330                                         ipmi::network::SERVICE,
331                                         networkInterfacePath,
332                                         ipv4Protocol,
333                                         ipaddress,
334                                         prefix);
335             }
336 
337             if (!gateway.empty())
338             {
339                 ipmi::setDbusProperty(bus,
340                                       systemObject.second,
341                                       systemObject.first,
342                                       ipmi::network::SYSTEMCONFIG_INTERFACE,
343                                       "DefaultGateway",
344                                       std::string(gateway));
345             }
346         }
347 
348     }
349     catch (InternalFailure& e)
350     {
351         log<level::ERR>("Failed to set network data",
352                         entry("PREFIX=%d", prefix),
353                         entry("ADDRESS=%s", ipaddress.c_str()),
354                         entry("GATEWAY=%s", gateway.c_str()),
355                         entry("VLANID=%d", vlanID),
356                         entry("IPSRC=%d", channelConf->ipsrc));
357 
358         commit<InternalFailure>();
359         rc = IPMI_CC_UNSPECIFIED_ERROR;
360     }
361 
362     channelConf->clear();
363     return rc;
364 }
365 
366 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
367                             ipmi_request_t request, ipmi_response_t response,
368                             ipmi_data_len_t data_len, ipmi_context_t context)
369 {
370     auto requestData = reinterpret_cast<const GetChannelAccessRequest*>
371                    (request);
372     std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse));
373     auto responseData = reinterpret_cast<GetChannelAccessResponse*>
374             (outPayload.data());
375 
376     /*
377      * The value Eh is used as a way to identify the current channel that
378      * the command is being received from.
379      */
380     constexpr auto channelE = 0x0E;
381     int channel = requestData->channelNumber;
382     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
383 
384     if (channel != channelE && ethdevice.empty())
385     {
386         *data_len = 0;
387         return IPMI_CC_INVALID_FIELD_REQUEST;
388     }
389 
390     /*
391      * [7:6] - reserved
392      * [5]   - 1b = Alerting disabled
393      * [4]   - 1b = per message authentication disabled
394      * [3]   - 0b = User level authentication enabled
395      * [2:0] - 2h = always available
396      */
397     constexpr auto channelSetting = 0x32;
398 
399     responseData->settings = channelSetting;
400     //Defaulting the channel privilege to administrator level.
401     responseData->privilegeLimit = PRIVILEGE_ADMIN;
402 
403     *data_len = outPayload.size();
404     memcpy(response, outPayload.data(), *data_len);
405 
406     return IPMI_CC_OK;
407 }
408 
409 // ATTENTION: This ipmi function is very hardcoded on purpose
410 // OpenBMC does not fully support IPMI.  This command is useful
411 // to have around because it enables testing of interfaces with
412 // the IPMI tool.
413 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0
414 // IPMI Table 6-2
415 #define IPMI_CHANNEL_TYPE_IPMB 1
416 // IPMI Table 6-3
417 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6
418 
419 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
420                              ipmi_request_t request, ipmi_response_t response,
421                              ipmi_data_len_t data_len, ipmi_context_t context)
422 {
423     ipmi_ret_t rc = IPMI_CC_OK;
424     uint8_t resp[] = {
425         1,
426         IPMI_CHANNEL_MEDIUM_TYPE_OTHER,
427         IPMI_CHANNEL_TYPE_IPMB,
428         1,0x41,0xA7,0x00,0,0};
429     uint8_t *p = (uint8_t*) request;
430     int channel = (*p) & CHANNEL_MASK;
431     std::string ethdevice = ipmi::network::ChanneltoEthernet(channel);
432 
433     printf("IPMI APP GET CHANNEL INFO\n");
434 
435     // The supported channels numbers are those which are configured.
436     // Channel Number E is used as way to identify the current channel
437     // that the command is being is received from.
438     if (channel != 0xe && ethdevice.empty()) {
439         rc = IPMI_CC_PARM_OUT_OF_RANGE;
440         *data_len = 0;
441     } else {
442         *data_len = sizeof(resp);
443         memcpy(response, resp, *data_len);
444     }
445 
446     return rc;
447 }
448