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