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