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