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