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