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