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