1 #include <arpa/inet.h>
2 #include <dirent.h>
3 #include <fcntl.h>
4 #include <linux/i2c-dev.h>
5 #include <linux/i2c.h>
6 #include <net/if.h>
7 #include <sys/ioctl.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10
11 #include <ipmid/utils.hpp>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 #include <sdbusplus/message/types.hpp>
15 #include <xyz/openbmc_project/Common/error.hpp>
16
17 #include <algorithm>
18 #include <chrono>
19
20 namespace ipmi
21 {
22
23 using namespace phosphor::logging;
24 using namespace sdbusplus::error::xyz::openbmc_project::common;
25
26 namespace network
27 {
28
29 /** @brief checks if the given ip is Link Local Ip or not.
30 * @param[in] ipaddress - IPAddress.
31 */
32 bool isLinkLocalIP(const std::string& ipaddress);
33
34 } // namespace network
35
36 // TODO There may be cases where an interface is implemented by multiple
37 // objects,to handle such cases we are interested on that object
38 // which are on interested busname.
39 // Currently mapper doesn't give the readable busname(gives busid) so we can't
40 // use busname to find the object,will do later once the support is there.
41
getDbusObject(sdbusplus::bus_t & bus,const std::string & interface,const std::string & serviceRoot,const std::string & match)42 DbusObjectInfo getDbusObject(
43 sdbusplus::bus_t& bus, const std::string& interface,
44 const std::string& serviceRoot, const std::string& match)
45 {
46 std::vector<DbusInterface> interfaces;
47 interfaces.emplace_back(interface);
48
49 ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot);
50 if (objectTree.empty())
51 {
52 lg2::error("No Object has implemented the interface: {INTERFACE}",
53 "INTERFACE", interface);
54 elog<InternalFailure>();
55 }
56
57 DbusObjectInfo objectInfo;
58
59 // if match is empty then return the first object
60 if (match == "")
61 {
62 objectInfo = std::make_pair(
63 objectTree.begin()->first,
64 std::move(objectTree.begin()->second.begin()->first));
65 return objectInfo;
66 }
67
68 // else search the match string in the object path
69 auto found = std::find_if(
70 objectTree.begin(), objectTree.end(), [&match](const auto& object) {
71 return (object.first.find(match) != std::string::npos);
72 });
73
74 if (found == objectTree.end())
75 {
76 lg2::error("Failed to find object which matches: {MATCH}", "MATCH",
77 match);
78 elog<InternalFailure>();
79 // elog<> throws an exception.
80 }
81
82 return make_pair(found->first, std::move(found->second.begin()->first));
83 }
84
getDbusProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,std::chrono::microseconds timeout)85 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
86 const std::string& objPath, const std::string& interface,
87 const std::string& property,
88 std::chrono::microseconds timeout)
89 {
90 Value value;
91
92 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
93 PROP_INTF, METHOD_GET);
94
95 method.append(interface, property);
96
97 auto reply = bus.call(method, timeout.count());
98 reply.read(value);
99
100 return value;
101 }
102
getAllDbusProperties(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,std::chrono::microseconds timeout)103 PropertyMap getAllDbusProperties(
104 sdbusplus::bus_t& bus, const std::string& service,
105 const std::string& objPath, const std::string& interface,
106 std::chrono::microseconds timeout)
107 {
108 PropertyMap properties;
109
110 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
111 PROP_INTF, METHOD_GET_ALL);
112
113 method.append(interface);
114
115 auto reply = bus.call(method, timeout.count());
116 reply.read(properties);
117
118 return properties;
119 }
120
getManagedObjects(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath)121 ObjectValueTree getManagedObjects(sdbusplus::bus_t& bus,
122 const std::string& service,
123 const std::string& objPath)
124 {
125 ipmi::ObjectValueTree interfaces;
126
127 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
128 "org.freedesktop.DBus.ObjectManager",
129 "GetManagedObjects");
130 auto reply = bus.call(method);
131 reply.read(interfaces);
132
133 return interfaces;
134 }
135
setDbusProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const Value & value,std::chrono::microseconds timeout)136 void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
137 const std::string& objPath, const std::string& interface,
138 const std::string& property, const Value& value,
139 std::chrono::microseconds timeout)
140 {
141 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
142 PROP_INTF, METHOD_SET);
143
144 method.append(interface, property, value);
145
146 if (!bus.call(method, timeout.count()))
147 {
148 lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
149 "interface: {INTERFACE}",
150 "PROPERTY", property, "PATH", objPath, "INTERFACE",
151 interface);
152 elog<InternalFailure>();
153 }
154 }
155
ServiceCache(const std::string & intf,const std::string & path)156 ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
157 intf(intf), path(path), cachedService(std::nullopt),
158 cachedBusName(std::nullopt)
159 {}
160
ServiceCache(std::string && intf,std::string && path)161 ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
162 intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt),
163 cachedBusName(std::nullopt)
164 {}
165
getService(sdbusplus::bus_t & bus)166 const std::string& ServiceCache::getService(sdbusplus::bus_t& bus)
167 {
168 if (!isValid(bus))
169 {
170 cachedBusName = bus.get_unique_name();
171 cachedService = ::ipmi::getService(bus, intf, path);
172 }
173
174 if (!cachedService)
175 {
176 throw std::runtime_error("Service not cached");
177 }
178 return cachedService.value();
179 }
180
invalidate()181 void ServiceCache::invalidate()
182 {
183 cachedBusName = std::nullopt;
184 cachedService = std::nullopt;
185 }
186
newMethodCall(sdbusplus::bus_t & bus,const char * intf,const char * method)187 sdbusplus::message_t ServiceCache::newMethodCall(
188 sdbusplus::bus_t& bus, const char* intf, const char* method)
189 {
190 return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
191 method);
192 }
193
isValid(sdbusplus::bus_t & bus) const194 bool ServiceCache::isValid(sdbusplus::bus_t& bus) const
195 {
196 return cachedService && cachedBusName == bus.get_unique_name();
197 }
198
getService(sdbusplus::bus_t & bus,const std::string & intf,const std::string & path)199 std::string getService(sdbusplus::bus_t& bus, const std::string& intf,
200 const std::string& path)
201 {
202 auto mapperCall =
203 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
204 "/xyz/openbmc_project/object_mapper",
205 "xyz.openbmc_project.ObjectMapper", "GetObject");
206
207 mapperCall.append(path);
208 mapperCall.append(std::vector<std::string>({intf}));
209
210 auto mapperResponseMsg = bus.call(mapperCall);
211
212 std::map<std::string, std::vector<std::string>> mapperResponse;
213 mapperResponseMsg.read(mapperResponse);
214
215 if (mapperResponse.begin() == mapperResponse.end())
216 {
217 throw std::runtime_error("ERROR in reading the mapper response");
218 }
219
220 return mapperResponse.begin()->first;
221 }
222
getSubTree(sdbusplus::bus_t & bus,const InterfaceList & interfaces,const std::string & subtreePath,int32_t depth)223 ObjectTree getSubTree(sdbusplus::bus_t& bus, const InterfaceList& interfaces,
224 const std::string& subtreePath, int32_t depth)
225 {
226 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
227 MAPPER_INTF, "GetSubTree");
228
229 mapperCall.append(subtreePath, depth, interfaces);
230
231 auto mapperReply = bus.call(mapperCall);
232 ObjectTree objectTree;
233 mapperReply.read(objectTree);
234
235 return objectTree;
236 }
237
getAllDbusObjects(sdbusplus::bus_t & bus,const std::string & serviceRoot,const std::string & interface,const std::string & match)238 ipmi::ObjectTree getAllDbusObjects(
239 sdbusplus::bus_t& bus, const std::string& serviceRoot,
240 const std::string& interface, const std::string& match)
241 {
242 std::vector<std::string> interfaces;
243 interfaces.emplace_back(interface);
244
245 ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot);
246 for (auto it = objectTree.begin(); it != objectTree.end();)
247 {
248 if (it->first.find(match) == std::string::npos)
249 {
250 it = objectTree.erase(it);
251 }
252 else
253 {
254 ++it;
255 }
256 }
257
258 return objectTree;
259 }
260
deleteAllDbusObjects(sdbusplus::bus_t & bus,const std::string & serviceRoot,const std::string & interface,const std::string & match)261 void deleteAllDbusObjects(sdbusplus::bus_t& bus, const std::string& serviceRoot,
262 const std::string& interface,
263 const std::string& match)
264 {
265 try
266 {
267 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
268
269 for (auto& object : objectTree)
270 {
271 method_no_args::callDbusMethod(bus, object.second.begin()->first,
272 object.first, DELETE_INTERFACE,
273 "Delete");
274 }
275 }
276 catch (const sdbusplus::exception_t& e)
277 {
278 lg2::info("sdbusplus exception - Unable to delete the objects, "
279 "service: {SERVICE}, interface: {INTERFACE}, error: {ERROR}",
280 "SERVICE", serviceRoot, "INTERFACE", interface, "ERROR", e);
281 }
282 }
283
convertToString(const InterfaceList & interfaces)284 static inline std::string convertToString(const InterfaceList& interfaces)
285 {
286 std::string intfStr;
287 for (const auto& intf : interfaces)
288 {
289 intfStr += "," + intf;
290 }
291 return intfStr;
292 }
293
getAllAncestors(sdbusplus::bus_t & bus,const std::string & path,InterfaceList && interfaces)294 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path,
295 InterfaceList&& interfaces)
296 {
297 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
298 MAPPER_INTF, "GetAncestors");
299 mapperCall.append(path, interfaces);
300
301 auto mapperReply = bus.call(mapperCall);
302 ObjectTree objectTree;
303 mapperReply.read(objectTree);
304
305 if (objectTree.empty())
306 {
307 lg2::error("No Object has implemented the interface: {INTERFACE}, "
308 "path: {PATH}",
309 "INTERFACE", convertToString(interfaces), "PATH", path);
310 elog<InternalFailure>();
311 }
312
313 return objectTree;
314 }
315
316 namespace method_no_args
317 {
318
callDbusMethod(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & method)319 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service,
320 const std::string& objPath, const std::string& interface,
321 const std::string& method)
322
323 {
324 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
325 interface.c_str(), method.c_str());
326 auto reply = bus.call(busMethod);
327 }
328
329 } // namespace method_no_args
330
331 /********* Begin co-routine yielding alternatives ***************/
332
getService(Context::ptr ctx,const std::string & intf,const std::string & path,std::string & service)333 boost::system::error_code getService(Context::ptr ctx, const std::string& intf,
334 const std::string& path,
335 std::string& service)
336 {
337 boost::system::error_code ec;
338 std::map<std::string, std::vector<std::string>> mapperResponse =
339 ctx->bus->yield_method_call<decltype(mapperResponse)>(
340 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
341 "/xyz/openbmc_project/object_mapper",
342 "xyz.openbmc_project.ObjectMapper", "GetObject", path,
343 std::vector<std::string>({intf}));
344
345 if (!ec)
346 {
347 service = std::move(mapperResponse.begin()->first);
348 }
349 return ec;
350 }
351
getSubTree(Context::ptr ctx,const InterfaceList & interfaces,const std::string & subtreePath,int32_t depth,ObjectTree & objectTree)352 boost::system::error_code getSubTree(
353 Context::ptr ctx, const InterfaceList& interfaces,
354 const std::string& subtreePath, int32_t depth, ObjectTree& objectTree)
355 {
356 boost::system::error_code ec;
357 objectTree = ctx->bus->yield_method_call<ObjectTree>(
358 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree",
359 subtreePath, depth, interfaces);
360
361 return ec;
362 }
363
getDbusObject(Context::ptr ctx,const std::string & interface,const std::string & subtreePath,const std::string & match,DbusObjectInfo & dbusObject)364 boost::system::error_code getDbusObject(
365 Context::ptr ctx, const std::string& interface,
366 const std::string& subtreePath, const std::string& match,
367 DbusObjectInfo& dbusObject)
368 {
369 std::vector<DbusInterface> interfaces;
370 interfaces.emplace_back(interface);
371
372 auto depth = 0;
373 ObjectTree objectTree;
374 boost::system::error_code ec =
375 getSubTree(ctx, interfaces, subtreePath, depth, objectTree);
376
377 if (ec)
378 {
379 return ec;
380 }
381
382 if (objectTree.empty())
383 {
384 lg2::error("No Object has implemented the interface: {INTERFACE}, "
385 "NetFn: {NETFN}, Cmd: {CMD}",
386 "INTERFACE", interface, "NETFN", lg2::hex, ctx->netFn, "CMD",
387 lg2::hex, ctx->cmd);
388 return boost::system::errc::make_error_code(
389 boost::system::errc::no_such_process);
390 }
391
392 // if match is empty then return the first object
393 if (match == "")
394 {
395 dbusObject = std::make_pair(
396 std::move(objectTree.begin()->first),
397 std::move(objectTree.begin()->second.begin()->first));
398 return ec;
399 }
400
401 // else search the match string in the object path
402 auto found = std::find_if(
403 objectTree.begin(), objectTree.end(), [&match](const auto& object) {
404 return (object.first.find(match) != std::string::npos);
405 });
406
407 if (found == objectTree.end())
408 {
409 lg2::error("Failed to find object which matches: {MATCH}, "
410 "NetFn: {NETFN}, Cmd: {CMD}",
411 "MATCH", match, "NETFN", lg2::hex, ctx->netFn, "CMD",
412 lg2::hex, ctx->cmd);
413 // set ec
414 return boost::system::errc::make_error_code(
415 boost::system::errc::no_such_file_or_directory);
416 }
417
418 dbusObject = std::make_pair(std::move(found->first),
419 std::move(found->second.begin()->first));
420 return ec;
421 }
422
getAllDbusProperties(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,PropertyMap & properties)423 boost::system::error_code getAllDbusProperties(
424 Context::ptr ctx, const std::string& service, const std::string& objPath,
425 const std::string& interface, PropertyMap& properties)
426 {
427 boost::system::error_code ec;
428 properties = ctx->bus->yield_method_call<PropertyMap>(
429 ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF,
430 METHOD_GET_ALL, interface);
431 return ec;
432 }
433
setDbusProperty(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const Value & value)434 boost::system::error_code setDbusProperty(
435 Context::ptr ctx, const std::string& service, const std::string& objPath,
436 const std::string& interface, const std::string& property,
437 const Value& value)
438 {
439 boost::system::error_code ec;
440 ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(),
441 objPath.c_str(), PROP_INTF, METHOD_SET,
442 interface, property, value);
443 return ec;
444 }
445
getAllDbusObjects(Context::ptr ctx,const std::string & serviceRoot,const std::string & interface,const std::string & match,ObjectTree & objectTree)446 boost::system::error_code getAllDbusObjects(
447 Context::ptr ctx, const std::string& serviceRoot,
448 const std::string& interface, const std::string& match,
449 ObjectTree& objectTree)
450 {
451 std::vector<std::string> interfaces;
452 interfaces.emplace_back(interface);
453
454 auto depth = 0;
455 boost::system::error_code ec =
456 getSubTree(ctx, interfaces, serviceRoot, depth, objectTree);
457 if (ec)
458 {
459 return ec;
460 }
461
462 for (auto it = objectTree.begin(); it != objectTree.end();)
463 {
464 if (it->first.find(match) == std::string::npos)
465 {
466 it = objectTree.erase(it);
467 }
468 else
469 {
470 ++it;
471 }
472 }
473
474 return ec;
475 }
476
deleteAllDbusObjects(Context::ptr ctx,const std::string & serviceRoot,const std::string & interface,const std::string & match)477 boost::system::error_code deleteAllDbusObjects(
478 Context::ptr ctx, const std::string& serviceRoot,
479 const std::string& interface, const std::string& match)
480 {
481 ObjectTree objectTree;
482 boost::system::error_code ec =
483 getAllDbusObjects(ctx, serviceRoot, interface, match, objectTree);
484 if (ec)
485 {
486 return ec;
487 }
488
489 for (auto& object : objectTree)
490 {
491 ctx->bus->yield_method_call(ctx->yield, ec,
492 object.second.begin()->first, object.first,
493 DELETE_INTERFACE, "Delete");
494 if (ec)
495 {
496 lg2::error("Failed to delete all objects, service: {SERVICE}, "
497 "interface: {INTERFACE}, NetFn: {NETFN}, "
498 "Cmd: {CMD}, Error: {ERROR}",
499 "SERVICE", serviceRoot, "INTERFACE", interface, "NETFN",
500 lg2::hex, ctx->netFn, "CMD", lg2::hex, ctx->cmd, "ERROR",
501 ec.message());
502 break;
503 }
504 }
505 return ec;
506 }
507
getManagedObjects(Context::ptr ctx,const std::string & service,const std::string & objPath,ObjectValueTree & objects)508 boost::system::error_code getManagedObjects(
509 Context::ptr ctx, const std::string& service, const std::string& objPath,
510 ObjectValueTree& objects)
511 {
512 boost::system::error_code ec;
513 objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>(
514 ctx->yield, ec, service.c_str(), objPath.c_str(),
515 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
516 return ec;
517 }
518
getAllAncestors(Context::ptr ctx,const std::string & path,const InterfaceList & interfaces,ObjectTree & objectTree)519 boost::system::error_code getAllAncestors(
520 Context::ptr ctx, const std::string& path, const InterfaceList& interfaces,
521 ObjectTree& objectTree)
522 {
523 std::string interfaceList = convertToString(interfaces);
524
525 boost::system::error_code ec;
526 objectTree = ctx->bus->yield_method_call<ObjectTree>(
527 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
528 "GetAncestors", path, interfaceList);
529
530 if (ec)
531 {
532 return ec;
533 }
534
535 if (objectTree.empty())
536 {
537 lg2::error("No Object has implemented the interface: {INTERFACE}, "
538 "path: {PATH}",
539 "INTERFACE", interfaceList, "PATH", path);
540 elog<InternalFailure>();
541 }
542
543 return ec;
544 }
545
callDbusMethod(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & method)546 boost::system::error_code callDbusMethod(
547 Context::ptr ctx, const std::string& service, const std::string& objPath,
548 const std::string& interface, const std::string& method)
549 {
550 boost::system::error_code ec;
551 ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface,
552 method);
553 return ec;
554 }
555
556 /********* End co-routine yielding alternatives ***************/
557
i2cWriteRead(std::string i2cBus,const uint8_t targetAddr,std::vector<uint8_t> writeData,std::vector<uint8_t> & readBuf)558 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t targetAddr,
559 std::vector<uint8_t> writeData,
560 std::vector<uint8_t>& readBuf)
561 {
562 // Open the i2c device, for low-level combined data write/read
563 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
564 if (i2cDev < 0)
565 {
566 lg2::error("Failed to open i2c bus: {BUS}", "BUS", i2cBus);
567 return ipmi::ccInvalidFieldRequest;
568 }
569
570 const size_t writeCount = writeData.size();
571 const size_t readCount = readBuf.size();
572 int msgCount = 0;
573 i2c_msg i2cmsg[2] = {};
574 if (writeCount)
575 {
576 // Data will be writtern to the target address
577 i2cmsg[msgCount].addr = targetAddr;
578 i2cmsg[msgCount].flags = 0x00;
579 i2cmsg[msgCount].len = writeCount;
580 i2cmsg[msgCount].buf = writeData.data();
581 msgCount++;
582 }
583 if (readCount)
584 {
585 // Data will be read into the buffer from the target address
586 i2cmsg[msgCount].addr = targetAddr;
587 i2cmsg[msgCount].flags = I2C_M_RD;
588 i2cmsg[msgCount].len = readCount;
589 i2cmsg[msgCount].buf = readBuf.data();
590 msgCount++;
591 }
592
593 i2c_rdwr_ioctl_data msgReadWrite = {};
594 msgReadWrite.msgs = i2cmsg;
595 msgReadWrite.nmsgs = msgCount;
596
597 // Perform the combined write/read
598 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
599 ::close(i2cDev);
600
601 if (ret < 0)
602 {
603 lg2::error("I2C WR Failed! {RET}", "RET", ret);
604 return ipmi::ccUnspecifiedError;
605 }
606 if (readCount)
607 {
608 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
609 }
610
611 return ipmi::ccSuccess;
612 }
613
614 } // namespace ipmi
615