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