1 #include "transporthandler.hpp"
2 
3 #include "app/channel.hpp"
4 #include "user_channel/channel_layer.hpp"
5 
6 #include <arpa/inet.h>
7 
8 #include <chrono>
9 #include <filesystem>
10 #include <fstream>
11 #include <ipmid/api.hpp>
12 #include <ipmid/utils.hpp>
13 #include <phosphor-logging/elog-errors.hpp>
14 #include <phosphor-logging/log.hpp>
15 #include <sdbusplus/message/types.hpp>
16 #include <sdbusplus/timer.hpp>
17 #include <string>
18 #include <xyz/openbmc_project/Common/error.hpp>
19 
20 #define SYSTEMD_NETWORKD_DBUS 1
21 
22 #ifdef SYSTEMD_NETWORKD_DBUS
23 #include <mapper.h>
24 #include <systemd/sd-bus.h>
25 #endif
26 
27 // timer for network changes
28 std::unique_ptr<phosphor::Timer> networkTimer = nullptr;
29 
30 const int SIZE_MAC = 18; // xx:xx:xx:xx:xx:xx
31 constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
32 
33 std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig;
34 
35 using namespace phosphor::logging;
36 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
37 
38 namespace fs = std::filesystem;
39 
40 void register_netfn_transport_functions() __attribute__((constructor));
41 
42 struct ChannelConfig_t* getChannelConfig(int channel)
43 {
44     auto item = channelConfig.find(channel);
45     if (item == channelConfig.end())
46     {
47         channelConfig[channel] = std::make_unique<struct ChannelConfig_t>();
48     }
49 
50     return channelConfig[channel].get();
51 }
52 
53 // Helper Function to get IP Address/NetMask/Gateway/MAC Address from Network
54 // Manager or Cache based on Set-In-Progress State
55 ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t* data, int channel)
56 {
57     ipmi_ret_t rc = IPMI_CC_OK;
58     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
59 
60     auto ethdevice = ipmi::getChannelName(channel);
61     // if ethdevice is an empty string they weren't expecting this channel.
62     if (ethdevice.empty())
63     {
64         // TODO: return error from getNetworkData()
65         return IPMI_CC_INVALID_FIELD_REQUEST;
66     }
67     auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
68     auto channelConf = getChannelConfig(channel);
69 
70     try
71     {
72         switch (static_cast<LanParam>(lan_param))
73         {
74             case LanParam::IP:
75             {
76                 std::string ipaddress;
77                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
78                 {
79                     try
80                     {
81                         auto ipObjectInfo =
82                             ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
83                                               ipmi::network::ROOT, ethIP);
84 
85                         auto properties = ipmi::getAllDbusProperties(
86                             bus, ipObjectInfo.second, ipObjectInfo.first,
87                             ipmi::network::IP_INTERFACE);
88 
89                         ipaddress =
90                             std::get<std::string>(properties["Address"]);
91                     }
92                     // ignore the exception, as it is a valid condition that
93                     // the system is not configured with any IP.
94                     catch (InternalFailure& e)
95                     {
96                         // nothing to do.
97                     }
98                 }
99                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
100                 {
101                     ipaddress = channelConf->ipaddr;
102                 }
103 
104                 inet_pton(AF_INET, ipaddress.c_str(),
105                           reinterpret_cast<void*>(data));
106             }
107             break;
108 
109             case LanParam::IPSRC:
110             {
111                 std::string networkInterfacePath;
112 
113                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
114                 {
115                     try
116                     {
117                         ipmi::ObjectTree ancestorMap;
118                         // if the system is having ip object,then
119                         // get the IP object.
120                         auto ipObject = ipmi::getDbusObject(
121                             bus, ipmi::network::IP_INTERFACE,
122                             ipmi::network::ROOT, ethIP);
123 
124                         // Get the parent interface of the IP object.
125                         try
126                         {
127                             ipmi::InterfaceList interfaces;
128                             interfaces.emplace_back(
129                                 ipmi::network::ETHERNET_INTERFACE);
130 
131                             ancestorMap = ipmi::getAllAncestors(
132                                 bus, ipObject.first, 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>(
139                                 "Unable to get the parent interface",
140                                 entry("PATH=%s", ipObject.first.c_str()),
141                                 entry("INTERFACE=%s",
142                                       ipmi::network::ETHERNET_INTERFACE));
143                             break;
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, ipmi::network::ETHERNET_INTERFACE,
155                             ipmi::network::ROOT, ethdevice);
156 
157                         networkInterfacePath = networkInterfaceObject.first;
158                     }
159 
160                     auto variant = ipmi::getDbusProperty(
161                         bus, ipmi::network::SERVICE, networkInterfacePath,
162                         ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled");
163 
164                     auto dhcpEnabled = std::get<bool>(variant);
165                     // As per IPMI spec 2=>DHCP, 1=STATIC
166                     auto ipsrc = dhcpEnabled ? ipmi::network::IPOrigin::DHCP
167                                              : ipmi::network::IPOrigin::STATIC;
168 
169                     std::memcpy(data, &ipsrc, ipmi::network::IPSRC_SIZE_BYTE);
170                 }
171                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
172                 {
173                     std::memcpy(data, &(channelConf->ipsrc),
174                                 ipmi::network::IPSRC_SIZE_BYTE);
175                 }
176             }
177             break;
178 
179             case LanParam::SUBNET:
180             {
181                 unsigned long mask{};
182                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
183                 {
184                     try
185                     {
186                         auto ipObjectInfo =
187                             ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
188                                               ipmi::network::ROOT, ethIP);
189 
190                         auto properties = ipmi::getAllDbusProperties(
191                             bus, ipObjectInfo.second, ipObjectInfo.first,
192                             ipmi::network::IP_INTERFACE);
193 
194                         auto prefix =
195                             std::get<uint8_t>(properties["PrefixLength"]);
196                         mask = ipmi::network::MASK_32_BIT;
197                         mask = htonl(mask << (ipmi::network::BITS_32 - prefix));
198                     }
199                     // ignore the exception, as it is a valid condition that
200                     // the system is not configured with any IP.
201                     catch (InternalFailure& e)
202                     {
203                         // nothing to do
204                     }
205                     std::memcpy(data, &mask,
206                                 ipmi::network::IPV4_ADDRESS_SIZE_BYTE);
207                 }
208                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
209                 {
210                     inet_pton(AF_INET, channelConf->netmask.c_str(),
211                               reinterpret_cast<void*>(data));
212                 }
213             }
214             break;
215 
216             case LanParam::GATEWAY:
217             {
218                 std::string gateway;
219 
220                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
221                 {
222                     try
223                     {
224                         auto systemObject = ipmi::getDbusObject(
225                             bus, ipmi::network::SYSTEMCONFIG_INTERFACE,
226                             ipmi::network::ROOT);
227 
228                         auto systemProperties = ipmi::getAllDbusProperties(
229                             bus, systemObject.second, systemObject.first,
230                             ipmi::network::SYSTEMCONFIG_INTERFACE);
231 
232                         gateway = std::get<std::string>(
233                             systemProperties["DefaultGateway"]);
234                     }
235                     // ignore the exception, as it is a valid condition that
236                     // the system is not configured with any IP.
237                     catch (InternalFailure& e)
238                     {
239                         // nothing to do
240                     }
241                 }
242                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
243                 {
244                     gateway = channelConf->gateway;
245                 }
246 
247                 inet_pton(AF_INET, gateway.c_str(),
248                           reinterpret_cast<void*>(data));
249             }
250             break;
251 
252             case LanParam::MAC:
253             {
254                 std::string macAddress;
255                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
256                 {
257                     auto macObjectInfo =
258                         ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
259                                             ipmi::network::ROOT, ethdevice);
260 
261                     auto variant = ipmi::getDbusProperty(
262                         bus, macObjectInfo.second, macObjectInfo.first,
263                         ipmi::network::MAC_INTERFACE, "MACAddress");
264 
265                     macAddress = std::get<std::string>(variant);
266                 }
267                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
268                 {
269                     macAddress = channelConf->macAddress;
270                 }
271 
272                 sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
273                        (data), (data + 1), (data + 2), (data + 3), (data + 4),
274                        (data + 5));
275             }
276             break;
277 
278             case LanParam::VLAN:
279             {
280                 uint16_t vlanID{};
281                 if (channelConf->lan_set_in_progress == SET_COMPLETE)
282                 {
283                     try
284                     {
285                         auto ipObjectInfo = ipmi::getIPObject(
286                             bus, ipmi::network::IP_INTERFACE,
287                             ipmi::network::ROOT, ipmi::network::IP_TYPE);
288 
289                         vlanID = static_cast<uint16_t>(
290                             ipmi::network::getVLAN(ipObjectInfo.first));
291 
292                         vlanID = htole16(vlanID);
293 
294                         if (vlanID)
295                         {
296                             // Enable the 16th bit
297                             vlanID |= htole16(ipmi::network::VLAN_ENABLE_MASK);
298                         }
299                     }
300                     // ignore the exception, as it is a valid condition that
301                     // the system is not configured with any IP.
302                     catch (InternalFailure& e)
303                     {
304                         // nothing to do
305                     }
306 
307                     std::memcpy(data, &vlanID, ipmi::network::VLAN_SIZE_BYTE);
308                 }
309                 else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
310                 {
311                     std::memcpy(data, &(channelConf->vlanID),
312                                 ipmi::network::VLAN_SIZE_BYTE);
313                 }
314             }
315             break;
316 
317             default:
318                 rc = IPMI_CC_PARM_OUT_OF_RANGE;
319         }
320     }
321     catch (InternalFailure& e)
322     {
323         commit<InternalFailure>();
324         rc = IPMI_CC_UNSPECIFIED_ERROR;
325         return rc;
326     }
327     return rc;
328 }
329 
330 namespace cipher
331 {
332 
333 std::vector<uint8_t> getCipherList()
334 {
335     std::vector<uint8_t> cipherList;
336 
337     std::ifstream jsonFile(configFile);
338     if (!jsonFile.is_open())
339     {
340         log<level::ERR>("Channel Cipher suites file not found");
341         elog<InternalFailure>();
342     }
343 
344     auto data = Json::parse(jsonFile, nullptr, false);
345     if (data.is_discarded())
346     {
347         log<level::ERR>("Parsing channel cipher suites JSON failed");
348         elog<InternalFailure>();
349     }
350 
351     // Byte 1 is reserved
352     cipherList.push_back(0x00);
353 
354     for (const auto& record : data)
355     {
356         cipherList.push_back(record.value(cipher, 0));
357     }
358 
359     return cipherList;
360 }
361 
362 } // namespace cipher
363 
364 ipmi_ret_t ipmi_transport_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
365                                    ipmi_request_t request,
366                                    ipmi_response_t response,
367                                    ipmi_data_len_t data_len,
368                                    ipmi_context_t context)
369 {
370     // Status code.
371     ipmi_ret_t rc = IPMI_CC_INVALID;
372     *data_len = 0;
373     return rc;
374 }
375 
376 struct set_lan_t
377 {
378     uint8_t channel;
379     uint8_t parameter;
380     uint8_t data[8]; // Per IPMI spec, not expecting more than this size
381 } __attribute__((packed));
382 
383 ipmi_ret_t checkAndUpdateNetwork(int channel)
384 {
385     auto channelConf = getChannelConfig(channel);
386     using namespace std::chrono_literals;
387     // time to wait before applying the network changes.
388     constexpr auto networkTimeout = 10000000us; // 10 sec
389 
390     // Skip the timer. Expecting more update as we are in SET_IN_PROGRESS
391     if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
392     {
393         return IPMI_CC_OK;
394     }
395 
396     // Start the timer, if it is direct single param update without
397     // SET_IN_PROGRESS or many params updated through SET_IN_PROGRESS to
398     // SET_COMPLETE Note: Even for update with SET_IN_PROGRESS, don't apply the
399     // changes immediately, as ipmitool sends each param individually
400     // through SET_IN_PROGRESS to SET_COMPLETE.
401     channelConf->flush = true;
402     if (!networkTimer)
403     {
404         log<level::ERR>("Network timer is not instantiated");
405         return IPMI_CC_UNSPECIFIED_ERROR;
406     }
407     // start the timer.
408     networkTimer->start(networkTimeout);
409     return IPMI_CC_OK;
410 }
411 
412 ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
413                                   ipmi_request_t request,
414                                   ipmi_response_t response,
415                                   ipmi_data_len_t data_len,
416                                   ipmi_context_t context)
417 {
418     ipmi_ret_t rc = IPMI_CC_OK;
419     *data_len = 0;
420 
421     char ipaddr[INET_ADDRSTRLEN];
422     char netmask[INET_ADDRSTRLEN];
423     char gateway[INET_ADDRSTRLEN];
424 
425     auto reqptr = reinterpret_cast<const set_lan_t*>(request);
426     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
427 
428     // channel number is the lower nibble
429     int channel = reqptr->channel & CHANNEL_MASK;
430     auto ethdevice = ipmi::getChannelName(channel);
431     if (ethdevice.empty())
432     {
433         return IPMI_CC_INVALID_FIELD_REQUEST;
434     }
435     auto channelConf = getChannelConfig(channel);
436 
437     switch (static_cast<LanParam>(reqptr->parameter))
438     {
439         case LanParam::IP:
440         {
441             std::snprintf(ipaddr, INET_ADDRSTRLEN,
442                           ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
443                           reqptr->data[1], reqptr->data[2], reqptr->data[3]);
444 
445             channelConf->ipaddr.assign(ipaddr);
446         }
447         break;
448 
449         case LanParam::IPSRC:
450         {
451             uint8_t ipsrc{};
452             std::memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE);
453             channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
454         }
455         break;
456 
457         case LanParam::MAC:
458         {
459             char mac[SIZE_MAC];
460 
461             std::snprintf(mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT,
462                           reqptr->data[0], reqptr->data[1], reqptr->data[2],
463                           reqptr->data[3], reqptr->data[4], reqptr->data[5]);
464 
465             auto macObjectInfo =
466                 ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
467                                     ipmi::network::ROOT, ethdevice);
468 
469             ipmi::setDbusProperty(
470                 bus, macObjectInfo.second, macObjectInfo.first,
471                 ipmi::network::MAC_INTERFACE, "MACAddress", std::string(mac));
472 
473             channelConf->macAddress = mac;
474         }
475         break;
476 
477         case LanParam::SUBNET:
478         {
479             std::snprintf(netmask, INET_ADDRSTRLEN,
480                           ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
481                           reqptr->data[1], reqptr->data[2], reqptr->data[3]);
482             channelConf->netmask.assign(netmask);
483         }
484         break;
485 
486         case LanParam::GATEWAY:
487         {
488             std::snprintf(gateway, INET_ADDRSTRLEN,
489                           ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
490                           reqptr->data[1], reqptr->data[2], reqptr->data[3]);
491             channelConf->gateway.assign(gateway);
492         }
493         break;
494 
495         case LanParam::VLAN:
496         {
497             uint16_t vlan{};
498             std::memcpy(&vlan, reqptr->data, ipmi::network::VLAN_SIZE_BYTE);
499             // We are not storing the enable bit
500             // We assume that ipmitool always send enable
501             // bit as 1.
502             vlan = le16toh(vlan);
503             channelConf->vlanID = vlan;
504         }
505         break;
506 
507         case LanParam::INPROGRESS:
508         {
509             if (reqptr->data[0] == SET_COMPLETE)
510             {
511                 channelConf->lan_set_in_progress = SET_COMPLETE;
512 
513                 log<level::INFO>(
514                     "Network data from Cache",
515                     entry("PREFIX=%s", channelConf->netmask.c_str()),
516                     entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
517                     entry("GATEWAY=%s", channelConf->gateway.c_str()),
518                     entry("VLAN=%d", channelConf->vlanID));
519             }
520             else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
521             {
522                 channelConf->lan_set_in_progress = SET_IN_PROGRESS;
523             }
524         }
525         break;
526 
527         default:
528         {
529             rc = IPMI_CC_PARM_NOT_SUPPORTED;
530             return rc;
531         }
532     }
533     rc = checkAndUpdateNetwork(channel);
534 
535     return rc;
536 }
537 
538 struct get_lan_t
539 {
540     uint8_t rev_channel;
541     uint8_t parameter;
542     uint8_t parameter_set;
543     uint8_t parameter_block;
544 } __attribute__((packed));
545 
546 ipmi_ret_t ipmi_transport_get_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
547                                   ipmi_request_t request,
548                                   ipmi_response_t response,
549                                   ipmi_data_len_t data_len,
550                                   ipmi_context_t context)
551 {
552     ipmi_ret_t rc = IPMI_CC_OK;
553     *data_len = 0;
554     const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0
555 
556     get_lan_t* reqptr = (get_lan_t*)request;
557     // channel number is the lower nibble
558     int channel = reqptr->rev_channel & CHANNEL_MASK;
559 
560     if (reqptr->rev_channel & 0x80) // Revision is bit 7
561     {
562         // Only current revision was requested
563         *data_len = sizeof(current_revision);
564         std::memcpy(response, &current_revision, *data_len);
565         return IPMI_CC_OK;
566     }
567 
568     static std::vector<uint8_t> cipherList;
569     static auto listInit = false;
570 
571     if (!listInit)
572     {
573         try
574         {
575             cipherList = cipher::getCipherList();
576             listInit = true;
577         }
578         catch (const std::exception& e)
579         {
580             return IPMI_CC_UNSPECIFIED_ERROR;
581         }
582     }
583 
584     auto ethdevice = ipmi::getChannelName(channel);
585     if (ethdevice.empty())
586     {
587         return IPMI_CC_INVALID_FIELD_REQUEST;
588     }
589     auto channelConf = getChannelConfig(channel);
590 
591     LanParam param = static_cast<LanParam>(reqptr->parameter);
592     switch (param)
593     {
594         case LanParam::INPROGRESS:
595         {
596             uint8_t buf[] = {current_revision,
597                              channelConf->lan_set_in_progress};
598             *data_len = sizeof(buf);
599             std::memcpy(response, &buf, *data_len);
600             break;
601         }
602         case LanParam::AUTHSUPPORT:
603         {
604             uint8_t buf[] = {current_revision, 0x04};
605             *data_len = sizeof(buf);
606             std::memcpy(response, &buf, *data_len);
607             break;
608         }
609         case LanParam::AUTHENABLES:
610         {
611             uint8_t buf[] = {current_revision, 0x04, 0x04, 0x04, 0x04, 0x04};
612             *data_len = sizeof(buf);
613             std::memcpy(response, &buf, *data_len);
614             break;
615         }
616         case LanParam::IP:
617         case LanParam::SUBNET:
618         case LanParam::GATEWAY:
619         case LanParam::MAC:
620         {
621             uint8_t buf[ipmi::network::MAC_ADDRESS_SIZE_BYTE + 1] = {};
622 
623             *data_len = sizeof(current_revision);
624             std::memcpy(buf, &current_revision, *data_len);
625 
626             if (getNetworkData(reqptr->parameter, &buf[1], channel) ==
627                 IPMI_CC_OK)
628             {
629                 if (param == LanParam::MAC)
630                 {
631                     *data_len = sizeof(buf);
632                 }
633                 else
634                 {
635                     *data_len = ipmi::network::IPV4_ADDRESS_SIZE_BYTE + 1;
636                 }
637                 std::memcpy(response, &buf, *data_len);
638             }
639             else
640             {
641                 rc = IPMI_CC_UNSPECIFIED_ERROR;
642             }
643             break;
644         }
645         case LanParam::VLAN:
646         {
647             uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1] = {};
648 
649             *data_len = sizeof(current_revision);
650             std::memcpy(buf, &current_revision, *data_len);
651             if (getNetworkData(reqptr->parameter, &buf[1], channel) ==
652                 IPMI_CC_OK)
653             {
654                 *data_len = sizeof(buf);
655                 std::memcpy(response, &buf, *data_len);
656             }
657             break;
658         }
659         case LanParam::IPSRC:
660         {
661             uint8_t buff[ipmi::network::IPSRC_SIZE_BYTE + 1] = {};
662             *data_len = sizeof(current_revision);
663             std::memcpy(buff, &current_revision, *data_len);
664             if (getNetworkData(reqptr->parameter, &buff[1], channel) ==
665                 IPMI_CC_OK)
666             {
667                 *data_len = sizeof(buff);
668                 std::memcpy(response, &buff, *data_len);
669             }
670             break;
671         }
672         case LanParam::CIPHER_SUITE_COUNT:
673         {
674             *(static_cast<uint8_t*>(response)) = current_revision;
675             // Byte 1 is reserved byte and does not indicate a cipher suite ID,
676             // so no of cipher suite entry count is one less than the size of
677             // the vector
678             auto count = static_cast<uint8_t>(cipherList.size() - 1);
679             *(static_cast<uint8_t*>(response) + 1) = count;
680             *data_len = sizeof(current_revision) + sizeof(count);
681             break;
682         }
683         case LanParam::CIPHER_SUITE_ENTRIES:
684         {
685             *(static_cast<uint8_t*>(response)) = current_revision;
686             // Byte 1 is reserved
687             std::copy_n(cipherList.data(), cipherList.size(),
688                         static_cast<uint8_t*>(response) + 1);
689             *data_len = sizeof(current_revision) +
690                         static_cast<uint8_t>(cipherList.size());
691             break;
692         }
693         default:
694             log<level::ERR>("Unsupported parameter",
695                             entry("PARAMETER=0x%x", reqptr->parameter));
696             rc = IPMI_CC_PARM_NOT_SUPPORTED;
697     }
698 
699     return rc;
700 }
701 
702 void applyChanges(int channel)
703 {
704     std::string ipaddress;
705     std::string gateway;
706     uint8_t prefix{};
707     uint32_t vlanID{};
708     std::string networkInterfacePath;
709     ipmi::DbusObjectInfo ipObject;
710     ipmi::DbusObjectInfo systemObject;
711 
712     auto ethdevice = ipmi::getChannelName(channel);
713     if (ethdevice.empty())
714     {
715         log<level::ERR>("Unable to get the interface name",
716                         entry("CHANNEL=%d", channel));
717         return;
718     }
719     auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE;
720     auto channelConf = getChannelConfig(channel);
721 
722     try
723     {
724         sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
725 
726         log<level::INFO>("Network data from Cache",
727                          entry("PREFIX=%s", channelConf->netmask.c_str()),
728                          entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
729                          entry("GATEWAY=%s", channelConf->gateway.c_str()),
730                          entry("VLAN=%d", channelConf->vlanID),
731                          entry("IPSRC=%d", channelConf->ipsrc));
732         if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
733         {
734             // get the first twelve bits which is vlan id
735             // not interested in rest of the bits.
736             channelConf->vlanID = le32toh(channelConf->vlanID);
737             vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
738         }
739 
740         // if the asked ip src is DHCP then not interested in
741         // any given data except vlan.
742         if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
743         {
744             // always get the system object
745             systemObject =
746                 ipmi::getDbusObject(bus, ipmi::network::SYSTEMCONFIG_INTERFACE,
747                                     ipmi::network::ROOT);
748 
749             // the below code is to determine the mode of the interface
750             // as the handling is same, if the system is configured with
751             // DHCP or user has given all the data.
752             try
753             {
754                 ipmi::ObjectTree ancestorMap;
755 
756                 ipmi::InterfaceList interfaces{
757                     ipmi::network::ETHERNET_INTERFACE};
758 
759                 // if the system is having ip object,then
760                 // get the IP object.
761                 ipObject = ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
762                                              ipmi::network::ROOT, ethIp);
763 
764                 // Get the parent interface of the IP object.
765                 try
766                 {
767                     ancestorMap = ipmi::getAllAncestors(bus, ipObject.first,
768                                                         std::move(interfaces));
769                 }
770                 catch (InternalFailure& e)
771                 {
772                     // if unable to get the parent interface
773                     // then commit the error and return.
774                     log<level::ERR>("Unable to get the parent interface",
775                                     entry("PATH=%s", ipObject.first.c_str()),
776                                     entry("INTERFACE=%s",
777                                           ipmi::network::ETHERNET_INTERFACE));
778                     commit<InternalFailure>();
779                     channelConf->clear();
780                     return;
781                 }
782 
783                 networkInterfacePath = ancestorMap.begin()->first;
784             }
785             catch (InternalFailure& e)
786             {
787                 // TODO Currently IPMI supports single interface,need to handle
788                 // Multiple interface through
789                 // https://github.com/openbmc/openbmc/issues/2138
790 
791                 // if there is no ip configured on the system,then
792                 // get the network interface object.
793                 auto networkInterfaceObject =
794                     ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE,
795                                         ipmi::network::ROOT, ethdevice);
796 
797                 networkInterfacePath = std::move(networkInterfaceObject.first);
798             }
799 
800             // get the configured mode on the system.
801             auto enableDHCP = std::get<bool>(ipmi::getDbusProperty(
802                 bus, ipmi::network::SERVICE, networkInterfacePath,
803                 ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled"));
804 
805             // if ip address source is not given then get the ip source mode
806             // from the system so that it can be applied later.
807             if (channelConf->ipsrc == ipmi::network::IPOrigin::UNSPECIFIED)
808             {
809                 channelConf->ipsrc = (enableDHCP)
810                                          ? ipmi::network::IPOrigin::DHCP
811                                          : ipmi::network::IPOrigin::STATIC;
812             }
813 
814             // check whether user has given all the data
815             // or the configured system interface is dhcp enabled,
816             // in both of the cases get the values from the cache.
817             if ((!channelConf->ipaddr.empty() &&
818                  !channelConf->netmask.empty() &&
819                  !channelConf->gateway.empty()) ||
820                 (enableDHCP)) // configured system interface mode = DHCP
821             {
822                 // convert mask into prefix
823                 ipaddress = channelConf->ipaddr;
824                 prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
825                 gateway = channelConf->gateway;
826             }
827             else // asked ip src = static and configured system src = static
828                  // or partially given data.
829             {
830                 // We have partial filled cache so get the remaining
831                 // info from the system.
832 
833                 // Get the network data from the system as user has
834                 // not given all the data then use the data fetched from the
835                 // system but it is implementation dependent,IPMI spec doesn't
836                 // force it.
837 
838                 // if system is not having any ip object don't throw error,
839                 try
840                 {
841                     auto properties = ipmi::getAllDbusProperties(
842                         bus, ipObject.second, ipObject.first,
843                         ipmi::network::IP_INTERFACE);
844 
845                     ipaddress =
846                         channelConf->ipaddr.empty()
847                             ? std::get<std::string>(properties["Address"])
848                             : channelConf->ipaddr;
849 
850                     prefix = channelConf->netmask.empty()
851                                  ? std::get<uint8_t>(properties["PrefixLength"])
852                                  : ipmi::network::toPrefix(
853                                        AF_INET, channelConf->netmask);
854                 }
855                 catch (InternalFailure& e)
856                 {
857                     log<level::INFO>(
858                         "Failed to get IP object which matches",
859                         entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
860                         entry("MATCH=%s", ethIp.c_str()));
861                 }
862 
863                 auto systemProperties = ipmi::getAllDbusProperties(
864                     bus, systemObject.second, systemObject.first,
865                     ipmi::network::SYSTEMCONFIG_INTERFACE);
866 
867                 gateway = channelConf->gateway.empty()
868                               ? std::get<std::string>(
869                                     systemProperties["DefaultGateway"])
870                               : channelConf->gateway;
871             }
872         }
873 
874         // Currently network manager doesn't support purging of all the
875         // ip addresses and the vlan interfaces from the parent interface,
876         // TODO once the support is there, will make the change here.
877         // https://github.com/openbmc/openbmc/issues/2141.
878 
879         // TODO Currently IPMI supports single interface,need to handle
880         // Multiple interface through
881         // https://github.com/openbmc/openbmc/issues/2138
882 
883         // instead of deleting all the vlan interfaces and
884         // all the ipv4 address,we will call reset method.
885         // delete all the vlan interfaces
886 
887         ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
888                                    ipmi::network::VLAN_INTERFACE);
889 
890         // set the interface mode  to static
891         auto networkInterfaceObject =
892             ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE,
893                                 ipmi::network::ROOT, ethdevice);
894 
895         // setting the physical interface mode to static.
896         ipmi::setDbusProperty(
897             bus, ipmi::network::SERVICE, networkInterfaceObject.first,
898             ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
899 
900         networkInterfacePath = networkInterfaceObject.first;
901 
902         // delete all the ipv4 addresses
903         ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
904                                    ipmi::network::IP_INTERFACE, ethIp);
905 
906         if (vlanID)
907         {
908             ipmi::network::createVLAN(bus, ipmi::network::SERVICE,
909                                       ipmi::network::ROOT, ethdevice, vlanID);
910 
911             auto networkInterfaceObject = ipmi::getDbusObject(
912                 bus, ipmi::network::VLAN_INTERFACE, ipmi::network::ROOT);
913 
914             networkInterfacePath = networkInterfaceObject.first;
915         }
916 
917         if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
918         {
919             ipmi::setDbusProperty(
920                 bus, ipmi::network::SERVICE, networkInterfacePath,
921                 ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", true);
922         }
923         else
924         {
925             // change the mode to static
926             ipmi::setDbusProperty(
927                 bus, ipmi::network::SERVICE, networkInterfacePath,
928                 ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
929 
930             if (!ipaddress.empty())
931             {
932                 ipmi::network::createIP(bus, ipmi::network::SERVICE,
933                                         networkInterfacePath, ipv4Protocol,
934                                         ipaddress, prefix);
935             }
936 
937             if (!gateway.empty())
938             {
939                 ipmi::setDbusProperty(bus, systemObject.second,
940                                       systemObject.first,
941                                       ipmi::network::SYSTEMCONFIG_INTERFACE,
942                                       "DefaultGateway", std::string(gateway));
943             }
944         }
945     }
946     catch (sdbusplus::exception::exception& e)
947     {
948         log<level::ERR>(
949             "Failed to set network data", entry("PREFIX=%d", prefix),
950             entry("ADDRESS=%s", ipaddress.c_str()),
951             entry("GATEWAY=%s", gateway.c_str()), entry("VLANID=%d", vlanID),
952             entry("IPSRC=%d", channelConf->ipsrc));
953 
954         commit<InternalFailure>();
955     }
956 
957     channelConf->clear();
958 }
959 
960 void commitNetworkChanges()
961 {
962     for (const auto& channel : channelConfig)
963     {
964         if (channel.second->flush)
965         {
966             applyChanges(channel.first);
967         }
968     }
969 }
970 
971 void createNetworkTimer()
972 {
973     if (!networkTimer)
974     {
975         std::function<void()> networkTimerCallback(
976             std::bind(&commitNetworkChanges));
977 
978         networkTimer = std::make_unique<phosphor::Timer>(networkTimerCallback);
979     }
980 }
981 
982 void register_netfn_transport_functions()
983 {
984     // As this timer is only for transport handler
985     // so creating it here.
986     createNetworkTimer();
987     // <Wildcard Command>
988     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_WILDCARD, NULL,
989                            ipmi_transport_wildcard, PRIVILEGE_USER);
990 
991     // <Set LAN Configuration Parameters>
992     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_SET_LAN, NULL,
993                            ipmi_transport_set_lan, PRIVILEGE_ADMIN);
994 
995     // <Get LAN Configuration Parameters>
996     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_GET_LAN, NULL,
997                            ipmi_transport_get_lan, PRIVILEGE_OPERATOR);
998 
999     return;
1000 }
1001