blob: f6880c3265ab19ba66043442cf6c7f4ff2fa912f [file] [log] [blame]
William A. Kennington III324d2602022-08-18 18:32:56 -07001#include "config.h"
2
Ratan Gupta3681a502017-06-17 19:20:04 +05303#include "util.hpp"
Ratan Gupta11cef802017-05-29 08:41:48 +05304
Patrick Venture189d44e2018-07-09 12:30:59 -07005#include "config_parser.hpp"
6#include "types.hpp"
Ratan Gupta8804feb2017-05-25 10:49:57 +05307
William A. Kennington III1c776022022-01-05 14:12:16 -08008#include <fmt/compile.h>
9#include <fmt/format.h>
Ratan Guptabc886292017-07-25 18:29:57 +053010#include <sys/wait.h>
Ratan Gupta3681a502017-06-17 19:20:04 +053011
Lei YU307554e2021-03-18 14:56:50 +080012#include <cctype>
William A. Kennington III69f45542022-09-24 23:28:14 -070013#include <charconv>
Manojkiran Edacc099a82020-05-11 14:25:16 +053014#include <fstream>
William A. Kennington IIIde433b72021-05-17 22:59:28 -070015#ifdef SYNC_MAC_FROM_INVENTORY
Manojkiran Edacc099a82020-05-11 14:25:16 +053016#include <nlohmann/json.hpp>
William A. Kennington IIIde433b72021-05-17 22:59:28 -070017#endif
Patrick Venture189d44e2018-07-09 12:30:59 -070018#include <phosphor-logging/elog-errors.hpp>
19#include <phosphor-logging/log.hpp>
20#include <string>
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -070021#include <string_view>
William A. Kennington III1137a972019-04-20 20:49:58 -070022#include <variant>
Patrick Venture189d44e2018-07-09 12:30:59 -070023#include <xyz/openbmc_project/Common/error.hpp>
Ratan Gupta8804feb2017-05-25 10:49:57 +053024
25namespace phosphor
26{
27namespace network
28{
Ratan Guptabc886292017-07-25 18:29:57 +053029
William A. Kennington III69f45542022-09-24 23:28:14 -070030using std::literals::string_view_literals::operator""sv;
Ratan Gupta8804feb2017-05-25 10:49:57 +053031using namespace phosphor::logging;
Ratan Gupta11cef802017-05-29 08:41:48 +053032using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Ratan Gupta8804feb2017-05-25 10:49:57 +053033
Lei YU3894ce72021-03-18 14:53:42 +080034namespace internal
35{
36
William A. Kennington III69f45542022-09-24 23:28:14 -070037void executeCommandinChildProcess(stdplus::const_zstring path, char** args)
Lei YU3894ce72021-03-18 14:53:42 +080038{
39 using namespace std::string_literals;
40 pid_t pid = fork();
Lei YU3894ce72021-03-18 14:53:42 +080041
42 if (pid == 0)
43 {
William A. Kennington III69f45542022-09-24 23:28:14 -070044 execv(path.c_str(), args);
45 exit(255);
Lei YU3894ce72021-03-18 14:53:42 +080046 }
47 else if (pid < 0)
48 {
49 auto error = errno;
50 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
51 elog<InternalFailure>();
52 }
53 else if (pid > 0)
54 {
William A. Kennington III69f45542022-09-24 23:28:14 -070055 int status;
Lei YU3894ce72021-03-18 14:53:42 +080056 while (waitpid(pid, &status, 0) == -1)
57 {
58 if (errno != EINTR)
William A. Kennington III69f45542022-09-24 23:28:14 -070059 {
Lei YU3894ce72021-03-18 14:53:42 +080060 status = -1;
61 break;
62 }
63 }
64
65 if (status < 0)
66 {
William A. Kennington III69f45542022-09-24 23:28:14 -070067 fmt::memory_buffer buf;
68 fmt::format_to(fmt::appender(buf), "`{}`", path);
69 for (size_t i = 0; args[i] != nullptr; ++i)
Lei YU3894ce72021-03-18 14:53:42 +080070 {
William A. Kennington III69f45542022-09-24 23:28:14 -070071 fmt::format_to(fmt::appender(buf), " `{}`", args[i]);
Lei YU3894ce72021-03-18 14:53:42 +080072 }
William A. Kennington III69f45542022-09-24 23:28:14 -070073 buf.push_back('\0');
Lei YU3894ce72021-03-18 14:53:42 +080074 log<level::ERR>("Unable to execute the command",
William A. Kennington III69f45542022-09-24 23:28:14 -070075 entry("CMD=%s", buf.data()),
Lei YU3894ce72021-03-18 14:53:42 +080076 entry("STATUS=%d", status));
77 elog<InternalFailure>();
78 }
79 }
80}
81
Lei YU307554e2021-03-18 14:56:50 +080082/** @brief Get ignored interfaces from environment */
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -070083std::string_view getIgnoredInterfacesEnv()
Lei YU307554e2021-03-18 14:56:50 +080084{
85 auto r = std::getenv("IGNORED_INTERFACES");
86 if (r == nullptr)
87 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -070088 return "";
Lei YU307554e2021-03-18 14:56:50 +080089 }
90 return r;
91}
92
93/** @brief Parse the comma separated interface names */
William A. Kennington III95530ec2022-08-19 01:44:39 -070094std::unordered_set<std::string_view>
95 parseInterfaces(std::string_view interfaces)
Lei YU307554e2021-03-18 14:56:50 +080096{
William A. Kennington III95530ec2022-08-19 01:44:39 -070097 std::unordered_set<std::string_view> result;
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -070098 while (true)
Lei YU307554e2021-03-18 14:56:50 +080099 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700100 auto sep = interfaces.find(',');
101 auto interface = interfaces.substr(0, sep);
102 while (!interface.empty() && std::isspace(interface.front()))
Lei YU307554e2021-03-18 14:56:50 +0800103 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700104 interface.remove_prefix(1);
Lei YU307554e2021-03-18 14:56:50 +0800105 }
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700106 while (!interface.empty() && std::isspace(interface.back()))
Lei YU307554e2021-03-18 14:56:50 +0800107 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700108 interface.remove_suffix(1);
Lei YU307554e2021-03-18 14:56:50 +0800109 }
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700110 if (!interface.empty())
111 {
112 result.insert(interface);
113 }
114 if (sep == interfaces.npos)
115 {
116 break;
117 }
118 interfaces = interfaces.substr(sep + 1);
Lei YU307554e2021-03-18 14:56:50 +0800119 }
120 return result;
121}
122
123/** @brief Get the ignored interfaces */
William A. Kennington III95530ec2022-08-19 01:44:39 -0700124const std::unordered_set<std::string_view>& getIgnoredInterfaces()
Lei YU307554e2021-03-18 14:56:50 +0800125{
126 static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv());
127 return ignoredInterfaces;
128}
129
Lei YU3894ce72021-03-18 14:53:42 +0800130} // namespace internal
Ratan Gupta8804feb2017-05-25 10:49:57 +0530131
William A. Kennington III69f45542022-09-24 23:28:14 -0700132void deleteInterface(stdplus::const_zstring intf)
Ratan Guptabc886292017-07-25 18:29:57 +0530133{
134 pid_t pid = fork();
Gunnar Mills57d9c502018-09-14 14:42:34 -0500135 int status{};
Ratan Guptabc886292017-07-25 18:29:57 +0530136
137 if (pid == 0)
138 {
139
140 execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr);
141 auto error = errno;
Gunnar Mills57d9c502018-09-14 14:42:34 -0500142 log<level::ERR>("Couldn't delete the device", entry("ERRNO=%d", error),
Ratan Guptabc886292017-07-25 18:29:57 +0530143 entry("INTF=%s", intf.c_str()));
144 elog<InternalFailure>();
145 }
146 else if (pid < 0)
147 {
148 auto error = errno;
Gunnar Mills57d9c502018-09-14 14:42:34 -0500149 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
Ratan Guptabc886292017-07-25 18:29:57 +0530150 elog<InternalFailure>();
151 }
152 else if (pid > 0)
153 {
154 while (waitpid(pid, &status, 0) == -1)
155 {
156 if (errno != EINTR)
Gunnar Mills57d9c502018-09-14 14:42:34 -0500157 { /* Error other than EINTR */
Ratan Guptabc886292017-07-25 18:29:57 +0530158 status = -1;
159 break;
160 }
161 }
162
Gunnar Mills57d9c502018-09-14 14:42:34 -0500163 if (status < 0)
Ratan Guptabc886292017-07-25 18:29:57 +0530164 {
165 log<level::ERR>("Unable to delete the interface",
Joseph Reynolds02653ca2018-05-10 15:55:09 -0500166 entry("INTF=%s", intf.c_str()),
167 entry("STATUS=%d", status));
Ratan Guptabc886292017-07-25 18:29:57 +0530168 elog<InternalFailure>();
169 }
170 }
171}
172
William A. Kennington III69f45542022-09-24 23:28:14 -0700173std::optional<std::string> interfaceToUbootEthAddr(std::string_view intf)
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700174{
William A. Kennington III69f45542022-09-24 23:28:14 -0700175 constexpr auto pfx = "eth"sv;
176 if (!intf.starts_with(pfx))
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700177 {
178 return std::nullopt;
179 }
William A. Kennington III69f45542022-09-24 23:28:14 -0700180 intf.remove_prefix(pfx.size());
181 auto last = intf.data() + intf.size();
182 unsigned long idx;
183 auto res = std::from_chars(intf.data(), last, idx);
184 if (res.ec != std::errc() || res.ptr != last)
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700185 {
186 return std::nullopt;
187 }
188 if (idx == 0)
189 {
190 return "ethaddr";
191 }
William A. Kennington III69f45542022-09-24 23:28:14 -0700192 return fmt::format(FMT_COMPILE("eth{}addr"), idx);
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700193}
194
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700195static std::optional<DHCPVal> systemdParseDHCP(std::string_view str)
196{
197 if (config::icaseeq(str, "ipv4"))
198 {
199 return DHCPVal{.v4 = true, .v6 = false};
200 }
201 if (config::icaseeq(str, "ipv6"))
202 {
203 return DHCPVal{.v4 = false, .v6 = true};
204 }
205 if (auto b = config::parseBool(str); b)
206 {
207 return DHCPVal{.v4 = *b, .v6 = *b};
208 }
209 return std::nullopt;
210}
211
212inline auto systemdParseLast(const config::Parser& config,
213 std::string_view section, std::string_view key,
214 auto&& fun)
215{
216 if (auto str = config.map.getLastValueString(section, key); str == nullptr)
217 {
218 auto err = fmt::format("Unable to get the value of {}[{}] from {}",
219 section, key, config.getFilename().native());
220 log<level::NOTICE>(err.c_str(),
221 entry("FILE=%s", config.getFilename().c_str()));
222 }
223 else if (auto val = fun(*str); !val)
224 {
225 auto err = fmt::format("Invalid value of {}[{}] from {}: {}", section,
226 key, config.getFilename().native(), *str);
227 log<level::NOTICE>(err.c_str(), entry("VALUE=%s", str->c_str()),
228 entry("FILE=%s", config.getFilename().c_str()));
229 }
230 else
231 {
232 return val;
233 }
234 return decltype(fun(std::string_view{}))(std::nullopt);
235}
236
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700237bool getIPv6AcceptRA(const config::Parser& config)
Ratan Gupta56187e72017-08-13 09:40:14 +0530238{
William A. Kennington III324d2602022-08-18 18:32:56 -0700239#ifdef ENABLE_IPV6_ACCEPT_RA
240 constexpr bool def = true;
241#else
242 constexpr bool def = false;
243#endif
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700244 return systemdParseLast(config, "Network", "IPv6AcceptRA",
245 config::parseBool)
246 .value_or(def);
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700247}
248
William A. Kennington III8060c0d2022-08-18 19:19:34 -0700249DHCPVal getDHCPValue(const config::Parser& config)
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700250{
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700251 return systemdParseLast(config, "Network", "DHCP", systemdParseDHCP)
252 .value_or(DHCPVal{.v4 = true, .v6 = true});
253}
William A. Kennington III324d2602022-08-18 18:32:56 -0700254
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700255bool getDHCPProp(const config::Parser& config, std::string_view key)
256{
257 return systemdParseLast(config, "DHCP", key, config::parseBool)
258 .value_or(true);
Ratan Gupta56187e72017-08-13 09:40:14 +0530259}
260
Ratan Guptabd303b12017-08-18 17:10:07 +0530261namespace mac_address
262{
263
264constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
265constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
266constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
267constexpr auto propIntf = "org.freedesktop.DBus.Properties";
268constexpr auto methodGet = "Get";
Manojkiran Edacc099a82020-05-11 14:25:16 +0530269constexpr auto configFile = "/usr/share/network/config.json";
Ratan Guptabd303b12017-08-18 17:10:07 +0530270
271using DbusObjectPath = std::string;
272using DbusService = std::string;
273using DbusInterface = std::string;
Gunnar Mills57d9c502018-09-14 14:42:34 -0500274using ObjectTree =
275 std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>;
Ratan Guptabd303b12017-08-18 17:10:07 +0530276
277constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager";
278constexpr auto invNetworkIntf =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500279 "xyz.openbmc_project.Inventory.Item.NetworkInterface";
Ratan Guptabd303b12017-08-18 17:10:07 +0530280constexpr auto invRoot = "/xyz/openbmc_project/inventory";
281
Patrick Williamsc38b0712022-07-22 19:26:54 -0500282ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName)
Ratan Guptabd303b12017-08-18 17:10:07 +0530283{
Manojkiran Edacc099a82020-05-11 14:25:16 +0530284
285 std::string interfaceName = intfName;
286
William A. Kennington III6f39c5e2021-05-13 18:39:23 -0700287#ifdef SYNC_MAC_FROM_INVENTORY
Manojkiran Edacc099a82020-05-11 14:25:16 +0530288 // load the config JSON from the Read Only Path
289 std::ifstream in(configFile);
290 nlohmann::json configJson;
291 in >> configJson;
292 interfaceName = configJson[intfName];
293#endif
294
Ratan Guptabd303b12017-08-18 17:10:07 +0530295 std::vector<DbusInterface> interfaces;
296 interfaces.emplace_back(invNetworkIntf);
297
298 auto depth = 0;
299
Gunnar Mills57d9c502018-09-14 14:42:34 -0500300 auto mapperCall =
301 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
Ratan Guptabd303b12017-08-18 17:10:07 +0530302
303 mapperCall.append(invRoot, depth, interfaces);
304
305 auto mapperReply = bus.call(mapperCall);
306 if (mapperReply.is_method_error())
307 {
308 log<level::ERR>("Error in mapper call");
309 elog<InternalFailure>();
310 }
311
312 ObjectTree objectTree;
313 mapperReply.read(objectTree);
314
315 if (objectTree.empty())
316 {
317 log<level::ERR>("No Object has implemented the interface",
318 entry("INTERFACE=%s", invNetworkIntf));
319 elog<InternalFailure>();
320 }
321
Alvin Wang38a63c32019-08-29 22:56:46 +0800322 DbusObjectPath objPath;
323 DbusService service;
Ratan Guptabd303b12017-08-18 17:10:07 +0530324
Alvin Wang38a63c32019-08-29 22:56:46 +0800325 if (1 == objectTree.size())
326 {
327 objPath = objectTree.begin()->first;
328 service = objectTree.begin()->second.begin()->first;
329 }
330 else
331 {
332 // If there are more than 2 objects, object path must contain the
333 // interface name
334 for (auto const& object : objectTree)
335 {
Manojkiran Edacc099a82020-05-11 14:25:16 +0530336 log<level::INFO>("interface",
337 entry("INT=%s", interfaceName.c_str()));
Alvin Wang38a63c32019-08-29 22:56:46 +0800338 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str()));
Manojkiran Edacc099a82020-05-11 14:25:16 +0530339
340 if (std::string::npos != object.first.find(interfaceName.c_str()))
Alvin Wang38a63c32019-08-29 22:56:46 +0800341 {
342 objPath = object.first;
343 service = object.second.begin()->first;
344 break;
345 }
346 }
347
348 if (objPath.empty())
349 {
350 log<level::ERR>("Can't find the object for the interface",
Manojkiran Edacc099a82020-05-11 14:25:16 +0530351 entry("intfName=%s", interfaceName.c_str()));
Alvin Wang38a63c32019-08-29 22:56:46 +0800352 elog<InternalFailure>();
353 }
354 }
Ratan Guptabd303b12017-08-18 17:10:07 +0530355
Gunnar Mills57d9c502018-09-14 14:42:34 -0500356 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
357 propIntf, methodGet);
Ratan Guptabd303b12017-08-18 17:10:07 +0530358
359 method.append(invNetworkIntf, "MACAddress");
360
361 auto reply = bus.call(method);
362 if (reply.is_method_error())
363 {
Gunnar Mills57d9c502018-09-14 14:42:34 -0500364 log<level::ERR>("Failed to get MACAddress",
Ratan Guptabd303b12017-08-18 17:10:07 +0530365 entry("PATH=%s", objPath.c_str()),
366 entry("INTERFACE=%s", invNetworkIntf));
367 elog<InternalFailure>();
368 }
369
William A. Kennington III1137a972019-04-20 20:49:58 -0700370 std::variant<std::string> value;
Ratan Guptabd303b12017-08-18 17:10:07 +0530371 reply.read(value);
William A. Kennington IIIb01d08f2022-11-03 12:50:00 -0700372 return ToAddr<ether_addr>{}(std::get<std::string>(value));
Ratan Guptabd303b12017-08-18 17:10:07 +0530373}
374
William A. Kennington III1137a972019-04-20 20:49:58 -0700375bool isEmpty(const ether_addr& mac)
376{
William A. Kennington IIIbb0eacc2022-10-21 15:22:06 -0700377 return mac == ether_addr{};
William A. Kennington III1137a972019-04-20 20:49:58 -0700378}
379
380bool isMulticast(const ether_addr& mac)
381{
382 return mac.ether_addr_octet[0] & 0b1;
383}
384
385bool isUnicast(const ether_addr& mac)
386{
387 return !isEmpty(mac) && !isMulticast(mac);
388}
389
Gunnar Mills57d9c502018-09-14 14:42:34 -0500390} // namespace mac_address
391} // namespace network
392} // namespace phosphor