blob: e65c17d7dcedb5c9573e4865b21ddb0322e79ce4 [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
Ratan Gupta3681a502017-06-17 19:20:04 +05308#include <arpa/inet.h>
William A. Kennington III1c776022022-01-05 14:12:16 -08009#include <fmt/compile.h>
10#include <fmt/format.h>
Ratan Guptabc886292017-07-25 18:29:57 +053011#include <sys/wait.h>
Ratan Gupta3681a502017-06-17 19:20:04 +053012
Lei YU307554e2021-03-18 14:56:50 +080013#include <cctype>
William A. Kennington III69f45542022-09-24 23:28:14 -070014#include <charconv>
Manojkiran Edacc099a82020-05-11 14:25:16 +053015#include <fstream>
William A. Kennington IIIde433b72021-05-17 22:59:28 -070016#ifdef SYNC_MAC_FROM_INVENTORY
Manojkiran Edacc099a82020-05-11 14:25:16 +053017#include <nlohmann/json.hpp>
William A. Kennington IIIde433b72021-05-17 22:59:28 -070018#endif
Patrick Venture189d44e2018-07-09 12:30:59 -070019#include <phosphor-logging/elog-errors.hpp>
20#include <phosphor-logging/log.hpp>
William A. Kennington III5058f572019-01-30 17:18:14 -080021#include <stdexcept>
William A. Kennington III12beaad2020-06-13 19:30:41 -070022#include <stdplus/raw.hpp>
Patrick Venture189d44e2018-07-09 12:30:59 -070023#include <string>
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -070024#include <string_view>
William A. Kennington III1137a972019-04-20 20:49:58 -070025#include <variant>
Patrick Venture189d44e2018-07-09 12:30:59 -070026#include <xyz/openbmc_project/Common/error.hpp>
Ratan Gupta8804feb2017-05-25 10:49:57 +053027
28namespace phosphor
29{
30namespace network
31{
Ratan Guptabc886292017-07-25 18:29:57 +053032
William A. Kennington III69f45542022-09-24 23:28:14 -070033using std::literals::string_view_literals::operator""sv;
Ratan Gupta8804feb2017-05-25 10:49:57 +053034using namespace phosphor::logging;
Ratan Gupta11cef802017-05-29 08:41:48 +053035using namespace sdbusplus::xyz::openbmc_project::Common::Error;
Ratan Gupta8804feb2017-05-25 10:49:57 +053036
Lei YU3894ce72021-03-18 14:53:42 +080037namespace internal
38{
39
William A. Kennington III69f45542022-09-24 23:28:14 -070040void executeCommandinChildProcess(stdplus::const_zstring path, char** args)
Lei YU3894ce72021-03-18 14:53:42 +080041{
42 using namespace std::string_literals;
43 pid_t pid = fork();
Lei YU3894ce72021-03-18 14:53:42 +080044
45 if (pid == 0)
46 {
William A. Kennington III69f45542022-09-24 23:28:14 -070047 execv(path.c_str(), args);
48 exit(255);
Lei YU3894ce72021-03-18 14:53:42 +080049 }
50 else if (pid < 0)
51 {
52 auto error = errno;
53 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
54 elog<InternalFailure>();
55 }
56 else if (pid > 0)
57 {
William A. Kennington III69f45542022-09-24 23:28:14 -070058 int status;
Lei YU3894ce72021-03-18 14:53:42 +080059 while (waitpid(pid, &status, 0) == -1)
60 {
61 if (errno != EINTR)
William A. Kennington III69f45542022-09-24 23:28:14 -070062 {
Lei YU3894ce72021-03-18 14:53:42 +080063 status = -1;
64 break;
65 }
66 }
67
68 if (status < 0)
69 {
William A. Kennington III69f45542022-09-24 23:28:14 -070070 fmt::memory_buffer buf;
71 fmt::format_to(fmt::appender(buf), "`{}`", path);
72 for (size_t i = 0; args[i] != nullptr; ++i)
Lei YU3894ce72021-03-18 14:53:42 +080073 {
William A. Kennington III69f45542022-09-24 23:28:14 -070074 fmt::format_to(fmt::appender(buf), " `{}`", args[i]);
Lei YU3894ce72021-03-18 14:53:42 +080075 }
William A. Kennington III69f45542022-09-24 23:28:14 -070076 buf.push_back('\0');
Lei YU3894ce72021-03-18 14:53:42 +080077 log<level::ERR>("Unable to execute the command",
William A. Kennington III69f45542022-09-24 23:28:14 -070078 entry("CMD=%s", buf.data()),
Lei YU3894ce72021-03-18 14:53:42 +080079 entry("STATUS=%d", status));
80 elog<InternalFailure>();
81 }
82 }
83}
84
Lei YU307554e2021-03-18 14:56:50 +080085/** @brief Get ignored interfaces from environment */
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -070086std::string_view getIgnoredInterfacesEnv()
Lei YU307554e2021-03-18 14:56:50 +080087{
88 auto r = std::getenv("IGNORED_INTERFACES");
89 if (r == nullptr)
90 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -070091 return "";
Lei YU307554e2021-03-18 14:56:50 +080092 }
93 return r;
94}
95
96/** @brief Parse the comma separated interface names */
William A. Kennington III95530ec2022-08-19 01:44:39 -070097std::unordered_set<std::string_view>
98 parseInterfaces(std::string_view interfaces)
Lei YU307554e2021-03-18 14:56:50 +080099{
William A. Kennington III95530ec2022-08-19 01:44:39 -0700100 std::unordered_set<std::string_view> result;
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700101 while (true)
Lei YU307554e2021-03-18 14:56:50 +0800102 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700103 auto sep = interfaces.find(',');
104 auto interface = interfaces.substr(0, sep);
105 while (!interface.empty() && std::isspace(interface.front()))
Lei YU307554e2021-03-18 14:56:50 +0800106 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700107 interface.remove_prefix(1);
Lei YU307554e2021-03-18 14:56:50 +0800108 }
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700109 while (!interface.empty() && std::isspace(interface.back()))
Lei YU307554e2021-03-18 14:56:50 +0800110 {
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700111 interface.remove_suffix(1);
Lei YU307554e2021-03-18 14:56:50 +0800112 }
William A. Kennington IIIee5b2c92021-04-28 02:31:28 -0700113 if (!interface.empty())
114 {
115 result.insert(interface);
116 }
117 if (sep == interfaces.npos)
118 {
119 break;
120 }
121 interfaces = interfaces.substr(sep + 1);
Lei YU307554e2021-03-18 14:56:50 +0800122 }
123 return result;
124}
125
126/** @brief Get the ignored interfaces */
William A. Kennington III95530ec2022-08-19 01:44:39 -0700127const std::unordered_set<std::string_view>& getIgnoredInterfaces()
Lei YU307554e2021-03-18 14:56:50 +0800128{
129 static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv());
130 return ignoredInterfaces;
131}
132
Lei YU3894ce72021-03-18 14:53:42 +0800133} // namespace internal
Ratan Gupta8804feb2017-05-25 10:49:57 +0530134
William A. Kennington IIIe5a48ab2019-04-22 03:55:23 -0700135constexpr auto familyVisit(auto&& visitor, int family)
136{
137 if (family == AF_INET)
138 {
139 return visitor.template operator()<AF_INET>();
140 }
141 else if (family == AF_INET6)
142 {
143 return visitor.template operator()<AF_INET6>();
144 }
145 throw std::invalid_argument("Invalid addr family");
146}
147
William A. Kennington III97b5dc62022-10-07 14:01:29 -0700148template <int family>
149typename FamilyTraits<family>::addr addrFromBuf(std::string_view buf)
William A. Kennington IIIa00b1c32019-02-01 18:57:17 -0800150{
William A. Kennington III97b5dc62022-10-07 14:01:29 -0700151 return stdplus::raw::copyFromStrict<typename FamilyTraits<family>::addr>(
152 buf);
153}
William A. Kennington IIIa00b1c32019-02-01 18:57:17 -0800154
William A. Kennington III97b5dc62022-10-07 14:01:29 -0700155InAddrAny addrFromBuf(int family, std::string_view buf)
156{
157 return familyVisit(
158 [=]<int f>() -> InAddrAny { return addrFromBuf<f>(buf); }, family);
William A. Kennington IIIa00b1c32019-02-01 18:57:17 -0800159}
160
William A. Kennington III9f228302022-10-07 14:02:49 -0700161bool isValidIP(int family, stdplus::const_zstring address) noexcept
Nagaraju Goruganti66b974d2017-10-03 08:43:08 -0500162{
163 unsigned char buf[sizeof(struct in6_addr)];
William A. Kennington III9f228302022-10-07 14:02:49 -0700164 return inet_pton(family, address.c_str(), buf) > 0;
Nagaraju Goruganti66b974d2017-10-03 08:43:08 -0500165}
166
William A. Kennington IIIff12acb2022-10-07 19:06:56 -0700167bool isValidIP(stdplus::const_zstring address) noexcept
168{
169 return isValidIP(AF_INET, address) || isValidIP(AF_INET6, address);
170}
171
William A. Kennington IIIe5a48ab2019-04-22 03:55:23 -0700172bool isValidPrefix(int family, uint8_t prefix)
Nagaraju Goruganti66b974d2017-10-03 08:43:08 -0500173{
William A. Kennington IIIe5a48ab2019-04-22 03:55:23 -0700174 return familyVisit(
175 [=]<int f>() noexcept { return isValidPrefix<f>(prefix); }, family);
Ratan Gupta8804feb2017-05-25 10:49:57 +0530176}
177
William A. Kennington III69f45542022-09-24 23:28:14 -0700178void deleteInterface(stdplus::const_zstring intf)
Ratan Guptabc886292017-07-25 18:29:57 +0530179{
180 pid_t pid = fork();
Gunnar Mills57d9c502018-09-14 14:42:34 -0500181 int status{};
Ratan Guptabc886292017-07-25 18:29:57 +0530182
183 if (pid == 0)
184 {
185
186 execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr);
187 auto error = errno;
Gunnar Mills57d9c502018-09-14 14:42:34 -0500188 log<level::ERR>("Couldn't delete the device", entry("ERRNO=%d", error),
Ratan Guptabc886292017-07-25 18:29:57 +0530189 entry("INTF=%s", intf.c_str()));
190 elog<InternalFailure>();
191 }
192 else if (pid < 0)
193 {
194 auto error = errno;
Gunnar Mills57d9c502018-09-14 14:42:34 -0500195 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
Ratan Guptabc886292017-07-25 18:29:57 +0530196 elog<InternalFailure>();
197 }
198 else if (pid > 0)
199 {
200 while (waitpid(pid, &status, 0) == -1)
201 {
202 if (errno != EINTR)
Gunnar Mills57d9c502018-09-14 14:42:34 -0500203 { /* Error other than EINTR */
Ratan Guptabc886292017-07-25 18:29:57 +0530204 status = -1;
205 break;
206 }
207 }
208
Gunnar Mills57d9c502018-09-14 14:42:34 -0500209 if (status < 0)
Ratan Guptabc886292017-07-25 18:29:57 +0530210 {
211 log<level::ERR>("Unable to delete the interface",
Joseph Reynolds02653ca2018-05-10 15:55:09 -0500212 entry("INTF=%s", intf.c_str()),
213 entry("STATUS=%d", status));
Ratan Guptabc886292017-07-25 18:29:57 +0530214 elog<InternalFailure>();
215 }
216 }
217}
218
William A. Kennington III69f45542022-09-24 23:28:14 -0700219std::optional<std::string> interfaceToUbootEthAddr(std::string_view intf)
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700220{
William A. Kennington III69f45542022-09-24 23:28:14 -0700221 constexpr auto pfx = "eth"sv;
222 if (!intf.starts_with(pfx))
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700223 {
224 return std::nullopt;
225 }
William A. Kennington III69f45542022-09-24 23:28:14 -0700226 intf.remove_prefix(pfx.size());
227 auto last = intf.data() + intf.size();
228 unsigned long idx;
229 auto res = std::from_chars(intf.data(), last, idx);
230 if (res.ec != std::errc() || res.ptr != last)
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700231 {
232 return std::nullopt;
233 }
234 if (idx == 0)
235 {
236 return "ethaddr";
237 }
William A. Kennington III69f45542022-09-24 23:28:14 -0700238 return fmt::format(FMT_COMPILE("eth{}addr"), idx);
William A. Kennington III7b9e8bd2019-04-23 19:31:31 -0700239}
240
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700241static std::optional<DHCPVal> systemdParseDHCP(std::string_view str)
242{
243 if (config::icaseeq(str, "ipv4"))
244 {
245 return DHCPVal{.v4 = true, .v6 = false};
246 }
247 if (config::icaseeq(str, "ipv6"))
248 {
249 return DHCPVal{.v4 = false, .v6 = true};
250 }
251 if (auto b = config::parseBool(str); b)
252 {
253 return DHCPVal{.v4 = *b, .v6 = *b};
254 }
255 return std::nullopt;
256}
257
258inline auto systemdParseLast(const config::Parser& config,
259 std::string_view section, std::string_view key,
260 auto&& fun)
261{
262 if (auto str = config.map.getLastValueString(section, key); str == nullptr)
263 {
264 auto err = fmt::format("Unable to get the value of {}[{}] from {}",
265 section, key, config.getFilename().native());
266 log<level::NOTICE>(err.c_str(),
267 entry("FILE=%s", config.getFilename().c_str()));
268 }
269 else if (auto val = fun(*str); !val)
270 {
271 auto err = fmt::format("Invalid value of {}[{}] from {}: {}", section,
272 key, config.getFilename().native(), *str);
273 log<level::NOTICE>(err.c_str(), entry("VALUE=%s", str->c_str()),
274 entry("FILE=%s", config.getFilename().c_str()));
275 }
276 else
277 {
278 return val;
279 }
280 return decltype(fun(std::string_view{}))(std::nullopt);
281}
282
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700283bool getIPv6AcceptRA(const config::Parser& config)
Ratan Gupta56187e72017-08-13 09:40:14 +0530284{
William A. Kennington III324d2602022-08-18 18:32:56 -0700285#ifdef ENABLE_IPV6_ACCEPT_RA
286 constexpr bool def = true;
287#else
288 constexpr bool def = false;
289#endif
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700290 return systemdParseLast(config, "Network", "IPv6AcceptRA",
291 config::parseBool)
292 .value_or(def);
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700293}
294
William A. Kennington III8060c0d2022-08-18 19:19:34 -0700295DHCPVal getDHCPValue(const config::Parser& config)
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700296{
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700297 return systemdParseLast(config, "Network", "DHCP", systemdParseDHCP)
298 .value_or(DHCPVal{.v4 = true, .v6 = true});
299}
William A. Kennington III324d2602022-08-18 18:32:56 -0700300
William A. Kennington IIIe94c9ff2022-08-18 20:12:27 -0700301bool getDHCPProp(const config::Parser& config, std::string_view key)
302{
303 return systemdParseLast(config, "DHCP", key, config::parseBool)
304 .value_or(true);
Ratan Gupta56187e72017-08-13 09:40:14 +0530305}
306
Ratan Guptabd303b12017-08-18 17:10:07 +0530307namespace mac_address
308{
309
310constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
311constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
312constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
313constexpr auto propIntf = "org.freedesktop.DBus.Properties";
314constexpr auto methodGet = "Get";
Manojkiran Edacc099a82020-05-11 14:25:16 +0530315constexpr auto configFile = "/usr/share/network/config.json";
Ratan Guptabd303b12017-08-18 17:10:07 +0530316
317using DbusObjectPath = std::string;
318using DbusService = std::string;
319using DbusInterface = std::string;
Gunnar Mills57d9c502018-09-14 14:42:34 -0500320using ObjectTree =
321 std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>;
Ratan Guptabd303b12017-08-18 17:10:07 +0530322
323constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager";
324constexpr auto invNetworkIntf =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500325 "xyz.openbmc_project.Inventory.Item.NetworkInterface";
Ratan Guptabd303b12017-08-18 17:10:07 +0530326constexpr auto invRoot = "/xyz/openbmc_project/inventory";
327
Patrick Williamsc38b0712022-07-22 19:26:54 -0500328ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName)
Ratan Guptabd303b12017-08-18 17:10:07 +0530329{
Manojkiran Edacc099a82020-05-11 14:25:16 +0530330
331 std::string interfaceName = intfName;
332
William A. Kennington III6f39c5e2021-05-13 18:39:23 -0700333#ifdef SYNC_MAC_FROM_INVENTORY
Manojkiran Edacc099a82020-05-11 14:25:16 +0530334 // load the config JSON from the Read Only Path
335 std::ifstream in(configFile);
336 nlohmann::json configJson;
337 in >> configJson;
338 interfaceName = configJson[intfName];
339#endif
340
Ratan Guptabd303b12017-08-18 17:10:07 +0530341 std::vector<DbusInterface> interfaces;
342 interfaces.emplace_back(invNetworkIntf);
343
344 auto depth = 0;
345
Gunnar Mills57d9c502018-09-14 14:42:34 -0500346 auto mapperCall =
347 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
Ratan Guptabd303b12017-08-18 17:10:07 +0530348
349 mapperCall.append(invRoot, depth, interfaces);
350
351 auto mapperReply = bus.call(mapperCall);
352 if (mapperReply.is_method_error())
353 {
354 log<level::ERR>("Error in mapper call");
355 elog<InternalFailure>();
356 }
357
358 ObjectTree objectTree;
359 mapperReply.read(objectTree);
360
361 if (objectTree.empty())
362 {
363 log<level::ERR>("No Object has implemented the interface",
364 entry("INTERFACE=%s", invNetworkIntf));
365 elog<InternalFailure>();
366 }
367
Alvin Wang38a63c32019-08-29 22:56:46 +0800368 DbusObjectPath objPath;
369 DbusService service;
Ratan Guptabd303b12017-08-18 17:10:07 +0530370
Alvin Wang38a63c32019-08-29 22:56:46 +0800371 if (1 == objectTree.size())
372 {
373 objPath = objectTree.begin()->first;
374 service = objectTree.begin()->second.begin()->first;
375 }
376 else
377 {
378 // If there are more than 2 objects, object path must contain the
379 // interface name
380 for (auto const& object : objectTree)
381 {
Manojkiran Edacc099a82020-05-11 14:25:16 +0530382 log<level::INFO>("interface",
383 entry("INT=%s", interfaceName.c_str()));
Alvin Wang38a63c32019-08-29 22:56:46 +0800384 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str()));
Manojkiran Edacc099a82020-05-11 14:25:16 +0530385
386 if (std::string::npos != object.first.find(interfaceName.c_str()))
Alvin Wang38a63c32019-08-29 22:56:46 +0800387 {
388 objPath = object.first;
389 service = object.second.begin()->first;
390 break;
391 }
392 }
393
394 if (objPath.empty())
395 {
396 log<level::ERR>("Can't find the object for the interface",
Manojkiran Edacc099a82020-05-11 14:25:16 +0530397 entry("intfName=%s", interfaceName.c_str()));
Alvin Wang38a63c32019-08-29 22:56:46 +0800398 elog<InternalFailure>();
399 }
400 }
Ratan Guptabd303b12017-08-18 17:10:07 +0530401
Gunnar Mills57d9c502018-09-14 14:42:34 -0500402 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
403 propIntf, methodGet);
Ratan Guptabd303b12017-08-18 17:10:07 +0530404
405 method.append(invNetworkIntf, "MACAddress");
406
407 auto reply = bus.call(method);
408 if (reply.is_method_error())
409 {
Gunnar Mills57d9c502018-09-14 14:42:34 -0500410 log<level::ERR>("Failed to get MACAddress",
Ratan Guptabd303b12017-08-18 17:10:07 +0530411 entry("PATH=%s", objPath.c_str()),
412 entry("INTERFACE=%s", invNetworkIntf));
413 elog<InternalFailure>();
414 }
415
William A. Kennington III1137a972019-04-20 20:49:58 -0700416 std::variant<std::string> value;
Ratan Guptabd303b12017-08-18 17:10:07 +0530417 reply.read(value);
William A. Kennington III1137a972019-04-20 20:49:58 -0700418 return fromString(std::get<std::string>(value));
419}
420
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -0700421static uint8_t decodeHex(std::string_view str)
William A. Kennington III1137a972019-04-20 20:49:58 -0700422{
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -0700423 uint8_t ret;
424 auto res = std::from_chars(str.begin(), str.end(), ret, 16);
425 if (res.ptr != str.end() || res.ec != std::errc())
426 {
427 throw std::invalid_argument("Not hex");
428 }
429 return ret;
430}
Potin Laida0b1d42021-12-26 20:08:20 +0800431
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -0700432ether_addr fromString(std::string_view str)
433{
434 ether_addr ret;
William A. Kennington III69f45542022-09-24 23:28:14 -0700435 if (str.size() == 12 && str.find(":") == str.npos)
William A. Kennington III1137a972019-04-20 20:49:58 -0700436 {
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -0700437 for (size_t i = 0; i < 6; ++i)
438 {
439 ret.ether_addr_octet[i] = decodeHex(str.substr(i * 2, 2));
440 }
William A. Kennington III1137a972019-04-20 20:49:58 -0700441 }
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -0700442 else
Potin Laida0b1d42021-12-26 20:08:20 +0800443 {
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -0700444 for (size_t i = 0; i < 5; ++i)
445 {
446 auto loc = str.find(":");
447 ret.ether_addr_octet[i] = decodeHex(str.substr(0, loc));
448 str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
449 if (str.empty())
450 {
451 throw std::invalid_argument("Missing mac data");
452 }
453 }
454 ret.ether_addr_octet[5] = decodeHex(str);
Potin Laida0b1d42021-12-26 20:08:20 +0800455 }
William A. Kennington IIIfeb7aab2022-10-03 17:21:44 -0700456 return ret;
Ratan Guptabd303b12017-08-18 17:10:07 +0530457}
458
William A. Kennington III1137a972019-04-20 20:49:58 -0700459bool isEmpty(const ether_addr& mac)
460{
William A. Kennington IIIbb0eacc2022-10-21 15:22:06 -0700461 return mac == ether_addr{};
William A. Kennington III1137a972019-04-20 20:49:58 -0700462}
463
464bool isMulticast(const ether_addr& mac)
465{
466 return mac.ether_addr_octet[0] & 0b1;
467}
468
469bool isUnicast(const ether_addr& mac)
470{
471 return !isEmpty(mac) && !isMulticast(mac);
472}
473
Gunnar Mills57d9c502018-09-14 14:42:34 -0500474} // namespace mac_address
475} // namespace network
476} // namespace phosphor