1 #include "config.h" 2 3 #include "ethernet_interface.hpp" 4 5 #include "config_parser.hpp" 6 #include "neighbor.hpp" 7 #include "network_manager.hpp" 8 #include "vlan_interface.hpp" 9 10 #include <arpa/inet.h> 11 #include <fmt/format.h> 12 #include <linux/ethtool.h> 13 #include <linux/rtnetlink.h> 14 #include <linux/sockios.h> 15 #include <net/if.h> 16 17 #include <algorithm> 18 #include <filesystem> 19 #include <fstream> 20 #include <phosphor-logging/elog-errors.hpp> 21 #include <phosphor-logging/log.hpp> 22 #include <sdbusplus/bus/match.hpp> 23 #include <sstream> 24 #include <stdplus/fd/create.hpp> 25 #include <stdplus/raw.hpp> 26 #include <string> 27 #include <string_view> 28 #include <unordered_map> 29 #include <variant> 30 #include <xyz/openbmc_project/Common/error.hpp> 31 32 namespace phosphor 33 { 34 namespace network 35 { 36 37 using namespace phosphor::logging; 38 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 39 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 40 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed; 41 using Argument = xyz::openbmc_project::Common::InvalidArgument; 42 constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1"; 43 constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link"; 44 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 45 constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/"; 46 constexpr auto METHOD_GET = "Get"; 47 48 std::map<EthernetInterface::DHCPConf, std::string> mapDHCPToSystemd = { 49 {EthernetInterface::DHCPConf::both, "true"}, 50 {EthernetInterface::DHCPConf::v4, "ipv4"}, 51 {EthernetInterface::DHCPConf::v6, "ipv6"}, 52 {EthernetInterface::DHCPConf::none, "false"}}; 53 54 static stdplus::Fd& getIFSock() 55 { 56 using namespace stdplus::fd; 57 static auto fd = 58 socket(SocketDomain::INet, SocketType::Datagram, SocketProto::IP); 59 return fd; 60 } 61 62 EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, 63 const std::string& objPath, 64 DHCPConf dhcpEnabled, Manager& parent, 65 bool emitSignal, 66 std::optional<bool> enabled) : 67 Ifaces(bus, objPath.c_str(), 68 emitSignal ? Ifaces::action::defer_emit 69 : Ifaces::action::emit_no_signals), 70 bus(bus), manager(parent), objPath(objPath) 71 { 72 auto intfName = objPath.substr(objPath.rfind("/") + 1); 73 std::replace(intfName.begin(), intfName.end(), '_', '.'); 74 interfaceName(intfName); 75 EthernetInterfaceIntf::dhcpEnabled(dhcpEnabled); 76 EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRAFromConf()); 77 EthernetInterfaceIntf::nicEnabled(enabled ? *enabled : queryNicEnabled()); 78 const auto& gatewayList = manager.getRouteTable().getDefaultGateway(); 79 const auto& gateway6List = manager.getRouteTable().getDefaultGateway6(); 80 std::string defaultGateway; 81 std::string defaultGateway6; 82 83 for (const auto& gateway : gatewayList) 84 { 85 if (gateway.first == intfName) 86 { 87 defaultGateway = gateway.second; 88 break; 89 } 90 } 91 92 for (const auto& gateway6 : gateway6List) 93 { 94 if (gateway6.first == intfName) 95 { 96 defaultGateway6 = gateway6.second; 97 break; 98 } 99 } 100 101 EthernetInterfaceIntf::defaultGateway(defaultGateway); 102 EthernetInterfaceIntf::defaultGateway6(defaultGateway6); 103 // Don't get the mac address from the system as the mac address 104 // would be same as parent interface. 105 if (intfName.find(".") == std::string::npos) 106 { 107 MacAddressIntf::macAddress(getMACAddress(intfName)); 108 } 109 EthernetInterfaceIntf::ntpServers(getNTPServersFromConf()); 110 111 EthernetInterfaceIntf::linkUp(linkUp()); 112 EthernetInterfaceIntf::mtu(mtu()); 113 114 #ifdef NIC_SUPPORTS_ETHTOOL 115 InterfaceInfo ifInfo = EthernetInterface::getInterfaceInfo(); 116 117 EthernetInterfaceIntf::autoNeg(std::get<2>(ifInfo)); 118 EthernetInterfaceIntf::speed(std::get<0>(ifInfo)); 119 #endif 120 121 // Emit deferred signal. 122 if (emitSignal) 123 { 124 this->emit_object_added(); 125 } 126 } 127 128 static IP::Protocol convertFamily(int family) 129 { 130 switch (family) 131 { 132 case AF_INET: 133 return IP::Protocol::IPv4; 134 case AF_INET6: 135 return IP::Protocol::IPv6; 136 } 137 138 throw std::invalid_argument("Bad address family"); 139 } 140 141 void EthernetInterface::disableDHCP(IP::Protocol protocol) 142 { 143 DHCPConf dhcpState = EthernetInterfaceIntf::dhcpEnabled(); 144 if (dhcpState == EthernetInterface::DHCPConf::both) 145 { 146 if (protocol == IP::Protocol::IPv4) 147 { 148 dhcpEnabled(EthernetInterface::DHCPConf::v6); 149 } 150 else if (protocol == IP::Protocol::IPv6) 151 { 152 dhcpEnabled(EthernetInterface::DHCPConf::v4); 153 } 154 } 155 else if ((dhcpState == EthernetInterface::DHCPConf::v4) && 156 (protocol == IP::Protocol::IPv4)) 157 { 158 dhcpEnabled(EthernetInterface::DHCPConf::none); 159 } 160 else if ((dhcpState == EthernetInterface::DHCPConf::v6) && 161 (protocol == IP::Protocol::IPv6)) 162 { 163 dhcpEnabled(EthernetInterface::DHCPConf::none); 164 } 165 } 166 167 bool EthernetInterface::dhcpIsEnabled(IP::Protocol family) 168 { 169 const auto cur = EthernetInterfaceIntf::dhcpEnabled(); 170 return cur == EthernetInterface::DHCPConf::both || 171 (family == IP::Protocol::IPv6 && 172 cur == EthernetInterface::DHCPConf::v6) || 173 (family == IP::Protocol::IPv4 && 174 cur == EthernetInterface::DHCPConf::v4); 175 } 176 177 bool EthernetInterface::originIsManuallyAssigned(IP::AddressOrigin origin) 178 { 179 return ( 180 #ifdef LINK_LOCAL_AUTOCONFIGURATION 181 (origin == IP::AddressOrigin::Static) 182 #else 183 (origin == IP::AddressOrigin::Static || 184 origin == IP::AddressOrigin::LinkLocal) 185 #endif 186 187 ); 188 } 189 190 void EthernetInterface::createIPAddressObjects() 191 { 192 addrs.clear(); 193 194 auto addrs = getInterfaceAddrs()[interfaceName()]; 195 196 for (auto& addr : addrs) 197 { 198 IP::Protocol addressType = convertFamily(addr.addrType); 199 IP::AddressOrigin origin = IP::AddressOrigin::Static; 200 if (dhcpIsEnabled(addressType)) 201 { 202 origin = IP::AddressOrigin::DHCP; 203 } 204 if (isLinkLocalIP(addr.ipaddress)) 205 { 206 origin = IP::AddressOrigin::LinkLocal; 207 } 208 // Obsolete parameter 209 std::string gateway = ""; 210 211 std::string ipAddressObjectPath = generateObjectPath( 212 addressType, addr.ipaddress, addr.prefix, gateway, origin); 213 214 this->addrs.insert_or_assign( 215 addr.ipaddress, 216 std::make_shared<phosphor::network::IPAddress>( 217 bus, ipAddressObjectPath.c_str(), *this, addressType, 218 addr.ipaddress, origin, addr.prefix, gateway)); 219 } 220 } 221 222 void EthernetInterface::createStaticNeighborObjects() 223 { 224 staticNeighbors.clear(); 225 226 NeighborFilter filter; 227 filter.interface = ifIndex(); 228 filter.state = NUD_PERMANENT; 229 auto neighbors = getCurrentNeighbors(filter); 230 for (const auto& neighbor : neighbors) 231 { 232 if (!neighbor.mac) 233 { 234 continue; 235 } 236 std::string ip = toString(neighbor.address); 237 std::string mac = mac_address::toString(*neighbor.mac); 238 std::string objectPath = generateStaticNeighborObjectPath(ip, mac); 239 staticNeighbors.emplace(ip, 240 std::make_shared<phosphor::network::Neighbor>( 241 bus, objectPath.c_str(), *this, ip, mac, 242 Neighbor::State::Permanent)); 243 } 244 } 245 246 unsigned EthernetInterface::ifIndex() const 247 { 248 unsigned idx = if_nametoindex(interfaceName().c_str()); 249 if (idx == 0) 250 { 251 throw std::system_error(errno, std::generic_category(), 252 "if_nametoindex"); 253 } 254 return idx; 255 } 256 257 ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress, 258 uint8_t prefixLength, std::string gateway) 259 { 260 if (dhcpIsEnabled(protType)) 261 { 262 log<level::INFO>("DHCP enabled on the interface"), 263 entry("INTERFACE=%s", interfaceName().c_str()); 264 disableDHCP(protType); 265 // Delete the IP address object and that reloads the networkd 266 // to allow the same IP address to be set as Static IP 267 deleteObject(ipaddress); 268 } 269 270 IP::AddressOrigin origin = IP::AddressOrigin::Static; 271 272 int addressFamily = (protType == IP::Protocol::IPv4) ? AF_INET : AF_INET6; 273 274 if (!isValidIP(addressFamily, ipaddress)) 275 { 276 log<level::ERR>("Not a valid IP address"), 277 entry("ADDRESS=%s", ipaddress.c_str()); 278 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"), 279 Argument::ARGUMENT_VALUE(ipaddress.c_str())); 280 } 281 282 // Gateway is an obsolete parameter 283 gateway = ""; 284 285 if (!isValidPrefix(addressFamily, prefixLength)) 286 { 287 log<level::ERR>("PrefixLength is not correct "), 288 entry("PREFIXLENGTH=%" PRIu8, prefixLength); 289 elog<InvalidArgument>( 290 Argument::ARGUMENT_NAME("prefixLength"), 291 Argument::ARGUMENT_VALUE(std::to_string(prefixLength).c_str())); 292 } 293 294 std::string objectPath = 295 generateObjectPath(protType, ipaddress, prefixLength, gateway, origin); 296 this->addrs.insert_or_assign(ipaddress, 297 std::make_shared<phosphor::network::IPAddress>( 298 bus, objectPath.c_str(), *this, protType, 299 ipaddress, origin, prefixLength, gateway)); 300 301 writeConfigurationFile(); 302 manager.reloadConfigs(); 303 304 return objectPath; 305 } 306 307 ObjectPath EthernetInterface::neighbor(std::string ipAddress, 308 std::string macAddress) 309 { 310 if (!isValidIP(AF_INET, ipAddress) && !isValidIP(AF_INET6, ipAddress)) 311 { 312 log<level::ERR>("Not a valid IP address", 313 entry("ADDRESS=%s", ipAddress.c_str())); 314 elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"), 315 Argument::ARGUMENT_VALUE(ipAddress.c_str())); 316 } 317 if (!mac_address::isUnicast(mac_address::fromString(macAddress))) 318 { 319 log<level::ERR>("Not a valid MAC address", 320 entry("MACADDRESS=%s", ipAddress.c_str())); 321 elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"), 322 Argument::ARGUMENT_VALUE(macAddress.c_str())); 323 } 324 325 std::string objectPath = 326 generateStaticNeighborObjectPath(ipAddress, macAddress); 327 staticNeighbors.emplace(ipAddress, 328 std::make_shared<phosphor::network::Neighbor>( 329 bus, objectPath.c_str(), *this, ipAddress, 330 macAddress, Neighbor::State::Permanent)); 331 332 writeConfigurationFile(); 333 manager.reloadConfigs(); 334 335 return objectPath; 336 } 337 338 #ifdef NIC_SUPPORTS_ETHTOOL 339 /* 340 Enable this code if your NIC driver supports the ETHTOOL features. 341 Do this by adding the following to your phosphor-network*.bbappend file. 342 EXTRA_OECONF_append = " --enable-nic-ethtool=yes" 343 The default compile mode is to omit getInterfaceInfo() 344 */ 345 InterfaceInfo EthernetInterface::getInterfaceInfo() const 346 { 347 ifreq ifr = {}; 348 ethtool_cmd edata = {}; 349 LinkSpeed speed = {}; 350 Autoneg autoneg = {}; 351 DuplexMode duplex = {}; 352 LinkUp linkState = {}; 353 NICEnabled enabled = {}; 354 MTU mtuSize = {}; 355 356 std::strncpy(ifr.ifr_name, interfaceName().c_str(), IFNAMSIZ - 1); 357 ifr.ifr_data = reinterpret_cast<char*>(&edata); 358 359 edata.cmd = ETHTOOL_GSET; 360 try 361 { 362 getIFSock().ioctl(SIOCETHTOOL, &ifr); 363 speed = edata.speed; 364 duplex = edata.duplex; 365 autoneg = edata.autoneg; 366 } 367 catch (const std::exception& e) 368 { 369 } 370 371 enabled = nicEnabled(); 372 linkState = linkUp(); 373 mtuSize = mtu(); 374 375 return std::make_tuple(speed, duplex, autoneg, linkState, enabled, mtuSize); 376 } 377 #endif 378 379 /** @brief get the mac address of the interface. 380 * @return macaddress on success 381 */ 382 383 std::string 384 EthernetInterface::getMACAddress(const std::string& interfaceName) const 385 { 386 std::string activeMACAddr = MacAddressIntf::macAddress(); 387 388 ifreq ifr = {}; 389 std::strncpy(ifr.ifr_name, interfaceName.c_str(), IFNAMSIZ - 1); 390 try 391 { 392 getIFSock().ioctl(SIOCGIFHWADDR, &ifr); 393 } 394 catch (const std::exception& e) 395 { 396 log<level::ERR>("ioctl failed for SIOCGIFHWADDR:", 397 entry("ERROR=%s", e.what())); 398 elog<InternalFailure>(); 399 } 400 401 static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= sizeof(ether_addr)); 402 std::string_view hwaddr(reinterpret_cast<char*>(ifr.ifr_hwaddr.sa_data), 403 sizeof(ifr.ifr_hwaddr.sa_data)); 404 return mac_address::toString(stdplus::raw::copyFrom<ether_addr>(hwaddr)); 405 } 406 407 std::string EthernetInterface::generateId(const std::string& ipaddress, 408 uint8_t prefixLength, 409 const std::string& gateway, 410 const std::string& origin) 411 { 412 std::stringstream hexId; 413 std::string hashString = ipaddress; 414 hashString += std::to_string(prefixLength); 415 hashString += gateway; 416 hashString += origin; 417 418 // Only want 8 hex digits. 419 hexId << std::hex << ((std::hash<std::string>{}(hashString)) & 0xFFFFFFFF); 420 return hexId.str(); 421 } 422 423 std::string EthernetInterface::generateNeighborId(const std::string& ipAddress, 424 const std::string& macAddress) 425 { 426 std::stringstream hexId; 427 std::string hashString = ipAddress + macAddress; 428 429 // Only want 8 hex digits. 430 hexId << std::hex << ((std::hash<std::string>{}(hashString)) & 0xFFFFFFFF); 431 return hexId.str(); 432 } 433 434 void EthernetInterface::deleteObject(const std::string& ipaddress) 435 { 436 auto it = addrs.find(ipaddress); 437 if (it == addrs.end()) 438 { 439 log<level::ERR>("DeleteObject:Unable to find the object."); 440 return; 441 } 442 this->addrs.erase(it); 443 444 writeConfigurationFile(); 445 manager.reloadConfigs(); 446 } 447 448 void EthernetInterface::deleteStaticNeighborObject(const std::string& ipAddress) 449 { 450 auto it = staticNeighbors.find(ipAddress); 451 if (it == staticNeighbors.end()) 452 { 453 log<level::ERR>( 454 "DeleteStaticNeighborObject:Unable to find the object."); 455 return; 456 } 457 staticNeighbors.erase(it); 458 459 writeConfigurationFile(); 460 manager.reloadConfigs(); 461 } 462 463 void EthernetInterface::deleteVLANFromSystem(const std::string& interface) 464 { 465 auto confDir = manager.getConfDir(); 466 fs::path networkFile = confDir; 467 networkFile /= systemd::config::networkFilePrefix + interface + 468 systemd::config::networkFileSuffix; 469 470 fs::path deviceFile = confDir; 471 deviceFile /= interface + systemd::config::deviceFileSuffix; 472 473 // delete the vlan network file 474 if (fs::is_regular_file(networkFile)) 475 { 476 fs::remove(networkFile); 477 } 478 479 // delete the vlan device file 480 if (fs::is_regular_file(deviceFile)) 481 { 482 fs::remove(deviceFile); 483 } 484 485 // TODO systemd doesn't delete the virtual network interface 486 // even after deleting all the related configuartion. 487 // https://github.com/systemd/systemd/issues/6600 488 try 489 { 490 deleteInterface(interface); 491 } 492 catch (const InternalFailure& e) 493 { 494 commit<InternalFailure>(); 495 } 496 } 497 498 void EthernetInterface::deleteVLANObject(const std::string& interface) 499 { 500 auto it = vlanInterfaces.find(interface); 501 if (it == vlanInterfaces.end()) 502 { 503 log<level::ERR>("DeleteVLANObject:Unable to find the object", 504 entry("INTERFACE=%s", interface.c_str())); 505 return; 506 } 507 508 deleteVLANFromSystem(interface); 509 // delete the interface 510 vlanInterfaces.erase(it); 511 512 writeConfigurationFile(); 513 manager.reloadConfigs(); 514 } 515 516 std::string EthernetInterface::generateObjectPath( 517 IP::Protocol addressType, const std::string& ipaddress, 518 uint8_t prefixLength, const std::string& gateway, 519 IP::AddressOrigin origin) const 520 { 521 std::string type = convertForMessage(addressType); 522 type = type.substr(type.rfind('.') + 1); 523 std::transform(type.begin(), type.end(), type.begin(), ::tolower); 524 525 std::filesystem::path objectPath; 526 objectPath /= objPath; 527 objectPath /= type; 528 objectPath /= 529 generateId(ipaddress, prefixLength, gateway, convertForMessage(origin)); 530 return objectPath.string(); 531 } 532 533 std::string EthernetInterface::generateStaticNeighborObjectPath( 534 const std::string& ipAddress, const std::string& macAddress) const 535 { 536 std::filesystem::path objectPath; 537 objectPath /= objPath; 538 objectPath /= "static_neighbor"; 539 objectPath /= generateNeighborId(ipAddress, macAddress); 540 return objectPath.string(); 541 } 542 543 bool EthernetInterface::ipv6AcceptRA(bool value) 544 { 545 if (value == EthernetInterfaceIntf::ipv6AcceptRA()) 546 { 547 return value; 548 } 549 EthernetInterfaceIntf::ipv6AcceptRA(value); 550 551 writeConfigurationFile(); 552 manager.reloadConfigs(); 553 554 return value; 555 } 556 557 EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value) 558 { 559 if (value == EthernetInterfaceIntf::dhcpEnabled()) 560 { 561 return value; 562 } 563 EthernetInterfaceIntf::dhcpEnabled(value); 564 565 writeConfigurationFile(); 566 manager.reloadConfigs(); 567 568 return value; 569 } 570 571 bool EthernetInterface::linkUp() const 572 { 573 bool value = EthernetInterfaceIntf::linkUp(); 574 575 ifreq ifr = {}; 576 std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); 577 try 578 { 579 getIFSock().ioctl(SIOCGIFFLAGS, &ifr); 580 value = static_cast<bool>(ifr.ifr_flags & IFF_RUNNING); 581 } 582 catch (const std::exception& e) 583 { 584 log<level::ERR>("ioctl failed for SIOCGIFFLAGS:", 585 entry("ERROR=%s", e.what())); 586 } 587 return value; 588 } 589 590 size_t EthernetInterface::mtu() const 591 { 592 size_t value = EthernetInterfaceIntf::mtu(); 593 594 ifreq ifr = {}; 595 std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); 596 try 597 { 598 getIFSock().ioctl(SIOCGIFMTU, &ifr); 599 value = ifr.ifr_mtu; 600 } 601 catch (const std::exception& e) 602 { 603 log<level::ERR>("ioctl failed for SIOCGIFMTU:", 604 entry("ERROR=%s", e.what())); 605 } 606 return value; 607 } 608 609 size_t EthernetInterface::mtu(size_t value) 610 { 611 if (value == EthernetInterfaceIntf::mtu()) 612 { 613 return value; 614 } 615 else if (value == 0) 616 { 617 return EthernetInterfaceIntf::mtu(); 618 } 619 620 ifreq ifr = {}; 621 std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1); 622 ifr.ifr_mtu = value; 623 624 try 625 { 626 getIFSock().ioctl(SIOCSIFMTU, &ifr); 627 } 628 catch (const std::exception& e) 629 { 630 log<level::ERR>("ioctl failed for SIOCSIFMTU:", 631 entry("ERROR=%s", strerror(errno))); 632 return EthernetInterfaceIntf::mtu(); 633 } 634 635 EthernetInterfaceIntf::mtu(value); 636 return value; 637 } 638 639 bool EthernetInterface::queryNicEnabled() const 640 { 641 constexpr auto svc = "org.freedesktop.network1"; 642 constexpr auto intf = "org.freedesktop.network1.Link"; 643 constexpr auto prop = "AdministrativeState"; 644 char* rpath; 645 sd_bus_path_encode("/org/freedesktop/network1/link", 646 std::to_string(ifIndex()).c_str(), &rpath); 647 std::string path(rpath); 648 free(rpath); 649 650 // Store / Parser for the AdministrativeState return value 651 std::optional<bool> ret; 652 auto cb = [&](const std::string& state) { 653 if (state != "initialized") 654 { 655 ret = state != "unmanaged"; 656 } 657 }; 658 659 // Build a matcher before making the property call to ensure we 660 // can eventually get the value. 661 sdbusplus::bus::match::match match( 662 bus, 663 fmt::format("type='signal',sender='{}',path='{}',interface='{}',member=" 664 "'PropertiesChanged',arg0='{}',", 665 svc, path, PROPERTY_INTERFACE, intf) 666 .c_str(), 667 [&](sdbusplus::message::message& m) { 668 std::string intf; 669 std::unordered_map<std::string, std::variant<std::string>> values; 670 try 671 { 672 m.read(intf, values); 673 auto it = values.find(prop); 674 // Ignore properties that aren't AdministrativeState 675 if (it != values.end()) 676 { 677 cb(std::get<std::string>(it->second)); 678 } 679 } 680 catch (const std::exception& e) 681 { 682 log<level::ERR>( 683 fmt::format( 684 "AdministrativeState match parsing failed on {}: {}", 685 interfaceName(), e.what()) 686 .c_str(), 687 entry("INTERFACE=%s", interfaceName().c_str()), 688 entry("ERROR=%s", e.what())); 689 } 690 }); 691 692 // Actively call for the value in case the interface is already configured 693 auto method = 694 bus.new_method_call(svc, path.c_str(), PROPERTY_INTERFACE, METHOD_GET); 695 method.append(intf, prop); 696 try 697 { 698 auto reply = bus.call(method); 699 std::variant<std::string> state; 700 reply.read(state); 701 cb(std::get<std::string>(state)); 702 } 703 catch (const std::exception& e) 704 { 705 log<level::ERR>( 706 fmt::format("Failed to get AdministrativeState on {}: {}", 707 interfaceName(), e.what()) 708 .c_str(), 709 entry("INTERFACE=%s", interfaceName().c_str()), 710 entry("ERROR=%s", e.what())); 711 } 712 713 // The interface is not yet configured by systemd-networkd, wait until it 714 // signals us a valid state. 715 while (!ret) 716 { 717 bus.wait(); 718 bus.process_discard(); 719 } 720 721 return *ret; 722 } 723 724 static void setNICAdminState(const char* intf, bool up) 725 { 726 ifreq ifr = {}; 727 std::strncpy(ifr.ifr_name, intf, IF_NAMESIZE - 1); 728 getIFSock().ioctl(SIOCGIFFLAGS, &ifr); 729 730 ifr.ifr_flags &= ~IFF_UP; 731 ifr.ifr_flags |= up ? IFF_UP : 0; 732 getIFSock().ioctl(SIOCSIFFLAGS, &ifr); 733 } 734 735 bool EthernetInterface::nicEnabled(bool value) 736 { 737 if (value == EthernetInterfaceIntf::nicEnabled()) 738 { 739 return value; 740 } 741 742 EthernetInterfaceIntf::nicEnabled(value); 743 writeConfigurationFile(); 744 if (!value) 745 { 746 // We only need to bring down the interface, networkd will always bring 747 // up managed interfaces 748 manager.addReloadPreHook([ifname = interfaceName()]() { 749 setNICAdminState(ifname.c_str(), false); 750 }); 751 } 752 manager.reloadConfigs(); 753 754 return value; 755 } 756 757 ServerList EthernetInterface::nameservers(ServerList /*value*/) 758 { 759 elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property")); 760 return EthernetInterfaceIntf::nameservers(); 761 } 762 763 ServerList EthernetInterface::staticNameServers(ServerList value) 764 { 765 for (const auto& nameserverip : value) 766 { 767 if (!isValidIP(AF_INET, nameserverip) && 768 !isValidIP(AF_INET6, nameserverip)) 769 { 770 log<level::ERR>("Not a valid IP address"), 771 entry("ADDRESS=%s", nameserverip.c_str()); 772 elog<InvalidArgument>( 773 Argument::ARGUMENT_NAME("StaticNameserver"), 774 Argument::ARGUMENT_VALUE(nameserverip.c_str())); 775 } 776 } 777 try 778 { 779 EthernetInterfaceIntf::staticNameServers(value); 780 781 writeConfigurationFile(); 782 manager.reloadConfigs(); 783 } 784 catch (const InternalFailure& e) 785 { 786 log<level::ERR>("Exception processing DNS entries"); 787 } 788 return EthernetInterfaceIntf::staticNameServers(); 789 } 790 791 void EthernetInterface::loadNameServers() 792 { 793 EthernetInterfaceIntf::nameservers(getNameServerFromResolvd()); 794 EthernetInterfaceIntf::staticNameServers(getstaticNameServerFromConf()); 795 } 796 797 ServerList EthernetInterface::getstaticNameServerFromConf() 798 { 799 fs::path confPath = manager.getConfDir(); 800 801 std::string fileName = systemd::config::networkFilePrefix + 802 interfaceName() + systemd::config::networkFileSuffix; 803 confPath /= fileName; 804 ServerList servers; 805 config::Parser parser(confPath.string()); 806 auto rc = config::ReturnCode::SUCCESS; 807 808 std::tie(rc, servers) = parser.getValues("Network", "DNS"); 809 if (rc != config::ReturnCode::SUCCESS) 810 { 811 log<level::DEBUG>("Unable to get the value for network[DNS]", 812 entry("RC=%d", rc)); 813 } 814 return servers; 815 } 816 817 ServerList EthernetInterface::getNameServerFromResolvd() 818 { 819 ServerList servers; 820 std::string OBJ_PATH = RESOLVED_SERVICE_PATH + std::to_string(ifIndex()); 821 822 /* 823 The DNS property under org.freedesktop.resolve1.Link interface contains 824 an array containing all DNS servers currently used by resolved. It 825 contains similar information as the DNS server data written to 826 /run/systemd/resolve/resolv.conf. 827 828 Each structure in the array consists of a numeric network interface index, 829 an address family, and a byte array containing the DNS server address 830 (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6). 831 The array contains DNS servers configured system-wide, including those 832 possibly read from a foreign /etc/resolv.conf or the DNS= setting in 833 /etc/systemd/resolved.conf, as well as per-interface DNS server 834 information either retrieved from systemd-networkd or configured by 835 external software via SetLinkDNS(). 836 */ 837 838 using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>; 839 std::variant<type> name; // Variable to capture the DNS property 840 auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(), 841 PROPERTY_INTERFACE, METHOD_GET); 842 843 method.append(RESOLVED_INTERFACE, "DNS"); 844 auto reply = bus.call(method); 845 846 try 847 { 848 reply.read(name); 849 } 850 catch (const sdbusplus::exception::exception& e) 851 { 852 log<level::ERR>("Failed to get DNS information from Systemd-Resolved"); 853 } 854 auto tupleVector = std::get_if<type>(&name); 855 for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i) 856 { 857 int addressFamily = std::get<0>(*i); 858 std::vector<uint8_t>& ipaddress = std::get<1>(*i); 859 860 switch (addressFamily) 861 { 862 case AF_INET: 863 if (ipaddress.size() == sizeof(struct in_addr)) 864 { 865 servers.push_back(toString( 866 *reinterpret_cast<struct in_addr*>(ipaddress.data()))); 867 } 868 else 869 { 870 log<level::ERR>( 871 "Invalid data recived from Systemd-Resolved"); 872 } 873 break; 874 875 case AF_INET6: 876 if (ipaddress.size() == sizeof(struct in6_addr)) 877 { 878 servers.push_back(toString( 879 *reinterpret_cast<struct in6_addr*>(ipaddress.data()))); 880 } 881 else 882 { 883 log<level::ERR>( 884 "Invalid data recived from Systemd-Resolved"); 885 } 886 break; 887 888 default: 889 log<level::ERR>( 890 "Unsupported address family in DNS from Systemd-Resolved"); 891 break; 892 } 893 } 894 return servers; 895 } 896 897 void EthernetInterface::loadVLAN(VlanId id) 898 { 899 std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id); 900 std::string path = objPath; 901 path += "_" + std::to_string(id); 902 903 DHCPConf dhcpEnabled = 904 getDHCPValue(manager.getConfDir().string(), vlanInterfaceName); 905 auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( 906 bus, path.c_str(), dhcpEnabled, EthernetInterfaceIntf::nicEnabled(), id, 907 *this, manager); 908 909 // Fetch the ip address from the system 910 // and create the dbus object. 911 vlanIntf->createIPAddressObjects(); 912 vlanIntf->createStaticNeighborObjects(); 913 vlanIntf->loadNameServers(); 914 915 this->vlanInterfaces.emplace(std::move(vlanInterfaceName), 916 std::move(vlanIntf)); 917 } 918 919 ObjectPath EthernetInterface::createVLAN(VlanId id) 920 { 921 std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id); 922 923 if (this->vlanInterfaces.count(vlanInterfaceName)) 924 { 925 log<level::ERR>("VLAN already exists", entry("VLANID=%u", id)); 926 elog<InvalidArgument>( 927 Argument::ARGUMENT_NAME("VLANId"), 928 Argument::ARGUMENT_VALUE(std::to_string(id).c_str())); 929 } 930 931 std::string path = objPath; 932 path += "_" + std::to_string(id); 933 934 // Pass the parents nicEnabled property, so that the child 935 // VLAN interface can inherit. 936 auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>( 937 bus, path.c_str(), EthernetInterface::DHCPConf::none, 938 EthernetInterfaceIntf::nicEnabled(), id, *this, manager); 939 940 // write the device file for the vlan interface. 941 vlanIntf->writeDeviceFile(); 942 943 this->vlanInterfaces.emplace(vlanInterfaceName, std::move(vlanIntf)); 944 945 writeConfigurationFile(); 946 manager.reloadConfigs(); 947 948 return path; 949 } 950 951 bool EthernetInterface::getIPv6AcceptRAFromConf() 952 { 953 fs::path confPath = manager.getConfDir(); 954 955 std::string fileName = systemd::config::networkFilePrefix + 956 interfaceName() + systemd::config::networkFileSuffix; 957 confPath /= fileName; 958 config::ValueList values; 959 config::Parser parser(confPath.string()); 960 auto rc = config::ReturnCode::SUCCESS; 961 std::tie(rc, values) = parser.getValues("Network", "IPv6AcceptRA"); 962 if (rc != config::ReturnCode::SUCCESS) 963 { 964 log<level::DEBUG>("Unable to get the value for Network[IPv6AcceptRA]", 965 entry("rc=%d", rc)); 966 return false; 967 } 968 return (values[0] == "true"); 969 } 970 971 ServerList EthernetInterface::getNTPServersFromConf() 972 { 973 fs::path confPath = manager.getConfDir(); 974 975 std::string fileName = systemd::config::networkFilePrefix + 976 interfaceName() + systemd::config::networkFileSuffix; 977 confPath /= fileName; 978 979 ServerList servers; 980 config::Parser parser(confPath.string()); 981 auto rc = config::ReturnCode::SUCCESS; 982 983 std::tie(rc, servers) = parser.getValues("Network", "NTP"); 984 if (rc != config::ReturnCode::SUCCESS) 985 { 986 log<level::DEBUG>("Unable to get the value for Network[NTP]", 987 entry("rc=%d", rc)); 988 } 989 990 return servers; 991 } 992 993 ServerList EthernetInterface::ntpServers(ServerList servers) 994 { 995 auto ntpServers = EthernetInterfaceIntf::ntpServers(servers); 996 997 writeConfigurationFile(); 998 manager.reloadConfigs(); 999 1000 return ntpServers; 1001 } 1002 // Need to merge the below function with the code which writes the 1003 // config file during factory reset. 1004 // TODO openbmc/openbmc#1751 1005 1006 void EthernetInterface::writeConfigurationFile() 1007 { 1008 // write all the static ip address in the systemd-network conf file 1009 1010 using namespace std::string_literals; 1011 namespace fs = std::filesystem; 1012 1013 // if there is vlan interafce then write the configuration file 1014 // for vlan also. 1015 1016 for (const auto& intf : vlanInterfaces) 1017 { 1018 intf.second->writeConfigurationFile(); 1019 } 1020 1021 fs::path confPath = manager.getConfDir(); 1022 1023 std::string fileName = systemd::config::networkFilePrefix + 1024 interfaceName() + systemd::config::networkFileSuffix; 1025 confPath /= fileName; 1026 std::fstream stream; 1027 1028 stream.open(confPath.c_str(), std::fstream::out); 1029 if (!stream.is_open()) 1030 { 1031 log<level::ERR>("Unable to open the file", 1032 entry("FILE=%s", confPath.c_str())); 1033 elog<InternalFailure>(); 1034 } 1035 1036 // Write the device 1037 stream << "[Match]\n"; 1038 stream << "Name=" << interfaceName() << "\n"; 1039 1040 auto addrs = getAddresses(); 1041 1042 // Write the link section 1043 stream << "[Link]\n"; 1044 #ifdef PERSIST_MAC 1045 auto mac = MacAddressIntf::macAddress(); 1046 if (!mac.empty()) 1047 { 1048 stream << "MACAddress=" << mac << "\n"; 1049 } 1050 #endif 1051 1052 if (!EthernetInterfaceIntf::nicEnabled()) 1053 { 1054 stream << "Unmanaged=yes\n"; 1055 } 1056 1057 // write the network section 1058 stream << "[Network]\n"; 1059 #ifdef LINK_LOCAL_AUTOCONFIGURATION 1060 stream << "LinkLocalAddressing=yes\n"; 1061 #else 1062 stream << "LinkLocalAddressing=no\n"; 1063 #endif 1064 stream << std::boolalpha 1065 << "IPv6AcceptRA=" << EthernetInterfaceIntf::ipv6AcceptRA() << "\n"; 1066 1067 // Add the VLAN entry 1068 for (const auto& intf : vlanInterfaces) 1069 { 1070 stream << "VLAN=" << intf.second->EthernetInterface::interfaceName() 1071 << "\n"; 1072 } 1073 // Add the NTP server 1074 for (const auto& ntp : EthernetInterfaceIntf::ntpServers()) 1075 { 1076 stream << "NTP=" << ntp << "\n"; 1077 } 1078 1079 // Add the DNS entry 1080 for (const auto& dns : EthernetInterfaceIntf::staticNameServers()) 1081 { 1082 stream << "DNS=" << dns << "\n"; 1083 } 1084 1085 // Add the DHCP entry 1086 stream << "DHCP="s + 1087 mapDHCPToSystemd[EthernetInterfaceIntf::dhcpEnabled()] + "\n"; 1088 1089 stream << "[IPv6AcceptRA]\n"; 1090 stream << "DHCPv6Client="; 1091 stream << (dhcpIsEnabled(IP::Protocol::IPv6) ? "true" : "false"); 1092 stream << "\n"; 1093 1094 // Static IP addresses 1095 for (const auto& addr : addrs) 1096 { 1097 if (originIsManuallyAssigned(addr.second->origin()) && 1098 !dhcpIsEnabled(addr.second->type())) 1099 { 1100 // Process all static addresses 1101 std::string address = addr.second->address() + "/" + 1102 std::to_string(addr.second->prefixLength()); 1103 1104 // build the address entries. Do not use [Network] shortcuts to 1105 // insert address entries. 1106 stream << "[Address]\n"; 1107 stream << "Address=" << address << "\n"; 1108 } 1109 } 1110 1111 if (!dhcpIsEnabled(IP::Protocol::IPv4)) 1112 { 1113 auto gateway = EthernetInterfaceIntf::defaultGateway(); 1114 if (!gateway.empty()) 1115 { 1116 stream << "[Route]\n"; 1117 stream << "Gateway=" << gateway << "\n"; 1118 } 1119 } 1120 1121 if (!dhcpIsEnabled(IP::Protocol::IPv6)) 1122 { 1123 auto gateway6 = EthernetInterfaceIntf::defaultGateway6(); 1124 if (!gateway6.empty()) 1125 { 1126 stream << "[Route]\n"; 1127 stream << "Gateway=" << gateway6 << "\n"; 1128 } 1129 } 1130 1131 // Write the neighbor sections 1132 for (const auto& neighbor : staticNeighbors) 1133 { 1134 stream << "[Neighbor]" 1135 << "\n"; 1136 stream << "Address=" << neighbor.second->ipAddress() << "\n"; 1137 stream << "MACAddress=" << neighbor.second->macAddress() << "\n"; 1138 } 1139 1140 // Write the dhcp section irrespective of whether DHCP is enabled or not 1141 writeDHCPSection(stream); 1142 1143 stream.close(); 1144 } 1145 1146 void EthernetInterface::writeDHCPSection(std::fstream& stream) 1147 { 1148 using namespace std::string_literals; 1149 // write the dhcp section 1150 stream << "[DHCP]\n"; 1151 1152 // Hardcoding the client identifier to mac, to address below issue 1153 // https://github.com/openbmc/openbmc/issues/1280 1154 stream << "ClientIdentifier=mac\n"; 1155 if (manager.getDHCPConf()) 1156 { 1157 auto value = manager.getDHCPConf()->dnsEnabled() ? "true"s : "false"s; 1158 stream << "UseDNS="s + value + "\n"; 1159 stream << "UseDomains="s + value + "\n"; 1160 1161 value = manager.getDHCPConf()->ntpEnabled() ? "true"s : "false"s; 1162 stream << "UseNTP="s + value + "\n"; 1163 1164 value = manager.getDHCPConf()->hostNameEnabled() ? "true"s : "false"s; 1165 stream << "UseHostname="s + value + "\n"; 1166 1167 value = 1168 manager.getDHCPConf()->sendHostNameEnabled() ? "true"s : "false"s; 1169 stream << "SendHostname="s + value + "\n"; 1170 } 1171 } 1172 1173 std::string EthernetInterface::macAddress([[maybe_unused]] std::string value) 1174 { 1175 #ifdef PERSIST_MAC 1176 ether_addr newMAC; 1177 try 1178 { 1179 newMAC = mac_address::fromString(value); 1180 } 1181 catch (const std::invalid_argument&) 1182 { 1183 log<level::ERR>("MACAddress is not valid.", 1184 entry("MAC=%s", value.c_str())); 1185 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 1186 Argument::ARGUMENT_VALUE(value.c_str())); 1187 } 1188 if (!mac_address::isUnicast(newMAC)) 1189 { 1190 log<level::ERR>("MACAddress is not valid.", 1191 entry("MAC=%s", value.c_str())); 1192 elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"), 1193 Argument::ARGUMENT_VALUE(value.c_str())); 1194 } 1195 1196 auto interface = interfaceName(); 1197 std::string validMAC = mac_address::toString(newMAC); 1198 1199 // We don't need to update the system if the address is unchanged 1200 ether_addr oldMAC = mac_address::fromString(MacAddressIntf::macAddress()); 1201 if (!stdplus::raw::equal(newMAC, oldMAC)) 1202 { 1203 // Update everything that depends on the MAC value 1204 for (const auto& [name, intf] : vlanInterfaces) 1205 { 1206 intf->MacAddressIntf::macAddress(validMAC); 1207 } 1208 MacAddressIntf::macAddress(validMAC); 1209 1210 writeConfigurationFile(); 1211 manager.addReloadPreHook([interface]() { 1212 // The MAC and LLADDRs will only update if the NIC is already down 1213 setNICAdminState(interface.c_str(), false); 1214 }); 1215 manager.reloadConfigs(); 1216 } 1217 1218 #ifdef HAVE_UBOOT_ENV 1219 // Ensure that the valid address is stored in the u-boot-env 1220 auto envVar = interfaceToUbootEthAddr(interface.c_str()); 1221 if (envVar) 1222 { 1223 // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100; 1224 // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB 1225 execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(), 1226 validMAC.c_str()); 1227 } 1228 #endif // HAVE_UBOOT_ENV 1229 1230 return value; 1231 #else 1232 elog<NotAllowed>( 1233 NotAllowedArgument::REASON("Writing MAC address is not allowed")); 1234 #endif // PERSIST_MAC 1235 } 1236 1237 void EthernetInterface::deleteAll() 1238 { 1239 // clear all the ip on the interface 1240 addrs.clear(); 1241 1242 writeConfigurationFile(); 1243 manager.reloadConfigs(); 1244 } 1245 1246 std::string EthernetInterface::defaultGateway(std::string gateway) 1247 { 1248 auto gw = EthernetInterfaceIntf::defaultGateway(); 1249 if (gw == gateway) 1250 { 1251 return gw; 1252 } 1253 1254 if (!isValidIP(AF_INET, gateway) && !gateway.empty()) 1255 { 1256 log<level::ERR>("Not a valid v4 Gateway", 1257 entry("GATEWAY=%s", gateway.c_str())); 1258 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 1259 Argument::ARGUMENT_VALUE(gateway.c_str())); 1260 } 1261 gw = EthernetInterfaceIntf::defaultGateway(gateway); 1262 1263 writeConfigurationFile(); 1264 manager.reloadConfigs(); 1265 1266 return gw; 1267 } 1268 1269 std::string EthernetInterface::defaultGateway6(std::string gateway) 1270 { 1271 auto gw = EthernetInterfaceIntf::defaultGateway6(); 1272 if (gw == gateway) 1273 { 1274 return gw; 1275 } 1276 1277 if (!isValidIP(AF_INET6, gateway) && !gateway.empty()) 1278 { 1279 log<level::ERR>("Not a valid v6 Gateway", 1280 entry("GATEWAY=%s", gateway.c_str())); 1281 elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"), 1282 Argument::ARGUMENT_VALUE(gateway.c_str())); 1283 } 1284 gw = EthernetInterfaceIntf::defaultGateway6(gateway); 1285 1286 writeConfigurationFile(); 1287 manager.reloadConfigs(); 1288 1289 return gw; 1290 } 1291 } // namespace network 1292 } // namespace phosphor 1293