1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "bmcweb_config.h" 19 20 #include "app.hpp" 21 #include "dbus_singleton.hpp" 22 #include "dbus_utility.hpp" 23 #include "error_messages.hpp" 24 #include "health.hpp" 25 #include "human_sort.hpp" 26 #include "query.hpp" 27 #include "registries/privilege_registry.hpp" 28 #include "utils/ip_utils.hpp" 29 #include "utils/json_utils.hpp" 30 31 #include <boost/url/format.hpp> 32 33 #include <array> 34 #include <optional> 35 #include <ranges> 36 #include <regex> 37 #include <string_view> 38 #include <vector> 39 40 namespace redfish 41 { 42 43 enum class LinkType 44 { 45 Local, 46 Global 47 }; 48 49 /** 50 * Structure for keeping IPv4 data required by Redfish 51 */ 52 struct IPv4AddressData 53 { 54 std::string id; 55 std::string address; 56 std::string domain; 57 std::string gateway; 58 std::string netmask; 59 std::string origin; 60 LinkType linktype{}; 61 bool isActive{}; 62 }; 63 64 /** 65 * Structure for keeping IPv6 data required by Redfish 66 */ 67 struct IPv6AddressData 68 { 69 std::string id; 70 std::string address; 71 std::string origin; 72 uint8_t prefixLength = 0; 73 }; 74 /** 75 * Structure for keeping basic single Ethernet Interface information 76 * available from DBus 77 */ 78 struct EthernetInterfaceData 79 { 80 uint32_t speed; 81 size_t mtuSize; 82 bool autoNeg; 83 bool dnsEnabled; 84 bool ntpEnabled; 85 bool hostNameEnabled; 86 bool linkUp; 87 bool nicEnabled; 88 bool ipv6AcceptRa; 89 std::string dhcpEnabled; 90 std::string operatingMode; 91 std::string hostName; 92 std::string defaultGateway; 93 std::string ipv6DefaultGateway; 94 std::string macAddress; 95 std::optional<uint32_t> vlanId; 96 std::vector<std::string> nameServers; 97 std::vector<std::string> staticNameServers; 98 std::vector<std::string> domainnames; 99 }; 100 101 struct DHCPParameters 102 { 103 std::optional<bool> dhcpv4Enabled; 104 std::optional<bool> useDnsServers; 105 std::optional<bool> useNtpServers; 106 std::optional<bool> useDomainName; 107 std::optional<std::string> dhcpv6OperatingMode; 108 }; 109 110 // Helper function that changes bits netmask notation (i.e. /24) 111 // into full dot notation 112 inline std::string getNetmask(unsigned int bits) 113 { 114 uint32_t value = 0xffffffff << (32 - bits); 115 std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 116 std::to_string((value >> 16) & 0xff) + "." + 117 std::to_string((value >> 8) & 0xff) + "." + 118 std::to_string(value & 0xff); 119 return netmask; 120 } 121 122 inline bool translateDhcpEnabledToBool(const std::string& inputDHCP, 123 bool isIPv4) 124 { 125 if (isIPv4) 126 { 127 return ( 128 (inputDHCP == 129 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4") || 130 (inputDHCP == 131 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both")); 132 } 133 return ((inputDHCP == 134 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6") || 135 (inputDHCP == 136 "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both")); 137 } 138 139 inline std::string getDhcpEnabledEnumeration(bool isIPv4, bool isIPv6) 140 { 141 if (isIPv4 && isIPv6) 142 { 143 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both"; 144 } 145 if (isIPv4) 146 { 147 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4"; 148 } 149 if (isIPv6) 150 { 151 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6"; 152 } 153 return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.none"; 154 } 155 156 inline std::string 157 translateAddressOriginDbusToRedfish(const std::string& inputOrigin, 158 bool isIPv4) 159 { 160 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static") 161 { 162 return "Static"; 163 } 164 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal") 165 { 166 if (isIPv4) 167 { 168 return "IPv4LinkLocal"; 169 } 170 return "LinkLocal"; 171 } 172 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") 173 { 174 if (isIPv4) 175 { 176 return "DHCP"; 177 } 178 return "DHCPv6"; 179 } 180 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC") 181 { 182 return "SLAAC"; 183 } 184 return ""; 185 } 186 187 inline bool extractEthernetInterfaceData( 188 const std::string& ethifaceId, 189 const dbus::utility::ManagedObjectType& dbusData, 190 EthernetInterfaceData& ethData) 191 { 192 bool idFound = false; 193 for (const auto& objpath : dbusData) 194 { 195 for (const auto& ifacePair : objpath.second) 196 { 197 if (objpath.first == "/xyz/openbmc_project/network/" + ethifaceId) 198 { 199 idFound = true; 200 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 201 { 202 for (const auto& propertyPair : ifacePair.second) 203 { 204 if (propertyPair.first == "MACAddress") 205 { 206 const std::string* mac = 207 std::get_if<std::string>(&propertyPair.second); 208 if (mac != nullptr) 209 { 210 ethData.macAddress = *mac; 211 } 212 } 213 } 214 } 215 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN") 216 { 217 for (const auto& propertyPair : ifacePair.second) 218 { 219 if (propertyPair.first == "Id") 220 { 221 const uint32_t* id = 222 std::get_if<uint32_t>(&propertyPair.second); 223 if (id != nullptr) 224 { 225 ethData.vlanId = *id; 226 } 227 } 228 } 229 } 230 else if (ifacePair.first == 231 "xyz.openbmc_project.Network.EthernetInterface") 232 { 233 for (const auto& propertyPair : ifacePair.second) 234 { 235 if (propertyPair.first == "AutoNeg") 236 { 237 const bool* autoNeg = 238 std::get_if<bool>(&propertyPair.second); 239 if (autoNeg != nullptr) 240 { 241 ethData.autoNeg = *autoNeg; 242 } 243 } 244 else if (propertyPair.first == "Speed") 245 { 246 const uint32_t* speed = 247 std::get_if<uint32_t>(&propertyPair.second); 248 if (speed != nullptr) 249 { 250 ethData.speed = *speed; 251 } 252 } 253 else if (propertyPair.first == "MTU") 254 { 255 const size_t* mtuSize = 256 std::get_if<size_t>(&propertyPair.second); 257 if (mtuSize != nullptr) 258 { 259 ethData.mtuSize = *mtuSize; 260 } 261 } 262 else if (propertyPair.first == "LinkUp") 263 { 264 const bool* linkUp = 265 std::get_if<bool>(&propertyPair.second); 266 if (linkUp != nullptr) 267 { 268 ethData.linkUp = *linkUp; 269 } 270 } 271 else if (propertyPair.first == "NICEnabled") 272 { 273 const bool* nicEnabled = 274 std::get_if<bool>(&propertyPair.second); 275 if (nicEnabled != nullptr) 276 { 277 ethData.nicEnabled = *nicEnabled; 278 } 279 } 280 else if (propertyPair.first == "IPv6AcceptRA") 281 { 282 const bool* ipv6AcceptRa = 283 std::get_if<bool>(&propertyPair.second); 284 if (ipv6AcceptRa != nullptr) 285 { 286 ethData.ipv6AcceptRa = *ipv6AcceptRa; 287 } 288 } 289 else if (propertyPair.first == "Nameservers") 290 { 291 const std::vector<std::string>* nameservers = 292 std::get_if<std::vector<std::string>>( 293 &propertyPair.second); 294 if (nameservers != nullptr) 295 { 296 ethData.nameServers = *nameservers; 297 } 298 } 299 else if (propertyPair.first == "StaticNameServers") 300 { 301 const std::vector<std::string>* staticNameServers = 302 std::get_if<std::vector<std::string>>( 303 &propertyPair.second); 304 if (staticNameServers != nullptr) 305 { 306 ethData.staticNameServers = *staticNameServers; 307 } 308 } 309 else if (propertyPair.first == "DHCPEnabled") 310 { 311 const std::string* dhcpEnabled = 312 std::get_if<std::string>(&propertyPair.second); 313 if (dhcpEnabled != nullptr) 314 { 315 ethData.dhcpEnabled = *dhcpEnabled; 316 } 317 } 318 else if (propertyPair.first == "DomainName") 319 { 320 const std::vector<std::string>* domainNames = 321 std::get_if<std::vector<std::string>>( 322 &propertyPair.second); 323 if (domainNames != nullptr) 324 { 325 ethData.domainnames = *domainNames; 326 } 327 } 328 else if (propertyPair.first == "DefaultGateway") 329 { 330 const std::string* defaultGateway = 331 std::get_if<std::string>(&propertyPair.second); 332 if (defaultGateway != nullptr) 333 { 334 std::string defaultGatewayStr = *defaultGateway; 335 if (defaultGatewayStr.empty()) 336 { 337 ethData.defaultGateway = "0.0.0.0"; 338 } 339 else 340 { 341 ethData.defaultGateway = defaultGatewayStr; 342 } 343 } 344 } 345 else if (propertyPair.first == "DefaultGateway6") 346 { 347 const std::string* defaultGateway6 = 348 std::get_if<std::string>(&propertyPair.second); 349 if (defaultGateway6 != nullptr) 350 { 351 std::string defaultGateway6Str = 352 *defaultGateway6; 353 if (defaultGateway6Str.empty()) 354 { 355 ethData.ipv6DefaultGateway = 356 "0:0:0:0:0:0:0:0"; 357 } 358 else 359 { 360 ethData.ipv6DefaultGateway = 361 defaultGateway6Str; 362 } 363 } 364 } 365 } 366 } 367 } 368 369 if (objpath.first == "/xyz/openbmc_project/network/dhcp") 370 { 371 if (ifacePair.first == 372 "xyz.openbmc_project.Network.DHCPConfiguration") 373 { 374 for (const auto& propertyPair : ifacePair.second) 375 { 376 if (propertyPair.first == "DNSEnabled") 377 { 378 const bool* dnsEnabled = 379 std::get_if<bool>(&propertyPair.second); 380 if (dnsEnabled != nullptr) 381 { 382 ethData.dnsEnabled = *dnsEnabled; 383 } 384 } 385 else if (propertyPair.first == "NTPEnabled") 386 { 387 const bool* ntpEnabled = 388 std::get_if<bool>(&propertyPair.second); 389 if (ntpEnabled != nullptr) 390 { 391 ethData.ntpEnabled = *ntpEnabled; 392 } 393 } 394 else if (propertyPair.first == "HostNameEnabled") 395 { 396 const bool* hostNameEnabled = 397 std::get_if<bool>(&propertyPair.second); 398 if (hostNameEnabled != nullptr) 399 { 400 ethData.hostNameEnabled = *hostNameEnabled; 401 } 402 } 403 } 404 } 405 } 406 // System configuration shows up in the global namespace, so no need 407 // to check eth number 408 if (ifacePair.first == 409 "xyz.openbmc_project.Network.SystemConfiguration") 410 { 411 for (const auto& propertyPair : ifacePair.second) 412 { 413 if (propertyPair.first == "HostName") 414 { 415 const std::string* hostname = 416 std::get_if<std::string>(&propertyPair.second); 417 if (hostname != nullptr) 418 { 419 ethData.hostName = *hostname; 420 } 421 } 422 } 423 } 424 } 425 } 426 return idFound; 427 } 428 429 // Helper function that extracts data for single ethernet ipv6 address 430 inline void extractIPV6Data(const std::string& ethifaceId, 431 const dbus::utility::ManagedObjectType& dbusData, 432 std::vector<IPv6AddressData>& ipv6Config) 433 { 434 const std::string ipPathStart = "/xyz/openbmc_project/network/" + 435 ethifaceId; 436 437 // Since there might be several IPv6 configurations aligned with 438 // single ethernet interface, loop over all of them 439 for (const auto& objpath : dbusData) 440 { 441 // Check if proper pattern for object path appears 442 if (objpath.first.str.starts_with(ipPathStart + "/")) 443 { 444 for (const auto& interface : objpath.second) 445 { 446 if (interface.first == "xyz.openbmc_project.Network.IP") 447 { 448 auto type = std::ranges::find_if(interface.second, 449 [](const auto& property) { 450 return property.first == "Type"; 451 }); 452 if (type == interface.second.end()) 453 { 454 continue; 455 } 456 457 const std::string* typeStr = 458 std::get_if<std::string>(&type->second); 459 460 if (typeStr == nullptr || 461 (*typeStr != 462 "xyz.openbmc_project.Network.IP.Protocol.IPv6")) 463 { 464 continue; 465 } 466 467 // Instance IPv6AddressData structure, and set as 468 // appropriate 469 IPv6AddressData& ipv6Address = ipv6Config.emplace_back(); 470 ipv6Address.id = 471 objpath.first.str.substr(ipPathStart.size()); 472 for (const auto& property : interface.second) 473 { 474 if (property.first == "Address") 475 { 476 const std::string* address = 477 std::get_if<std::string>(&property.second); 478 if (address != nullptr) 479 { 480 ipv6Address.address = *address; 481 } 482 } 483 else if (property.first == "Origin") 484 { 485 const std::string* origin = 486 std::get_if<std::string>(&property.second); 487 if (origin != nullptr) 488 { 489 ipv6Address.origin = 490 translateAddressOriginDbusToRedfish(*origin, 491 false); 492 } 493 } 494 else if (property.first == "PrefixLength") 495 { 496 const uint8_t* prefix = 497 std::get_if<uint8_t>(&property.second); 498 if (prefix != nullptr) 499 { 500 ipv6Address.prefixLength = *prefix; 501 } 502 } 503 else if (property.first == "Type" || 504 property.first == "Gateway") 505 { 506 // Type & Gateway is not used 507 } 508 else 509 { 510 BMCWEB_LOG_ERROR( 511 "Got extra property: {} on the {} object", 512 property.first, objpath.first.str); 513 } 514 } 515 } 516 } 517 } 518 } 519 } 520 521 // Helper function that extracts data for single ethernet ipv4 address 522 inline void extractIPData(const std::string& ethifaceId, 523 const dbus::utility::ManagedObjectType& dbusData, 524 std::vector<IPv4AddressData>& ipv4Config) 525 { 526 const std::string ipPathStart = "/xyz/openbmc_project/network/" + 527 ethifaceId; 528 529 // Since there might be several IPv4 configurations aligned with 530 // single ethernet interface, loop over all of them 531 for (const auto& objpath : dbusData) 532 { 533 // Check if proper pattern for object path appears 534 if (objpath.first.str.starts_with(ipPathStart + "/")) 535 { 536 for (const auto& interface : objpath.second) 537 { 538 if (interface.first == "xyz.openbmc_project.Network.IP") 539 { 540 auto type = std::ranges::find_if(interface.second, 541 [](const auto& property) { 542 return property.first == "Type"; 543 }); 544 if (type == interface.second.end()) 545 { 546 continue; 547 } 548 549 const std::string* typeStr = 550 std::get_if<std::string>(&type->second); 551 552 if (typeStr == nullptr || 553 (*typeStr != 554 "xyz.openbmc_project.Network.IP.Protocol.IPv4")) 555 { 556 continue; 557 } 558 559 // Instance IPv4AddressData structure, and set as 560 // appropriate 561 IPv4AddressData& ipv4Address = ipv4Config.emplace_back(); 562 ipv4Address.id = 563 objpath.first.str.substr(ipPathStart.size()); 564 for (const auto& property : interface.second) 565 { 566 if (property.first == "Address") 567 { 568 const std::string* address = 569 std::get_if<std::string>(&property.second); 570 if (address != nullptr) 571 { 572 ipv4Address.address = *address; 573 } 574 } 575 else if (property.first == "Origin") 576 { 577 const std::string* origin = 578 std::get_if<std::string>(&property.second); 579 if (origin != nullptr) 580 { 581 ipv4Address.origin = 582 translateAddressOriginDbusToRedfish(*origin, 583 true); 584 } 585 } 586 else if (property.first == "PrefixLength") 587 { 588 const uint8_t* mask = 589 std::get_if<uint8_t>(&property.second); 590 if (mask != nullptr) 591 { 592 // convert it to the string 593 ipv4Address.netmask = getNetmask(*mask); 594 } 595 } 596 else if (property.first == "Type" || 597 property.first == "Gateway") 598 { 599 // Type & Gateway is not used 600 } 601 else 602 { 603 BMCWEB_LOG_ERROR( 604 "Got extra property: {} on the {} object", 605 property.first, objpath.first.str); 606 } 607 } 608 // Check if given address is local, or global 609 ipv4Address.linktype = 610 ipv4Address.address.starts_with("169.254.") 611 ? LinkType::Local 612 : LinkType::Global; 613 } 614 } 615 } 616 } 617 } 618 619 /** 620 * @brief Deletes given IPv4 interface 621 * 622 * @param[in] ifaceId Id of interface whose IP should be deleted 623 * @param[in] ipHash DBus Hash id of IP that should be deleted 624 * @param[io] asyncResp Response object that will be returned to client 625 * 626 * @return None 627 */ 628 inline void deleteIPAddress(const std::string& ifaceId, 629 const std::string& ipHash, 630 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 631 { 632 crow::connections::systemBus->async_method_call( 633 [asyncResp](const boost::system::error_code& ec) { 634 if (ec) 635 { 636 messages::internalError(asyncResp->res); 637 } 638 }, 639 "xyz.openbmc_project.Network", 640 "/xyz/openbmc_project/network/" + ifaceId + ipHash, 641 "xyz.openbmc_project.Object.Delete", "Delete"); 642 } 643 644 inline void updateIPv4DefaultGateway( 645 const std::string& ifaceId, const std::string& gateway, 646 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 647 { 648 sdbusplus::asio::setProperty( 649 *crow::connections::systemBus, "xyz.openbmc_project.Network", 650 "/xyz/openbmc_project/network/" + ifaceId, 651 "xyz.openbmc_project.Network.EthernetInterface", "DefaultGateway", 652 gateway, [asyncResp](const boost::system::error_code& ec) { 653 if (ec) 654 { 655 messages::internalError(asyncResp->res); 656 return; 657 } 658 asyncResp->res.result(boost::beast::http::status::no_content); 659 }); 660 } 661 /** 662 * @brief Creates a static IPv4 entry 663 * 664 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 665 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 666 * @param[in] gateway IPv4 address of this interfaces gateway 667 * @param[in] address IPv4 address to assign to this interface 668 * @param[io] asyncResp Response object that will be returned to client 669 * 670 * @return None 671 */ 672 inline void createIPv4(const std::string& ifaceId, uint8_t prefixLength, 673 const std::string& gateway, const std::string& address, 674 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 675 { 676 auto createIpHandler = [asyncResp, ifaceId, 677 gateway](const boost::system::error_code& ec) { 678 if (ec) 679 { 680 messages::internalError(asyncResp->res); 681 return; 682 } 683 updateIPv4DefaultGateway(ifaceId, gateway, asyncResp); 684 }; 685 686 crow::connections::systemBus->async_method_call( 687 std::move(createIpHandler), "xyz.openbmc_project.Network", 688 "/xyz/openbmc_project/network/" + ifaceId, 689 "xyz.openbmc_project.Network.IP.Create", "IP", 690 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, prefixLength, 691 gateway); 692 } 693 694 /** 695 * @brief Deletes the IPv6 entry for this interface and creates a replacement 696 * static IPv6 entry 697 * 698 * @param[in] ifaceId Id of interface upon which to create the IPv6 entry 699 * @param[in] id The unique hash entry identifying the DBus entry 700 * @param[in] prefixLength IPv6 prefix syntax for the subnet mask 701 * @param[in] address IPv6 address to assign to this interface 702 * @param[io] asyncResp Response object that will be returned to client 703 * 704 * @return None 705 */ 706 707 enum class IpVersion 708 { 709 IpV4, 710 IpV6 711 }; 712 713 inline void deleteAndCreateIPAddress( 714 IpVersion version, const std::string& ifaceId, const std::string& id, 715 uint8_t prefixLength, const std::string& address, 716 const std::string& gateway, 717 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 718 { 719 crow::connections::systemBus->async_method_call( 720 [asyncResp, version, ifaceId, address, prefixLength, 721 gateway](const boost::system::error_code& ec) { 722 if (ec) 723 { 724 messages::internalError(asyncResp->res); 725 } 726 std::string protocol = "xyz.openbmc_project.Network.IP.Protocol."; 727 protocol += version == IpVersion::IpV4 ? "IPv4" : "IPv6"; 728 crow::connections::systemBus->async_method_call( 729 [asyncResp](const boost::system::error_code& ec2) { 730 if (ec2) 731 { 732 messages::internalError(asyncResp->res); 733 } 734 }, 735 "xyz.openbmc_project.Network", 736 "/xyz/openbmc_project/network/" + ifaceId, 737 "xyz.openbmc_project.Network.IP.Create", "IP", protocol, address, 738 prefixLength, gateway); 739 }, 740 "xyz.openbmc_project.Network", 741 "/xyz/openbmc_project/network/" + ifaceId + id, 742 "xyz.openbmc_project.Object.Delete", "Delete"); 743 } 744 745 /** 746 * @brief Creates IPv6 with given data 747 * 748 * @param[in] ifaceId Id of interface whose IP should be added 749 * @param[in] prefixLength Prefix length that needs to be added 750 * @param[in] address IP address that needs to be added 751 * @param[io] asyncResp Response object that will be returned to client 752 * 753 * @return None 754 */ 755 inline void createIPv6(const std::string& ifaceId, uint8_t prefixLength, 756 const std::string& address, 757 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 758 { 759 auto createIpHandler = [asyncResp, 760 address](const boost::system::error_code& ec) { 761 if (ec) 762 { 763 if (ec == boost::system::errc::io_error) 764 { 765 messages::propertyValueFormatError(asyncResp->res, address, 766 "Address"); 767 } 768 else 769 { 770 messages::internalError(asyncResp->res); 771 } 772 } 773 }; 774 // Passing null for gateway, as per redfish spec IPv6StaticAddresses object 775 // does not have associated gateway property 776 crow::connections::systemBus->async_method_call( 777 std::move(createIpHandler), "xyz.openbmc_project.Network", 778 "/xyz/openbmc_project/network/" + ifaceId, 779 "xyz.openbmc_project.Network.IP.Create", "IP", 780 "xyz.openbmc_project.Network.IP.Protocol.IPv6", address, prefixLength, 781 ""); 782 } 783 784 /** 785 * Function that retrieves all properties for given Ethernet Interface 786 * Object 787 * from EntityManager Network Manager 788 * @param ethiface_id a eth interface id to query on DBus 789 * @param callback a function that shall be called to convert Dbus output 790 * into JSON 791 */ 792 template <typename CallbackFunc> 793 void getEthernetIfaceData(const std::string& ethifaceId, 794 CallbackFunc&& callback) 795 { 796 sdbusplus::message::object_path path("/xyz/openbmc_project/network"); 797 dbus::utility::getManagedObjects( 798 "xyz.openbmc_project.Network", path, 799 [ethifaceId{std::string{ethifaceId}}, 800 callback{std::forward<CallbackFunc>(callback)}]( 801 const boost::system::error_code& ec, 802 const dbus::utility::ManagedObjectType& resp) { 803 EthernetInterfaceData ethData{}; 804 std::vector<IPv4AddressData> ipv4Data; 805 std::vector<IPv6AddressData> ipv6Data; 806 807 if (ec) 808 { 809 callback(false, ethData, ipv4Data, ipv6Data); 810 return; 811 } 812 813 bool found = extractEthernetInterfaceData(ethifaceId, resp, ethData); 814 if (!found) 815 { 816 callback(false, ethData, ipv4Data, ipv6Data); 817 return; 818 } 819 820 extractIPData(ethifaceId, resp, ipv4Data); 821 // Fix global GW 822 for (IPv4AddressData& ipv4 : ipv4Data) 823 { 824 if (((ipv4.linktype == LinkType::Global) && 825 (ipv4.gateway == "0.0.0.0")) || 826 (ipv4.origin == "DHCP") || (ipv4.origin == "Static")) 827 { 828 ipv4.gateway = ethData.defaultGateway; 829 } 830 } 831 832 extractIPV6Data(ethifaceId, resp, ipv6Data); 833 // Finally make a callback with useful data 834 callback(true, ethData, ipv4Data, ipv6Data); 835 }); 836 } 837 838 /** 839 * Function that retrieves all Ethernet Interfaces available through Network 840 * Manager 841 * @param callback a function that shall be called to convert Dbus output 842 * into JSON. 843 */ 844 template <typename CallbackFunc> 845 void getEthernetIfaceList(CallbackFunc&& callback) 846 { 847 sdbusplus::message::object_path path("/xyz/openbmc_project/network"); 848 dbus::utility::getManagedObjects( 849 "xyz.openbmc_project.Network", path, 850 [callback{std::forward<CallbackFunc>(callback)}]( 851 const boost::system::error_code& ec, 852 const dbus::utility::ManagedObjectType& resp) { 853 // Callback requires vector<string> to retrieve all available 854 // ethernet interfaces 855 std::vector<std::string> ifaceList; 856 ifaceList.reserve(resp.size()); 857 if (ec) 858 { 859 callback(false, ifaceList); 860 return; 861 } 862 863 // Iterate over all retrieved ObjectPaths. 864 for (const auto& objpath : resp) 865 { 866 // And all interfaces available for certain ObjectPath. 867 for (const auto& interface : objpath.second) 868 { 869 // If interface is 870 // xyz.openbmc_project.Network.EthernetInterface, this is 871 // what we're looking for. 872 if (interface.first == 873 "xyz.openbmc_project.Network.EthernetInterface") 874 { 875 std::string ifaceId = objpath.first.filename(); 876 if (ifaceId.empty()) 877 { 878 continue; 879 } 880 // and put it into output vector. 881 ifaceList.emplace_back(ifaceId); 882 } 883 } 884 } 885 886 std::ranges::sort(ifaceList, AlphanumLess<std::string>()); 887 888 // Finally make a callback with useful data 889 callback(true, ifaceList); 890 }); 891 } 892 893 inline void 894 handleHostnamePatch(const std::string& hostname, 895 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 896 { 897 // SHOULD handle host names of up to 255 characters(RFC 1123) 898 if (hostname.length() > 255) 899 { 900 messages::propertyValueFormatError(asyncResp->res, hostname, 901 "HostName"); 902 return; 903 } 904 sdbusplus::asio::setProperty( 905 *crow::connections::systemBus, "xyz.openbmc_project.Network", 906 "/xyz/openbmc_project/network/config", 907 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", hostname, 908 [asyncResp](const boost::system::error_code& ec) { 909 if (ec) 910 { 911 messages::internalError(asyncResp->res); 912 } 913 }); 914 } 915 916 inline void 917 handleMTUSizePatch(const std::string& ifaceId, const size_t mtuSize, 918 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 919 { 920 sdbusplus::message::object_path objPath = "/xyz/openbmc_project/network/" + 921 ifaceId; 922 sdbusplus::asio::setProperty( 923 *crow::connections::systemBus, "xyz.openbmc_project.Network", objPath, 924 "xyz.openbmc_project.Network.EthernetInterface", "MTU", mtuSize, 925 [asyncResp](const boost::system::error_code& ec) { 926 if (ec) 927 { 928 messages::internalError(asyncResp->res); 929 } 930 }); 931 } 932 933 inline void 934 handleDomainnamePatch(const std::string& ifaceId, 935 const std::string& domainname, 936 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 937 { 938 std::vector<std::string> vectorDomainname = {domainname}; 939 sdbusplus::asio::setProperty( 940 *crow::connections::systemBus, "xyz.openbmc_project.Network", 941 "/xyz/openbmc_project/network/" + ifaceId, 942 "xyz.openbmc_project.Network.EthernetInterface", "DomainName", 943 vectorDomainname, [asyncResp](const boost::system::error_code& ec) { 944 if (ec) 945 { 946 messages::internalError(asyncResp->res); 947 } 948 }); 949 } 950 951 inline bool isHostnameValid(const std::string& hostname) 952 { 953 // A valid host name can never have the dotted-decimal form (RFC 1123) 954 if (std::ranges::all_of(hostname, ::isdigit)) 955 { 956 return false; 957 } 958 // Each label(hostname/subdomains) within a valid FQDN 959 // MUST handle host names of up to 63 characters (RFC 1123) 960 // labels cannot start or end with hyphens (RFC 952) 961 // labels can start with numbers (RFC 1123) 962 const static std::regex pattern( 963 "^[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]$"); 964 965 return std::regex_match(hostname, pattern); 966 } 967 968 inline bool isDomainnameValid(const std::string& domainname) 969 { 970 // Can have multiple subdomains 971 // Top Level Domain's min length is 2 character 972 const static std::regex pattern( 973 "^([A-Za-z0-9][a-zA-Z0-9\\-]{1,61}|[a-zA-Z0-9]{1,30}\\.)*[a-zA-Z]{2,}$"); 974 975 return std::regex_match(domainname, pattern); 976 } 977 978 inline void handleFqdnPatch(const std::string& ifaceId, const std::string& fqdn, 979 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 980 { 981 // Total length of FQDN must not exceed 255 characters(RFC 1035) 982 if (fqdn.length() > 255) 983 { 984 messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN"); 985 return; 986 } 987 988 size_t pos = fqdn.find('.'); 989 if (pos == std::string::npos) 990 { 991 messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN"); 992 return; 993 } 994 995 std::string hostname; 996 std::string domainname; 997 domainname = (fqdn).substr(pos + 1); 998 hostname = (fqdn).substr(0, pos); 999 1000 if (!isHostnameValid(hostname) || !isDomainnameValid(domainname)) 1001 { 1002 messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN"); 1003 return; 1004 } 1005 1006 handleHostnamePatch(hostname, asyncResp); 1007 handleDomainnamePatch(ifaceId, domainname, asyncResp); 1008 } 1009 1010 inline void 1011 handleMACAddressPatch(const std::string& ifaceId, 1012 const std::string& macAddress, 1013 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1014 { 1015 static constexpr std::string_view dbusNotAllowedError = 1016 "xyz.openbmc_project.Common.Error.NotAllowed"; 1017 1018 sdbusplus::asio::setProperty( 1019 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1020 "/xyz/openbmc_project/network/" + ifaceId, 1021 "xyz.openbmc_project.Network.MACAddress", "MACAddress", macAddress, 1022 [asyncResp](const boost::system::error_code& ec, 1023 const sdbusplus::message_t& msg) { 1024 if (ec) 1025 { 1026 const sd_bus_error* err = msg.get_error(); 1027 if (err == nullptr) 1028 { 1029 messages::internalError(asyncResp->res); 1030 return; 1031 } 1032 if (err->name == dbusNotAllowedError) 1033 { 1034 messages::propertyNotWritable(asyncResp->res, "MACAddress"); 1035 return; 1036 } 1037 messages::internalError(asyncResp->res); 1038 return; 1039 } 1040 }); 1041 } 1042 1043 inline void setDHCPEnabled(const std::string& ifaceId, 1044 const std::string& propertyName, const bool v4Value, 1045 const bool v6Value, 1046 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1047 { 1048 const std::string dhcp = getDhcpEnabledEnumeration(v4Value, v6Value); 1049 sdbusplus::asio::setProperty( 1050 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1051 "/xyz/openbmc_project/network/" + ifaceId, 1052 "xyz.openbmc_project.Network.EthernetInterface", propertyName, dhcp, 1053 [asyncResp](const boost::system::error_code& ec) { 1054 if (ec) 1055 { 1056 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1057 messages::internalError(asyncResp->res); 1058 return; 1059 } 1060 messages::success(asyncResp->res); 1061 }); 1062 } 1063 1064 inline void setEthernetInterfaceBoolProperty( 1065 const std::string& ifaceId, const std::string& propertyName, 1066 const bool& value, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1067 { 1068 sdbusplus::asio::setProperty( 1069 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1070 "/xyz/openbmc_project/network/" + ifaceId, 1071 "xyz.openbmc_project.Network.EthernetInterface", propertyName, value, 1072 [asyncResp](const boost::system::error_code& ec) { 1073 if (ec) 1074 { 1075 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1076 messages::internalError(asyncResp->res); 1077 return; 1078 } 1079 }); 1080 } 1081 1082 inline void setDHCPv4Config(const std::string& propertyName, const bool& value, 1083 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1084 { 1085 BMCWEB_LOG_DEBUG("{} = {}", propertyName, value); 1086 sdbusplus::asio::setProperty( 1087 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1088 "/xyz/openbmc_project/network/dhcp", 1089 "xyz.openbmc_project.Network.DHCPConfiguration", propertyName, value, 1090 [asyncResp](const boost::system::error_code& ec) { 1091 if (ec) 1092 { 1093 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1094 messages::internalError(asyncResp->res); 1095 return; 1096 } 1097 }); 1098 } 1099 1100 inline void handleSLAACAutoConfigPatch( 1101 const std::string& ifaceId, bool ipv6AutoConfigEnabled, 1102 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1103 { 1104 sdbusplus::message::object_path path("/xyz/openbmc_project/network"); 1105 path /= ifaceId; 1106 sdbusplus::asio::setProperty( 1107 *crow::connections::systemBus, "xyz.openbmc_project.Network", path, 1108 "xyz.openbmc_project.Network.EthernetInterface", "IPv6AcceptRA", 1109 ipv6AutoConfigEnabled, 1110 [asyncResp](const boost::system::error_code& ec) { 1111 if (ec) 1112 { 1113 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1114 messages::internalError(asyncResp->res); 1115 return; 1116 } 1117 messages::success(asyncResp->res); 1118 }); 1119 } 1120 1121 inline void handleDHCPPatch(const std::string& ifaceId, 1122 const EthernetInterfaceData& ethData, 1123 const DHCPParameters& v4dhcpParms, 1124 const DHCPParameters& v6dhcpParms, 1125 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1126 { 1127 bool ipv4Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 1128 bool ipv6Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, false); 1129 1130 bool nextv4DHCPState = 1131 v4dhcpParms.dhcpv4Enabled ? *v4dhcpParms.dhcpv4Enabled : ipv4Active; 1132 1133 bool nextv6DHCPState{}; 1134 if (v6dhcpParms.dhcpv6OperatingMode) 1135 { 1136 if ((*v6dhcpParms.dhcpv6OperatingMode != "Enabled") && 1137 (*v6dhcpParms.dhcpv6OperatingMode != "Disabled")) 1138 { 1139 messages::propertyValueFormatError(asyncResp->res, 1140 *v6dhcpParms.dhcpv6OperatingMode, 1141 "OperatingMode"); 1142 return; 1143 } 1144 nextv6DHCPState = (*v6dhcpParms.dhcpv6OperatingMode == "Enabled"); 1145 } 1146 else 1147 { 1148 nextv6DHCPState = ipv6Active; 1149 } 1150 1151 bool nextDNS{}; 1152 if (v4dhcpParms.useDnsServers && v6dhcpParms.useDnsServers) 1153 { 1154 if (*v4dhcpParms.useDnsServers != *v6dhcpParms.useDnsServers) 1155 { 1156 messages::generalError(asyncResp->res); 1157 return; 1158 } 1159 nextDNS = *v4dhcpParms.useDnsServers; 1160 } 1161 else if (v4dhcpParms.useDnsServers) 1162 { 1163 nextDNS = *v4dhcpParms.useDnsServers; 1164 } 1165 else if (v6dhcpParms.useDnsServers) 1166 { 1167 nextDNS = *v6dhcpParms.useDnsServers; 1168 } 1169 else 1170 { 1171 nextDNS = ethData.dnsEnabled; 1172 } 1173 1174 bool nextNTP{}; 1175 if (v4dhcpParms.useNtpServers && v6dhcpParms.useNtpServers) 1176 { 1177 if (*v4dhcpParms.useNtpServers != *v6dhcpParms.useNtpServers) 1178 { 1179 messages::generalError(asyncResp->res); 1180 return; 1181 } 1182 nextNTP = *v4dhcpParms.useNtpServers; 1183 } 1184 else if (v4dhcpParms.useNtpServers) 1185 { 1186 nextNTP = *v4dhcpParms.useNtpServers; 1187 } 1188 else if (v6dhcpParms.useNtpServers) 1189 { 1190 nextNTP = *v6dhcpParms.useNtpServers; 1191 } 1192 else 1193 { 1194 nextNTP = ethData.ntpEnabled; 1195 } 1196 1197 bool nextUseDomain{}; 1198 if (v4dhcpParms.useDomainName && v6dhcpParms.useDomainName) 1199 { 1200 if (*v4dhcpParms.useDomainName != *v6dhcpParms.useDomainName) 1201 { 1202 messages::generalError(asyncResp->res); 1203 return; 1204 } 1205 nextUseDomain = *v4dhcpParms.useDomainName; 1206 } 1207 else if (v4dhcpParms.useDomainName) 1208 { 1209 nextUseDomain = *v4dhcpParms.useDomainName; 1210 } 1211 else if (v6dhcpParms.useDomainName) 1212 { 1213 nextUseDomain = *v6dhcpParms.useDomainName; 1214 } 1215 else 1216 { 1217 nextUseDomain = ethData.hostNameEnabled; 1218 } 1219 1220 BMCWEB_LOG_DEBUG("set DHCPEnabled..."); 1221 setDHCPEnabled(ifaceId, "DHCPEnabled", nextv4DHCPState, nextv6DHCPState, 1222 asyncResp); 1223 BMCWEB_LOG_DEBUG("set DNSEnabled..."); 1224 setDHCPv4Config("DNSEnabled", nextDNS, asyncResp); 1225 BMCWEB_LOG_DEBUG("set NTPEnabled..."); 1226 setDHCPv4Config("NTPEnabled", nextNTP, asyncResp); 1227 BMCWEB_LOG_DEBUG("set HostNameEnabled..."); 1228 setDHCPv4Config("HostNameEnabled", nextUseDomain, asyncResp); 1229 } 1230 1231 inline std::vector<IPv4AddressData>::const_iterator getNextStaticIpEntry( 1232 const std::vector<IPv4AddressData>::const_iterator& head, 1233 const std::vector<IPv4AddressData>::const_iterator& end) 1234 { 1235 return std::find_if(head, end, [](const IPv4AddressData& value) { 1236 return value.origin == "Static"; 1237 }); 1238 } 1239 1240 inline std::vector<IPv6AddressData>::const_iterator getNextStaticIpEntry( 1241 const std::vector<IPv6AddressData>::const_iterator& head, 1242 const std::vector<IPv6AddressData>::const_iterator& end) 1243 { 1244 return std::find_if(head, end, [](const IPv6AddressData& value) { 1245 return value.origin == "Static"; 1246 }); 1247 } 1248 1249 inline void 1250 handleIPv4StaticPatch(const std::string& ifaceId, 1251 nlohmann::json::array_t& input, 1252 const std::vector<IPv4AddressData>& ipv4Data, 1253 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1254 { 1255 if (input.empty()) 1256 { 1257 messages::propertyValueTypeError(asyncResp->res, input, 1258 "IPv4StaticAddresses"); 1259 return; 1260 } 1261 1262 unsigned entryIdx = 1; 1263 // Find the first static IP address currently active on the NIC and 1264 // match it to the first JSON element in the IPv4StaticAddresses array. 1265 // Match each subsequent JSON element to the next static IP programmed 1266 // into the NIC. 1267 std::vector<IPv4AddressData>::const_iterator nicIpEntry = 1268 getNextStaticIpEntry(ipv4Data.cbegin(), ipv4Data.cend()); 1269 1270 for (nlohmann::json& thisJson : input) 1271 { 1272 std::string pathString = "IPv4StaticAddresses/" + 1273 std::to_string(entryIdx); 1274 1275 if (!thisJson.is_null() && !thisJson.empty()) 1276 { 1277 std::optional<std::string> address; 1278 std::optional<std::string> subnetMask; 1279 std::optional<std::string> gateway; 1280 1281 if (!json_util::readJson(thisJson, asyncResp->res, "Address", 1282 address, "SubnetMask", subnetMask, 1283 "Gateway", gateway)) 1284 { 1285 messages::propertyValueFormatError(asyncResp->res, thisJson, 1286 pathString); 1287 return; 1288 } 1289 1290 // Find the address/subnet/gateway values. Any values that are 1291 // not explicitly provided are assumed to be unmodified from the 1292 // current state of the interface. Merge existing state into the 1293 // current request. 1294 if (address) 1295 { 1296 if (!ip_util::ipv4VerifyIpAndGetBitcount(*address)) 1297 { 1298 messages::propertyValueFormatError(asyncResp->res, *address, 1299 pathString + "/Address"); 1300 return; 1301 } 1302 } 1303 else if (nicIpEntry != ipv4Data.cend()) 1304 { 1305 address = (nicIpEntry->address); 1306 } 1307 else 1308 { 1309 messages::propertyMissing(asyncResp->res, 1310 pathString + "/Address"); 1311 return; 1312 } 1313 1314 uint8_t prefixLength = 0; 1315 if (subnetMask) 1316 { 1317 if (!ip_util::ipv4VerifyIpAndGetBitcount(*subnetMask, 1318 &prefixLength)) 1319 { 1320 messages::propertyValueFormatError( 1321 asyncResp->res, *subnetMask, 1322 pathString + "/SubnetMask"); 1323 return; 1324 } 1325 } 1326 else if (nicIpEntry != ipv4Data.cend()) 1327 { 1328 if (!ip_util::ipv4VerifyIpAndGetBitcount(nicIpEntry->netmask, 1329 &prefixLength)) 1330 { 1331 messages::propertyValueFormatError( 1332 asyncResp->res, nicIpEntry->netmask, 1333 pathString + "/SubnetMask"); 1334 return; 1335 } 1336 } 1337 else 1338 { 1339 messages::propertyMissing(asyncResp->res, 1340 pathString + "/SubnetMask"); 1341 return; 1342 } 1343 1344 if (gateway) 1345 { 1346 if (!ip_util::ipv4VerifyIpAndGetBitcount(*gateway)) 1347 { 1348 messages::propertyValueFormatError(asyncResp->res, *gateway, 1349 pathString + "/Gateway"); 1350 return; 1351 } 1352 } 1353 else if (nicIpEntry != ipv4Data.cend()) 1354 { 1355 gateway = nicIpEntry->gateway; 1356 } 1357 else 1358 { 1359 messages::propertyMissing(asyncResp->res, 1360 pathString + "/Gateway"); 1361 return; 1362 } 1363 1364 if (nicIpEntry != ipv4Data.cend()) 1365 { 1366 deleteAndCreateIPAddress(IpVersion::IpV4, ifaceId, 1367 nicIpEntry->id, prefixLength, *address, 1368 *gateway, asyncResp); 1369 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1370 ipv4Data.cend()); 1371 } 1372 else 1373 { 1374 createIPv4(ifaceId, prefixLength, *gateway, *address, 1375 asyncResp); 1376 } 1377 entryIdx++; 1378 } 1379 else 1380 { 1381 if (nicIpEntry == ipv4Data.cend()) 1382 { 1383 // Requesting a DELETE/DO NOT MODIFY action for an item 1384 // that isn't present on the eth(n) interface. Input JSON is 1385 // in error, so bail out. 1386 if (thisJson.is_null()) 1387 { 1388 messages::resourceCannotBeDeleted(asyncResp->res); 1389 return; 1390 } 1391 messages::propertyValueFormatError(asyncResp->res, thisJson, 1392 pathString); 1393 return; 1394 } 1395 1396 if (thisJson.is_null()) 1397 { 1398 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp); 1399 } 1400 if (nicIpEntry != ipv4Data.cend()) 1401 { 1402 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1403 ipv4Data.cend()); 1404 } 1405 entryIdx++; 1406 } 1407 } 1408 } 1409 1410 inline void handleStaticNameServersPatch( 1411 const std::string& ifaceId, 1412 const std::vector<std::string>& updatedStaticNameServers, 1413 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1414 { 1415 sdbusplus::asio::setProperty( 1416 *crow::connections::systemBus, "xyz.openbmc_project.Network", 1417 "/xyz/openbmc_project/network/" + ifaceId, 1418 "xyz.openbmc_project.Network.EthernetInterface", "StaticNameServers", 1419 updatedStaticNameServers, 1420 [asyncResp](const boost::system::error_code& ec) { 1421 if (ec) 1422 { 1423 messages::internalError(asyncResp->res); 1424 return; 1425 } 1426 }); 1427 } 1428 1429 inline void handleIPv6StaticAddressesPatch( 1430 const std::string& ifaceId, const nlohmann::json::array_t& input, 1431 const std::vector<IPv6AddressData>& ipv6Data, 1432 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1433 { 1434 if (input.empty()) 1435 { 1436 messages::propertyValueTypeError(asyncResp->res, input, 1437 "IPv6StaticAddresses"); 1438 return; 1439 } 1440 size_t entryIdx = 1; 1441 std::vector<IPv6AddressData>::const_iterator nicIpEntry = 1442 getNextStaticIpEntry(ipv6Data.cbegin(), ipv6Data.cend()); 1443 for (const nlohmann::json& thisJson : input) 1444 { 1445 std::string pathString = "IPv6StaticAddresses/" + 1446 std::to_string(entryIdx); 1447 1448 if (!thisJson.is_null() && !thisJson.empty()) 1449 { 1450 std::optional<std::string> address; 1451 std::optional<uint8_t> prefixLength; 1452 nlohmann::json thisJsonCopy = thisJson; 1453 if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", 1454 address, "PrefixLength", prefixLength)) 1455 { 1456 messages::propertyValueFormatError(asyncResp->res, thisJson, 1457 pathString); 1458 return; 1459 } 1460 1461 const std::string* addr = nullptr; 1462 uint8_t prefix = 0; 1463 1464 // Find the address and prefixLength values. Any values that are 1465 // not explicitly provided are assumed to be unmodified from the 1466 // current state of the interface. Merge existing state into the 1467 // current request. 1468 if (address) 1469 { 1470 addr = &(*address); 1471 } 1472 else if (nicIpEntry != ipv6Data.end()) 1473 { 1474 addr = &(nicIpEntry->address); 1475 } 1476 else 1477 { 1478 messages::propertyMissing(asyncResp->res, 1479 pathString + "/Address"); 1480 return; 1481 } 1482 1483 if (prefixLength) 1484 { 1485 prefix = *prefixLength; 1486 } 1487 else if (nicIpEntry != ipv6Data.end()) 1488 { 1489 prefix = nicIpEntry->prefixLength; 1490 } 1491 else 1492 { 1493 messages::propertyMissing(asyncResp->res, 1494 pathString + "/PrefixLength"); 1495 return; 1496 } 1497 1498 if (nicIpEntry != ipv6Data.end()) 1499 { 1500 deleteAndCreateIPAddress(IpVersion::IpV6, ifaceId, 1501 nicIpEntry->id, prefix, *addr, "", 1502 asyncResp); 1503 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1504 ipv6Data.cend()); 1505 } 1506 else 1507 { 1508 createIPv6(ifaceId, *prefixLength, *addr, asyncResp); 1509 } 1510 entryIdx++; 1511 } 1512 else 1513 { 1514 if (nicIpEntry == ipv6Data.end()) 1515 { 1516 // Requesting a DELETE/DO NOT MODIFY action for an item 1517 // that isn't present on the eth(n) interface. Input JSON is 1518 // in error, so bail out. 1519 if (thisJson.is_null()) 1520 { 1521 messages::resourceCannotBeDeleted(asyncResp->res); 1522 return; 1523 } 1524 messages::propertyValueFormatError(asyncResp->res, thisJson, 1525 pathString); 1526 return; 1527 } 1528 1529 if (thisJson.is_null()) 1530 { 1531 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp); 1532 } 1533 if (nicIpEntry != ipv6Data.cend()) 1534 { 1535 nicIpEntry = getNextStaticIpEntry(++nicIpEntry, 1536 ipv6Data.cend()); 1537 } 1538 entryIdx++; 1539 } 1540 } 1541 } 1542 1543 inline std::string extractParentInterfaceName(const std::string& ifaceId) 1544 { 1545 std::size_t pos = ifaceId.find('_'); 1546 return ifaceId.substr(0, pos); 1547 } 1548 1549 inline void 1550 parseInterfaceData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1551 const std::string& ifaceId, 1552 const EthernetInterfaceData& ethData, 1553 const std::vector<IPv4AddressData>& ipv4Data, 1554 const std::vector<IPv6AddressData>& ipv6Data) 1555 { 1556 nlohmann::json& jsonResponse = asyncResp->res.jsonValue; 1557 jsonResponse["Id"] = ifaceId; 1558 jsonResponse["@odata.id"] = boost::urls::format( 1559 "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", ifaceId); 1560 jsonResponse["InterfaceEnabled"] = ethData.nicEnabled; 1561 1562 if constexpr (bmcwebEnableHealthPopulate) 1563 { 1564 constexpr std::array<std::string_view, 1> inventoryForEthernet = { 1565 "xyz.openbmc_project.Inventory.Item.Ethernet"}; 1566 auto health = std::make_shared<HealthPopulate>(asyncResp); 1567 dbus::utility::getSubTreePaths( 1568 "/", 0, inventoryForEthernet, 1569 [health](const boost::system::error_code& ec, 1570 const dbus::utility::MapperGetSubTreePathsResponse& resp) { 1571 if (ec) 1572 { 1573 return; 1574 } 1575 1576 health->inventory = resp; 1577 }); 1578 1579 health->populate(); 1580 } 1581 1582 if (ethData.nicEnabled) 1583 { 1584 jsonResponse["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown"; 1585 jsonResponse["Status"]["State"] = "Enabled"; 1586 } 1587 else 1588 { 1589 jsonResponse["LinkStatus"] = "NoLink"; 1590 jsonResponse["Status"]["State"] = "Disabled"; 1591 } 1592 1593 jsonResponse["SpeedMbps"] = ethData.speed; 1594 jsonResponse["MTUSize"] = ethData.mtuSize; 1595 jsonResponse["MACAddress"] = ethData.macAddress; 1596 jsonResponse["DHCPv4"]["DHCPEnabled"] = 1597 translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 1598 jsonResponse["DHCPv4"]["UseNTPServers"] = ethData.ntpEnabled; 1599 jsonResponse["DHCPv4"]["UseDNSServers"] = ethData.dnsEnabled; 1600 jsonResponse["DHCPv4"]["UseDomainName"] = ethData.hostNameEnabled; 1601 1602 jsonResponse["DHCPv6"]["OperatingMode"] = 1603 translateDhcpEnabledToBool(ethData.dhcpEnabled, false) ? "Enabled" 1604 : "Disabled"; 1605 jsonResponse["DHCPv6"]["UseNTPServers"] = ethData.ntpEnabled; 1606 jsonResponse["DHCPv6"]["UseDNSServers"] = ethData.dnsEnabled; 1607 jsonResponse["DHCPv6"]["UseDomainName"] = ethData.hostNameEnabled; 1608 jsonResponse["StatelessAddressAutoConfig"]["IPv6AutoConfigEnabled"] = 1609 ethData.ipv6AcceptRa; 1610 1611 if (!ethData.hostName.empty()) 1612 { 1613 jsonResponse["HostName"] = ethData.hostName; 1614 1615 // When domain name is empty then it means, that it is a network 1616 // without domain names, and the host name itself must be treated as 1617 // FQDN 1618 std::string fqdn = ethData.hostName; 1619 if (!ethData.domainnames.empty()) 1620 { 1621 fqdn += "." + ethData.domainnames[0]; 1622 } 1623 jsonResponse["FQDN"] = fqdn; 1624 } 1625 1626 if (ethData.vlanId) 1627 { 1628 jsonResponse["EthernetInterfaceType"] = "Virtual"; 1629 jsonResponse["VLAN"]["VLANEnable"] = true; 1630 jsonResponse["VLAN"]["VLANId"] = *ethData.vlanId; 1631 jsonResponse["VLAN"]["Tagged"] = true; 1632 1633 nlohmann::json::array_t relatedInterfaces; 1634 nlohmann::json& parentInterface = relatedInterfaces.emplace_back(); 1635 parentInterface["@odata.id"] = 1636 boost::urls::format("/redfish/v1/Managers/bmc/EthernetInterfaces", 1637 extractParentInterfaceName(ifaceId)); 1638 jsonResponse["Links"]["RelatedInterfaces"] = 1639 std::move(relatedInterfaces); 1640 } 1641 else 1642 { 1643 jsonResponse["EthernetInterfaceType"] = "Physical"; 1644 } 1645 1646 jsonResponse["NameServers"] = ethData.nameServers; 1647 jsonResponse["StaticNameServers"] = ethData.staticNameServers; 1648 1649 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 1650 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 1651 ipv4Array = nlohmann::json::array(); 1652 ipv4StaticArray = nlohmann::json::array(); 1653 for (const auto& ipv4Config : ipv4Data) 1654 { 1655 std::string gatewayStr = ipv4Config.gateway; 1656 if (gatewayStr.empty()) 1657 { 1658 gatewayStr = "0.0.0.0"; 1659 } 1660 nlohmann::json::object_t ipv4; 1661 ipv4["AddressOrigin"] = ipv4Config.origin; 1662 ipv4["SubnetMask"] = ipv4Config.netmask; 1663 ipv4["Address"] = ipv4Config.address; 1664 ipv4["Gateway"] = gatewayStr; 1665 1666 if (ipv4Config.origin == "Static") 1667 { 1668 ipv4StaticArray.push_back(ipv4); 1669 } 1670 1671 ipv4Array.emplace_back(std::move(ipv4)); 1672 } 1673 1674 std::string ipv6GatewayStr = ethData.ipv6DefaultGateway; 1675 if (ipv6GatewayStr.empty()) 1676 { 1677 ipv6GatewayStr = "0:0:0:0:0:0:0:0"; 1678 } 1679 1680 jsonResponse["IPv6DefaultGateway"] = ipv6GatewayStr; 1681 1682 nlohmann::json& ipv6Array = jsonResponse["IPv6Addresses"]; 1683 nlohmann::json& ipv6StaticArray = jsonResponse["IPv6StaticAddresses"]; 1684 ipv6Array = nlohmann::json::array(); 1685 ipv6StaticArray = nlohmann::json::array(); 1686 nlohmann::json& ipv6AddrPolicyTable = 1687 jsonResponse["IPv6AddressPolicyTable"]; 1688 ipv6AddrPolicyTable = nlohmann::json::array(); 1689 for (const auto& ipv6Config : ipv6Data) 1690 { 1691 nlohmann::json::object_t ipv6; 1692 ipv6["Address"] = ipv6Config.address; 1693 ipv6["PrefixLength"] = ipv6Config.prefixLength; 1694 ipv6["AddressOrigin"] = ipv6Config.origin; 1695 1696 ipv6Array.emplace_back(std::move(ipv6)); 1697 if (ipv6Config.origin == "Static") 1698 { 1699 nlohmann::json::object_t ipv6Static; 1700 ipv6Static["Address"] = ipv6Config.address; 1701 ipv6Static["PrefixLength"] = ipv6Config.prefixLength; 1702 ipv6StaticArray.emplace_back(std::move(ipv6Static)); 1703 } 1704 } 1705 } 1706 1707 inline void afterDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1708 const std::string& ifaceId, 1709 const boost::system::error_code& ec, 1710 const sdbusplus::message_t& m) 1711 { 1712 if (!ec) 1713 { 1714 return; 1715 } 1716 const sd_bus_error* dbusError = m.get_error(); 1717 if (dbusError == nullptr) 1718 { 1719 messages::internalError(asyncResp->res); 1720 return; 1721 } 1722 BMCWEB_LOG_DEBUG("DBus error: {}", dbusError->name); 1723 1724 if (std::string_view("org.freedesktop.DBus.Error.UnknownObject") == 1725 dbusError->name) 1726 { 1727 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 1728 ifaceId); 1729 return; 1730 } 1731 if (std::string_view("org.freedesktop.DBus.Error.UnknownMethod") == 1732 dbusError->name) 1733 { 1734 messages::resourceCannotBeDeleted(asyncResp->res); 1735 return; 1736 } 1737 messages::internalError(asyncResp->res); 1738 } 1739 1740 inline void afterVlanCreate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1741 const std::string& parentInterfaceUri, 1742 const std::string& vlanInterface, 1743 const boost::system::error_code& ec, 1744 const sdbusplus::message_t& m 1745 1746 ) 1747 { 1748 if (ec) 1749 { 1750 const sd_bus_error* dbusError = m.get_error(); 1751 if (dbusError == nullptr) 1752 { 1753 messages::internalError(asyncResp->res); 1754 return; 1755 } 1756 BMCWEB_LOG_DEBUG("DBus error: {}", dbusError->name); 1757 1758 if (std::string_view( 1759 "xyz.openbmc_project.Common.Error.ResourceNotFound") == 1760 dbusError->name) 1761 { 1762 messages::propertyValueNotInList( 1763 asyncResp->res, parentInterfaceUri, 1764 "Links/RelatedInterfaces/0/@odata.id"); 1765 return; 1766 } 1767 if (std::string_view( 1768 "xyz.openbmc_project.Common.Error.InvalidArgument") == 1769 dbusError->name) 1770 { 1771 messages::resourceAlreadyExists(asyncResp->res, "EthernetInterface", 1772 "Id", vlanInterface); 1773 return; 1774 } 1775 messages::internalError(asyncResp->res); 1776 return; 1777 } 1778 1779 const boost::urls::url vlanInterfaceUri = boost::urls::format( 1780 "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", vlanInterface); 1781 asyncResp->res.addHeader("Location", vlanInterfaceUri.buffer()); 1782 } 1783 1784 inline void requestEthernetInterfacesRoutes(App& app) 1785 { 1786 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/") 1787 .privileges(redfish::privileges::getEthernetInterfaceCollection) 1788 .methods(boost::beast::http::verb::get)( 1789 [&app](const crow::Request& req, 1790 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1791 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1792 { 1793 return; 1794 } 1795 1796 asyncResp->res.jsonValue["@odata.type"] = 1797 "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 1798 asyncResp->res.jsonValue["@odata.id"] = 1799 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1800 asyncResp->res.jsonValue["Name"] = 1801 "Ethernet Network Interface Collection"; 1802 asyncResp->res.jsonValue["Description"] = 1803 "Collection of EthernetInterfaces for this Manager"; 1804 1805 // Get eth interface list, and call the below callback for JSON 1806 // preparation 1807 getEthernetIfaceList( 1808 [asyncResp](const bool& success, 1809 const std::vector<std::string>& ifaceList) { 1810 if (!success) 1811 { 1812 messages::internalError(asyncResp->res); 1813 return; 1814 } 1815 1816 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 1817 ifaceArray = nlohmann::json::array(); 1818 for (const std::string& ifaceItem : ifaceList) 1819 { 1820 nlohmann::json::object_t iface; 1821 iface["@odata.id"] = boost::urls::format( 1822 "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", 1823 ifaceItem); 1824 ifaceArray.push_back(std::move(iface)); 1825 } 1826 1827 asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); 1828 asyncResp->res.jsonValue["@odata.id"] = 1829 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 1830 }); 1831 }); 1832 1833 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/") 1834 .privileges(redfish::privileges::postEthernetInterfaceCollection) 1835 .methods(boost::beast::http::verb::post)( 1836 [&app](const crow::Request& req, 1837 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1838 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1839 { 1840 return; 1841 } 1842 1843 bool vlanEnable = false; 1844 uint32_t vlanId = 0; 1845 nlohmann::json::array_t relatedInterfaces; 1846 1847 if (!json_util::readJsonPatch(req, asyncResp->res, "VLAN/VLANEnable", 1848 vlanEnable, "VLAN/VLANId", vlanId, 1849 "Links/RelatedInterfaces", 1850 relatedInterfaces)) 1851 { 1852 return; 1853 } 1854 1855 if (relatedInterfaces.size() != 1) 1856 { 1857 messages::arraySizeTooLong(asyncResp->res, 1858 "Links/RelatedInterfaces", 1859 relatedInterfaces.size()); 1860 return; 1861 } 1862 1863 std::string parentInterfaceUri; 1864 if (!json_util::readJson(relatedInterfaces[0], asyncResp->res, 1865 "@odata.id", parentInterfaceUri)) 1866 { 1867 messages::propertyMissing(asyncResp->res, 1868 "Links/RelatedInterfaces/0/@odata.id"); 1869 return; 1870 } 1871 BMCWEB_LOG_INFO("Parent Interface URI: {}", parentInterfaceUri); 1872 1873 boost::system::result<boost::urls::url_view> parsedUri = 1874 boost::urls::parse_relative_ref(parentInterfaceUri); 1875 if (!parsedUri) 1876 { 1877 messages::propertyValueFormatError( 1878 asyncResp->res, parentInterfaceUri, 1879 "Links/RelatedInterfaces/0/@odata.id"); 1880 return; 1881 } 1882 1883 std::string parentInterface; 1884 if (!crow::utility::readUrlSegments( 1885 *parsedUri, "redfish", "v1", "Managers", "bmc", 1886 "EthernetInterfaces", std::ref(parentInterface))) 1887 { 1888 messages::propertyValueNotInList( 1889 asyncResp->res, parentInterfaceUri, 1890 "Links/RelatedInterfaces/0/@odata.id"); 1891 return; 1892 } 1893 1894 if (!vlanEnable) 1895 { 1896 // In OpenBMC implementation, VLANEnable cannot be false on 1897 // create 1898 messages::propertyValueIncorrect(asyncResp->res, "VLAN/VLANEnable", 1899 "false"); 1900 return; 1901 } 1902 1903 std::string vlanInterface = parentInterface + "_" + 1904 std::to_string(vlanId); 1905 crow::connections::systemBus->async_method_call( 1906 [asyncResp, parentInterfaceUri, 1907 vlanInterface](const boost::system::error_code& ec, 1908 const sdbusplus::message_t& m) { 1909 afterVlanCreate(asyncResp, parentInterfaceUri, vlanInterface, ec, 1910 m); 1911 }, 1912 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 1913 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", parentInterface, 1914 vlanId); 1915 }); 1916 1917 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/") 1918 .privileges(redfish::privileges::getEthernetInterface) 1919 .methods(boost::beast::http::verb::get)( 1920 [&app](const crow::Request& req, 1921 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1922 const std::string& ifaceId) { 1923 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1924 { 1925 return; 1926 } 1927 getEthernetIfaceData( 1928 ifaceId, 1929 [asyncResp, ifaceId](const bool& success, 1930 const EthernetInterfaceData& ethData, 1931 const std::vector<IPv4AddressData>& ipv4Data, 1932 const std::vector<IPv6AddressData>& ipv6Data) { 1933 if (!success) 1934 { 1935 // TODO(Pawel)consider distinguish between non 1936 // existing object, and other errors 1937 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 1938 ifaceId); 1939 return; 1940 } 1941 1942 asyncResp->res.jsonValue["@odata.type"] = 1943 "#EthernetInterface.v1_9_0.EthernetInterface"; 1944 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface"; 1945 asyncResp->res.jsonValue["Description"] = 1946 "Management Network Interface"; 1947 1948 parseInterfaceData(asyncResp, ifaceId, ethData, ipv4Data, ipv6Data); 1949 }); 1950 }); 1951 1952 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/") 1953 .privileges(redfish::privileges::patchEthernetInterface) 1954 .methods(boost::beast::http::verb::patch)( 1955 [&app](const crow::Request& req, 1956 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1957 const std::string& ifaceId) { 1958 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1959 { 1960 return; 1961 } 1962 std::optional<std::string> hostname; 1963 std::optional<std::string> fqdn; 1964 std::optional<std::string> macAddress; 1965 std::optional<std::string> ipv6DefaultGateway; 1966 std::optional<nlohmann::json::array_t> ipv4StaticAddresses; 1967 std::optional<nlohmann::json::array_t> ipv6StaticAddresses; 1968 std::optional<std::vector<std::string>> staticNameServers; 1969 std::optional<nlohmann::json> dhcpv4; 1970 std::optional<nlohmann::json> dhcpv6; 1971 std::optional<bool> ipv6AutoConfigEnabled; 1972 std::optional<bool> interfaceEnabled; 1973 std::optional<size_t> mtuSize; 1974 DHCPParameters v4dhcpParms; 1975 DHCPParameters v6dhcpParms; 1976 // clang-format off 1977 if (!json_util::readJsonPatch( 1978 req, asyncResp->res, 1979 "DHCPv4", dhcpv4, 1980 "DHCPv6", dhcpv6, 1981 "FQDN", fqdn, 1982 "HostName", hostname, 1983 "IPv4StaticAddresses", ipv4StaticAddresses, 1984 "IPv6DefaultGateway", ipv6DefaultGateway, 1985 "IPv6StaticAddresses", ipv6StaticAddresses, 1986 "InterfaceEnabled", interfaceEnabled, 1987 "MACAddress", macAddress, 1988 "MTUSize", mtuSize, 1989 "StatelessAddressAutoConfig/IPv6AutoConfigEnabled", ipv6AutoConfigEnabled, 1990 "StaticNameServers", staticNameServers 1991 ) 1992 ) 1993 { 1994 return; 1995 } 1996 //clang-format on 1997 if (dhcpv4) 1998 { 1999 if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", 2000 v4dhcpParms.dhcpv4Enabled, "UseDNSServers", 2001 v4dhcpParms.useDnsServers, "UseNTPServers", 2002 v4dhcpParms.useNtpServers, "UseDomainName", 2003 v4dhcpParms.useDomainName)) 2004 { 2005 return; 2006 } 2007 } 2008 2009 if (dhcpv6) 2010 { 2011 if (!json_util::readJson(*dhcpv6, asyncResp->res, "OperatingMode", 2012 v6dhcpParms.dhcpv6OperatingMode, 2013 "UseDNSServers", v6dhcpParms.useDnsServers, 2014 "UseNTPServers", v6dhcpParms.useNtpServers, 2015 "UseDomainName", 2016 v6dhcpParms.useDomainName)) 2017 { 2018 return; 2019 } 2020 } 2021 2022 // Get single eth interface data, and call the below callback 2023 // for JSON preparation 2024 getEthernetIfaceData( 2025 ifaceId, 2026 [asyncResp, ifaceId, hostname = std::move(hostname), 2027 fqdn = std::move(fqdn), macAddress = std::move(macAddress), 2028 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 2029 ipv6DefaultGateway = std::move(ipv6DefaultGateway), 2030 ipv6StaticAddresses = std::move(ipv6StaticAddresses), 2031 staticNameServers = std::move(staticNameServers), 2032 dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6), mtuSize, 2033 ipv6AutoConfigEnabled, v4dhcpParms = std::move(v4dhcpParms), 2034 v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled]( 2035 const bool& success, const EthernetInterfaceData& ethData, 2036 const std::vector<IPv4AddressData>& ipv4Data, 2037 const std::vector<IPv6AddressData>& ipv6Data) { 2038 if (!success) 2039 { 2040 // ... otherwise return error 2041 // TODO(Pawel)consider distinguish between non 2042 // existing object, and other errors 2043 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 2044 ifaceId); 2045 return; 2046 } 2047 2048 if (dhcpv4 || dhcpv6) 2049 { 2050 handleDHCPPatch(ifaceId, ethData, v4dhcpParms, v6dhcpParms, 2051 asyncResp); 2052 } 2053 2054 if (hostname) 2055 { 2056 handleHostnamePatch(*hostname, asyncResp); 2057 } 2058 2059 if (ipv6AutoConfigEnabled) 2060 { 2061 handleSLAACAutoConfigPatch(ifaceId, *ipv6AutoConfigEnabled, 2062 asyncResp); 2063 } 2064 2065 if (fqdn) 2066 { 2067 handleFqdnPatch(ifaceId, *fqdn, asyncResp); 2068 } 2069 2070 if (macAddress) 2071 { 2072 handleMACAddressPatch(ifaceId, *macAddress, asyncResp); 2073 } 2074 2075 if (ipv4StaticAddresses) 2076 { 2077 // TODO(ed) for some reason the capture of 2078 // ipv4Addresses above is returning a const value, 2079 // not a non-const value. This doesn't really work 2080 // for us, as we need to be able to efficiently move 2081 // out the intermedia nlohmann::json objects. This 2082 // makes a copy of the structure, and operates on 2083 // that, but could be done more efficiently 2084 nlohmann::json::array_t ipv4Static = *ipv4StaticAddresses; 2085 handleIPv4StaticPatch(ifaceId, ipv4Static, ipv4Data, asyncResp); 2086 } 2087 2088 if (staticNameServers) 2089 { 2090 handleStaticNameServersPatch(ifaceId, *staticNameServers, 2091 asyncResp); 2092 } 2093 2094 if (ipv6DefaultGateway) 2095 { 2096 messages::propertyNotWritable(asyncResp->res, 2097 "IPv6DefaultGateway"); 2098 } 2099 2100 if (ipv6StaticAddresses) 2101 { 2102 handleIPv6StaticAddressesPatch(ifaceId, *ipv6StaticAddresses, 2103 ipv6Data, asyncResp); 2104 } 2105 2106 if (interfaceEnabled) 2107 { 2108 setEthernetInterfaceBoolProperty(ifaceId, "NICEnabled", 2109 *interfaceEnabled, asyncResp); 2110 } 2111 2112 if (mtuSize) 2113 { 2114 handleMTUSizePatch(ifaceId, *mtuSize, asyncResp); 2115 } 2116 }); 2117 }); 2118 2119 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/") 2120 .privileges(redfish::privileges::deleteEthernetInterface) 2121 .methods(boost::beast::http::verb::delete_)( 2122 [&app](const crow::Request& req, 2123 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2124 const std::string& ifaceId) { 2125 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2126 { 2127 return; 2128 } 2129 2130 crow::connections::systemBus->async_method_call( 2131 [asyncResp, ifaceId](const boost::system::error_code& ec, 2132 const sdbusplus::message_t& m) { 2133 afterDelete(asyncResp, ifaceId, ec, m); 2134 }, 2135 "xyz.openbmc_project.Network", 2136 std::string("/xyz/openbmc_project/network/") + ifaceId, 2137 "xyz.openbmc_project.Object.Delete", "Delete"); 2138 }); 2139 } 2140 2141 } // namespace redfish 2142