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