blob: 3851e8237dcde823210aadc5bf8023251ee0898a [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
125 if (ipmi::network::isLinkLocalIP(variant.get<std::string>()))
126 {
127 continue;
128 }
129 else
130 {
131 break;
132 }
133 }
134 return objectInfo;
135}
136
137Value getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
138 const std::string& objPath, const std::string& interface,
139 const std::string& property)
140{
141
142 Value value;
143
144 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
145 PROP_INTF, METHOD_GET);
146
147 method.append(interface, property);
148
149 try
150 {
151 auto reply = bus.call(method);
152 reply.read(value);
153 }
154 catch (sdbusplus::exception_t&)
155 {
156 log<level::ERR>("Failed to get property",
157 entry("PROPERTY=%s", property.c_str()),
158 entry("PATH=%s", objPath.c_str()),
159 entry("INTERFACE=%s", interface.c_str()));
160 elog<InternalFailure>();
161 }
162
163 return value;
164}
165
166PropertyMap getAllDbusProperties(sdbusplus::bus::bus& bus,
167 const std::string& service,
168 const std::string& objPath,
169 const std::string& interface)
170{
171 PropertyMap properties;
172
173 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
174 PROP_INTF, METHOD_GET_ALL);
175
176 method.append(interface);
177
178 try
179 {
180 auto reply = bus.call(method);
181 reply.read(properties);
182 }
183 catch (sdbusplus::exception_t&)
184 {
185 log<level::ERR>("Failed to get all properties",
186 entry("PATH=%s", objPath.c_str()),
187 entry("INTERFACE=%s", interface.c_str()));
188 elog<InternalFailure>();
189 }
190
191 return properties;
192}
193
194ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
195 const std::string& service,
196 const std::string& objPath)
197{
198 ipmi::ObjectValueTree interfaces;
199
200 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
201 "org.freedesktop.DBus.ObjectManager",
202 "GetManagedObjects");
203
204 try
205 {
206 auto reply = bus.call(method);
207 reply.read(interfaces);
208 }
209 catch (sdbusplus::exception_t&)
210 {
211 log<level::ERR>("Failed to get managed objects",
212 entry("PATH=%s", objPath.c_str()));
213 elog<InternalFailure>();
214 }
215
216 return interfaces;
217}
218
219void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
220 const std::string& objPath, const std::string& interface,
221 const std::string& property, const Value& value)
222{
223 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
224 PROP_INTF, METHOD_SET);
225
226 method.append(interface, property, value);
227
228 try
229 {
230 bus.call(method);
231 }
232 catch (sdbusplus::exception_t&)
233 {
234 log<level::ERR>("Failed to set property",
235 entry("PROPERTY=%s", property.c_str()),
236 entry("PATH=%s", objPath.c_str()),
237 entry("INTERFACE=%s", interface.c_str()));
238 elog<InternalFailure>();
239 }
240}
241
242ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
243 intf(intf), path(path), cachedService(std::experimental::nullopt),
244 cachedBusName(std::experimental::nullopt)
245{
246}
247
248ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
249 intf(std::move(intf)), path(std::move(path)),
250 cachedService(std::experimental::nullopt),
251 cachedBusName(std::experimental::nullopt)
252{
253}
254
255const std::string& ServiceCache::getService(sdbusplus::bus::bus& bus)
256{
257 if (!isValid(bus))
258 {
259 cachedBusName = bus.get_unique_name();
260 cachedService = ::ipmi::getService(bus, intf, path);
261 }
262 return cachedService.value();
263}
264
265void ServiceCache::invalidate()
266{
267 cachedBusName = std::experimental::nullopt;
268 cachedService = std::experimental::nullopt;
269}
270
271sdbusplus::message::message
272 ServiceCache::newMethodCall(sdbusplus::bus::bus& bus, const char* intf,
273 const char* method)
274{
275 return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
276 method);
277}
278
279bool ServiceCache::isValid(sdbusplus::bus::bus& bus) const
280{
281 return cachedService && cachedBusName == bus.get_unique_name();
282}
283
284std::string getService(sdbusplus::bus::bus& bus, const std::string& intf,
285 const std::string& path)
286{
287 auto mapperCall =
288 bus.new_method_call("xyz.openbmc_project.ObjectMapper",
289 "/xyz/openbmc_project/object_mapper",
290 "xyz.openbmc_project.ObjectMapper", "GetObject");
291
292 mapperCall.append(path);
293 mapperCall.append(std::vector<std::string>({intf}));
294
295 std::map<std::string, std::vector<std::string>> mapperResponse;
296 try
297 {
298 auto mapperResponseMsg = bus.call(mapperCall);
299 mapperResponseMsg.read(mapperResponse);
300 }
301 catch (sdbusplus::exception_t&)
302 {
303 throw std::runtime_error("ERROR in mapper call");
304 }
305
306 if (mapperResponse.begin() == mapperResponse.end())
307 {
308 throw std::runtime_error("ERROR in reading the mapper response");
309 }
310
311 return mapperResponse.begin()->first;
312}
313
314ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus::bus& bus,
315 const std::string& serviceRoot,
316 const std::string& interface,
317 const std::string& match)
318{
319 std::vector<std::string> interfaces;
320 interfaces.emplace_back(interface);
321
322 auto depth = 0;
323
324 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
325 MAPPER_INTF, "GetSubTree");
326
327 mapperCall.append(serviceRoot, depth, interfaces);
328
329 ObjectTree objectTree;
330 try
331 {
332 auto mapperReply = bus.call(mapperCall);
333 mapperReply.read(objectTree);
334 }
335 catch (sdbusplus::exception_t&)
336 {
337 log<level::ERR>("Error in mapper call",
338 entry("SERVICEROOT=%s", serviceRoot.c_str()),
339 entry("INTERFACE=%s", interface.c_str()));
340
341 elog<InternalFailure>();
342 }
343
344 for (auto it = objectTree.begin(); it != objectTree.end();)
345 {
346 if (it->first.find(match) == std::string::npos)
347 {
348 it = objectTree.erase(it);
349 }
350 else
351 {
352 ++it;
353 }
354 }
355
356 return objectTree;
357}
358
359void deleteAllDbusObjects(sdbusplus::bus::bus& bus,
360 const std::string& serviceRoot,
361 const std::string& interface,
362 const std::string& match)
363{
364 try
365 {
366 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
367
368 for (auto& object : objectTree)
369 {
370 method_no_args::callDbusMethod(bus, object.second.begin()->first,
371 object.first, DELETE_INTERFACE,
372 "Delete");
373 }
374 }
375 catch (sdbusplus::exception::exception& e)
376 {
377 log<level::INFO>("sdbusplus exception - Unable to delete the objects",
378 entry("ERROR=%s", e.what()),
379 entry("INTERFACE=%s", interface.c_str()),
380 entry("SERVICE=%s", serviceRoot.c_str()));
381 }
382}
383
384ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path,
385 InterfaceList&& interfaces)
386{
387 auto convertToString = [](InterfaceList& interfaces) -> std::string {
388 std::string intfStr;
389 for (const auto& intf : interfaces)
390 {
391 intfStr += "," + intf;
392 }
393 return intfStr;
394 };
395
396 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
397 MAPPER_INTF, "GetAncestors");
398 mapperCall.append(path, interfaces);
399
400 ObjectTree objectTree;
401 try
402 {
403 auto mapperReply = bus.call(mapperCall);
404 mapperReply.read(objectTree);
405 }
406 catch (sdbusplus::exception_t&)
407 {
408 log<level::ERR>(
409 "Error in mapper call", entry("PATH=%s", path.c_str()),
410 entry("INTERFACES=%s", convertToString(interfaces).c_str()));
411
412 elog<InternalFailure>();
413 }
414
415 if (objectTree.empty())
416 {
417 log<level::ERR>(
418 "No Object has implemented the interface",
419 entry("PATH=%s", path.c_str()),
420 entry("INTERFACES=%s", convertToString(interfaces).c_str()));
421 elog<InternalFailure>();
422 }
423
424 return objectTree;
425}
426
427namespace method_no_args
428{
429
430void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service,
431 const std::string& objPath, const std::string& interface,
432 const std::string& method)
433
434{
435 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
436 interface.c_str(), method.c_str());
437
438 try
439 {
440 bus.call(busMethod);
441 }
442 catch (sdbusplus::exception_t&)
443 {
444 log<level::ERR>("Failed to execute method",
445 entry("METHOD=%s", method.c_str()),
446 entry("PATH=%s", objPath.c_str()),
447 entry("INTERFACE=%s", interface.c_str()));
448 elog<InternalFailure>();
449 }
450}
451
452} // namespace method_no_args
453
454namespace network
455{
456
457bool isLinkLocalIP(const std::string& address)
458{
459 return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
460}
461
462void createIP(sdbusplus::bus::bus& bus, const std::string& service,
463 const std::string& objPath, const std::string& protocolType,
464 const std::string& ipaddress, uint8_t prefix)
465{
466 std::string gateway = "";
467
468 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
469 IP_CREATE_INTERFACE, "IP");
470
471 busMethod.append(protocolType, ipaddress, prefix, gateway);
472
473 try
474 {
475 bus.call(busMethod);
476 }
477 catch (sdbusplus::exception_t&)
478 {
479 log<level::ERR>("Failed to execute method", entry("METHOD=%s", "IP"),
480 entry("PATH=%s", objPath.c_str()));
481 elog<InternalFailure>();
482 }
483}
484
485void createVLAN(sdbusplus::bus::bus& bus, const std::string& service,
486 const std::string& objPath, const std::string& interfaceName,
487 uint32_t vlanID)
488{
489 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
490 VLAN_CREATE_INTERFACE, "VLAN");
491
492 busMethod.append(interfaceName, vlanID);
493
494 try
495 {
496 bus.call(busMethod);
497 }
498 catch (sdbusplus::exception_t&)
499 {
500 log<level::ERR>("Failed to execute method", entry("METHOD=%s", "VLAN"),
501 entry("PATH=%s", objPath.c_str()));
502 elog<InternalFailure>();
503 }
504}
505
506uint8_t toPrefix(int addressFamily, const std::string& subnetMask)
507{
508 if (addressFamily == AF_INET6)
509 {
510 return 0;
511 }
512
513 uint32_t buff{};
514
515 auto rc = inet_pton(addressFamily, subnetMask.c_str(), &buff);
516 if (rc <= 0)
517 {
518 log<level::ERR>("inet_pton failed:",
519 entry("SUBNETMASK=%s", subnetMask.c_str()));
520 return 0;
521 }
522
523 buff = be32toh(buff);
524 // total no of bits - total no of leading zero == total no of ones
525 if (((sizeof(buff) * 8) - (__builtin_ctz(buff))) ==
526 __builtin_popcount(buff))
527 {
528 return __builtin_popcount(buff);
529 }
530 else
531 {
532 log<level::ERR>("Invalid Mask",
533 entry("SUBNETMASK=%s", subnetMask.c_str()));
534 return 0;
535 }
536}
537
538uint32_t getVLAN(const std::string& path)
539{
540 // Path would be look like
541 // /xyz/openbmc_project/network/eth0_443/ipv4
542
543 uint32_t vlanID = 0;
544 try
545 {
546 auto intfObjectPath = path.substr(0, path.find(IP_TYPE) - 1);
547
548 auto intfName = intfObjectPath.substr(intfObjectPath.rfind("/") + 1);
549
550 auto index = intfName.find("_");
551 if (index != std::string::npos)
552 {
553 auto str = intfName.substr(index + 1);
554 vlanID = std::stoul(str);
555 }
556 }
557 catch (std::exception& e)
558 {
559 log<level::ERR>("Exception occurred during getVLAN",
560 entry("PATH=%s", path.c_str()),
561 entry("EXCEPTION=%s", e.what()));
562 }
563 return vlanID;
564}
565
566} // namespace network
567} // namespace ipmi