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