xref: /openbmc/phosphor-networkd/src/util.cpp (revision f78a415e154bac274e1d07ce8128c69e9d1cd710)
1 #include "config.h"
2 
3 #include "util.hpp"
4 
5 #include "config_parser.hpp"
6 #include "types.hpp"
7 
8 #include <fmt/compile.h>
9 #include <fmt/format.h>
10 #include <sys/wait.h>
11 
12 #include <cctype>
13 #include <phosphor-logging/elog-errors.hpp>
14 #include <phosphor-logging/lg2.hpp>
15 #include <string>
16 #include <string_view>
17 #include <xyz/openbmc_project/Common/error.hpp>
18 
19 namespace phosphor
20 {
21 namespace network
22 {
23 
24 using std::literals::string_view_literals::operator""sv;
25 using namespace phosphor::logging;
26 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
27 
28 namespace internal
29 {
30 
31 void executeCommandinChildProcess(stdplus::const_zstring path, char** args)
32 {
33     using namespace std::string_literals;
34     pid_t pid = fork();
35 
36     if (pid == 0)
37     {
38         execv(path.c_str(), args);
39         exit(255);
40     }
41     else if (pid < 0)
42     {
43         auto error = errno;
44         lg2::error("Error occurred during fork: {ERRNO}", "ERRNO", error);
45         elog<InternalFailure>();
46     }
47     else if (pid > 0)
48     {
49         int status;
50         while (waitpid(pid, &status, 0) == -1)
51         {
52             if (errno != EINTR)
53             {
54                 status = -1;
55                 break;
56             }
57         }
58 
59         if (status < 0)
60         {
61             fmt::memory_buffer buf;
62             fmt::format_to(fmt::appender(buf), "`{}`", path);
63             for (size_t i = 0; args[i] != nullptr; ++i)
64             {
65                 fmt::format_to(fmt::appender(buf), " `{}`", args[i]);
66             }
67             buf.push_back('\0');
68             lg2::error("Unable to execute the command {CMD}: {STATUS}", "CMD",
69                        buf.data(), "STATUS", status);
70             elog<InternalFailure>();
71         }
72     }
73 }
74 
75 /** @brief Get ignored interfaces from environment */
76 std::string_view getIgnoredInterfacesEnv()
77 {
78     auto r = std::getenv("IGNORED_INTERFACES");
79     if (r == nullptr)
80     {
81         return "";
82     }
83     return r;
84 }
85 
86 /** @brief Parse the comma separated interface names */
87 std::unordered_set<std::string_view>
88     parseInterfaces(std::string_view interfaces)
89 {
90     std::unordered_set<std::string_view> result;
91     while (true)
92     {
93         auto sep = interfaces.find(',');
94         auto interface = interfaces.substr(0, sep);
95         while (!interface.empty() && std::isspace(interface.front()))
96         {
97             interface.remove_prefix(1);
98         }
99         while (!interface.empty() && std::isspace(interface.back()))
100         {
101             interface.remove_suffix(1);
102         }
103         if (!interface.empty())
104         {
105             result.insert(interface);
106         }
107         if (sep == interfaces.npos)
108         {
109             break;
110         }
111         interfaces = interfaces.substr(sep + 1);
112     }
113     return result;
114 }
115 
116 /** @brief Get the ignored interfaces */
117 const std::unordered_set<std::string_view>& getIgnoredInterfaces()
118 {
119     static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv());
120     return ignoredInterfaces;
121 }
122 
123 } // namespace internal
124 
125 std::optional<std::string> interfaceToUbootEthAddr(std::string_view intf)
126 {
127     constexpr auto pfx = "eth"sv;
128     if (!intf.starts_with(pfx))
129     {
130         return std::nullopt;
131     }
132     intf.remove_prefix(pfx.size());
133     unsigned idx;
134     try
135     {
136         idx = DecodeInt<unsigned, 10>{}(intf);
137     }
138     catch (...)
139     {
140         return std::nullopt;
141     }
142     if (idx == 0)
143     {
144         return "ethaddr";
145     }
146     return fmt::format(FMT_COMPILE("eth{}addr"), idx);
147 }
148 
149 static std::optional<DHCPVal> systemdParseDHCP(std::string_view str)
150 {
151     if (config::icaseeq(str, "ipv4"))
152     {
153         return DHCPVal{.v4 = true, .v6 = false};
154     }
155     if (config::icaseeq(str, "ipv6"))
156     {
157         return DHCPVal{.v4 = false, .v6 = true};
158     }
159     if (auto b = config::parseBool(str); b)
160     {
161         return DHCPVal{.v4 = *b, .v6 = *b};
162     }
163     return std::nullopt;
164 }
165 
166 inline auto systemdParseLast(const config::Parser& config,
167                              std::string_view section, std::string_view key,
168                              auto&& fun)
169 {
170     if (!config.getFileExists())
171     {
172     }
173     else if (auto str = config.map.getLastValueString(section, key);
174              str == nullptr)
175     {
176         lg2::notice("Unable to get the value of {SECTION}[{KEY}] from {FILE}",
177                     "SECTION", section, "KEY", key, "FILE",
178                     config.getFilename());
179     }
180     else if (auto val = fun(*str); !val)
181     {
182         lg2::notice("Invalid value of {SECTION}[{KEY}] from {FILE}: {VALUE}",
183                     "SECTION", section, "KEY", key, "FILE",
184                     config.getFilename(), "VALUE", *str);
185     }
186     else
187     {
188         return val;
189     }
190     return decltype(fun(std::string_view{}))(std::nullopt);
191 }
192 
193 bool getIPv6AcceptRA(const config::Parser& config)
194 {
195 #ifdef ENABLE_IPV6_ACCEPT_RA
196     constexpr bool def = true;
197 #else
198     constexpr bool def = false;
199 #endif
200     return systemdParseLast(config, "Network", "IPv6AcceptRA",
201                             config::parseBool)
202         .value_or(def);
203 }
204 
205 DHCPVal getDHCPValue(const config::Parser& config)
206 {
207     return systemdParseLast(config, "Network", "DHCP", systemdParseDHCP)
208         .value_or(DHCPVal{.v4 = true, .v6 = true});
209 }
210 
211 bool getDHCPProp(const config::Parser& config, std::string_view key)
212 {
213     return systemdParseLast(config, "DHCP", key, config::parseBool)
214         .value_or(true);
215 }
216 
217 namespace mac_address
218 {
219 
220 bool isEmpty(const ether_addr& mac)
221 {
222     return mac == ether_addr{};
223 }
224 
225 bool isMulticast(const ether_addr& mac)
226 {
227     return mac.ether_addr_octet[0] & 0b1;
228 }
229 
230 bool isUnicast(const ether_addr& mac)
231 {
232     return !isEmpty(mac) && !isMulticast(mac);
233 }
234 
235 } // namespace mac_address
236 } // namespace network
237 } // namespace phosphor
238