1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "channel_mgmt.hpp"
18 
19 #include "apphandler.hpp"
20 #include "user_layer.hpp"
21 
22 #include <ifaddrs.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 
27 #include <boost/interprocess/sync/scoped_lock.hpp>
28 #include <ipmid/utils.hpp>
29 #include <phosphor-logging/lg2.hpp>
30 #include <sdbusplus/bus/match.hpp>
31 #include <sdbusplus/server/object.hpp>
32 
33 #include <cerrno>
34 #include <exception>
35 #include <filesystem>
36 #include <fstream>
37 #include <unordered_map>
38 
39 namespace ipmi
40 {
41 
42 static constexpr const char* channelAccessDefaultFilename =
43     "/usr/share/ipmi-providers/channel_access.json";
44 static constexpr const char* channelConfigDefaultFilename =
45     "/usr/share/ipmi-providers/channel_config.json";
46 static constexpr const char* channelNvDataFilename =
47     "/var/lib/ipmi/channel_access_nv.json";
48 static constexpr const char* channelVolatileDataFilename =
49     "/run/ipmi/channel_access_volatile.json";
50 
51 // TODO: Get the service name dynamically..
52 static constexpr const char* networkIntfServiceName =
53     "xyz.openbmc_project.Network";
54 static constexpr const char* networkIntfObjectBasePath =
55     "/xyz/openbmc_project/network";
56 static constexpr const char* networkChConfigIntfName =
57     "xyz.openbmc_project.Channel.ChannelAccess";
58 static constexpr const char* privilegePropertyString = "MaxPrivilege";
59 static constexpr const char* dBusPropertiesInterface =
60     "org.freedesktop.DBus.Properties";
61 static constexpr const char* propertiesChangedSignal = "PropertiesChanged";
62 static constexpr const char* interfaceAddedSignal = "InterfacesAdded";
63 static constexpr const char* interfaceRemovedSignal = "InterfacesRemoved";
64 
65 // STRING DEFINES: Should sync with key's in JSON
66 static constexpr const char* nameString = "name";
67 static constexpr const char* isValidString = "is_valid";
68 static constexpr const char* activeSessionsString = "active_sessions";
69 static constexpr const char* maxTransferSizeString = "max_transfer_size";
70 static constexpr const char* channelInfoString = "channel_info";
71 static constexpr const char* mediumTypeString = "medium_type";
72 static constexpr const char* protocolTypeString = "protocol_type";
73 static constexpr const char* sessionSupportedString = "session_supported";
74 static constexpr const char* isIpmiString = "is_ipmi";
75 static constexpr const char* isManagementNIC = "is_management_nic";
76 static constexpr const char* accessModeString = "access_mode";
77 static constexpr const char* userAuthDisabledString = "user_auth_disabled";
78 static constexpr const char* perMsgAuthDisabledString = "per_msg_auth_disabled";
79 static constexpr const char* alertingDisabledString = "alerting_disabled";
80 static constexpr const char* privLimitString = "priv_limit";
81 
82 // Default values
83 static constexpr const char* defaultChannelName = "RESERVED";
84 static constexpr const uint8_t defaultMediumType =
85     static_cast<uint8_t>(EChannelMediumType::reserved);
86 static constexpr const uint8_t defaultProtocolType =
87     static_cast<uint8_t>(EChannelProtocolType::reserved);
88 static constexpr const uint8_t defaultSessionSupported =
89     static_cast<uint8_t>(EChannelSessSupported::none);
90 static constexpr const uint8_t defaultAuthType =
91     static_cast<uint8_t>(EAuthType::none);
92 static constexpr const bool defaultIsIpmiState = false;
93 static constexpr size_t smallChannelSize = 64;
94 
95 std::unique_ptr<sdbusplus::bus::match_t> chPropertiesSignal
96     __attribute__((init_priority(101)));
97 
98 std::unique_ptr<sdbusplus::bus::match_t> chInterfaceAddedSignal
99     __attribute__((init_priority(101)));
100 
101 std::unique_ptr<sdbusplus::bus::match_t> chInterfaceRemovedSignal
102     __attribute__((init_priority(101)));
103 
104 // String mappings use in JSON config file
105 static std::unordered_map<std::string, EChannelMediumType> mediumTypeMap = {
106     {"reserved", EChannelMediumType::reserved},
107     {"ipmb", EChannelMediumType::ipmb},
108     {"icmb-v1.0", EChannelMediumType::icmbV10},
109     {"icmb-v0.9", EChannelMediumType::icmbV09},
110     {"lan-802.3", EChannelMediumType::lan8032},
111     {"serial", EChannelMediumType::serial},
112     {"other-lan", EChannelMediumType::otherLan},
113     {"pci-smbus", EChannelMediumType::pciSmbus},
114     {"smbus-v1.0", EChannelMediumType::smbusV11},
115     {"smbus-v2.0", EChannelMediumType::smbusV20},
116     {"usb-1x", EChannelMediumType::usbV1x},
117     {"usb-2x", EChannelMediumType::usbV2x},
118     {"system-interface", EChannelMediumType::systemInterface},
119     {"oem", EChannelMediumType::oem},
120     {"unknown", EChannelMediumType::unknown}};
121 
122 static std::unordered_map<EInterfaceIndex, std::string> interfaceMap = {
123     {interfaceKCS, "SMS"},
124     {interfaceLAN1, "eth0"},
125     {interfaceUnknown, "unknown"}};
126 
127 static std::unordered_map<std::string, EChannelProtocolType> protocolTypeMap = {
128     {"na", EChannelProtocolType::na},
129     {"ipmb-1.0", EChannelProtocolType::ipmbV10},
130     {"icmb-2.0", EChannelProtocolType::icmbV11},
131     {"reserved", EChannelProtocolType::reserved},
132     {"ipmi-smbus", EChannelProtocolType::ipmiSmbus},
133     {"kcs", EChannelProtocolType::kcs},
134     {"smic", EChannelProtocolType::smic},
135     {"bt-10", EChannelProtocolType::bt10},
136     {"bt-15", EChannelProtocolType::bt15},
137     {"tmode", EChannelProtocolType::tMode},
138     {"oem", EChannelProtocolType::oem}};
139 
140 static std::array<std::string, 4> accessModeList = {
141     "disabled", "pre-boot", "always_available", "shared"};
142 
143 static std::array<std::string, 4> sessionSupportList = {
144     "session-less", "single-session", "multi-session", "session-based"};
145 
146 const std::array<std::string, PRIVILEGE_OEM + 1> privList = {
147     "priv-reserved", "priv-callback", "priv-user",
148     "priv-operator", "priv-admin",    "priv-oem"};
149 
getChannelName(const uint8_t chNum)150 std::string ChannelConfig::getChannelName(const uint8_t chNum)
151 {
152     if (!isValidChannel(chNum))
153     {
154         lg2::error("Invalid channel number: {CHANNEL_ID}", "CHANNEL_ID", chNum);
155         throw std::invalid_argument("Invalid channel number");
156     }
157 
158     return channelData[chNum].chName;
159 }
160 
convertToChannelNumberFromChannelName(const std::string & chName)161 int ChannelConfig::convertToChannelNumberFromChannelName(
162     const std::string& chName)
163 {
164     for (const auto& it : channelData)
165     {
166         if (it.chName == chName)
167         {
168             return it.chID;
169         }
170     }
171     lg2::error("Invalid channel name: {CHANNEL}", "CHANNEL", chName);
172     throw std::invalid_argument("Invalid channel name");
173 
174     return -1;
175 }
176 
getChannelNameFromPath(const std::string & path)177 std::string ChannelConfig::getChannelNameFromPath(const std::string& path)
178 {
179     const size_t length = strlen(networkIntfObjectBasePath);
180     if (((length + 1) >= path.size()) ||
181         path.compare(0, length, networkIntfObjectBasePath))
182     {
183         lg2::error("Invalid object path: {PATH}", "PATH", path);
184         throw std::invalid_argument("Invalid object path");
185     }
186     std::string chName(path, length + 1);
187     return chName;
188 }
189 
processChAccessPropChange(const std::string & path,const DbusChObjProperties & chProperties)190 void ChannelConfig::processChAccessPropChange(
191     const std::string& path, const DbusChObjProperties& chProperties)
192 {
193     // Get interface name from path. ex: '/xyz/openbmc_project/network/eth0'
194     std::string chName;
195     try
196     {
197         chName = getChannelNameFromPath(path);
198     }
199     catch (const std::invalid_argument& e)
200     {
201         lg2::error("Exception: {MSG}", "MSG", e.what());
202         return;
203     }
204 
205     // Get the MaxPrivilege property value from the signal
206     std::string intfPrivStr;
207     std::string propName;
208     for (const auto& prop : chProperties)
209     {
210         if (prop.first == privilegePropertyString)
211         {
212             propName = privilegePropertyString;
213             intfPrivStr = std::get<std::string>(prop.second);
214             break;
215         }
216     }
217 
218     if (propName != privilegePropertyString)
219     {
220         lg2::error("Unknown signal caught.");
221         return;
222     }
223 
224     if (intfPrivStr.empty())
225     {
226         lg2::error("Invalid privilege string for intf {INTF}", "INTF", chName);
227         return;
228     }
229 
230     uint8_t intfPriv = 0;
231     int chNum;
232     try
233     {
234         intfPriv = static_cast<uint8_t>(convertToPrivLimitIndex(intfPrivStr));
235         chNum = convertToChannelNumberFromChannelName(chName);
236     }
237     catch (const std::invalid_argument& e)
238     {
239         lg2::error("Exception: {MSG}", "MSG", e.what());
240         return;
241     }
242 
243     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
244         channelLock{*channelMutex};
245     // skip updating the values, if this property change originated from IPMI.
246     if (signalFlag & (1 << chNum))
247     {
248         signalFlag &= ~(1 << chNum);
249         lg2::debug("Request originated from IPMI so ignoring signal");
250         return;
251     }
252 
253     // Update both volatile & Non-volatile, if there is mismatch.
254     // as property change other than IPMI, has to update both volatile &
255     // non-volatile data.
256     checkAndReloadVolatileData();
257     checkAndReloadNVData();
258     if (channelData[chNum].chAccess.chNonVolatileData.privLimit != intfPriv)
259     {
260         // Update NV data
261         channelData[chNum].chAccess.chNonVolatileData.privLimit = intfPriv;
262         if (writeChannelPersistData() != 0)
263         {
264             lg2::error("Failed to update the persist data file");
265             return;
266         }
267 
268         // Update Volatile data
269         if (channelData[chNum].chAccess.chVolatileData.privLimit != intfPriv)
270         {
271             channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv;
272             if (writeChannelVolatileData() != 0)
273             {
274                 lg2::error("Failed to update the volatile data file");
275                 return;
276             }
277         }
278     }
279 
280     return;
281 }
282 
getChannelConfigObject()283 ChannelConfig& getChannelConfigObject()
284 {
285     static ChannelConfig channelConfig;
286     return channelConfig;
287 }
288 
~ChannelConfig()289 ChannelConfig::~ChannelConfig()
290 {
291     if (signalHndlrObjectState)
292     {
293         chPropertiesSignal.reset();
294         chInterfaceAddedSignal.reset();
295         chInterfaceRemovedSignal.reset();
296         sigHndlrLock.unlock();
297     }
298 }
299 
ChannelConfig()300 ChannelConfig::ChannelConfig() : bus(ipmid_get_sd_bus_connection())
301 {
302     std::ofstream mutexCleanUpFile;
303     mutexCleanUpFile.open(ipmiChMutexCleanupLockFile,
304                           std::ofstream::out | std::ofstream::app);
305     if (!mutexCleanUpFile.good())
306     {
307         lg2::debug("Unable to open mutex cleanup file");
308         return;
309     }
310     mutexCleanUpFile.close();
311     mutexCleanupLock =
312         boost::interprocess::file_lock(ipmiChMutexCleanupLockFile);
313     if (mutexCleanupLock.try_lock())
314     {
315         boost::interprocess::named_recursive_mutex::remove(ipmiChannelMutex);
316         channelMutex =
317             std::make_unique<boost::interprocess::named_recursive_mutex>(
318                 boost::interprocess::open_or_create, ipmiChannelMutex);
319         mutexCleanupLock.lock_sharable();
320     }
321     else
322     {
323         mutexCleanupLock.lock_sharable();
324         channelMutex =
325             std::make_unique<boost::interprocess::named_recursive_mutex>(
326                 boost::interprocess::open_or_create, ipmiChannelMutex);
327     }
328 
329     initChannelPersistData();
330 
331     sigHndlrLock = boost::interprocess::file_lock(channelNvDataFilename);
332     // Register it for single object and single process either netipmid /
333     // host-ipmid
334     if (chPropertiesSignal == nullptr && sigHndlrLock.try_lock())
335     {
336         lg2::debug("Registering channel signal handler.");
337         chPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>(
338             bus,
339             sdbusplus::bus::match::rules::path_namespace(
340                 networkIntfObjectBasePath) +
341                 sdbusplus::bus::match::rules::type::signal() +
342                 sdbusplus::bus::match::rules::member(propertiesChangedSignal) +
343                 sdbusplus::bus::match::rules::interface(
344                     dBusPropertiesInterface) +
345                 sdbusplus::bus::match::rules::argN(0, networkChConfigIntfName),
346             [&](sdbusplus::message_t& msg) {
347                 DbusChObjProperties props;
348                 std::string iface;
349                 std::string path = msg.get_path();
350                 msg.read(iface, props);
351                 processChAccessPropChange(path, props);
352             });
353         signalHndlrObjectState = true;
354 
355         chInterfaceAddedSignal = std::make_unique<sdbusplus::bus::match_t>(
356             bus,
357             sdbusplus::bus::match::rules::type::signal() +
358                 sdbusplus::bus::match::rules::member(interfaceAddedSignal) +
359                 sdbusplus::bus::match::rules::argNpath(
360                     0, std::string(networkIntfObjectBasePath) + "/"),
361             [&](sdbusplus::message_t&) { initChannelPersistData(); });
362 
363         chInterfaceRemovedSignal = std::make_unique<sdbusplus::bus::match_t>(
364             bus,
365             sdbusplus::bus::match::rules::type::signal() +
366                 sdbusplus::bus::match::rules::member(interfaceRemovedSignal) +
367                 sdbusplus::bus::match::rules::argNpath(
368                     0, std::string(networkIntfObjectBasePath) + "/"),
369             [&](sdbusplus::message_t&) { initChannelPersistData(); });
370     }
371 }
372 
isValidChannel(const uint8_t chNum)373 bool ChannelConfig::isValidChannel(const uint8_t chNum)
374 {
375     if (chNum >= maxIpmiChannels)
376     {
377         lg2::debug("Invalid channel ID - Out of range");
378         return false;
379     }
380 
381     if (channelData[chNum].isChValid == false)
382     {
383         lg2::debug("Channel is not valid");
384     }
385 
386     return channelData[chNum].isChValid;
387 }
388 
389 EChannelSessSupported
getChannelSessionSupport(const uint8_t chNum)390     ChannelConfig::getChannelSessionSupport(const uint8_t chNum)
391 {
392     EChannelSessSupported chSessSupport =
393         (EChannelSessSupported)channelData[chNum].chInfo.sessionSupported;
394     return chSessSupport;
395 }
396 
isValidAuthType(const uint8_t chNum,const EAuthType & authType)397 bool ChannelConfig::isValidAuthType(const uint8_t chNum,
398                                     const EAuthType& authType)
399 {
400     if ((authType < EAuthType::md2) || (authType > EAuthType::oem))
401     {
402         lg2::debug("Invalid authentication type");
403         return false;
404     }
405 
406     uint8_t authTypeSupported = channelData[chNum].chInfo.authTypeSupported;
407     if (!(authTypeSupported & (1 << static_cast<uint8_t>(authType))))
408     {
409         lg2::debug("Authentication type is not supported.");
410         return false;
411     }
412 
413     return true;
414 }
415 
getChannelActiveSessions(const uint8_t chNum)416 int ChannelConfig::getChannelActiveSessions(const uint8_t chNum)
417 {
418     // TODO: TEMPORARY FIX
419     // Channels active session count is managed separately
420     // by monitoring channel session which includes LAN and
421     // RAKP layer changes. This will be updated, once the
422     // authentication part is implemented.
423     return channelData[chNum].activeSessCount;
424 }
425 
getChannelMaxTransferSize(uint8_t chNum)426 size_t ChannelConfig::getChannelMaxTransferSize(uint8_t chNum)
427 {
428     return channelData[chNum].maxTransferSize;
429 }
430 
getChannelInfo(const uint8_t chNum,ChannelInfo & chInfo)431 Cc ChannelConfig::getChannelInfo(const uint8_t chNum, ChannelInfo& chInfo)
432 {
433     if (!isValidChannel(chNum))
434     {
435         lg2::debug("Invalid channel");
436         return ccInvalidFieldRequest;
437     }
438 
439     std::copy_n(reinterpret_cast<uint8_t*>(&channelData[chNum].chInfo),
440                 sizeof(channelData[chNum].chInfo),
441                 reinterpret_cast<uint8_t*>(&chInfo));
442     return ccSuccess;
443 }
444 
getChannelAccessData(const uint8_t chNum,ChannelAccess & chAccessData)445 Cc ChannelConfig::getChannelAccessData(const uint8_t chNum,
446                                        ChannelAccess& chAccessData)
447 {
448     if (!isValidChannel(chNum))
449     {
450         lg2::debug("Invalid channel");
451         return ccInvalidFieldRequest;
452     }
453 
454     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
455     {
456         lg2::debug("Session-less channel doesn't have access data.");
457         return ccActionNotSupportedForChannel;
458     }
459 
460     if (checkAndReloadVolatileData() != 0)
461     {
462         return ccUnspecifiedError;
463     }
464 
465     std::copy_n(
466         reinterpret_cast<uint8_t*>(&channelData[chNum].chAccess.chVolatileData),
467         sizeof(channelData[chNum].chAccess.chVolatileData),
468         reinterpret_cast<uint8_t*>(&chAccessData));
469 
470     return ccSuccess;
471 }
472 
setChannelAccessData(const uint8_t chNum,const ChannelAccess & chAccessData,const uint8_t setFlag)473 Cc ChannelConfig::setChannelAccessData(const uint8_t chNum,
474                                        const ChannelAccess& chAccessData,
475                                        const uint8_t setFlag)
476 {
477     if (!isValidChannel(chNum))
478     {
479         lg2::debug("Invalid channel");
480         return ccInvalidFieldRequest;
481     }
482 
483     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
484     {
485         lg2::debug("Session-less channel doesn't have access data.");
486         return ccActionNotSupportedForChannel;
487     }
488 
489     if ((setFlag & setAccessMode) &&
490         (!isValidAccessMode(chAccessData.accessMode)))
491     {
492         lg2::debug("Invalid access mode specified");
493         return ccAccessModeNotSupportedForChannel;
494     }
495     if ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))
496     {
497         lg2::debug("Invalid privilege limit specified");
498         return ccInvalidFieldRequest;
499     }
500 
501     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
502         channelLock{*channelMutex};
503 
504     if (checkAndReloadVolatileData() != 0)
505     {
506         return ccUnspecifiedError;
507     }
508 
509     if (setFlag & setAccessMode)
510     {
511         channelData[chNum].chAccess.chVolatileData.accessMode =
512             chAccessData.accessMode;
513     }
514     if (setFlag & setUserAuthEnabled)
515     {
516         channelData[chNum].chAccess.chVolatileData.userAuthDisabled =
517             chAccessData.userAuthDisabled;
518     }
519     if (setFlag & setMsgAuthEnabled)
520     {
521         channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled =
522             chAccessData.perMsgAuthDisabled;
523     }
524     if (setFlag & setAlertingEnabled)
525     {
526         channelData[chNum].chAccess.chVolatileData.alertingDisabled =
527             chAccessData.alertingDisabled;
528     }
529     if (setFlag & setPrivLimit)
530     {
531         channelData[chNum].chAccess.chVolatileData.privLimit =
532             chAccessData.privLimit;
533     }
534 
535     // Write Volatile data to file
536     if (writeChannelVolatileData() != 0)
537     {
538         lg2::debug("Failed to update the channel volatile data");
539         return ccUnspecifiedError;
540     }
541     return ccSuccess;
542 }
543 
getChannelAccessPersistData(const uint8_t chNum,ChannelAccess & chAccessData)544 Cc ChannelConfig::getChannelAccessPersistData(const uint8_t chNum,
545                                               ChannelAccess& chAccessData)
546 {
547     if (!isValidChannel(chNum))
548     {
549         lg2::debug("Invalid channel");
550         return ccInvalidFieldRequest;
551     }
552 
553     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
554     {
555         lg2::debug("Session-less channel doesn't have access data.");
556         return ccActionNotSupportedForChannel;
557     }
558 
559     if (checkAndReloadNVData() != 0)
560     {
561         return ccUnspecifiedError;
562     }
563 
564     std::copy_n(reinterpret_cast<uint8_t*>(
565                     &channelData[chNum].chAccess.chNonVolatileData),
566                 sizeof(channelData[chNum].chAccess.chNonVolatileData),
567                 reinterpret_cast<uint8_t*>(&chAccessData));
568 
569     return ccSuccess;
570 }
571 
setChannelAccessPersistData(const uint8_t chNum,const ChannelAccess & chAccessData,const uint8_t setFlag)572 Cc ChannelConfig::setChannelAccessPersistData(const uint8_t chNum,
573                                               const ChannelAccess& chAccessData,
574                                               const uint8_t setFlag)
575 {
576     if (!isValidChannel(chNum))
577     {
578         lg2::debug("Invalid channel");
579         return ccInvalidFieldRequest;
580     }
581 
582     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
583     {
584         lg2::debug("Session-less channel doesn't have access data.");
585         return ccActionNotSupportedForChannel;
586     }
587 
588     if ((setFlag & setAccessMode) &&
589         (!isValidAccessMode(chAccessData.accessMode)))
590     {
591         lg2::debug("Invalid access mode specified");
592         return ccAccessModeNotSupportedForChannel;
593     }
594     if ((setFlag & setPrivLimit) && (!isValidPrivLimit(chAccessData.privLimit)))
595     {
596         lg2::debug("Invalid privilege limit specified");
597         return ccInvalidFieldRequest;
598     }
599 
600     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
601         channelLock{*channelMutex};
602 
603     if (checkAndReloadNVData() != 0)
604     {
605         return ccUnspecifiedError;
606     }
607 
608     if (setFlag & setAccessMode)
609     {
610         channelData[chNum].chAccess.chNonVolatileData.accessMode =
611             chAccessData.accessMode;
612     }
613     if (setFlag & setUserAuthEnabled)
614     {
615         channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled =
616             chAccessData.userAuthDisabled;
617     }
618     if (setFlag & setMsgAuthEnabled)
619     {
620         channelData[chNum].chAccess.chNonVolatileData.perMsgAuthDisabled =
621             chAccessData.perMsgAuthDisabled;
622     }
623     if (setFlag & setAlertingEnabled)
624     {
625         channelData[chNum].chAccess.chNonVolatileData.alertingDisabled =
626             chAccessData.alertingDisabled;
627     }
628     if (setFlag & setPrivLimit)
629     {
630         // Send Update to network channel config interfaces over dbus
631         std::string privStr = convertToPrivLimitString(chAccessData.privLimit);
632         std::string networkIntfObj = std::string(networkIntfObjectBasePath) +
633                                      "/" + channelData[chNum].chName;
634         try
635         {
636             if (0 != setDbusProperty(networkIntfServiceName, networkIntfObj,
637                                      networkChConfigIntfName,
638                                      privilegePropertyString, privStr))
639             {
640                 lg2::debug("Network interface '{INTERFACE}' does not exist",
641                            "INTERFACE", channelData[chNum].chName);
642                 return ccUnspecifiedError;
643             }
644         }
645         catch (const sdbusplus::exception_t& e)
646         {
647             lg2::error("Exception: Network interface does not exist");
648             return ccInvalidFieldRequest;
649         }
650         signalFlag |= (1 << chNum);
651         channelData[chNum].chAccess.chNonVolatileData.privLimit =
652             chAccessData.privLimit;
653     }
654 
655     // Write persistent data to file
656     if (writeChannelPersistData() != 0)
657     {
658         lg2::debug("Failed to update the presist data file");
659         return ccUnspecifiedError;
660     }
661     return ccSuccess;
662 }
663 
getChannelAuthTypeSupported(const uint8_t chNum,uint8_t & authTypeSupported)664 Cc ChannelConfig::getChannelAuthTypeSupported(const uint8_t chNum,
665                                               uint8_t& authTypeSupported)
666 {
667     if (!isValidChannel(chNum))
668     {
669         lg2::debug("Invalid channel");
670         return ccInvalidFieldRequest;
671     }
672 
673     authTypeSupported = channelData[chNum].chInfo.authTypeSupported;
674     return ccSuccess;
675 }
676 
getChannelEnabledAuthType(const uint8_t chNum,const uint8_t priv,EAuthType & authType)677 Cc ChannelConfig::getChannelEnabledAuthType(
678     const uint8_t chNum, const uint8_t priv, EAuthType& authType)
679 {
680     if (!isValidChannel(chNum))
681     {
682         lg2::debug("Invalid channel");
683         return ccInvalidFieldRequest;
684     }
685 
686     if (getChannelSessionSupport(chNum) == EChannelSessSupported::none)
687     {
688         lg2::debug("Sessionless channel doesn't have access data.");
689         return ccInvalidFieldRequest;
690     }
691 
692     if (!isValidPrivLimit(priv))
693     {
694         lg2::debug("Invalid privilege specified.");
695         return ccInvalidFieldRequest;
696     }
697 
698     // TODO: Hardcoded for now. Need to implement.
699     authType = EAuthType::none;
700 
701     return ccSuccess;
702 }
703 
getUpdatedFileTime(const std::string & fileName)704 std::time_t ChannelConfig::getUpdatedFileTime(const std::string& fileName)
705 {
706     struct stat fileStat;
707     if (stat(fileName.c_str(), &fileStat) != 0)
708     {
709         lg2::debug("Error in getting last updated time stamp");
710         return -EIO;
711     }
712     return fileStat.st_mtime;
713 }
714 
715 EChannelAccessMode
convertToAccessModeIndex(const std::string & mode)716     ChannelConfig::convertToAccessModeIndex(const std::string& mode)
717 {
718     auto iter = std::find(accessModeList.begin(), accessModeList.end(), mode);
719     if (iter == accessModeList.end())
720     {
721         lg2::error("Invalid access mode: {MODE_STR}", "MODE_STR", mode);
722         throw std::invalid_argument("Invalid access mode.");
723     }
724 
725     return static_cast<EChannelAccessMode>(
726         std::distance(accessModeList.begin(), iter));
727 }
728 
convertToAccessModeString(const uint8_t value)729 std::string ChannelConfig::convertToAccessModeString(const uint8_t value)
730 {
731     if (accessModeList.size() <= value)
732     {
733         lg2::error("Invalid access mode: {MODE_IDX}", "MODE_IDX", value);
734         throw std::invalid_argument("Invalid access mode.");
735     }
736 
737     return accessModeList.at(value);
738 }
739 
740 CommandPrivilege
convertToPrivLimitIndex(const std::string & value)741     ChannelConfig::convertToPrivLimitIndex(const std::string& value)
742 {
743     auto iter = std::find(privList.begin(), privList.end(), value);
744     if (iter == privList.end())
745     {
746         lg2::error("Invalid privilege: {PRIV_STR}", "PRIV_STR", value);
747         throw std::invalid_argument("Invalid privilege.");
748     }
749 
750     return static_cast<CommandPrivilege>(std::distance(privList.begin(), iter));
751 }
752 
convertToPrivLimitString(const uint8_t value)753 std::string ChannelConfig::convertToPrivLimitString(const uint8_t value)
754 {
755     if (privList.size() <= value)
756     {
757         lg2::error("Invalid privilege: {PRIV_IDX.", "PRIV_IDX", value);
758         throw std::invalid_argument("Invalid privilege.");
759     }
760 
761     return privList.at(value);
762 }
763 
764 EChannelSessSupported
convertToSessionSupportIndex(const std::string & value)765     ChannelConfig::convertToSessionSupportIndex(const std::string& value)
766 {
767     auto iter =
768         std::find(sessionSupportList.begin(), sessionSupportList.end(), value);
769     if (iter == sessionSupportList.end())
770     {
771         lg2::error("Invalid session supported: {SESS_STR}", "SESS_STR", value);
772         throw std::invalid_argument("Invalid session supported.");
773     }
774 
775     return static_cast<EChannelSessSupported>(
776         std::distance(sessionSupportList.begin(), iter));
777 }
778 
779 EChannelMediumType
convertToMediumTypeIndex(const std::string & value)780     ChannelConfig::convertToMediumTypeIndex(const std::string& value)
781 {
782     std::unordered_map<std::string, EChannelMediumType>::iterator it =
783         mediumTypeMap.find(value);
784     if (it == mediumTypeMap.end())
785     {
786         lg2::error("Invalid medium type: {MEDIUM_STR}", "MEDIUM_STR", value);
787         throw std::invalid_argument("Invalid medium type.");
788     }
789 
790     return static_cast<EChannelMediumType>(it->second);
791 }
792 
793 EChannelProtocolType
convertToProtocolTypeIndex(const std::string & value)794     ChannelConfig::convertToProtocolTypeIndex(const std::string& value)
795 {
796     std::unordered_map<std::string, EChannelProtocolType>::iterator it =
797         protocolTypeMap.find(value);
798     if (it == protocolTypeMap.end())
799     {
800         lg2::error("Invalid protocol type: {PROTO_STR}", "PROTO_STR", value);
801         throw std::invalid_argument("Invalid protocol type.");
802     }
803 
804     return static_cast<EChannelProtocolType>(it->second);
805 }
806 
readJsonFile(const std::string & configFile)807 Json ChannelConfig::readJsonFile(const std::string& configFile)
808 {
809     std::ifstream jsonFile(configFile);
810     if (!jsonFile.good())
811     {
812         lg2::info("JSON file '{FILE_NAME}' not found", "FILE_NAME", configFile);
813         return nullptr;
814     }
815 
816     Json data = nullptr;
817     try
818     {
819         data = Json::parse(jsonFile, nullptr, false);
820     }
821     catch (const Json::parse_error& e)
822     {
823         lg2::debug("Corrupted channel config: {MSG}", "MSG", e.what());
824         throw std::runtime_error("Corrupted channel config file");
825     }
826 
827     return data;
828 }
829 
writeJsonFile(const std::string & configFile,const Json & jsonData)830 int ChannelConfig::writeJsonFile(const std::string& configFile,
831                                  const Json& jsonData)
832 {
833     const std::string tmpFile = configFile + "_tmp";
834     int fd = open(tmpFile.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_SYNC,
835                   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
836     if (fd < 0)
837     {
838         lg2::error("Error in creating json file '{FILE_NAME}'", "FILE_NAME",
839                    tmpFile);
840         return -EIO;
841     }
842     const auto& writeData = jsonData.dump();
843     if (write(fd, writeData.c_str(), writeData.size()) !=
844         static_cast<ssize_t>(writeData.size()))
845     {
846         close(fd);
847         lg2::error("Error in writing configuration file '{FILE_NAME}'",
848                    "FILE_NAME", tmpFile);
849         return -EIO;
850     }
851     close(fd);
852 
853     if (std::rename(tmpFile.c_str(), configFile.c_str()) != 0)
854     {
855         lg2::error("Error in renaming temporary data file '{FILE_NAME}'",
856                    "FILE_NAME", tmpFile);
857         return -EIO;
858     }
859 
860     return 0;
861 }
862 
setDefaultChannelConfig(const uint8_t chNum,const std::string & chName)863 void ChannelConfig::setDefaultChannelConfig(const uint8_t chNum,
864                                             const std::string& chName)
865 {
866     channelData[chNum].chName = chName;
867     channelData[chNum].chID = chNum;
868     channelData[chNum].isChValid = false;
869     channelData[chNum].activeSessCount = 0;
870     channelData[chNum].isManagementNIC = false;
871 
872     channelData[chNum].chInfo.mediumType = defaultMediumType;
873     channelData[chNum].chInfo.protocolType = defaultProtocolType;
874     channelData[chNum].chInfo.sessionSupported = defaultSessionSupported;
875     channelData[chNum].chInfo.isIpmi = defaultIsIpmiState;
876     channelData[chNum].chInfo.authTypeSupported = defaultAuthType;
877 }
878 
getManagementNICID()879 uint8_t ChannelConfig::getManagementNICID()
880 {
881     static bool idFound = false;
882     static uint8_t id = 0;
883 
884     if (idFound)
885     {
886         return id;
887     }
888 
889     for (uint8_t chIdx = 0; chIdx < maxIpmiChannels; chIdx++)
890     {
891         if (channelData[chIdx].isManagementNIC)
892         {
893             id = chIdx;
894             idFound = true;
895             break;
896         }
897     }
898 
899     if (!idFound)
900     {
901         id = static_cast<uint8_t>(EChannelID::chanLan1);
902         idFound = true;
903     }
904     return id;
905 }
906 
loadChannelConfig()907 int ChannelConfig::loadChannelConfig()
908 {
909     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
910         channelLock{*channelMutex};
911 
912     Json data = readJsonFile(channelConfigDefaultFilename);
913     if (data.empty())
914     {
915         lg2::debug("Error in opening IPMI Channel data file");
916         return -EIO;
917     }
918 
919     channelData.fill(ChannelProperties{});
920 
921     // Collect the list of NIC interfaces connected to the BMC. Use this
922     // information to only add IPMI channels that have active NIC interfaces.
923     struct ifaddrs *ifaddr = nullptr, *ifa = nullptr;
924     if (int err = getifaddrs(&ifaddr); err < 0)
925     {
926         lg2::debug("Unable to acquire network interfaces");
927         return -EIO;
928     }
929 
930     for (int chNum = 0; chNum < maxIpmiChannels; chNum++)
931     {
932         try
933         {
934             std::string chKey = std::to_string(chNum);
935             Json jsonChData = data[chKey].get<Json>();
936             if (jsonChData.is_null())
937             {
938                 // If user didn't want to configure specific channel (say
939                 // reserved channel), then load that index with default values.
940                 setDefaultChannelConfig(chNum, defaultChannelName);
941                 continue;
942             }
943             Json jsonChInfo = jsonChData[channelInfoString].get<Json>();
944             if (jsonChInfo.is_null())
945             {
946                 lg2::error("Invalid/corrupted channel config file");
947                 freeifaddrs(ifaddr);
948                 return -EBADMSG;
949             }
950 
951             bool channelFound = true;
952             // Confirm the LAN channel is present
953             if (jsonChInfo[mediumTypeString].get<std::string>() == "lan-802.3")
954             {
955                 channelFound = false;
956                 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
957                 {
958                     if (jsonChData[nameString].get<std::string>() ==
959                         ifa->ifa_name)
960                     {
961                         channelFound = true;
962                         break;
963                     }
964                 }
965             }
966             ChannelProperties& chData = channelData[chNum];
967             chData.chID = chNum;
968             chData.chName = jsonChData[nameString].get<std::string>();
969             chData.isChValid = channelFound &&
970                                jsonChData[isValidString].get<bool>();
971             chData.activeSessCount = jsonChData.value(activeSessionsString, 0);
972             chData.maxTransferSize =
973                 jsonChData.value(maxTransferSizeString, smallChannelSize);
974             if (jsonChData.count(isManagementNIC) != 0)
975             {
976                 chData.isManagementNIC =
977                     jsonChData[isManagementNIC].get<bool>();
978             }
979 
980             std::string medTypeStr =
981                 jsonChInfo[mediumTypeString].get<std::string>();
982             chData.chInfo.mediumType =
983                 static_cast<uint8_t>(convertToMediumTypeIndex(medTypeStr));
984             std::string protoTypeStr =
985                 jsonChInfo[protocolTypeString].get<std::string>();
986             chData.chInfo.protocolType =
987                 static_cast<uint8_t>(convertToProtocolTypeIndex(protoTypeStr));
988             std::string sessStr =
989                 jsonChInfo[sessionSupportedString].get<std::string>();
990             chData.chInfo.sessionSupported =
991                 static_cast<uint8_t>(convertToSessionSupportIndex(sessStr));
992             chData.chInfo.isIpmi = jsonChInfo[isIpmiString].get<bool>();
993             chData.chInfo.authTypeSupported = defaultAuthType;
994         }
995         catch (const Json::exception& e)
996         {
997             lg2::debug("Json Exception caught: {MSG}", "MSG", e.what());
998             freeifaddrs(ifaddr);
999 
1000             return -EBADMSG;
1001         }
1002         catch (const std::invalid_argument& e)
1003         {
1004             lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1005             freeifaddrs(ifaddr);
1006             return -EBADMSG;
1007         }
1008     }
1009     freeifaddrs(ifaddr);
1010 
1011     return 0;
1012 }
1013 
readChannelVolatileData()1014 int ChannelConfig::readChannelVolatileData()
1015 {
1016     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1017         channelLock{*channelMutex};
1018 
1019     Json data = readJsonFile(channelVolatileDataFilename);
1020     if (data == nullptr)
1021     {
1022         lg2::debug("Error in opening IPMI Channel data file");
1023         return -EIO;
1024     }
1025     try
1026     {
1027         // Fill in global structure
1028         for (auto it = data.begin(); it != data.end(); ++it)
1029         {
1030             std::string chKey = it.key();
1031             uint8_t chNum = std::stoi(chKey, nullptr, 10);
1032             if (chNum >= maxIpmiChannels)
1033             {
1034                 lg2::debug("Invalid channel access entry in config file");
1035                 throw std::out_of_range("Out of range - channel number");
1036             }
1037             Json jsonChData = it.value();
1038             if (!jsonChData.is_null())
1039             {
1040                 std::string accModeStr =
1041                     jsonChData[accessModeString].get<std::string>();
1042                 channelData[chNum].chAccess.chVolatileData.accessMode =
1043                     static_cast<uint8_t>(convertToAccessModeIndex(accModeStr));
1044                 channelData[chNum].chAccess.chVolatileData.userAuthDisabled =
1045                     jsonChData[userAuthDisabledString].get<bool>();
1046                 channelData[chNum].chAccess.chVolatileData.perMsgAuthDisabled =
1047                     jsonChData[perMsgAuthDisabledString].get<bool>();
1048                 channelData[chNum].chAccess.chVolatileData.alertingDisabled =
1049                     jsonChData[alertingDisabledString].get<bool>();
1050                 std::string privStr =
1051                     jsonChData[privLimitString].get<std::string>();
1052                 channelData[chNum].chAccess.chVolatileData.privLimit =
1053                     static_cast<uint8_t>(convertToPrivLimitIndex(privStr));
1054             }
1055             else
1056             {
1057                 lg2::error(
1058                     "Invalid/corrupted volatile channel access file '{FILE}'",
1059                     "FILE", channelVolatileDataFilename);
1060                 throw std::runtime_error(
1061                     "Corrupted volatile channel access file");
1062             }
1063         }
1064     }
1065     catch (const Json::exception& e)
1066     {
1067         lg2::debug("Json Exception caught: {MSG}", "MSG", e.what());
1068         throw std::runtime_error("Corrupted volatile channel access file");
1069     }
1070     catch (const std::invalid_argument& e)
1071     {
1072         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1073         throw std::runtime_error("Corrupted volatile channel access file");
1074     }
1075 
1076     // Update the timestamp
1077     voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename);
1078     return 0;
1079 }
1080 
readChannelPersistData()1081 int ChannelConfig::readChannelPersistData()
1082 {
1083     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1084         channelLock{*channelMutex};
1085 
1086     Json data = readJsonFile(channelNvDataFilename);
1087     if (data == nullptr)
1088     {
1089         lg2::debug("Error in opening IPMI Channel data file");
1090         return -EIO;
1091     }
1092     try
1093     {
1094         // Fill in global structure
1095         for (auto it = data.begin(); it != data.end(); ++it)
1096         {
1097             std::string chKey = it.key();
1098             uint8_t chNum = std::stoi(chKey, nullptr, 10);
1099             if (chNum >= maxIpmiChannels)
1100             {
1101                 lg2::debug("Invalid channel access entry in config file");
1102                 throw std::out_of_range("Out of range - channel number");
1103             }
1104             Json jsonChData = it.value();
1105             if (!jsonChData.is_null())
1106             {
1107                 std::string accModeStr =
1108                     jsonChData[accessModeString].get<std::string>();
1109                 channelData[chNum].chAccess.chNonVolatileData.accessMode =
1110                     static_cast<uint8_t>(convertToAccessModeIndex(accModeStr));
1111                 channelData[chNum].chAccess.chNonVolatileData.userAuthDisabled =
1112                     jsonChData[userAuthDisabledString].get<bool>();
1113                 channelData[chNum]
1114                     .chAccess.chNonVolatileData.perMsgAuthDisabled =
1115                     jsonChData[perMsgAuthDisabledString].get<bool>();
1116                 channelData[chNum].chAccess.chNonVolatileData.alertingDisabled =
1117                     jsonChData[alertingDisabledString].get<bool>();
1118                 std::string privStr =
1119                     jsonChData[privLimitString].get<std::string>();
1120                 channelData[chNum].chAccess.chNonVolatileData.privLimit =
1121                     static_cast<uint8_t>(convertToPrivLimitIndex(privStr));
1122             }
1123             else
1124             {
1125                 lg2::error("Invalid/corrupted nv channel access file {FILE}",
1126                            "FILE", channelNvDataFilename);
1127                 throw std::runtime_error("Corrupted nv channel access file");
1128             }
1129         }
1130     }
1131     catch (const Json::exception& e)
1132     {
1133         lg2::debug("Json Exception caught: {MSG}", "MSG", e.what());
1134         throw std::runtime_error("Corrupted nv channel access file");
1135     }
1136     catch (const std::invalid_argument& e)
1137     {
1138         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1139         throw std::runtime_error("Corrupted nv channel access file");
1140     }
1141 
1142     // Update the timestamp
1143     nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename);
1144     return 0;
1145 }
1146 
writeChannelVolatileData()1147 int ChannelConfig::writeChannelVolatileData()
1148 {
1149     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1150         channelLock{*channelMutex};
1151     Json outData;
1152 
1153     try
1154     {
1155         for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
1156         {
1157             if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
1158             {
1159                 Json jsonObj;
1160                 std::string chKey = std::to_string(chNum);
1161                 std::string accModeStr = convertToAccessModeString(
1162                     channelData[chNum].chAccess.chVolatileData.accessMode);
1163                 jsonObj[accessModeString] = accModeStr;
1164                 jsonObj[userAuthDisabledString] =
1165                     channelData[chNum].chAccess.chVolatileData.userAuthDisabled;
1166                 jsonObj[perMsgAuthDisabledString] =
1167                     channelData[chNum]
1168                         .chAccess.chVolatileData.perMsgAuthDisabled;
1169                 jsonObj[alertingDisabledString] =
1170                     channelData[chNum].chAccess.chVolatileData.alertingDisabled;
1171                 std::string privStr = convertToPrivLimitString(
1172                     channelData[chNum].chAccess.chVolatileData.privLimit);
1173                 jsonObj[privLimitString] = privStr;
1174 
1175                 outData[chKey] = jsonObj;
1176             }
1177         }
1178     }
1179     catch (const std::invalid_argument& e)
1180     {
1181         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1182         return -EINVAL;
1183     }
1184 
1185     if (writeJsonFile(channelVolatileDataFilename, outData) != 0)
1186     {
1187         lg2::debug("Error in write JSON data to file");
1188         return -EIO;
1189     }
1190 
1191     // Update the timestamp
1192     voltFileLastUpdatedTime = getUpdatedFileTime(channelVolatileDataFilename);
1193     return 0;
1194 }
1195 
writeChannelPersistData()1196 int ChannelConfig::writeChannelPersistData()
1197 {
1198     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1199         channelLock{*channelMutex};
1200     Json outData;
1201 
1202     try
1203     {
1204         for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
1205         {
1206             if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
1207             {
1208                 Json jsonObj;
1209                 std::string chKey = std::to_string(chNum);
1210                 std::string accModeStr = convertToAccessModeString(
1211                     channelData[chNum].chAccess.chNonVolatileData.accessMode);
1212                 jsonObj[accessModeString] = accModeStr;
1213                 jsonObj[userAuthDisabledString] =
1214                     channelData[chNum]
1215                         .chAccess.chNonVolatileData.userAuthDisabled;
1216                 jsonObj[perMsgAuthDisabledString] =
1217                     channelData[chNum]
1218                         .chAccess.chNonVolatileData.perMsgAuthDisabled;
1219                 jsonObj[alertingDisabledString] =
1220                     channelData[chNum]
1221                         .chAccess.chNonVolatileData.alertingDisabled;
1222                 std::string privStr = convertToPrivLimitString(
1223                     channelData[chNum].chAccess.chNonVolatileData.privLimit);
1224                 jsonObj[privLimitString] = privStr;
1225 
1226                 outData[chKey] = jsonObj;
1227             }
1228         }
1229     }
1230     catch (const std::invalid_argument& e)
1231     {
1232         lg2::error("Corrupted config: {MSG}", "MSG", e.what());
1233         return -EINVAL;
1234     }
1235 
1236     if (writeJsonFile(channelNvDataFilename, outData) != 0)
1237     {
1238         lg2::debug("Error in write JSON data to file");
1239         return -EIO;
1240     }
1241 
1242     // Update the timestamp
1243     nvFileLastUpdatedTime = getUpdatedFileTime(channelNvDataFilename);
1244     return 0;
1245 }
1246 
checkAndReloadNVData()1247 int ChannelConfig::checkAndReloadNVData()
1248 {
1249     std::time_t updateTime = getUpdatedFileTime(channelNvDataFilename);
1250     int ret = 0;
1251     if (updateTime != nvFileLastUpdatedTime || updateTime == -EIO)
1252     {
1253         try
1254         {
1255             ret = readChannelPersistData();
1256         }
1257         catch (const std::exception& e)
1258         {
1259             lg2::error("Exception caught in readChannelPersistData: {MSG}",
1260                        "MSG", e.what());
1261             ret = -EIO;
1262         }
1263     }
1264     return ret;
1265 }
1266 
checkAndReloadVolatileData()1267 int ChannelConfig::checkAndReloadVolatileData()
1268 {
1269     std::time_t updateTime = getUpdatedFileTime(channelVolatileDataFilename);
1270     int ret = 0;
1271     if (updateTime != voltFileLastUpdatedTime || updateTime == -EIO)
1272     {
1273         try
1274         {
1275             ret = readChannelVolatileData();
1276         }
1277         catch (const std::exception& e)
1278         {
1279             lg2::error("Exception caught in readChannelVolatileData: {MSG}",
1280                        "MSG", e.what());
1281             ret = -EIO;
1282         }
1283     }
1284     return ret;
1285 }
1286 
setDbusProperty(const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const DbusVariant & value)1287 int ChannelConfig::setDbusProperty(
1288     const std::string& service, const std::string& objPath,
1289     const std::string& interface, const std::string& property,
1290     const DbusVariant& value)
1291 {
1292     try
1293     {
1294         auto method =
1295             bus.new_method_call(service.c_str(), objPath.c_str(),
1296                                 "org.freedesktop.DBus.Properties", "Set");
1297 
1298         method.append(interface, property, value);
1299 
1300         auto reply = bus.call(method);
1301     }
1302     catch (const sdbusplus::exception_t& e)
1303     {
1304         lg2::debug(
1305             "set-property {SERVICE}:{OBJPATH}/{INTERFACE}.{PROP} failed: {MSG}",
1306             "SERVICE", service, "OBJPATH", objPath, "INTERFACE", interface,
1307             "PROP", property);
1308         return -EIO;
1309     }
1310 
1311     return 0;
1312 }
1313 
syncNetworkChannelConfig()1314 int ChannelConfig::syncNetworkChannelConfig()
1315 {
1316     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1317         channelLock{*channelMutex};
1318     bool isUpdated = false;
1319     for (uint8_t chNum = 0; chNum < maxIpmiChannels; chNum++)
1320     {
1321         if (getChannelSessionSupport(chNum) != EChannelSessSupported::none)
1322         {
1323             std::string intfPrivStr;
1324             uint8_t intfPriv = 0;
1325             try
1326             {
1327                 std::string networkIntfObj =
1328                     std::string(networkIntfObjectBasePath) + "/" +
1329                     channelData[chNum].chName;
1330                 auto propValue = ipmi::getDbusProperty(
1331                     bus, networkIntfServiceName, networkIntfObj,
1332                     networkChConfigIntfName, privilegePropertyString);
1333 
1334                 intfPrivStr = std::get<std::string>(propValue);
1335                 intfPriv =
1336                     static_cast<uint8_t>(convertToPrivLimitIndex(intfPrivStr));
1337             }
1338             catch (const std::bad_variant_access& e)
1339             {
1340                 lg2::debug("Network interface '{INTERFACE}' does not exist",
1341                            "INTERFACE", channelData[chNum].chName);
1342                 continue;
1343             }
1344             catch (const sdbusplus::exception_t& e)
1345             {
1346                 lg2::debug("Network interface '{INTERFACE}' does not exist",
1347                            "INTERFACE", channelData[chNum].chName);
1348                 continue;
1349             }
1350             catch (const std::invalid_argument& e)
1351             {
1352                 lg2::debug("exception: Invalid privilege");
1353                 continue;
1354             }
1355 
1356             if (channelData[chNum].chAccess.chNonVolatileData.privLimit !=
1357                 intfPriv)
1358             {
1359                 isUpdated = true;
1360                 channelData[chNum].chAccess.chNonVolatileData.privLimit =
1361                     intfPriv;
1362                 channelData[chNum].chAccess.chVolatileData.privLimit = intfPriv;
1363             }
1364         }
1365     }
1366 
1367     if (isUpdated)
1368     {
1369         // Write persistent data to file
1370         if (writeChannelPersistData() != 0)
1371         {
1372             lg2::debug("Failed to update the persistent data file");
1373             return -EIO;
1374         }
1375         // Write Volatile data to file
1376         if (writeChannelVolatileData() != 0)
1377         {
1378             lg2::debug("Failed to update the channel volatile data");
1379             return -EIO;
1380         }
1381     }
1382 
1383     return 0;
1384 }
1385 
initChannelPersistData()1386 void ChannelConfig::initChannelPersistData()
1387 {
1388     boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
1389         channelLock{*channelMutex};
1390 
1391     /* Always read the channel config */
1392     if (loadChannelConfig() != 0)
1393     {
1394         lg2::error("Failed to read channel config file");
1395         throw std::ios_base::failure("Failed to load channel configuration");
1396     }
1397 
1398     /* Populate the channel persist data */
1399     if (readChannelPersistData() != 0)
1400     {
1401         // Copy default NV data to RW location
1402         std::filesystem::copy_file(channelAccessDefaultFilename,
1403                                    channelNvDataFilename);
1404 
1405         // Load the channel access NV data
1406         if (readChannelPersistData() != 0)
1407         {
1408             lg2::error("Failed to read channel access NV data");
1409             throw std::ios_base::failure(
1410                 "Failed to read channel access NV configuration");
1411         }
1412     }
1413 
1414     // First check the volatile data file
1415     // If not present, load the default values
1416     if (readChannelVolatileData() != 0)
1417     {
1418         // Copy default volatile data to temporary location
1419         // NV file(channelNvDataFilename) must have created by now.
1420         std::filesystem::copy_file(channelNvDataFilename,
1421                                    channelVolatileDataFilename);
1422 
1423         // Load the channel access volatile data
1424         if (readChannelVolatileData() != 0)
1425         {
1426             lg2::error("Failed to read channel access volatile data");
1427             throw std::ios_base::failure(
1428                 "Failed to read channel access volatile configuration");
1429         }
1430     }
1431 
1432     // Synchronize the channel config(priv) with network channel
1433     // configuration(priv) over dbus
1434     if (syncNetworkChannelConfig() != 0)
1435     {
1436         lg2::error(
1437             "Failed to synchronize data with network channel config over dbus");
1438         throw std::ios_base::failure(
1439             "Failed to synchronize data with network channel config over dbus");
1440     }
1441 
1442     lg2::debug("Successfully completed channel data initialization.");
1443     return;
1444 }
1445 
1446 } // namespace ipmi
1447