Confirm presence of NIC devices described in channel_config

The channel_config.json file describes the IPMI channel arrangement
assigned to the BMC. There is an implied expectation that this
configuration file is a 1:1 description of a set of channels assigned
to the BMC and the baseboard containing the BMC. There are vendors who
may prefer to create a smaller subset of firmware images, and apply
them to mulitple different final assemblies. One example of this is
the use of two final assemblies that differ only in the number of
network PHY devices populated on the board.

This commit compares the channel_config entries for the LAN channels
against the LAN devices reported by getifaddrs. If a LAN channel is
described in the channel_config file, but is missing from the
getifaddrs device list, the channel is marked as invalid, effectively
removing the channel from IPMI. This permits run time identification
of the LAN devices without adding unique vendor specific
identification tests.

Tested:
Used a channel_config file that defined two NIC devices, one on
channel 1, and the other on channel 3. The configuration file marked
both of these entries "isValid = true".
This change was then run on a BMC with only a single NIC attached. The
getifaddrs function returned only the "lo" and the "eth0" devices. The
"eth0" device matched the channel_config entry for channelId == 3, and
a channel name == "eth0".
Using "ipmitool raw 6 0x42 3" returns channel information for eth0.
Using "ipmitool raw 6 0x42 1" returns a 0xcc response code.
Prior to this change this command returned a success response code, and
data for an unknown NIC device.

Used these same commands on a baseboard which populated both NIC eth0,
and eth1.
The ipmitool commands return a success response code, and modify their
respective NIC channels.

Change-Id: I8b52cecdd815ec56efc6af97ad53325cb5d54c06
Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com>
diff --git a/user_channel/channel_mgmt.cpp b/user_channel/channel_mgmt.cpp
index b682ccb..f43741d 100644
--- a/user_channel/channel_mgmt.cpp
+++ b/user_channel/channel_mgmt.cpp
@@ -19,7 +19,9 @@
 #include "apphandler.hpp"
 #include "user_layer.hpp"
 
+#include <ifaddrs.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <boost/interprocess/sync/scoped_lock.hpp>
@@ -905,6 +907,15 @@
 
     channelData.fill(ChannelProperties{});
 
+    // Collect the list of NIC interfaces connected to the BMC. Use this
+    // information to only add IPMI channels that have active NIC interfaces.
+    struct ifaddrs *ifaddr, *ifa;
+    if (int err = getifaddrs(&ifaddr); err < 0)
+    {
+        log<level::DEBUG>("Unable to acquire network interfaces");
+        return -EIO;
+    }
+
     for (int chNum = 0; chNum < maxIpmiChannels; chNum++)
     {
         try
@@ -925,13 +936,30 @@
             if (jsonChInfo.is_null())
             {
                 log<level::ERR>("Invalid/corrupted channel config file");
+                freeifaddrs(ifaddr);
                 return -EBADMSG;
             }
 
+            bool channelFound = true;
+            // Confirm the LAN channel is present
+            if (jsonChInfo[mediumTypeString].get<std::string>() == "lan-802.3")
+            {
+                channelFound = false;
+                for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
+                {
+                    if (jsonChData[nameString].get<std::string>() ==
+                        ifa->ifa_name)
+                    {
+                        channelFound = true;
+                        break;
+                    }
+                }
+            }
             ChannelProperties& chData = channelData[chNum];
-            chData.chName = jsonChData[nameString].get<std::string>();
             chData.chID = chNum;
-            chData.isChValid = jsonChData[isValidString].get<bool>();
+            chData.chName = jsonChData[nameString].get<std::string>();
+            chData.isChValid =
+                channelFound && jsonChData[isValidString].get<bool>();
             chData.activeSessCount = jsonChData.value(activeSessionsString, 0);
             chData.maxTransferSize =
                 jsonChData.value(maxTransferSizeString, smallChannelSize);
@@ -960,14 +988,18 @@
         {
             log<level::DEBUG>("Json Exception caught.",
                               entry("MSG=%s", e.what()));
+            freeifaddrs(ifaddr);
+
             return -EBADMSG;
         }
         catch (const std::invalid_argument& e)
         {
             log<level::ERR>("Corrupted config.", entry("MSG=%s", e.what()));
+            freeifaddrs(ifaddr);
             return -EBADMSG;
         }
     }
+    freeifaddrs(ifaddr);
 
     return 0;
 }