blob: 5df9afaeb339ed18a79ed1ac1d3d695a44a58877 [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{
William A. Kennington III4a688fc2022-11-15 15:58:44 -0800216 if (!config.getFileExists())
217 {
218 }
219 else if (auto str = config.map.getLastValueString(section, key);
220 str == nullptr)
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700221 {
222 auto err = fmt::format("Unable to get the value of {}[{}] from {}",
223 section, key, config.getFilename().native());
224 log<level::NOTICE>(err.c_str(),
225 entry("FILE=%s", config.getFilename().c_str()));
226 }
227 else if (auto val = fun(*str); !val)
228 {
229 auto err = fmt::format("Invalid value of {}[{}] from {}: {}", section,
230 key, config.getFilename().native(), *str);
231 log<level::NOTICE>(err.c_str(), entry("VALUE=%s", str->c_str()),
232 entry("FILE=%s", config.getFilename().c_str()));
233 }
234 else
235 {
236 return val;
237 }
238 return decltype(fun(std::string_view{}))(std::nullopt);
239}
240
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700241bool getIPv6AcceptRA(const config::Parser& config)
Ratan Gupta56187e72017-08-13 09:40:14 +0530242{
William A. Kennington III324d2602022-08-18 18:32:56 -0700243#ifdef ENABLE_IPV6_ACCEPT_RA
244 constexpr bool def = true;
245#else
246 constexpr bool def = false;
247#endif
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700248 return systemdParseLast(config, "Network", "IPv6AcceptRA",
249 config::parseBool)
250 .value_or(def);
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700251}
252
William A. Kennington III8060c0d2022-08-18 19:19:34 -0700253DHCPVal getDHCPValue(const config::Parser& config)
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700254{
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700255 return systemdParseLast(config, "Network", "DHCP", systemdParseDHCP)
256 .value_or(DHCPVal{.v4 = true, .v6 = true});
257}
William A. Kennington III324d2602022-08-18 18:32:56 -0700258
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700259bool getDHCPProp(const config::Parser& config, std::string_view key)
260{
261 return systemdParseLast(config, "DHCP", key, config::parseBool)
262 .value_or(true);
Ratan Gupta56187e72017-08-13 09:40:14 +0530263}
264
Ratan Guptabd303b12017-08-18 17:10:07 +0530265namespace mac_address
266{
267
268constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
269constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
270constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
271constexpr auto propIntf = "org.freedesktop.DBus.Properties";
272constexpr auto methodGet = "Get";
Manojkiran Edacc099a82020-05-11 14:25:16 +0530273constexpr auto configFile = "/usr/share/network/config.json";
Ratan Guptabd303b12017-08-18 17:10:07 +0530274
275using DbusObjectPath = std::string;
276using DbusService = std::string;
277using DbusInterface = std::string;
Gunnar Mills57d9c502018-09-14 14:42:34 -0500278using ObjectTree =
279 std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>;
Ratan Guptabd303b12017-08-18 17:10:07 +0530280
281constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager";
282constexpr auto invNetworkIntf =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500283 "xyz.openbmc_project.Inventory.Item.NetworkInterface";
Ratan Guptabd303b12017-08-18 17:10:07 +0530284constexpr auto invRoot = "/xyz/openbmc_project/inventory";
285
Patrick Williamsc38b0712022-07-22 19:26:54 -0500286ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName)
Ratan Guptabd303b12017-08-18 17:10:07 +0530287{
Manojkiran Edacc099a82020-05-11 14:25:16 +0530288
289 std::string interfaceName = intfName;
290
William A. Kennington III6f39c5e2021-05-13 18:39:23 -0700291#ifdef SYNC_MAC_FROM_INVENTORY
Manojkiran Edacc099a82020-05-11 14:25:16 +0530292 // load the config JSON from the Read Only Path
293 std::ifstream in(configFile);
294 nlohmann::json configJson;
295 in >> configJson;
296 interfaceName = configJson[intfName];
297#endif
298
Ratan Guptabd303b12017-08-18 17:10:07 +0530299 std::vector<DbusInterface> interfaces;
300 interfaces.emplace_back(invNetworkIntf);
301
302 auto depth = 0;
303
Gunnar Mills57d9c502018-09-14 14:42:34 -0500304 auto mapperCall =
305 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
Ratan Guptabd303b12017-08-18 17:10:07 +0530306
307 mapperCall.append(invRoot, depth, interfaces);
308
309 auto mapperReply = bus.call(mapperCall);
310 if (mapperReply.is_method_error())
311 {
312 log<level::ERR>("Error in mapper call");
313 elog<InternalFailure>();
314 }
315
316 ObjectTree objectTree;
317 mapperReply.read(objectTree);
318
319 if (objectTree.empty())
320 {
321 log<level::ERR>("No Object has implemented the interface",
322 entry("INTERFACE=%s", invNetworkIntf));
323 elog<InternalFailure>();
324 }
325
Alvin Wang38a63c32019-08-29 22:56:46 +0800326 DbusObjectPath objPath;
327 DbusService service;
Ratan Guptabd303b12017-08-18 17:10:07 +0530328
Alvin Wang38a63c32019-08-29 22:56:46 +0800329 if (1 == objectTree.size())
330 {
331 objPath = objectTree.begin()->first;
332 service = objectTree.begin()->second.begin()->first;
333 }
334 else
335 {
336 // If there are more than 2 objects, object path must contain the
337 // interface name
338 for (auto const& object : objectTree)
339 {
Manojkiran Edacc099a82020-05-11 14:25:16 +0530340 log<level::INFO>("interface",
341 entry("INT=%s", interfaceName.c_str()));
Alvin Wang38a63c32019-08-29 22:56:46 +0800342 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str()));
Manojkiran Edacc099a82020-05-11 14:25:16 +0530343
344 if (std::string::npos != object.first.find(interfaceName.c_str()))
Alvin Wang38a63c32019-08-29 22:56:46 +0800345 {
346 objPath = object.first;
347 service = object.second.begin()->first;
348 break;
349 }
350 }
351
352 if (objPath.empty())
353 {
354 log<level::ERR>("Can't find the object for the interface",
Manojkiran Edacc099a82020-05-11 14:25:16 +0530355 entry("intfName=%s", interfaceName.c_str()));
Alvin Wang38a63c32019-08-29 22:56:46 +0800356 elog<InternalFailure>();
357 }
358 }
Ratan Guptabd303b12017-08-18 17:10:07 +0530359
Gunnar Mills57d9c502018-09-14 14:42:34 -0500360 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
361 propIntf, methodGet);
Ratan Guptabd303b12017-08-18 17:10:07 +0530362
363 method.append(invNetworkIntf, "MACAddress");
364
365 auto reply = bus.call(method);
366 if (reply.is_method_error())
367 {
Gunnar Mills57d9c502018-09-14 14:42:34 -0500368 log<level::ERR>("Failed to get MACAddress",
Ratan Guptabd303b12017-08-18 17:10:07 +0530369 entry("PATH=%s", objPath.c_str()),
370 entry("INTERFACE=%s", invNetworkIntf));
371 elog<InternalFailure>();
372 }
373
William A. Kennington III1137a972019-04-20 20:49:58 -0700374 std::variant<std::string> value;
Ratan Guptabd303b12017-08-18 17:10:07 +0530375 reply.read(value);
William A. Kennington IIIb01d08f2022-11-03 12:50:00 -0700376 return ToAddr<ether_addr>{}(std::get<std::string>(value));
Ratan Guptabd303b12017-08-18 17:10:07 +0530377}
378
William A. Kennington III1137a972019-04-20 20:49:58 -0700379bool isEmpty(const ether_addr& mac)
380{
William A. Kennington IIIbb0eacc2022-10-21 15:22:06 -0700381 return mac == ether_addr{};
William A. Kennington III1137a972019-04-20 20:49:58 -0700382}
383
384bool isMulticast(const ether_addr& mac)
385{
386 return mac.ether_addr_octet[0] & 0b1;
387}
388
389bool isUnicast(const ether_addr& mac)
390{
391 return !isEmpty(mac) && !isMulticast(mac);
392}
393
Gunnar Mills57d9c502018-09-14 14:42:34 -0500394} // namespace mac_address
395} // namespace network
396} // namespace phosphor