1 #include <stdio.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <arpa/inet.h>
5 #include <string>
6 #include <experimental/filesystem>
7 
8 #include "host-ipmid/ipmid-api.h"
9 #include "ipmid.hpp"
10 #include "transporthandler.hpp"
11 #include "utils.hpp"
12 #include "net.hpp"
13 
14 #include <phosphor-logging/log.hpp>
15 #include <phosphor-logging/elog-errors.hpp>
16 #include "xyz/openbmc_project/Common/error.hpp"
17 
18 #define SYSTEMD_NETWORKD_DBUS 1
19 
20 #ifdef SYSTEMD_NETWORKD_DBUS
21 #include <systemd/sd-bus.h>
22 #include <mapper.h>
23 #endif
24 
25 const int SIZE_MAC = 18; //xx:xx:xx:xx:xx:xx
26 
27 std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig;
28 
29 using namespace phosphor::logging;
30 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
31 namespace fs = std::experimental::filesystem;
32 
33 void register_netfn_transport_functions() __attribute__((constructor));
34 
35 struct ChannelConfig_t* getChannelConfig(int channel)
36 {
37     auto item = channelConfig.find(channel);
38     if (item == channelConfig.end())
39     {
40         channelConfig[channel] = std::make_unique<struct ChannelConfig_t>();
41     }
42 
43     return channelConfig[channel].get();
44 }
45 
46 // Helper Function to get IP Address/NetMask/Gateway/MAC Address from Network Manager or
47 // Cache based on Set-In-Progress State
48 ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t* data, int channel)
49 {
50     ipmi_ret_t rc = IPMI_CC_OK;
51     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
52 
53     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
54     // if ethdevice is an empty string they weren't expecting this channel.
55     if (ethdevice.empty())
56     {
57         // TODO: return error from getNetworkData()
58         return IPMI_CC_INVALID_FIELD_REQUEST;
59     }
60     auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
61     auto channelConf = getChannelConfig(channel);
62 
63     try
64     {
65         switch (lan_param)
66         {
67             case LAN_PARM_IP:
68             {
69                 std::string ipaddress;
70                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
71                 {
72                     try
73                     {
74                         auto ipObjectInfo = ipmi::getIPObject(
75                                 bus,
76                                 ipmi::network::IP_INTERFACE,
77                                 ipmi::network::ROOT,
78                                 ethIP);
79 
80                         auto properties = ipmi::getAllDbusProperties(
81                                 bus,
82                                 ipObjectInfo.second,
83                                 ipObjectInfo.first,
84                                 ipmi::network::IP_INTERFACE);
85 
86                         ipaddress = properties["Address"].get<std::string>();
87                     }
88                     // ignore the exception, as it is a valid condtion that
89                     // system is not confiured with any ip.
90                     catch (InternalFailure& e)
91                     {
92                         // nothing to do.
93                     }
94                 }
95                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
96                 {
97                     ipaddress = channelConf->ipaddr;
98                 }
99 
100                 inet_pton(AF_INET, ipaddress.c_str(),
101                           reinterpret_cast<void*>(data));
102             }
103             break;
104 
105             case LAN_PARM_IPSRC:
106             {
107                 std::string networkInterfacePath;
108 
109                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
110                 {
111                     try
112                     {
113                         ipmi::ObjectTree ancestorMap;
114                         // if the system is having ip object,then
115                         // get the IP object.
116                         auto ipObject = ipmi::getDbusObject(
117                                 bus,
118                                 ipmi::network::IP_INTERFACE,
119                                 ipmi::network::ROOT,
120                                 ethIP);
121 
122                         // Get the parent interface of the IP object.
123                         try
124                         {
125                             ipmi::InterfaceList interfaces;
126                             interfaces.emplace_back(
127                                     ipmi::network::ETHERNET_INTERFACE);
128 
129                             ancestorMap = ipmi::getAllAncestors(
130                                     bus,
131                                     ipObject.first,
132                                     std::move(interfaces));
133                         }
134                         catch (InternalFailure& e)
135                         {
136                             // if unable to get the parent interface
137                             // then commit the error and return.
138                             log<level::ERR>("Unable to get the parent interface",
139                                     entry("PATH=%s", ipObject.first.c_str()),
140                                     entry("INTERFACE=%s",
141                                         ipmi::network::ETHERNET_INTERFACE));
142                             break;
143 
144                         }
145                         // for an ip object there would be single parent
146                         // interface.
147                         networkInterfacePath = ancestorMap.begin()->first;
148                     }
149                     catch (InternalFailure& e)
150                     {
151                         // if there is no ip configured on the system,then
152                         // get the network interface object.
153                         auto networkInterfaceObject = ipmi::getDbusObject(
154                                 bus,
155                                 ipmi::network::ETHERNET_INTERFACE,
156                                 ipmi::network::ROOT,
157                                 ethdevice);
158 
159                         networkInterfacePath = networkInterfaceObject.first;
160                     }
161 
162                     auto variant = ipmi::getDbusProperty(
163                             bus,
164                             ipmi::network::SERVICE,
165                             networkInterfacePath,
166                             ipmi::network::ETHERNET_INTERFACE,
167                             "DHCPEnabled");
168 
169                     auto dhcpEnabled = variant.get<bool>();
170                     // As per IPMI spec 2=>DHCP, 1=STATIC
171                     auto ipsrc = dhcpEnabled ? ipmi::network::IPOrigin::DHCP :
172                                                ipmi::network::IPOrigin::STATIC;
173 
174                     memcpy(data, &ipsrc, ipmi::network::IPSRC_SIZE_BYTE);
175                 }
176                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
177                 {
178                    memcpy(data, &(channelConf->ipsrc),
179                           ipmi::network::IPSRC_SIZE_BYTE);
180                 }
181             }
182             break;
183 
184             case LAN_PARM_SUBNET:
185             {
186                 unsigned long mask {};
187                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
188                 {
189                     try
190                     {
191                         auto ipObjectInfo = ipmi::getIPObject(
192                                 bus,
193                                 ipmi::network::IP_INTERFACE,
194                                 ipmi::network::ROOT,
195                                 ipmi::network::IP_TYPE);
196 
197                         auto properties = ipmi::getAllDbusProperties(
198                                 bus,
199                                 ipObjectInfo.second,
200                                 ipObjectInfo.first,
201                                 ipmi::network::IP_INTERFACE);
202 
203                         auto prefix = properties["PrefixLength"].get<uint8_t>();
204                         mask = ipmi::network::MASK_32_BIT;
205                         mask = htonl(mask << (ipmi::network::BITS_32 - prefix));
206                     }
207                     // ignore the exception, as it is a valid condtion that
208                     // system is not confiured with any ip.
209                     catch (InternalFailure& e)
210                     {
211                         // nothing to do
212                     }
213                     memcpy(data, &mask, ipmi::network::IPV4_ADDRESS_SIZE_BYTE);
214                 }
215                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
216                 {
217                     inet_pton(AF_INET, channelConf->netmask.c_str(),
218                               reinterpret_cast<void*>(data));
219                 }
220 
221             }
222             break;
223 
224             case LAN_PARM_GATEWAY:
225             {
226                 std::string gateway;
227 
228                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
229                 {
230                     try
231                     {
232                         auto systemObject = ipmi::getDbusObject(
233                                 bus,
234                                 ipmi::network::SYSTEMCONFIG_INTERFACE,
235                                 ipmi::network::ROOT);
236 
237                         auto systemProperties = ipmi::getAllDbusProperties(
238                                 bus,
239                                 systemObject.second,
240                                 systemObject.first,
241                                 ipmi::network::SYSTEMCONFIG_INTERFACE);
242 
243                         gateway = systemProperties["DefaultGateway"].get<
244                             std::string>();
245                     }
246                     // ignore the exception, as it is a valid condtion that
247                     // system is not confiured with any ip.
248                     catch (InternalFailure& e)
249                     {
250                         // nothing to do
251                     }
252 
253                 }
254                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
255                 {
256                     gateway = channelConf->gateway;
257                 }
258 
259                 inet_pton(AF_INET, gateway.c_str(),
260                           reinterpret_cast<void*>(data));
261             }
262             break;
263 
264             case LAN_PARM_MAC:
265             {
266                 std::string macAddress;
267                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
268                 {
269                     auto macObjectInfo = ipmi::getDbusObject(
270                                              bus,
271                                              ipmi::network::MAC_INTERFACE,
272                                              ipmi::network::ROOT,
273                                              ethdevice);
274 
275                     auto variant = ipmi::getDbusProperty(
276                                      bus,
277                                      macObjectInfo.second,
278                                      macObjectInfo.first,
279                                      ipmi::network::MAC_INTERFACE,
280                                      "MACAddress");
281 
282                     macAddress = variant.get<std::string>();
283 
284                 }
285                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
286                 {
287                     macAddress = channelConf->macAddress;
288                 }
289 
290                 sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
291                        (data),
292                        (data + 1),
293                        (data + 2),
294                        (data + 3),
295                        (data + 4),
296                        (data + 5));
297             }
298             break;
299 
300             case LAN_PARM_VLAN:
301             {
302                 uint16_t vlanID {};
303                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
304                 {
305                     try
306                     {
307                         auto ipObjectInfo = ipmi::getIPObject(
308                                 bus,
309                                 ipmi::network::IP_INTERFACE,
310                                 ipmi::network::ROOT,
311                                 ipmi::network::IP_TYPE);
312 
313                         vlanID = static_cast<uint16_t>(
314                                 ipmi::network::getVLAN(ipObjectInfo.first));
315 
316                         vlanID = htole16(vlanID);
317 
318                         if (vlanID)
319                         {
320                             //Enable the 16th bit
321                             vlanID |= htole16(ipmi::network::VLAN_ENABLE_MASK);
322                         }
323                     }
324                     // ignore the exception, as it is a valid condtion that
325                     // system is not confiured with any ip.
326                     catch (InternalFailure& e)
327                     {
328                         // nothing to do
329                     }
330 
331                     memcpy(data, &vlanID, ipmi::network::VLAN_SIZE_BYTE);
332                 }
333                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
334                 {
335                     memcpy(data, &(channelConf->vlanID),
336                            ipmi::network::VLAN_SIZE_BYTE);
337                 }
338             }
339             break;
340 
341             default:
342                 rc = IPMI_CC_PARM_OUT_OF_RANGE;
343         }
344     }
345     catch (InternalFailure& e)
346     {
347         commit<InternalFailure>();
348         rc = IPMI_CC_UNSPECIFIED_ERROR;
349         return rc;
350     }
351     return rc;
352 }
353 
354 ipmi_ret_t ipmi_transport_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
355                               ipmi_request_t request, ipmi_response_t response,
356                               ipmi_data_len_t data_len, ipmi_context_t context)
357 {
358     printf("Handling TRANSPORT WILDCARD Netfn:[0x%X], Cmd:[0x%X]\n",netfn, cmd);
359     // Status code.
360     ipmi_ret_t rc = IPMI_CC_INVALID;
361     *data_len = 0;
362     return rc;
363 }
364 
365 struct set_lan_t
366 {
367     uint8_t channel;
368     uint8_t parameter;
369     uint8_t data[8]; // Per IPMI spec, not expecting more than this size
370 }  __attribute__((packed));
371 
372 ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn,
373                                   ipmi_cmd_t cmd,
374                                   ipmi_request_t request,
375                                   ipmi_response_t response,
376                                   ipmi_data_len_t data_len,
377                                   ipmi_context_t context)
378 {
379     ipmi_ret_t rc = IPMI_CC_OK;
380     *data_len = 0;
381 
382     char ipaddr[INET_ADDRSTRLEN];
383     char netmask[INET_ADDRSTRLEN];
384     char gateway[INET_ADDRSTRLEN];
385 
386     auto reqptr = reinterpret_cast<const set_lan_t*>(request);
387     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
388 
389     // channel number is the lower nibble
390     int channel = reqptr->channel & CHANNEL_MASK;
391     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
392     if (ethdevice.empty())
393     {
394         return IPMI_CC_INVALID_FIELD_REQUEST;
395     }
396     auto channelConf = getChannelConfig(channel);
397 
398     switch (reqptr->parameter)
399     {
400         case LAN_PARM_IP:
401         {
402             snprintf(ipaddr, INET_ADDRSTRLEN, ipmi::network::IP_ADDRESS_FORMAT,
403                      reqptr->data[0], reqptr->data[1],
404                      reqptr->data[2], reqptr->data[3]);
405 
406             channelConf->ipaddr.assign(ipaddr);
407         }
408         break;
409 
410         case LAN_PARM_IPSRC:
411         {
412             uint8_t ipsrc{};
413             memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE);
414             channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
415         }
416         break;
417 
418         case LAN_PARM_MAC:
419         {
420             char mac[SIZE_MAC];
421 
422             snprintf(mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT,
423                      reqptr->data[0],
424                      reqptr->data[1],
425                      reqptr->data[2],
426                      reqptr->data[3],
427                      reqptr->data[4],
428                      reqptr->data[5]);
429 
430             auto macObjectInfo = ipmi::getDbusObject(
431                                      bus,
432                                      ipmi::network::MAC_INTERFACE,
433                                      ipmi::network::ROOT,
434                                      ethdevice);
435 
436             ipmi::setDbusProperty(bus,
437                                   macObjectInfo.second,
438                                   macObjectInfo.first,
439                                   ipmi::network::MAC_INTERFACE,
440                                   "MACAddress",
441                                   std::string(mac));
442 
443             channelConf->macAddress = mac;
444         }
445         break;
446 
447         case LAN_PARM_SUBNET:
448         {
449             snprintf(netmask, INET_ADDRSTRLEN, ipmi::network::IP_ADDRESS_FORMAT,
450                      reqptr->data[0], reqptr->data[1],
451                      reqptr->data[2], reqptr->data[3]);
452             channelConf->netmask.assign(netmask);
453         }
454         break;
455 
456         case LAN_PARM_GATEWAY:
457         {
458             snprintf(gateway, INET_ADDRSTRLEN, ipmi::network::IP_ADDRESS_FORMAT,
459                      reqptr->data[0], reqptr->data[1],
460                      reqptr->data[2], reqptr->data[3]);
461             channelConf->gateway.assign(gateway);
462         }
463         break;
464 
465         case LAN_PARM_VLAN:
466         {
467             uint16_t vlan {};
468             memcpy(&vlan, reqptr->data, ipmi::network::VLAN_SIZE_BYTE);
469             // We are not storing the enable bit
470             // We assume that ipmitool always send enable
471             // bit as 1.
472             vlan = le16toh(vlan);
473             channelConf->vlanID = vlan;
474         }
475         break;
476 
477         case LAN_PARM_INPROGRESS:
478         {
479             if (reqptr->data[0] == SET_COMPLETE)
480             {
481                 channelConf->lan_set_in_progress = SET_COMPLETE;
482 
483                 log<level::INFO>("Network data from Cache",
484                                  entry("PREFIX=%s", channelConf->netmask.c_str()),
485                                  entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
486                                  entry("GATEWAY=%s", channelConf->gateway.c_str()),
487                                  entry("VLAN=%d", channelConf->vlanID));
488 
489                 log<level::INFO>("Use Set Channel Access command to apply");
490             }
491             else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
492             {
493                 channelConf->lan_set_in_progress = SET_IN_PROGRESS;
494             }
495         }
496         break;
497 
498         default:
499         {
500             rc = IPMI_CC_PARM_NOT_SUPPORTED;
501         }
502     }
503 
504     return rc;
505 }
506 
507 struct get_lan_t
508 {
509     uint8_t rev_channel;
510     uint8_t parameter;
511     uint8_t parameter_set;
512     uint8_t parameter_block;
513 }  __attribute__((packed));
514 
515 ipmi_ret_t ipmi_transport_get_lan(ipmi_netfn_t netfn,
516                                   ipmi_cmd_t cmd,
517                                   ipmi_request_t request,
518                                   ipmi_response_t response,
519                                   ipmi_data_len_t data_len,
520                                   ipmi_context_t context)
521 {
522     ipmi_ret_t rc = IPMI_CC_OK;
523     *data_len = 0;
524     const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0
525 
526     get_lan_t *reqptr = (get_lan_t*) request;
527     // channel number is the lower nibble
528     int channel = reqptr->rev_channel & CHANNEL_MASK;
529 
530     if (reqptr->rev_channel & 0x80) // Revision is bit 7
531     {
532         // Only current revision was requested
533         *data_len = sizeof(current_revision);
534         memcpy(response, &current_revision, *data_len);
535         return IPMI_CC_OK;
536     }
537 
538     auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
539     if (ethdevice.empty())
540     {
541         return IPMI_CC_INVALID_FIELD_REQUEST;
542     }
543     auto channelConf = getChannelConfig(channel);
544 
545     if (reqptr->parameter == LAN_PARM_INPROGRESS)
546     {
547         uint8_t buf[] = {current_revision, channelConf->lan_set_in_progress};
548         *data_len = sizeof(buf);
549         memcpy(response, &buf, *data_len);
550     }
551     else if (reqptr->parameter == LAN_PARM_AUTHSUPPORT)
552     {
553         uint8_t buf[] = {current_revision,0x04};
554         *data_len = sizeof(buf);
555         memcpy(response, &buf, *data_len);
556     }
557     else if (reqptr->parameter == LAN_PARM_AUTHENABLES)
558     {
559         uint8_t buf[] = {current_revision,0x04,0x04,0x04,0x04,0x04};
560         *data_len = sizeof(buf);
561         memcpy(response, &buf, *data_len);
562     }
563     else if ((reqptr->parameter == LAN_PARM_IP) ||
564              (reqptr->parameter == LAN_PARM_SUBNET) ||
565              (reqptr->parameter == LAN_PARM_GATEWAY) ||
566              (reqptr->parameter == LAN_PARM_MAC))
567     {
568         uint8_t buf[ipmi::network::MAC_ADDRESS_SIZE_BYTE + 1] = {};
569 
570         *data_len = sizeof(current_revision);
571         memcpy(buf, &current_revision, *data_len);
572 
573         if (getNetworkData(reqptr->parameter, &buf[1], channel) == IPMI_CC_OK)
574         {
575             if (reqptr->parameter == LAN_PARM_MAC)
576             {
577                 *data_len = sizeof(buf);
578             }
579             else
580             {
581                 *data_len = ipmi::network::IPV4_ADDRESS_SIZE_BYTE + 1;
582             }
583             memcpy(response, &buf, *data_len);
584         }
585         else
586         {
587             rc = IPMI_CC_UNSPECIFIED_ERROR;
588         }
589     }
590     else if (reqptr->parameter == LAN_PARM_VLAN)
591     {
592         uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1] = {};
593 
594         *data_len = sizeof(current_revision);
595         memcpy(buf, &current_revision, *data_len);
596         if (getNetworkData(reqptr->parameter, &buf[1], channel) == IPMI_CC_OK)
597         {
598             *data_len = sizeof(buf);
599             memcpy(response, &buf, *data_len);
600         }
601     }
602     else if (reqptr->parameter == LAN_PARM_IPSRC)
603     {
604         uint8_t buff[ipmi::network::IPSRC_SIZE_BYTE + 1] = {};
605         *data_len = sizeof(current_revision);
606         memcpy(buff, &current_revision, *data_len);
607         if (getNetworkData(reqptr->parameter, &buff[1], channel) == IPMI_CC_OK)
608         {
609             *data_len = sizeof(buff);
610             memcpy(response, &buff, *data_len);
611         }
612     }
613     else
614     {
615         log<level::ERR>("Unsupported parameter",
616                         entry("PARAMETER=0x%x", reqptr->parameter));
617         rc = IPMI_CC_PARM_NOT_SUPPORTED;
618     }
619 
620     return rc;
621 }
622 
623 void register_netfn_transport_functions()
624 {
625     // <Wildcard Command>
626     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_TRANSPORT, IPMI_CMD_WILDCARD);
627     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_WILDCARD, NULL, ipmi_transport_wildcard,
628                            PRIVILEGE_USER);
629 
630     // <Set LAN Configuration Parameters>
631     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_TRANSPORT, IPMI_CMD_SET_LAN);
632     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_SET_LAN, NULL, ipmi_transport_set_lan,
633                            PRIVILEGE_ADMIN);
634 
635     // <Get LAN Configuration Parameters>
636     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_TRANSPORT, IPMI_CMD_GET_LAN);
637     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_GET_LAN, NULL, ipmi_transport_get_lan,
638                            PRIVILEGE_OPERATOR);
639 
640     return;
641 }
642