1 #include "apphandler.h"
2 
3 #include <arpa/inet.h>
4 #include <mapper.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <systemd/sd-bus.h>
8 
9 #include "host-ipmid/ipmid-api.h"
10 
11 #if __has_include(<filesystem>)
12 #include <filesystem>
13 #elif __has_include(<experimental/filesystem>)
14 #include <experimental/filesystem>
15 namespace std
16 {
17 // splice experimental::filesystem into std
18 namespace filesystem = std::experimental::filesystem;
19 } // namespace std
20 #else
21 #error filesystem not available
22 #endif
23 
24 #include "app/channel.hpp"
25 #include "app/watchdog.hpp"
26 #include "ipmid.hpp"
27 #include "nlohmann/json.hpp"
28 #include "transporthandler.hpp"
29 #include "types.hpp"
30 #include "utils.hpp"
31 
32 #include <array>
33 #include <cstddef>
34 #include <fstream>
35 #include <phosphor-logging/elog-errors.hpp>
36 #include <phosphor-logging/log.hpp>
37 #include <string>
38 #include <vector>
39 #include <xyz/openbmc_project/Common/error.hpp>
40 #include <xyz/openbmc_project/Software/Activation/server.hpp>
41 #include <xyz/openbmc_project/Software/Version/server.hpp>
42 
43 extern sd_bus* bus;
44 
45 constexpr auto bmc_interface = "xyz.openbmc_project.Inventory.Item.Bmc";
46 constexpr auto bmc_guid_interface = "xyz.openbmc_project.Common.UUID";
47 constexpr auto bmc_guid_property = "UUID";
48 constexpr auto bmc_guid_len = 16;
49 
50 static constexpr auto redundancyIntf =
51     "xyz.openbmc_project.Software.RedundancyPriority";
52 static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version";
53 static constexpr auto activationIntf =
54     "xyz.openbmc_project.Software.Activation";
55 static constexpr auto softwareRoot = "/xyz/openbmc_project/software";
56 
57 void register_netfn_app_functions() __attribute__((constructor));
58 
59 using namespace phosphor::logging;
60 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
61 using Version = sdbusplus::xyz::openbmc_project::Software::server::Version;
62 using Activation =
63     sdbusplus::xyz::openbmc_project::Software::server::Activation;
64 namespace fs = std::filesystem;
65 
66 // Offset in get device id command.
67 typedef struct
68 {
69     uint8_t id;
70     uint8_t revision;
71     uint8_t fw[2];
72     uint8_t ipmi_ver;
73     uint8_t addn_dev_support;
74     uint8_t manuf_id[3];
75     uint8_t prod_id[2];
76     uint8_t aux[4];
77 } __attribute__((packed)) ipmi_device_id_t;
78 
79 /**
80  * @brief Returns the Version info from primary s/w object
81  *
82  * Get the Version info from the active s/w object which is having high
83  * "Priority" value(a smaller number is a higher priority) and "Purpose"
84  * is "BMC" from the list of all s/w objects those are implementing
85  * RedundancyPriority interface from the given softwareRoot path.
86  *
87  * @return On success returns the Version info from primary s/w object.
88  *
89  */
90 std::string getActiveSoftwareVersionInfo()
91 {
92     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
93 
94     std::string revision{};
95     auto objectTree =
96         ipmi::getAllDbusObjects(bus, softwareRoot, redundancyIntf, "");
97     if (objectTree.empty())
98     {
99         log<level::ERR>("No Obj has implemented the s/w redundancy interface",
100                         entry("INTERFACE=%s", redundancyIntf));
101         elog<InternalFailure>();
102     }
103 
104     auto objectFound = false;
105     for (auto& softObject : objectTree)
106     {
107         auto service = ipmi::getService(bus, redundancyIntf, softObject.first);
108         auto objValueTree = ipmi::getManagedObjects(bus, service, softwareRoot);
109 
110         auto minPriority = 0xFF;
111         for (const auto& objIter : objValueTree)
112         {
113             try
114             {
115                 auto& intfMap = objIter.second;
116                 auto& redundancyPriorityProps = intfMap.at(redundancyIntf);
117                 auto& versionProps = intfMap.at(versionIntf);
118                 auto& activationProps = intfMap.at(activationIntf);
119                 auto priority =
120                     redundancyPriorityProps.at("Priority").get<uint8_t>();
121                 auto purpose = versionProps.at("Purpose").get<std::string>();
122                 auto activation =
123                     activationProps.at("Activation").get<std::string>();
124                 auto version = versionProps.at("Version").get<std::string>();
125                 if ((Version::convertVersionPurposeFromString(purpose) ==
126                      Version::VersionPurpose::BMC) &&
127                     (Activation::convertActivationsFromString(activation) ==
128                      Activation::Activations::Active))
129                 {
130                     if (priority < minPriority)
131                     {
132                         minPriority = priority;
133                         objectFound = true;
134                         revision = std::move(version);
135                     }
136                 }
137             }
138             catch (const std::exception& e)
139             {
140                 log<level::ERR>(e.what());
141             }
142         }
143     }
144 
145     if (!objectFound)
146     {
147         log<level::ERR>("Could not found an BMC software Object");
148         elog<InternalFailure>();
149     }
150 
151     return revision;
152 }
153 
154 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
155                                          ipmi_request_t request,
156                                          ipmi_response_t response,
157                                          ipmi_data_len_t data_len,
158                                          ipmi_context_t context)
159 {
160     ipmi_ret_t rc = IPMI_CC_OK;
161     *data_len = 0;
162 
163     log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n");
164     return rc;
165 }
166 
167 typedef struct
168 {
169     char major;
170     char minor;
171     uint16_t d[2];
172 } rev_t;
173 
174 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will     */
175 /* return -1 if not in those formats, this routine knows how to parse       */
176 /* version = v0.6-19-gf363f61-dirty                                         */
177 /*            ^ ^ ^^          ^                                             */
178 /*            | |  |----------|-- additional details                        */
179 /*            | |---------------- Minor                                     */
180 /*            |------------------ Major                                     */
181 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715                        */
182 /*                ^ ^  ^^ ^                                                 */
183 /*                | |  |--|---------- additional details                    */
184 /*                | |---------------- Minor                                 */
185 /*                |------------------ Major                                 */
186 /* Additional details : If the option group exists it will force Auxiliary  */
187 /* Firmware Revision Information 4th byte to 1 indicating the build was     */
188 /* derived with additional edits                                            */
189 int convert_version(const char* p, rev_t* rev)
190 {
191     std::string s(p);
192     std::string token;
193     uint16_t commits;
194 
195     auto location = s.find_first_of('v');
196     if (location != std::string::npos)
197     {
198         s = s.substr(location + 1);
199     }
200 
201     if (!s.empty())
202     {
203         location = s.find_first_of(".");
204         if (location != std::string::npos)
205         {
206             rev->major =
207                 static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
208             token = s.substr(location + 1);
209         }
210 
211         if (!token.empty())
212         {
213             location = token.find_first_of(".-");
214             if (location != std::string::npos)
215             {
216                 rev->minor = static_cast<char>(
217                     std::stoi(token.substr(0, location), 0, 16));
218                 token = token.substr(location + 1);
219             }
220         }
221 
222         // Capture the number of commits on top of the minor tag.
223         // I'm using BE format like the ipmi spec asked for
224         location = token.find_first_of(".-");
225         if (!token.empty())
226         {
227             commits = std::stoi(token.substr(0, location), 0, 16);
228             rev->d[0] = (commits >> 8) | (commits << 8);
229 
230             // commit number we skip
231             location = token.find_first_of(".-");
232             if (location != std::string::npos)
233             {
234                 token = token.substr(location + 1);
235             }
236         }
237         else
238         {
239             rev->d[0] = 0;
240         }
241 
242         if (location != std::string::npos)
243         {
244             token = token.substr(location + 1);
245         }
246 
247         // Any value of the optional parameter forces it to 1
248         location = token.find_first_of(".-");
249         if (location != std::string::npos)
250         {
251             token = token.substr(location + 1);
252         }
253         commits = (!token.empty()) ? 1 : 0;
254 
255         // We do this operation to get this displayed in least significant bytes
256         // of ipmitool device id command.
257         rev->d[1] = (commits >> 8) | (commits << 8);
258     }
259 
260     return 0;
261 }
262 
263 ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
264                                   ipmi_request_t request,
265                                   ipmi_response_t response,
266                                   ipmi_data_len_t data_len,
267                                   ipmi_context_t context)
268 {
269     ipmi_ret_t rc = IPMI_CC_OK;
270     int r = -1;
271     rev_t rev = {0};
272     static ipmi_device_id_t dev_id{};
273     static bool dev_id_initialized = false;
274     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
275 
276     // Data length
277     *data_len = sizeof(dev_id);
278 
279     if (!dev_id_initialized)
280     {
281         try
282         {
283             auto version = getActiveSoftwareVersionInfo();
284             r = convert_version(version.c_str(), &rev);
285         }
286         catch (const std::exception& e)
287         {
288             log<level::ERR>(e.what());
289         }
290 
291         if (r >= 0)
292         {
293             // bit7 identifies if the device is available
294             // 0=normal operation
295             // 1=device firmware, SDR update,
296             // or self-initialization in progress.
297             // our SDR is normal working condition, so mask:
298             dev_id.fw[0] = 0x7F & rev.major;
299 
300             rev.minor = (rev.minor > 99 ? 99 : rev.minor);
301             dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
302             memcpy(&dev_id.aux, rev.d, 4);
303         }
304 
305         // IPMI Spec version 2.0
306         dev_id.ipmi_ver = 2;
307 
308         std::ifstream dev_id_file(filename);
309         if (dev_id_file.is_open())
310         {
311             auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
312             if (!data.is_discarded())
313             {
314                 dev_id.id = data.value("id", 0);
315                 dev_id.revision = data.value("revision", 0);
316                 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
317                 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
318                 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
319                 dev_id.manuf_id[0] = data.value("manuf_id", 0);
320                 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
321                 dev_id.prod_id[0] = data.value("prod_id", 0);
322                 dev_id.aux[3] = data.value("aux", 0);
323                 dev_id.aux[2] = data.value("aux", 0) >> 8;
324                 dev_id.aux[1] = data.value("aux", 0) >> 16;
325                 dev_id.aux[0] = data.value("aux", 0) >> 24;
326 
327                 // Don't read the file every time if successful
328                 dev_id_initialized = true;
329             }
330             else
331             {
332                 log<level::ERR>("Device ID JSON parser failure");
333                 rc = IPMI_CC_UNSPECIFIED_ERROR;
334             }
335         }
336         else
337         {
338             log<level::ERR>("Device ID file not found");
339             rc = IPMI_CC_UNSPECIFIED_ERROR;
340         }
341     }
342 
343     // Pack the actual response
344     memcpy(response, &dev_id, *data_len);
345 
346     return rc;
347 }
348 
349 ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
350                                           ipmi_request_t request,
351                                           ipmi_response_t response,
352                                           ipmi_data_len_t data_len,
353                                           ipmi_context_t context)
354 {
355     ipmi_ret_t rc = IPMI_CC_OK;
356 
357     // Byte 2:
358     //  55h - No error.
359     //  56h - Self Test function not implemented in this controller.
360     //  57h - Corrupted or inaccesssible data or devices.
361     //  58h - Fatal hardware error.
362     //  FFh - reserved.
363     //  all other: Device-specific 'internal failure'.
364     //  Byte 3:
365     //      For byte 2 = 55h, 56h, FFh:     00h
366     //      For byte 2 = 58h, all other:    Device-specific
367     //      For byte 2 = 57h:   self-test error bitfield.
368     //      Note: returning 57h does not imply that all test were run.
369     //      [7] 1b = Cannot access SEL device.
370     //      [6] 1b = Cannot access SDR Repository.
371     //      [5] 1b = Cannot access BMC FRU device.
372     //      [4] 1b = IPMB signal lines do not respond.
373     //      [3] 1b = SDR Repository empty.
374     //      [2] 1b = Internal Use Area of BMC FRU corrupted.
375     //      [1] 1b = controller update 'boot block' firmware corrupted.
376     //      [0] 1b = controller operational firmware corrupted.
377 
378     char selftestresults[2] = {0};
379 
380     *data_len = 2;
381 
382     selftestresults[0] = 0x56;
383     selftestresults[1] = 0;
384 
385     memcpy(response, selftestresults, *data_len);
386 
387     return rc;
388 }
389 
390 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
391                                     ipmi_request_t request,
392                                     ipmi_response_t response,
393                                     ipmi_data_len_t data_len,
394                                     ipmi_context_t context)
395 {
396     const char* objname = "/org/openbmc/control/chassis0";
397     const char* iface = "org.freedesktop.DBus.Properties";
398     const char* chassis_iface = "org.openbmc.control.Chassis";
399     sd_bus_message* reply = NULL;
400     sd_bus_error error = SD_BUS_ERROR_NULL;
401     int r = 0;
402     char* uuid = NULL;
403     char* busname = NULL;
404 
405     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
406     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
407     // order
408     // Ex: 0x2332fc2c40e66298e511f2782395a361
409 
410     const int resp_size = 16;     // Response is 16 hex bytes per IPMI Spec
411     uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
412     // Point resp end of array to save in reverse order
413     int resp_loc = resp_size - 1;
414     int i = 0;
415     char* tokptr = NULL;
416     char* id_octet = NULL;
417 
418     // Status code.
419     ipmi_ret_t rc = IPMI_CC_OK;
420     *data_len = 0;
421 
422     // Call Get properties method with the interface and property name
423     r = mapper_get_service(bus, objname, &busname);
424     if (r < 0)
425     {
426         log<level::ERR>("Failed to get bus name", entry("BUS=%s", objname),
427                         entry("ERRNO=0x%X", -r));
428         goto finish;
429     }
430     r = sd_bus_call_method(bus, busname, objname, iface, "Get", &error, &reply,
431                            "ss", chassis_iface, "uuid");
432     if (r < 0)
433     {
434         log<level::ERR>("Failed to call Get Method", entry("ERRNO=0x%X", -r));
435         rc = IPMI_CC_UNSPECIFIED_ERROR;
436         goto finish;
437     }
438 
439     r = sd_bus_message_read(reply, "v", "s", &uuid);
440     if (r < 0 || uuid == NULL)
441     {
442         log<level::ERR>("Failed to get a response", entry("ERRNO=0x%X", -r));
443         rc = IPMI_CC_RESPONSE_ERROR;
444         goto finish;
445     }
446 
447     // Traverse the UUID
448     // Get the UUID octects separated by dash
449     id_octet = strtok_r(uuid, "-", &tokptr);
450 
451     if (id_octet == NULL)
452     {
453         // Error
454         log<level::ERR>("Unexpected UUID format", entry("UUID=%s", uuid));
455         rc = IPMI_CC_RESPONSE_ERROR;
456         goto finish;
457     }
458 
459     while (id_octet != NULL)
460     {
461         // Calculate the octet string size since it varies
462         // Divide it by 2 for the array size since 1 byte is built from 2 chars
463         int tmp_size = strlen(id_octet) / 2;
464 
465         for (i = 0; i < tmp_size; i++)
466         {
467             // Holder of the 2 chars that will become a byte
468             char tmp_array[3] = {0};
469             strncpy(tmp_array, id_octet, 2); // 2 chars at a time
470 
471             int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
472             // Copy end to first
473             memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1);
474             resp_loc--;
475             id_octet += 2; // Finished with the 2 chars, advance
476         }
477         id_octet = strtok_r(NULL, "-", &tokptr); // Get next octet
478     }
479 
480     // Data length
481     *data_len = resp_size;
482 
483     // Pack the actual response
484     memcpy(response, &resp_uuid, *data_len);
485 
486 finish:
487     sd_bus_error_free(&error);
488     reply = sd_bus_message_unref(reply);
489     free(busname);
490 
491     return rc;
492 }
493 
494 ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
495                                         ipmi_request_t request,
496                                         ipmi_response_t response,
497                                         ipmi_data_len_t data_len,
498                                         ipmi_context_t context)
499 {
500 
501     // Status code.
502     ipmi_ret_t rc = IPMI_CC_OK;
503 
504     // Per IPMI 2.0 spec, the input and output buffer size must be the max
505     // buffer size minus one byte to allocate space for the length byte.
506     uint8_t str[] = {0x01, MAX_IPMI_BUFFER - 1, MAX_IPMI_BUFFER - 1, 0x0A,
507                      0x01};
508 
509     // Data length
510     *data_len = sizeof(str);
511 
512     // Pack the actual response
513     memcpy(response, &str, *data_len);
514 
515     return rc;
516 }
517 
518 ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
519                                      ipmi_request_t request,
520                                      ipmi_response_t response,
521                                      ipmi_data_len_t data_len,
522                                      ipmi_context_t context)
523 {
524     // Status code.
525     ipmi_ret_t rc = IPMI_CC_INVALID;
526 
527     *data_len = strlen("THIS IS WILDCARD");
528 
529     // Now pack actual response
530     memcpy(response, "THIS IS WILDCARD", *data_len);
531 
532     return rc;
533 }
534 
535 ipmi_ret_t ipmi_app_get_sys_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
536                                  ipmi_request_t request,
537                                  ipmi_response_t response,
538                                  ipmi_data_len_t data_len,
539                                  ipmi_context_t context)
540 
541 {
542     ipmi_ret_t rc = IPMI_CC_OK;
543     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
544 
545     try
546     {
547         // Get the Inventory object implementing BMC interface
548         ipmi::DbusObjectInfo bmcObject =
549             ipmi::getDbusObject(bus, bmc_interface);
550 
551         // Read UUID property value from bmcObject
552         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
553         auto variant =
554             ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first,
555                                   bmc_guid_interface, bmc_guid_property);
556         std::string guidProp = variant.get<std::string>();
557 
558         // Erase "-" characters from the property value
559         guidProp.erase(std::remove(guidProp.begin(), guidProp.end(), '-'),
560                        guidProp.end());
561 
562         auto guidPropLen = guidProp.length();
563         // Validate UUID data
564         // Divide by 2 as 1 byte is built from 2 chars
565         if ((guidPropLen <= 0) || ((guidPropLen / 2) != bmc_guid_len))
566 
567         {
568             log<level::ERR>("Invalid UUID property value",
569                             entry("UUID_LENGTH=%d", guidPropLen));
570             return IPMI_CC_RESPONSE_ERROR;
571         }
572 
573         // Convert data in RFC4122(MSB) format to LSB format
574         // Get 2 characters at a time as 1 byte is built from 2 chars and
575         // convert to hex byte
576         // TODO: Data printed for GUID command is not as per the
577         // GUID format defined in IPMI specification 2.0 section 20.8
578         // Ticket raised: https://sourceforge.net/p/ipmitool/bugs/501/
579         uint8_t respGuid[bmc_guid_len];
580         for (size_t i = 0, respLoc = (bmc_guid_len - 1);
581              i < guidPropLen && respLoc >= 0; i += 2, respLoc--)
582         {
583             auto value = static_cast<uint8_t>(
584                 std::stoi(guidProp.substr(i, 2).c_str(), NULL, 16));
585             respGuid[respLoc] = value;
586         }
587 
588         *data_len = bmc_guid_len;
589         memcpy(response, &respGuid, bmc_guid_len);
590     }
591     catch (const InternalFailure& e)
592     {
593         log<level::ERR>("Failed in reading BMC UUID property",
594                         entry("INTERFACE=%s", bmc_interface),
595                         entry("PROPERTY_INTERFACE=%s", bmc_guid_interface),
596                         entry("PROPERTY=%s", bmc_guid_property));
597         return IPMI_CC_UNSPECIFIED_ERROR;
598     }
599     return rc;
600 }
601 
602 void register_netfn_app_functions()
603 {
604     // <Get BT Interface Capabilities>
605     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL,
606                            ipmi_app_get_bt_capabilities, PRIVILEGE_USER);
607 
608     // <Wildcard Command>
609     ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL,
610                            ipmi_app_wildcard_handler, PRIVILEGE_USER);
611 
612     // <Reset Watchdog Timer>
613     ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL,
614                            ipmi_app_watchdog_reset, PRIVILEGE_OPERATOR);
615 
616     // <Set Watchdog Timer>
617     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL,
618                            ipmi_app_watchdog_set, PRIVILEGE_OPERATOR);
619 
620     // <Get Watchdog Timer>
621     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_WD, NULL,
622                            ipmi_app_watchdog_get, PRIVILEGE_OPERATOR);
623 
624     // <Get Device ID>
625     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL,
626                            ipmi_app_get_device_id, PRIVILEGE_USER);
627 
628     // <Get Self Test Results>
629     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
630                            ipmi_app_get_self_test_results, PRIVILEGE_USER);
631 
632     // <Get Device GUID>
633     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL,
634                            ipmi_app_get_device_guid, PRIVILEGE_USER);
635 
636     // <Set ACPI Power State>
637     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL,
638                            ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN);
639 
640     // <Get Channel Access>
641     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL,
642                            ipmi_get_channel_access, PRIVILEGE_USER);
643 
644     // <Get Channel Info Command>
645     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL,
646                            ipmi_app_channel_info, PRIVILEGE_USER);
647 
648     // <Get System GUID Command>
649     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SYS_GUID, NULL,
650                            ipmi_app_get_sys_guid, PRIVILEGE_USER);
651 
652     // <Get Channel Cipher Suites Command>
653     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_CIPHER_SUITES, NULL,
654                            getChannelCipherSuites, PRIVILEGE_CALLBACK);
655     return;
656 }
657