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