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