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