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