Fix 404 handling in Redfish EthernetInterfaces

Previously, when a user requested a network interface that was
non-sensical, like:
/redfish/v1/Managers/bmc/EthernetInterfaces/foobar/VLANs
OR
/redfish/v1/Managers/bmc/EthernetInterfaces/foobar

They would be presented with a 200-OK, and a partially filled in object.
While this doesn't matter much for the casual redfish user, who uses the
collection to properly enumerate devices, this causes an issue with
security scanners, which think they've found some vulnerability when
they can throw whatever injection text they want to in the message, and
it shows up in the response.

This patchset corrects this, and causes the urls referenced above to
properly return 404, and the appropriate "ResourceNotFound" error
message.

Tested:
Attempted both URLs shown above.  Both return 404.

Ran redfish service validator, observed no errors related to
EthernetInterface, or sub nodes.  Attempted good URLs, and observed no
change to the payload.

Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Change-Id: Idb2758858d4dbaf421c0cef28b1d5d02402e0ad8
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index f1ef23f..1f6b766 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -158,16 +158,18 @@
     return "";
 }
 
-inline void extractEthernetInterfaceData(const std::string &ethiface_id,
+inline bool extractEthernetInterfaceData(const std::string &ethiface_id,
                                          const GetManagedObjects &dbus_data,
                                          EthernetInterfaceData &ethData)
 {
+    bool idFound = false;
     for (const auto &objpath : dbus_data)
     {
         for (const auto &ifacePair : objpath.second)
         {
             if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
             {
+                idFound = true;
                 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
                 {
                     for (const auto &propertyPair : ifacePair.second)
@@ -285,6 +287,7 @@
             }
         }
     }
+    return idFound;
 }
 
 // Helper function that extracts data for single ethernet ipv4 address
@@ -736,7 +739,14 @@
                 return;
             }
 
-            extractEthernetInterfaceData(ethiface_id, resp, ethData);
+            bool found =
+                extractEthernetInterfaceData(ethiface_id, resp, ethData);
+            if (!found)
+            {
+                callback(false, ethData, ipv4Data);
+                return;
+            }
+
             extractIPData(ethiface_id, resp, ipv4Data);
 
             // Fix global GW
@@ -771,7 +781,7 @@
             GetManagedObjects &resp) {
             // Callback requires vector<string> to retrieve all available
             // ethernet interfaces
-            std::vector<std::string> iface_list;
+            boost::container::flat_set<std::string> iface_list;
             iface_list.reserve(resp.size());
             if (error_code)
             {
@@ -797,8 +807,7 @@
                         if (last_pos != std::string::npos)
                         {
                             // and put it into output vector.
-                            iface_list.emplace_back(
-                                iface_id.substr(last_pos + 1));
+                            iface_list.emplace(iface_id.substr(last_pos + 1));
                         }
                     }
                 }
@@ -846,20 +855,21 @@
         res.jsonValue["Name"] = "Ethernet Network Interface Collection";
         res.jsonValue["Description"] =
             "Collection of EthernetInterfaces for this Manager";
-
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         // Get eth interface list, and call the below callback for JSON
         // preparation
         getEthernetIfaceList(
-            [&res](const bool &success,
-                   const std::vector<std::string> &iface_list) {
+            [asyncResp](
+                const bool &success,
+                const boost::container::flat_set<std::string> &iface_list) {
                 if (!success)
                 {
-                    messages::internalError(res);
-                    res.end();
+                    messages::internalError(asyncResp->res);
                     return;
                 }
 
-                nlohmann::json &iface_array = res.jsonValue["Members"];
+                nlohmann::json &iface_array =
+                    asyncResp->res.jsonValue["Members"];
                 iface_array = nlohmann::json::array();
                 std::string tag = "_";
                 for (const std::string &iface_item : iface_list)
@@ -874,10 +884,10 @@
                     }
                 }
 
-                res.jsonValue["Members@odata.count"] = iface_array.size();
-                res.jsonValue["@odata.id"] =
+                asyncResp->res.jsonValue["Members@odata.count"] =
+                    iface_array.size();
+                asyncResp->res.jsonValue["@odata.id"] =
                     "/redfish/v1/Managers/bmc/EthernetInterfaces";
-                res.end();
             });
     }
 };
@@ -1285,6 +1295,12 @@
                                                "EthernetInterface", iface_id);
                     return;
                 }
+
+                // because this has no dependence on the interface at this
+                // point, it needs to be done after we know the interface
+                // exists, not before.
+                getDHCPConfigData(asyncResp);
+
                 asyncResp->res.jsonValue["@odata.type"] =
                     "#EthernetInterface.v1_4_1.EthernetInterface";
                 asyncResp->res.jsonValue["@odata.context"] =
@@ -1296,7 +1312,6 @@
                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
                                    ipv4Data);
             });
-        getDHCPConfigData(asyncResp);
     }
 
     void doPatch(crow::Response &res, const crow::Request &req,
@@ -1699,12 +1714,21 @@
         getEthernetIfaceList(
             [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}](
                 const bool &success,
-                const std::vector<std::string> &iface_list) {
+                const boost::container::flat_set<std::string> &iface_list) {
                 if (!success)
                 {
                     messages::internalError(asyncResp->res);
                     return;
                 }
+
+                if (iface_list.find(rootInterfaceName) == iface_list.end())
+                {
+                    messages::resourceNotFound(asyncResp->res,
+                                               "VLanNetworkInterfaceCollection",
+                                               rootInterfaceName);
+                    return;
+                }
+
                 asyncResp->res.jsonValue["@odata.type"] =
                     "#VLanNetworkInterfaceCollection."
                     "VLanNetworkInterfaceCollection";