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 <charconv> 14 #include <fstream> 15 #ifdef SYNC_MAC_FROM_INVENTORY 16 #include <nlohmann/json.hpp> 17 #endif 18 #include <phosphor-logging/elog-errors.hpp> 19 #include <phosphor-logging/log.hpp> 20 #include <stdexcept> 21 #include <stdplus/raw.hpp> 22 #include <string> 23 #include <string_view> 24 #include <variant> 25 #include <xyz/openbmc_project/Common/error.hpp> 26 27 namespace phosphor 28 { 29 namespace network 30 { 31 32 using std::literals::string_view_literals::operator""sv; 33 using namespace phosphor::logging; 34 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 35 36 namespace internal 37 { 38 39 void executeCommandinChildProcess(stdplus::const_zstring path, char** args) 40 { 41 using namespace std::string_literals; 42 pid_t pid = fork(); 43 44 if (pid == 0) 45 { 46 execv(path.c_str(), args); 47 exit(255); 48 } 49 else if (pid < 0) 50 { 51 auto error = errno; 52 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error)); 53 elog<InternalFailure>(); 54 } 55 else if (pid > 0) 56 { 57 int status; 58 while (waitpid(pid, &status, 0) == -1) 59 { 60 if (errno != EINTR) 61 { 62 status = -1; 63 break; 64 } 65 } 66 67 if (status < 0) 68 { 69 fmt::memory_buffer buf; 70 fmt::format_to(fmt::appender(buf), "`{}`", path); 71 for (size_t i = 0; args[i] != nullptr; ++i) 72 { 73 fmt::format_to(fmt::appender(buf), " `{}`", args[i]); 74 } 75 buf.push_back('\0'); 76 log<level::ERR>("Unable to execute the command", 77 entry("CMD=%s", buf.data()), 78 entry("STATUS=%d", status)); 79 elog<InternalFailure>(); 80 } 81 } 82 } 83 84 /** @brief Get ignored interfaces from environment */ 85 std::string_view getIgnoredInterfacesEnv() 86 { 87 auto r = std::getenv("IGNORED_INTERFACES"); 88 if (r == nullptr) 89 { 90 return ""; 91 } 92 return r; 93 } 94 95 /** @brief Parse the comma separated interface names */ 96 std::unordered_set<std::string_view> 97 parseInterfaces(std::string_view interfaces) 98 { 99 std::unordered_set<std::string_view> result; 100 while (true) 101 { 102 auto sep = interfaces.find(','); 103 auto interface = interfaces.substr(0, sep); 104 while (!interface.empty() && std::isspace(interface.front())) 105 { 106 interface.remove_prefix(1); 107 } 108 while (!interface.empty() && std::isspace(interface.back())) 109 { 110 interface.remove_suffix(1); 111 } 112 if (!interface.empty()) 113 { 114 result.insert(interface); 115 } 116 if (sep == interfaces.npos) 117 { 118 break; 119 } 120 interfaces = interfaces.substr(sep + 1); 121 } 122 return result; 123 } 124 125 /** @brief Get the ignored interfaces */ 126 const std::unordered_set<std::string_view>& getIgnoredInterfaces() 127 { 128 static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv()); 129 return ignoredInterfaces; 130 } 131 132 } // namespace internal 133 134 constexpr auto familyVisit(auto&& visitor, int family) 135 { 136 if (family == AF_INET) 137 { 138 return visitor.template operator()<AF_INET>(); 139 } 140 else if (family == AF_INET6) 141 { 142 return visitor.template operator()<AF_INET6>(); 143 } 144 throw std::invalid_argument("Invalid addr family"); 145 } 146 147 template <int family> 148 typename FamilyTraits<family>::addr addrFromBuf(std::string_view buf) 149 { 150 return stdplus::raw::copyFromStrict<typename FamilyTraits<family>::addr>( 151 buf); 152 } 153 154 InAddrAny addrFromBuf(int family, std::string_view buf) 155 { 156 return familyVisit( 157 [=]<int f>() -> InAddrAny { return addrFromBuf<f>(buf); }, family); 158 } 159 160 void deleteInterface(stdplus::const_zstring intf) 161 { 162 pid_t pid = fork(); 163 int status{}; 164 165 if (pid == 0) 166 { 167 168 execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr); 169 auto error = errno; 170 log<level::ERR>("Couldn't delete the device", entry("ERRNO=%d", error), 171 entry("INTF=%s", intf.c_str())); 172 elog<InternalFailure>(); 173 } 174 else if (pid < 0) 175 { 176 auto error = errno; 177 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error)); 178 elog<InternalFailure>(); 179 } 180 else if (pid > 0) 181 { 182 while (waitpid(pid, &status, 0) == -1) 183 { 184 if (errno != EINTR) 185 { /* Error other than EINTR */ 186 status = -1; 187 break; 188 } 189 } 190 191 if (status < 0) 192 { 193 log<level::ERR>("Unable to delete the interface", 194 entry("INTF=%s", intf.c_str()), 195 entry("STATUS=%d", status)); 196 elog<InternalFailure>(); 197 } 198 } 199 } 200 201 std::optional<std::string> interfaceToUbootEthAddr(std::string_view intf) 202 { 203 constexpr auto pfx = "eth"sv; 204 if (!intf.starts_with(pfx)) 205 { 206 return std::nullopt; 207 } 208 intf.remove_prefix(pfx.size()); 209 auto last = intf.data() + intf.size(); 210 unsigned long idx; 211 auto res = std::from_chars(intf.data(), last, idx); 212 if (res.ec != std::errc() || res.ptr != last) 213 { 214 return std::nullopt; 215 } 216 if (idx == 0) 217 { 218 return "ethaddr"; 219 } 220 return fmt::format(FMT_COMPILE("eth{}addr"), idx); 221 } 222 223 static std::optional<DHCPVal> systemdParseDHCP(std::string_view str) 224 { 225 if (config::icaseeq(str, "ipv4")) 226 { 227 return DHCPVal{.v4 = true, .v6 = false}; 228 } 229 if (config::icaseeq(str, "ipv6")) 230 { 231 return DHCPVal{.v4 = false, .v6 = true}; 232 } 233 if (auto b = config::parseBool(str); b) 234 { 235 return DHCPVal{.v4 = *b, .v6 = *b}; 236 } 237 return std::nullopt; 238 } 239 240 inline auto systemdParseLast(const config::Parser& config, 241 std::string_view section, std::string_view key, 242 auto&& fun) 243 { 244 if (auto str = config.map.getLastValueString(section, key); str == nullptr) 245 { 246 auto err = fmt::format("Unable to get the value of {}[{}] from {}", 247 section, key, config.getFilename().native()); 248 log<level::NOTICE>(err.c_str(), 249 entry("FILE=%s", config.getFilename().c_str())); 250 } 251 else if (auto val = fun(*str); !val) 252 { 253 auto err = fmt::format("Invalid value of {}[{}] from {}: {}", section, 254 key, config.getFilename().native(), *str); 255 log<level::NOTICE>(err.c_str(), entry("VALUE=%s", str->c_str()), 256 entry("FILE=%s", config.getFilename().c_str())); 257 } 258 else 259 { 260 return val; 261 } 262 return decltype(fun(std::string_view{}))(std::nullopt); 263 } 264 265 bool getIPv6AcceptRA(const config::Parser& config) 266 { 267 #ifdef ENABLE_IPV6_ACCEPT_RA 268 constexpr bool def = true; 269 #else 270 constexpr bool def = false; 271 #endif 272 return systemdParseLast(config, "Network", "IPv6AcceptRA", 273 config::parseBool) 274 .value_or(def); 275 } 276 277 DHCPVal getDHCPValue(const config::Parser& config) 278 { 279 return systemdParseLast(config, "Network", "DHCP", systemdParseDHCP) 280 .value_or(DHCPVal{.v4 = true, .v6 = true}); 281 } 282 283 bool getDHCPProp(const config::Parser& config, std::string_view key) 284 { 285 return systemdParseLast(config, "DHCP", key, config::parseBool) 286 .value_or(true); 287 } 288 289 namespace mac_address 290 { 291 292 constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper"; 293 constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper"; 294 constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; 295 constexpr auto propIntf = "org.freedesktop.DBus.Properties"; 296 constexpr auto methodGet = "Get"; 297 constexpr auto configFile = "/usr/share/network/config.json"; 298 299 using DbusObjectPath = std::string; 300 using DbusService = std::string; 301 using DbusInterface = std::string; 302 using ObjectTree = 303 std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>; 304 305 constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager"; 306 constexpr auto invNetworkIntf = 307 "xyz.openbmc_project.Inventory.Item.NetworkInterface"; 308 constexpr auto invRoot = "/xyz/openbmc_project/inventory"; 309 310 ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName) 311 { 312 313 std::string interfaceName = intfName; 314 315 #ifdef SYNC_MAC_FROM_INVENTORY 316 // load the config JSON from the Read Only Path 317 std::ifstream in(configFile); 318 nlohmann::json configJson; 319 in >> configJson; 320 interfaceName = configJson[intfName]; 321 #endif 322 323 std::vector<DbusInterface> interfaces; 324 interfaces.emplace_back(invNetworkIntf); 325 326 auto depth = 0; 327 328 auto mapperCall = 329 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree"); 330 331 mapperCall.append(invRoot, depth, interfaces); 332 333 auto mapperReply = bus.call(mapperCall); 334 if (mapperReply.is_method_error()) 335 { 336 log<level::ERR>("Error in mapper call"); 337 elog<InternalFailure>(); 338 } 339 340 ObjectTree objectTree; 341 mapperReply.read(objectTree); 342 343 if (objectTree.empty()) 344 { 345 log<level::ERR>("No Object has implemented the interface", 346 entry("INTERFACE=%s", invNetworkIntf)); 347 elog<InternalFailure>(); 348 } 349 350 DbusObjectPath objPath; 351 DbusService service; 352 353 if (1 == objectTree.size()) 354 { 355 objPath = objectTree.begin()->first; 356 service = objectTree.begin()->second.begin()->first; 357 } 358 else 359 { 360 // If there are more than 2 objects, object path must contain the 361 // interface name 362 for (auto const& object : objectTree) 363 { 364 log<level::INFO>("interface", 365 entry("INT=%s", interfaceName.c_str())); 366 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str())); 367 368 if (std::string::npos != object.first.find(interfaceName.c_str())) 369 { 370 objPath = object.first; 371 service = object.second.begin()->first; 372 break; 373 } 374 } 375 376 if (objPath.empty()) 377 { 378 log<level::ERR>("Can't find the object for the interface", 379 entry("intfName=%s", interfaceName.c_str())); 380 elog<InternalFailure>(); 381 } 382 } 383 384 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 385 propIntf, methodGet); 386 387 method.append(invNetworkIntf, "MACAddress"); 388 389 auto reply = bus.call(method); 390 if (reply.is_method_error()) 391 { 392 log<level::ERR>("Failed to get MACAddress", 393 entry("PATH=%s", objPath.c_str()), 394 entry("INTERFACE=%s", invNetworkIntf)); 395 elog<InternalFailure>(); 396 } 397 398 std::variant<std::string> value; 399 reply.read(value); 400 return ToAddr<ether_addr>{}(std::get<std::string>(value)); 401 } 402 403 bool isEmpty(const ether_addr& mac) 404 { 405 return mac == ether_addr{}; 406 } 407 408 bool isMulticast(const ether_addr& mac) 409 { 410 return mac.ether_addr_octet[0] & 0b1; 411 } 412 413 bool isUnicast(const ether_addr& mac) 414 { 415 return !isEmpty(mac) && !isMulticast(mac); 416 } 417 418 } // namespace mac_address 419 } // namespace network 420 } // namespace phosphor 421