xref: /openbmc/phosphor-networkd/src/util.cpp (revision 3bf1c74e)
1 #include "util.hpp"
2 
3 #include "config_parser.hpp"
4 #include "types.hpp"
5 
6 #include <arpa/inet.h>
7 #include <dirent.h>
8 #include <net/if.h>
9 #include <sys/wait.h>
10 
11 #include <algorithm>
12 #include <cctype>
13 #include <cstdlib>
14 #include <cstring>
15 #include <filesystem>
16 #include <fstream>
17 #include <list>
18 #ifdef SYNC_MAC_FROM_INVENTORY
19 #include <nlohmann/json.hpp>
20 #endif
21 #include <phosphor-logging/elog-errors.hpp>
22 #include <phosphor-logging/log.hpp>
23 #include <stdexcept>
24 #include <stdplus/raw.hpp>
25 #include <string>
26 #include <variant>
27 #include <xyz/openbmc_project/Common/error.hpp>
28 
29 namespace phosphor
30 {
31 namespace network
32 {
33 
34 using namespace phosphor::logging;
35 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
36 namespace fs = std::filesystem;
37 
38 namespace internal
39 {
40 
41 void executeCommandinChildProcess(const char* path, char** args)
42 {
43     using namespace std::string_literals;
44     pid_t pid = fork();
45     int status{};
46 
47     if (pid == 0)
48     {
49         execv(path, args);
50         auto error = errno;
51         // create the command from var args.
52         std::string command = path + " "s;
53 
54         for (int i = 0; args[i]; i++)
55         {
56             command += args[i] + " "s;
57         }
58 
59         log<level::ERR>("Couldn't exceute the command",
60                         entry("ERRNO=%d", error),
61                         entry("CMD=%s", command.c_str()));
62         elog<InternalFailure>();
63     }
64     else if (pid < 0)
65     {
66         auto error = errno;
67         log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
68         elog<InternalFailure>();
69     }
70     else if (pid > 0)
71     {
72         while (waitpid(pid, &status, 0) == -1)
73         {
74             if (errno != EINTR)
75             { // Error other than EINTR
76                 status = -1;
77                 break;
78             }
79         }
80 
81         if (status < 0)
82         {
83             std::string command = path + " "s;
84             for (int i = 0; args[i]; i++)
85             {
86                 command += args[i] + " "s;
87             }
88 
89             log<level::ERR>("Unable to execute the command",
90                             entry("CMD=%s", command.c_str()),
91                             entry("STATUS=%d", status));
92             elog<InternalFailure>();
93         }
94     }
95 }
96 
97 /** @brief Get ignored interfaces from environment */
98 std::string_view getIgnoredInterfacesEnv()
99 {
100     auto r = std::getenv("IGNORED_INTERFACES");
101     if (r == nullptr)
102     {
103         return "";
104     }
105     return r;
106 }
107 
108 /** @brief Parse the comma separated interface names */
109 std::set<std::string_view> parseInterfaces(std::string_view interfaces)
110 {
111     std::set<std::string_view> result;
112     while (true)
113     {
114         auto sep = interfaces.find(',');
115         auto interface = interfaces.substr(0, sep);
116         while (!interface.empty() && std::isspace(interface.front()))
117         {
118             interface.remove_prefix(1);
119         }
120         while (!interface.empty() && std::isspace(interface.back()))
121         {
122             interface.remove_suffix(1);
123         }
124         if (!interface.empty())
125         {
126             result.insert(interface);
127         }
128         if (sep == interfaces.npos)
129         {
130             break;
131         }
132         interfaces = interfaces.substr(sep + 1);
133     }
134     return result;
135 }
136 
137 /** @brief Get the ignored interfaces */
138 const std::set<std::string_view>& getIgnoredInterfaces()
139 {
140     static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv());
141     return ignoredInterfaces;
142 }
143 
144 } // namespace internal
145 
146 uint8_t toCidr(int addressFamily, const std::string& subnetMask)
147 {
148     uint32_t subnet[sizeof(in6_addr) / sizeof(uint32_t)];
149     if (inet_pton(addressFamily, subnetMask.c_str(), &subnet) != 1)
150     {
151         log<level::ERR>("inet_pton failed:",
152                         entry("SUBNETMASK=%s", subnetMask.c_str()));
153         return 0;
154     }
155 
156     static_assert(sizeof(in6_addr) % sizeof(uint32_t) == 0);
157     static_assert(sizeof(in_addr) % sizeof(uint32_t) == 0);
158     auto i = (addressFamily == AF_INET ? sizeof(in_addr) : sizeof(in6_addr)) /
159              sizeof(uint32_t);
160     while (i > 0)
161     {
162         if (subnet[--i] != 0)
163         {
164             auto v = be32toh(subnet[i]);
165             static_assert(sizeof(unsigned) == sizeof(uint32_t));
166             auto trailing = __builtin_ctz(v);
167             auto ret = (i + 1) * 32 - trailing;
168             bool valid = ~v == 0 || 32 == trailing + __builtin_clz(~v);
169             while (i > 0 && (valid = (~subnet[--i] == 0) && valid))
170                 ;
171             if (!valid)
172             {
173                 log<level::ERR>("Invalid netmask",
174                                 entry("SUBNETMASK=%s", subnetMask.c_str()));
175                 return 0;
176             }
177             return ret;
178         }
179     }
180     return 0;
181 }
182 
183 std::string toMask(int addressFamily, uint8_t prefix)
184 {
185     if (addressFamily == AF_INET6)
186     {
187         // TODO:- conversion for v6
188         return "";
189     }
190 
191     if (prefix < 1 || prefix > 30)
192     {
193         log<level::ERR>("Invalid Prefix", entry("PREFIX=%d", prefix));
194         return "";
195     }
196     /* Create the netmask from the number of bits */
197     unsigned long mask = 0;
198     for (auto i = 0; i < prefix; i++)
199     {
200         mask |= 1 << (31 - i);
201     }
202     struct in_addr netmask;
203     netmask.s_addr = htonl(mask);
204     return inet_ntoa(netmask);
205 }
206 
207 InAddrAny addrFromBuf(int addressFamily, std::string_view buf)
208 {
209     if (addressFamily == AF_INET)
210     {
211         struct in_addr ret;
212         if (buf.size() != sizeof(ret))
213         {
214             throw std::runtime_error("Buf not in_addr sized");
215         }
216         memcpy(&ret, buf.data(), sizeof(ret));
217         return ret;
218     }
219     else if (addressFamily == AF_INET6)
220     {
221         struct in6_addr ret;
222         if (buf.size() != sizeof(ret))
223         {
224             throw std::runtime_error("Buf not in6_addr sized");
225         }
226         memcpy(&ret, buf.data(), sizeof(ret));
227         return ret;
228     }
229 
230     throw std::runtime_error("Unsupported address family");
231 }
232 
233 std::string toString(const struct in_addr& addr)
234 {
235     std::string ip(INET_ADDRSTRLEN, '\0');
236     if (inet_ntop(AF_INET, &addr, ip.data(), ip.size()) == nullptr)
237     {
238         throw std::runtime_error("Failed to convert IP4 to string");
239     }
240 
241     ip.resize(strlen(ip.c_str()));
242     return ip;
243 }
244 
245 std::string toString(const struct in6_addr& addr)
246 {
247     std::string ip(INET6_ADDRSTRLEN, '\0');
248     if (inet_ntop(AF_INET6, &addr, ip.data(), ip.size()) == nullptr)
249     {
250         throw std::runtime_error("Failed to convert IP6 to string");
251     }
252 
253     ip.resize(strlen(ip.c_str()));
254     return ip;
255 }
256 
257 std::string toString(const InAddrAny& addr)
258 {
259     if (std::holds_alternative<struct in_addr>(addr))
260     {
261         const auto& v = std::get<struct in_addr>(addr);
262         return toString(v);
263     }
264     else if (std::holds_alternative<struct in6_addr>(addr))
265     {
266         const auto& v = std::get<struct in6_addr>(addr);
267         return toString(v);
268     }
269 
270     throw std::runtime_error("Invalid addr type");
271 }
272 
273 bool isLinkLocalIP(const std::string& address)
274 {
275     return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
276 }
277 
278 bool isValidIP(int addressFamily, const std::string& address)
279 {
280     unsigned char buf[sizeof(struct in6_addr)];
281 
282     return inet_pton(addressFamily, address.c_str(), buf) > 0;
283 }
284 
285 bool isValidPrefix(int addressFamily, uint8_t prefixLength)
286 {
287     if (addressFamily == AF_INET)
288     {
289         if (prefixLength < IPV4_MIN_PREFIX_LENGTH ||
290             prefixLength > IPV4_MAX_PREFIX_LENGTH)
291         {
292             return false;
293         }
294     }
295 
296     if (addressFamily == AF_INET6)
297     {
298         if (prefixLength < IPV4_MIN_PREFIX_LENGTH ||
299             prefixLength > IPV6_MAX_PREFIX_LENGTH)
300         {
301             return false;
302         }
303     }
304 
305     return true;
306 }
307 
308 IntfAddrMap getInterfaceAddrs()
309 {
310     IntfAddrMap intfMap{};
311     struct ifaddrs* ifaddr = nullptr;
312 
313     // attempt to fill struct with ifaddrs
314     if (getifaddrs(&ifaddr) == -1)
315     {
316         auto error = errno;
317         log<level::ERR>("Error occurred during the getifaddrs call",
318                         entry("ERRNO=%s", strerror(error)));
319         elog<InternalFailure>();
320     }
321 
322     AddrPtr ifaddrPtr(ifaddr);
323     ifaddr = nullptr;
324 
325     std::string intfName{};
326 
327     for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
328     {
329         // walk interfaces
330         if (ifa->ifa_addr == nullptr)
331         {
332             continue;
333         }
334 
335         // get only INET interfaces not ipv6
336         if (ifa->ifa_addr->sa_family == AF_INET ||
337             ifa->ifa_addr->sa_family == AF_INET6)
338         {
339             // if loopback, or not running ignore
340             if ((ifa->ifa_flags & IFF_LOOPBACK) ||
341                 !(ifa->ifa_flags & IFF_RUNNING))
342             {
343                 continue;
344             }
345             intfName = ifa->ifa_name;
346             AddrInfo info{};
347             char ip[INET6_ADDRSTRLEN] = {0};
348             char subnetMask[INET6_ADDRSTRLEN] = {0};
349 
350             if (ifa->ifa_addr->sa_family == AF_INET)
351             {
352 
353                 inet_ntop(ifa->ifa_addr->sa_family,
354                           &(((struct sockaddr_in*)(ifa->ifa_addr))->sin_addr),
355                           ip, sizeof(ip));
356 
357                 inet_ntop(
358                     ifa->ifa_addr->sa_family,
359                     &(((struct sockaddr_in*)(ifa->ifa_netmask))->sin_addr),
360                     subnetMask, sizeof(subnetMask));
361             }
362             else
363             {
364                 inet_ntop(ifa->ifa_addr->sa_family,
365                           &(((struct sockaddr_in6*)(ifa->ifa_addr))->sin6_addr),
366                           ip, sizeof(ip));
367 
368                 inet_ntop(
369                     ifa->ifa_addr->sa_family,
370                     &(((struct sockaddr_in6*)(ifa->ifa_netmask))->sin6_addr),
371                     subnetMask, sizeof(subnetMask));
372             }
373 
374             info.addrType = ifa->ifa_addr->sa_family;
375             info.ipaddress = ip;
376             info.prefix = toCidr(info.addrType, std::string(subnetMask));
377             intfMap[intfName].push_back(info);
378         }
379     }
380     return intfMap;
381 }
382 
383 InterfaceList getInterfaces()
384 {
385     InterfaceList interfaces{};
386     struct ifaddrs* ifaddr = nullptr;
387 
388     // attempt to fill struct with ifaddrs
389     if (getifaddrs(&ifaddr) == -1)
390     {
391         auto error = errno;
392         log<level::ERR>("Error occurred during the getifaddrs call",
393                         entry("ERRNO=%d", error));
394         elog<InternalFailure>();
395     }
396 
397     AddrPtr ifaddrPtr(ifaddr);
398     ifaddr = nullptr;
399     const auto& ignoredInterfaces = internal::getIgnoredInterfaces();
400 
401     for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
402     {
403         // walk interfaces
404         // if loopback ignore
405         if (ifa->ifa_flags & IFF_LOOPBACK ||
406             ignoredInterfaces.find(ifa->ifa_name) != ignoredInterfaces.end())
407         {
408             continue;
409         }
410         interfaces.emplace(ifa->ifa_name);
411     }
412     return interfaces;
413 }
414 
415 void deleteInterface(const std::string& intf)
416 {
417     pid_t pid = fork();
418     int status{};
419 
420     if (pid == 0)
421     {
422 
423         execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr);
424         auto error = errno;
425         log<level::ERR>("Couldn't delete the device", entry("ERRNO=%d", error),
426                         entry("INTF=%s", intf.c_str()));
427         elog<InternalFailure>();
428     }
429     else if (pid < 0)
430     {
431         auto error = errno;
432         log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
433         elog<InternalFailure>();
434     }
435     else if (pid > 0)
436     {
437         while (waitpid(pid, &status, 0) == -1)
438         {
439             if (errno != EINTR)
440             { /* Error other than EINTR */
441                 status = -1;
442                 break;
443             }
444         }
445 
446         if (status < 0)
447         {
448             log<level::ERR>("Unable to delete the interface",
449                             entry("INTF=%s", intf.c_str()),
450                             entry("STATUS=%d", status));
451             elog<InternalFailure>();
452         }
453     }
454 }
455 
456 std::optional<std::string> interfaceToUbootEthAddr(const char* intf)
457 {
458     constexpr char ethPrefix[] = "eth";
459     constexpr size_t ethPrefixLen = sizeof(ethPrefix) - 1;
460     if (strncmp(ethPrefix, intf, ethPrefixLen) != 0)
461     {
462         return std::nullopt;
463     }
464     const auto intfSuffix = intf + ethPrefixLen;
465     if (intfSuffix[0] == '\0')
466     {
467         return std::nullopt;
468     }
469     char* end;
470     unsigned long idx = strtoul(intfSuffix, &end, 10);
471     if (end[0] != '\0')
472     {
473         return std::nullopt;
474     }
475     if (idx == 0)
476     {
477         return "ethaddr";
478     }
479     return "eth" + std::to_string(idx) + "addr";
480 }
481 
482 EthernetInterfaceIntf::DHCPConf getDHCPValue(const std::string& confDir,
483                                              const std::string& intf)
484 {
485     EthernetInterfaceIntf::DHCPConf dhcp =
486         EthernetInterfaceIntf::DHCPConf::none;
487     // Get the interface mode value from systemd conf
488     // using namespace std::string_literals;
489     fs::path confPath = confDir;
490     std::string fileName = systemd::config::networkFilePrefix + intf +
491                            systemd::config::networkFileSuffix;
492     confPath /= fileName;
493 
494     auto rc = config::ReturnCode::SUCCESS;
495     config::ValueList values;
496     config::Parser parser(confPath.string());
497 
498     std::tie(rc, values) = parser.getValues("Network", "DHCP");
499     if (rc != config::ReturnCode::SUCCESS)
500     {
501         log<level::DEBUG>("Unable to get the value for Network[DHCP]",
502                           entry("RC=%d", rc));
503         return dhcp;
504     }
505     // There will be only single value for DHCP key.
506     if (values[0] == "true")
507     {
508         dhcp = EthernetInterfaceIntf::DHCPConf::both;
509     }
510     else if (values[0] == "ipv4")
511     {
512         dhcp = EthernetInterfaceIntf::DHCPConf::v4;
513     }
514     else if (values[0] == "ipv6")
515     {
516         dhcp = EthernetInterfaceIntf::DHCPConf::v6;
517     }
518     return dhcp;
519 }
520 
521 namespace mac_address
522 {
523 
524 constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
525 constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
526 constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
527 constexpr auto propIntf = "org.freedesktop.DBus.Properties";
528 constexpr auto methodGet = "Get";
529 constexpr auto configFile = "/usr/share/network/config.json";
530 
531 using DbusObjectPath = std::string;
532 using DbusService = std::string;
533 using DbusInterface = std::string;
534 using ObjectTree =
535     std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>;
536 
537 constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager";
538 constexpr auto invNetworkIntf =
539     "xyz.openbmc_project.Inventory.Item.NetworkInterface";
540 constexpr auto invRoot = "/xyz/openbmc_project/inventory";
541 
542 ether_addr getfromInventory(sdbusplus::bus::bus& bus,
543                             const std::string& intfName)
544 {
545 
546     std::string interfaceName = intfName;
547 
548 #ifdef SYNC_MAC_FROM_INVENTORY
549     // load the config JSON from the Read Only Path
550     std::ifstream in(configFile);
551     nlohmann::json configJson;
552     in >> configJson;
553     interfaceName = configJson[intfName];
554 #endif
555 
556     std::vector<DbusInterface> interfaces;
557     interfaces.emplace_back(invNetworkIntf);
558 
559     auto depth = 0;
560 
561     auto mapperCall =
562         bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
563 
564     mapperCall.append(invRoot, depth, interfaces);
565 
566     auto mapperReply = bus.call(mapperCall);
567     if (mapperReply.is_method_error())
568     {
569         log<level::ERR>("Error in mapper call");
570         elog<InternalFailure>();
571     }
572 
573     ObjectTree objectTree;
574     mapperReply.read(objectTree);
575 
576     if (objectTree.empty())
577     {
578         log<level::ERR>("No Object has implemented the interface",
579                         entry("INTERFACE=%s", invNetworkIntf));
580         elog<InternalFailure>();
581     }
582 
583     DbusObjectPath objPath;
584     DbusService service;
585 
586     if (1 == objectTree.size())
587     {
588         objPath = objectTree.begin()->first;
589         service = objectTree.begin()->second.begin()->first;
590     }
591     else
592     {
593         // If there are more than 2 objects, object path must contain the
594         // interface name
595         for (auto const& object : objectTree)
596         {
597             log<level::INFO>("interface",
598                              entry("INT=%s", interfaceName.c_str()));
599             log<level::INFO>("object", entry("OBJ=%s", object.first.c_str()));
600 
601             if (std::string::npos != object.first.find(interfaceName.c_str()))
602             {
603                 objPath = object.first;
604                 service = object.second.begin()->first;
605                 break;
606             }
607         }
608 
609         if (objPath.empty())
610         {
611             log<level::ERR>("Can't find the object for the interface",
612                             entry("intfName=%s", interfaceName.c_str()));
613             elog<InternalFailure>();
614         }
615     }
616 
617     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
618                                       propIntf, methodGet);
619 
620     method.append(invNetworkIntf, "MACAddress");
621 
622     auto reply = bus.call(method);
623     if (reply.is_method_error())
624     {
625         log<level::ERR>("Failed to get MACAddress",
626                         entry("PATH=%s", objPath.c_str()),
627                         entry("INTERFACE=%s", invNetworkIntf));
628         elog<InternalFailure>();
629     }
630 
631     std::variant<std::string> value;
632     reply.read(value);
633     return fromString(std::get<std::string>(value));
634 }
635 
636 ether_addr fromString(const char* str)
637 {
638     struct ether_addr* mac = ether_aton(str);
639     if (mac == nullptr)
640     {
641         throw std::invalid_argument("Invalid MAC Address");
642     }
643     return *mac;
644 }
645 
646 std::string toString(const ether_addr& mac)
647 {
648     char buf[18] = {0};
649     snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac.ether_addr_octet[0],
650              mac.ether_addr_octet[1], mac.ether_addr_octet[2],
651              mac.ether_addr_octet[3], mac.ether_addr_octet[4],
652              mac.ether_addr_octet[5]);
653     return buf;
654 }
655 
656 bool isEmpty(const ether_addr& mac)
657 {
658     return stdplus::raw::equal(mac, ether_addr{});
659 }
660 
661 bool isMulticast(const ether_addr& mac)
662 {
663     return mac.ether_addr_octet[0] & 0b1;
664 }
665 
666 bool isUnicast(const ether_addr& mac)
667 {
668     return !isEmpty(mac) && !isMulticast(mac);
669 }
670 
671 } // namespace mac_address
672 } // namespace network
673 } // namespace phosphor
674