blob: 27c25de11ecbfc6c2e5a70c110e7d83d78e5ec19 [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001#include "phosphor-ipmi-host/utils.hpp"
2
3#include <arpa/inet.h>
4#include <dirent.h>
5#include <net/if.h>
6
7#include <phosphor-logging/elog-errors.hpp>
8#include <phosphor-logging/log.hpp>
9#include <xyz/openbmc_project/Common/error.hpp>
10
11namespace ipmi
12{
13
14using namespace phosphor::logging;
15using namespace sdbusplus::xyz::openbmc_project::Common::Error;
16
17namespace network
18{
19
20/** @brief checks if the given ip is Link Local Ip or not.
21 * @param[in] ipaddress - IPAddress.
22 */
23bool isLinkLocalIP(const std::string& ipaddress);
24
25} // namespace network
26
27// TODO There may be cases where an interface is implemented by multiple
28// objects,to handle such cases we are interested on that object
29// which are on interested busname.
30// Currently mapper doesn't give the readable busname(gives busid) so we can't
31// use busname to find the object,will do later once the support is there.
32
33DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus,
34 const std::string& interface,
35 const std::string& serviceRoot,
36 const std::string& match)
37{
38 std::vector<DbusInterface> interfaces;
39 interfaces.emplace_back(interface);
40
41 auto depth = 0;
42
43 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
44 MAPPER_INTF, "GetSubTree");
45
46 mapperCall.append(serviceRoot, depth, interfaces);
47
48 ObjectTree objectTree;
49 try
50 {
51 auto mapperReply = bus.call(mapperCall);
52 mapperReply.read(objectTree);
53 }
54 catch (sdbusplus::exception_t&)
55 {
56 log<level::ERR>("Error in mapper call");
57 elog<InternalFailure>();
58 }
59
60 if (objectTree.empty())
61 {
62 log<level::ERR>("No Object has implemented the interface",
63 entry("INTERFACE=%s", interface.c_str()));
64 elog<InternalFailure>();
65 }
66
67 DbusObjectInfo objectInfo;
68
69 // if match is empty then return the first object
70 if (match == "")
71 {
72 objectInfo = std::make_pair(
73 objectTree.begin()->first,
74 std::move(objectTree.begin()->second.begin()->first));
75 return objectInfo;
76 }
77
78 // else search the match string in the object path
79 auto objectFound = false;
80 for (auto& object : objectTree)
81 {
82 if (object.first.find(match) != std::string::npos)
83 {
84 objectFound = true;
85 objectInfo = make_pair(object.first,
86 std::move(object.second.begin()->first));
87 break;
88 }
89 }
90
91 if (!objectFound)
92 {
93 log<level::ERR>("Failed to find object which matches",
94 entry("MATCH=%s", match.c_str()));
95 elog<InternalFailure>();
96 }
97 return objectInfo;
98}
99
100DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus,
101 const std::string& interface,
102 const std::string& serviceRoot,
103 const std::string& match)
104{
105 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
106
107 if (objectTree.empty())
108 {
109 log<level::ERR>("No Object has implemented the IP interface",
110 entry("INTERFACE=%s", interface.c_str()));
111 elog<InternalFailure>();
112 }
113
114 DbusObjectInfo objectInfo;
115
116 for (auto& object : objectTree)
117 {
118 auto variant = ipmi::getDbusProperty(
119 bus, object.second.begin()->first, object.first,
120 ipmi::network::IP_INTERFACE, "Address");
121
122 objectInfo = std::make_pair(object.first, object.second.begin()->first);
123
124 // if LinkLocalIP found look for Non-LinkLocalIP
James Feist880b7332018-12-06 11:14:02 -0800125 if (ipmi::network::isLinkLocalIP(
126 sdbusplus::message::variant_ns::get<std::string>(variant)))
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700127 {
128 continue;
129 }
130 else
131 {
132 break;
133 }
134 }
135 return objectInfo;
136}
137
138Value getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
139 const std::string& objPath, const std::string& interface,
140 const std::string& property)
141{
142
143 Value value;
144
145 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
146 PROP_INTF, METHOD_GET);
147
148 method.append(interface, property);
149
150 try
151 {
152 auto reply = bus.call(method);
153 reply.read(value);
154 }
155 catch (sdbusplus::exception_t&)
156 {
157 log<level::ERR>("Failed to get property",
158 entry("PROPERTY=%s", property.c_str()),
159 entry("PATH=%s", objPath.c_str()),
160 entry("INTERFACE=%s", interface.c_str()));
161 elog<InternalFailure>();
162 }
163
164 return value;
165}
166
167PropertyMap getAllDbusProperties(sdbusplus::bus::bus& bus,
168 const std::string& service,
169 const std::string& objPath,
170 const std::string& interface)
171{
172 PropertyMap properties;
173
174 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
175 PROP_INTF, METHOD_GET_ALL);
176
177 method.append(interface);
178
179 try
180 {
181 auto reply = bus.call(method);
182 reply.read(properties);
183 }
184 catch (sdbusplus::exception_t&)
185 {
186 log<level::ERR>("Failed to get all properties",
187 entry("PATH=%s", objPath.c_str()),
188 entry("INTERFACE=%s", interface.c_str()));
189 elog<InternalFailure>();
190 }
191
192 return properties;
193}
194
195ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
196 const std::string& service,
197 const std::string& objPath)
198{
199 ipmi::ObjectValueTree interfaces;
200
201 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
202 "org.freedesktop.DBus.ObjectManager",
203 "GetManagedObjects");
204
205 try
206 {
207 auto reply = bus.call(method);
208 reply.read(interfaces);
209 }
210 catch (sdbusplus::exception_t&)
211 {
212 log<level::ERR>("Failed to get managed objects",
213 entry("PATH=%s", objPath.c_str()));
214 elog<InternalFailure>();
215 }
216
217 return interfaces;
218}
219
220void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
221 const std::string& objPath, const std::string& interface,
222 const std::string& property, const Value& value)
223{
224 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
225 PROP_INTF, METHOD_SET);
226
227 method.append(interface, property, value);
228
229 try
230 {
231 bus.call(method);
232 }
233 catch (sdbusplus::exception_t&)
234 {
235 log<level::ERR>("Failed to set property",
236 entry("PROPERTY=%s", property.c_str()),
237 entry("PATH=%s", objPath.c_str()),
238 entry("INTERFACE=%s", interface.c_str()));
239 elog<InternalFailure>();
240 }
241}
242
243ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
244 intf(intf), path(path), cachedService(std::experimental::nullopt),
245 cachedBusName(std::experimental::nullopt)
246{
247}
248
249ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
250 intf(std::move(intf)), path(std::move(path)),
251 cachedService(std::experimental::nullopt),
252 cachedBusName(std::experimental::nullopt)
253{
254}
255
256const std::string& ServiceCache::getService(sdbusplus::bus::bus& bus)
257{
258 if (!isValid(bus))
259 {
260 cachedBusName = bus.get_unique_name();
261 cachedService = ::ipmi::getService(bus, intf, path);
262 }
263 return cachedService.value();
264}
265
266void ServiceCache::invalidate()
267{
268 cachedBusName = std::experimental::nullopt;
269 cachedService = std::experimental::nullopt;
270}
271
272sdbusplus::message::message
273 ServiceCache::newMethodCall(sdbusplus::bus::bus& bus, const char* intf,
274 const char* method)
275{
276 return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
277 method);
278}
279
280bool ServiceCache::isValid(sdbusplus::bus::bus& bus) const
281{
282 return cachedService && cachedBusName == bus.get_unique_name();
283}
284
285std::string getService(sdbusplus::bus::bus& bus, const std::string& intf,
286 const std::string& path)
287{
288 auto mapperCall =
289 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
290 "/xyz/openbmc_project/object_mapper",
291 "xyz.openbmc_project.ObjectMapper", "GetObject");
292
293 mapperCall.append(path);
294 mapperCall.append(std::vector<std::string>({intf}));
295
296 std::map<std::string, std::vector<std::string>> mapperResponse;
297 try
298 {
299 auto mapperResponseMsg = bus.call(mapperCall);
300 mapperResponseMsg.read(mapperResponse);
301 }
302 catch (sdbusplus::exception_t&)
303 {
304 throw std::runtime_error("ERROR in mapper call");
305 }
306
307 if (mapperResponse.begin() == mapperResponse.end())
308 {
309 throw std::runtime_error("ERROR in reading the mapper response");
310 }
311
312 return mapperResponse.begin()->first;
313}
314
315ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus::bus& bus,
316 const std::string& serviceRoot,
317 const std::string& interface,
318 const std::string& match)
319{
320 std::vector<std::string> interfaces;
321 interfaces.emplace_back(interface);
322
323 auto depth = 0;
324
325 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
326 MAPPER_INTF, "GetSubTree");
327
328 mapperCall.append(serviceRoot, depth, interfaces);
329
330 ObjectTree objectTree;
331 try
332 {
333 auto mapperReply = bus.call(mapperCall);
334 mapperReply.read(objectTree);
335 }
336 catch (sdbusplus::exception_t&)
337 {
338 log<level::ERR>("Error in mapper call",
339 entry("SERVICEROOT=%s", serviceRoot.c_str()),
340 entry("INTERFACE=%s", interface.c_str()));
341
342 elog<InternalFailure>();
343 }
344
345 for (auto it = objectTree.begin(); it != objectTree.end();)
346 {
347 if (it->first.find(match) == std::string::npos)
348 {
349 it = objectTree.erase(it);
350 }
351 else
352 {
353 ++it;
354 }
355 }
356
357 return objectTree;
358}
359
360void deleteAllDbusObjects(sdbusplus::bus::bus& bus,
361 const std::string& serviceRoot,
362 const std::string& interface,
363 const std::string& match)
364{
365 try
366 {
367 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
368
369 for (auto& object : objectTree)
370 {
371 method_no_args::callDbusMethod(bus, object.second.begin()->first,
372 object.first, DELETE_INTERFACE,
373 "Delete");
374 }
375 }
376 catch (sdbusplus::exception::exception& e)
377 {
378 log<level::INFO>("sdbusplus exception - Unable to delete the objects",
379 entry("ERROR=%s", e.what()),
380 entry("INTERFACE=%s", interface.c_str()),
381 entry("SERVICE=%s", serviceRoot.c_str()));
382 }
383}
384
385ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path,
386 InterfaceList&& interfaces)
387{
388 auto convertToString = [](InterfaceList& interfaces) -> std::string {
389 std::string intfStr;
390 for (const auto& intf : interfaces)
391 {
392 intfStr += "," + intf;
393 }
394 return intfStr;
395 };
396
397 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
398 MAPPER_INTF, "GetAncestors");
399 mapperCall.append(path, interfaces);
400
401 ObjectTree objectTree;
402 try
403 {
404 auto mapperReply = bus.call(mapperCall);
405 mapperReply.read(objectTree);
406 }
407 catch (sdbusplus::exception_t&)
408 {
409 log<level::ERR>(
410 "Error in mapper call", entry("PATH=%s", path.c_str()),
411 entry("INTERFACES=%s", convertToString(interfaces).c_str()));
412
413 elog<InternalFailure>();
414 }
415
416 if (objectTree.empty())
417 {
418 log<level::ERR>(
419 "No Object has implemented the interface",
420 entry("PATH=%s", path.c_str()),
421 entry("INTERFACES=%s", convertToString(interfaces).c_str()));
422 elog<InternalFailure>();
423 }
424
425 return objectTree;
426}
427
428namespace method_no_args
429{
430
431void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service,
432 const std::string& objPath, const std::string& interface,
433 const std::string& method)
434
435{
436 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
437 interface.c_str(), method.c_str());
438
439 try
440 {
441 bus.call(busMethod);
442 }
443 catch (sdbusplus::exception_t&)
444 {
445 log<level::ERR>("Failed to execute method",
446 entry("METHOD=%s", method.c_str()),
447 entry("PATH=%s", objPath.c_str()),
448 entry("INTERFACE=%s", interface.c_str()));
449 elog<InternalFailure>();
450 }
451}
452
453} // namespace method_no_args
454
455namespace network
456{
457
458bool isLinkLocalIP(const std::string& address)
459{
460 return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
461}
462
463void createIP(sdbusplus::bus::bus& bus, const std::string& service,
464 const std::string& objPath, const std::string& protocolType,
465 const std::string& ipaddress, uint8_t prefix)
466{
467 std::string gateway = "";
468
469 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
470 IP_CREATE_INTERFACE, "IP");
471
472 busMethod.append(protocolType, ipaddress, prefix, gateway);
473
474 try
475 {
476 bus.call(busMethod);
477 }
478 catch (sdbusplus::exception_t&)
479 {
480 log<level::ERR>("Failed to execute method", entry("METHOD=%s", "IP"),
481 entry("PATH=%s", objPath.c_str()));
482 elog<InternalFailure>();
483 }
484}
485
486void createVLAN(sdbusplus::bus::bus& bus, const std::string& service,
487 const std::string& objPath, const std::string& interfaceName,
488 uint32_t vlanID)
489{
490 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
491 VLAN_CREATE_INTERFACE, "VLAN");
492
493 busMethod.append(interfaceName, vlanID);
494
495 try
496 {
497 bus.call(busMethod);
498 }
499 catch (sdbusplus::exception_t&)
500 {
501 log<level::ERR>("Failed to execute method", entry("METHOD=%s", "VLAN"),
502 entry("PATH=%s", objPath.c_str()));
503 elog<InternalFailure>();
504 }
505}
506
507uint8_t toPrefix(int addressFamily, const std::string& subnetMask)
508{
509 if (addressFamily == AF_INET6)
510 {
511 return 0;
512 }
513
514 uint32_t buff{};
515
516 auto rc = inet_pton(addressFamily, subnetMask.c_str(), &buff);
517 if (rc <= 0)
518 {
519 log<level::ERR>("inet_pton failed:",
520 entry("SUBNETMASK=%s", subnetMask.c_str()));
521 return 0;
522 }
523
524 buff = be32toh(buff);
525 // total no of bits - total no of leading zero == total no of ones
526 if (((sizeof(buff) * 8) - (__builtin_ctz(buff))) ==
527 __builtin_popcount(buff))
528 {
529 return __builtin_popcount(buff);
530 }
531 else
532 {
533 log<level::ERR>("Invalid Mask",
534 entry("SUBNETMASK=%s", subnetMask.c_str()));
535 return 0;
536 }
537}
538
539uint32_t getVLAN(const std::string& path)
540{
541 // Path would be look like
542 // /xyz/openbmc_project/network/eth0_443/ipv4
543
544 uint32_t vlanID = 0;
545 try
546 {
547 auto intfObjectPath = path.substr(0, path.find(IP_TYPE) - 1);
548
549 auto intfName = intfObjectPath.substr(intfObjectPath.rfind("/") + 1);
550
551 auto index = intfName.find("_");
552 if (index != std::string::npos)
553 {
554 auto str = intfName.substr(index + 1);
555 vlanID = std::stoul(str);
556 }
557 }
558 catch (std::exception& e)
559 {
560 log<level::ERR>("Exception occurred during getVLAN",
561 entry("PATH=%s", path.c_str()),
562 entry("EXCEPTION=%s", e.what()));
563 }
564 return vlanID;
565}
566
567} // namespace network
568} // namespace ipmi