multiple channel configuration support

Previously, the ipmi daemon only supported eth0 and hard-coded it
to channel 1.  This allows one to map via a configuration.  The
channel number provided is checked against a configuration to retrieve
the ethernet device identifier, e.g. eth0.

Tested: Ran on a quanta-q71l and was able to properly set MAC, IP,
Netmask, Gateway IP, and then verified the data was set for the eth1
via `ip addr show eth1`.

Change-Id: I92f63188297304e9454fd0d6fe32bc6cf84bb181
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 50bc473..f4326c1 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -9,6 +9,7 @@
 #include "ipmid.hpp"
 #include "transporthandler.hpp"
 #include "utils.hpp"
+#include "net.hpp"
 
 #include <phosphor-logging/log.hpp>
 #include <phosphor-logging/elog-errors.hpp>
@@ -23,7 +24,7 @@
 
 const int SIZE_MAC = 18; //xx:xx:xx:xx:xx:xx
 
-struct ChannelConfig_t channelConfig;
+std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig;
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
@@ -31,13 +32,34 @@
 
 void register_netfn_transport_functions() __attribute__((constructor));
 
+struct ChannelConfig_t* getChannelConfig(int channel)
+{
+    auto item = channelConfig.find(channel);
+    if (item == channelConfig.end())
+    {
+        channelConfig[channel] = std::make_unique<struct ChannelConfig_t>();
+    }
+
+    return channelConfig[channel].get();
+}
+
 // Helper Function to get IP Address/NetMask/Gateway/MAC Address from Network Manager or
 // Cache based on Set-In-Progress State
-ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t* data)
+ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t* data, int channel)
 {
     ipmi_ret_t rc = IPMI_CC_OK;
     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
 
+    auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
+    // if ethdevice is an empty string they weren't expecting this channel.
+    if (ethdevice.empty())
+    {
+        // TODO: return error from getNetworkData()
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
+    auto channelConf = getChannelConfig(channel);
+
     try
     {
         switch (lan_param)
@@ -45,7 +67,7 @@
             case LAN_PARM_IP:
             {
                 std::string ipaddress;
-                if (channelConfig.lan_set_in_progress == SET_COMPLETE)
+                if (channelConf->lan_set_in_progress == SET_COMPLETE)
                 {
                     try
                     {
@@ -53,7 +75,7 @@
                                 bus,
                                 ipmi::network::IP_INTERFACE,
                                 ipmi::network::ROOT,
-                                ipmi::network::IP_TYPE);
+                                ethIP);
 
                         auto properties = ipmi::getAllDbusProperties(
                                 bus,
@@ -62,7 +84,6 @@
                                 ipmi::network::IP_INTERFACE);
 
                         ipaddress = properties["Address"].get<std::string>();
-
                     }
                     // ignore the exception, as it is a valid condtion that
                     // system is not confiured with any ip.
@@ -71,9 +92,9 @@
                         // nothing to do.
                     }
                 }
-                else if (channelConfig.lan_set_in_progress == SET_IN_PROGRESS)
+                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
                 {
-                    ipaddress = channelConfig.ipaddr;
+                    ipaddress = channelConf->ipaddr;
                 }
 
                 inet_pton(AF_INET, ipaddress.c_str(),
@@ -85,7 +106,7 @@
             {
                 std::string networkInterfacePath;
 
-                if (channelConfig.lan_set_in_progress == SET_COMPLETE)
+                if (channelConf->lan_set_in_progress == SET_COMPLETE)
                 {
                     try
                     {
@@ -96,7 +117,7 @@
                                 bus,
                                 ipmi::network::IP_INTERFACE,
                                 ipmi::network::ROOT,
-                                ipmi::network::IP_TYPE);
+                                ethIP);
 
                         // Get the parent interface of the IP object.
                         try
@@ -133,7 +154,7 @@
                                 bus,
                                 ipmi::network::ETHERNET_INTERFACE,
                                 ipmi::network::ROOT,
-                                ipmi::network::INTERFACE);
+                                ethdevice);
 
                         networkInterfacePath = networkInterfaceObject.first;
                     }
@@ -152,9 +173,9 @@
 
                     memcpy(data, &ipsrc, ipmi::network::IPSRC_SIZE_BYTE);
                 }
-                else if (channelConfig.lan_set_in_progress == SET_IN_PROGRESS)
+                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
                 {
-                   memcpy(data, &(channelConfig.ipsrc),
+                   memcpy(data, &(channelConf->ipsrc),
                           ipmi::network::IPSRC_SIZE_BYTE);
                 }
             }
@@ -163,7 +184,7 @@
             case LAN_PARM_SUBNET:
             {
                 unsigned long mask {};
-                if (channelConfig.lan_set_in_progress == SET_COMPLETE)
+                if (channelConf->lan_set_in_progress == SET_COMPLETE)
                 {
                     try
                     {
@@ -191,9 +212,9 @@
                     }
                     memcpy(data, &mask, ipmi::network::IPV4_ADDRESS_SIZE_BYTE);
                 }
-                else if (channelConfig.lan_set_in_progress == SET_IN_PROGRESS)
+                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
                 {
-                    inet_pton(AF_INET, channelConfig.netmask.c_str(),
+                    inet_pton(AF_INET, channelConf->netmask.c_str(),
                               reinterpret_cast<void*>(data));
                 }
 
@@ -204,7 +225,7 @@
             {
                 std::string gateway;
 
-                if (channelConfig.lan_set_in_progress == SET_COMPLETE)
+                if (channelConf->lan_set_in_progress == SET_COMPLETE)
                 {
                     try
                     {
@@ -230,9 +251,9 @@
                     }
 
                 }
-                else if (channelConfig.lan_set_in_progress == SET_IN_PROGRESS)
+                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
                 {
-                    gateway = channelConfig.gateway;
+                    gateway = channelConf->gateway;
                 }
 
                 inet_pton(AF_INET, gateway.c_str(),
@@ -243,12 +264,13 @@
             case LAN_PARM_MAC:
             {
                 std::string macAddress;
-                if (channelConfig.lan_set_in_progress == SET_COMPLETE)
+                if (channelConf->lan_set_in_progress == SET_COMPLETE)
                 {
                     auto macObjectInfo = ipmi::getDbusObject(
                                              bus,
                                              ipmi::network::MAC_INTERFACE,
-                                             ipmi::network::ROOT);
+                                             ipmi::network::ROOT,
+                                             ethdevice);
 
                     auto variant = ipmi::getDbusProperty(
                                      bus,
@@ -260,9 +282,9 @@
                     macAddress = variant.get<std::string>();
 
                 }
-                else if (channelConfig.lan_set_in_progress == SET_IN_PROGRESS)
+                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
                 {
-                    macAddress = channelConfig.macAddress;
+                    macAddress = channelConf->macAddress;
                 }
 
                 sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
@@ -278,7 +300,7 @@
             case LAN_PARM_VLAN:
             {
                 uint16_t vlanID {};
-                if (channelConfig.lan_set_in_progress == SET_COMPLETE)
+                if (channelConf->lan_set_in_progress == SET_COMPLETE)
                 {
                     try
                     {
@@ -308,9 +330,9 @@
 
                     memcpy(data, &vlanID, ipmi::network::VLAN_SIZE_BYTE);
                 }
-                else if (channelConfig.lan_set_in_progress == SET_IN_PROGRESS)
+                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
                 {
-                    memcpy(data, &(channelConfig.vlanID),
+                    memcpy(data, &(channelConf->vlanID),
                            ipmi::network::VLAN_SIZE_BYTE);
                 }
             }
@@ -364,6 +386,15 @@
     auto reqptr = reinterpret_cast<const set_lan_t*>(request);
     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
 
+    // channel number is the lower nibble
+    int channel = reqptr->channel & CHANNEL_MASK;
+    auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
+    if (ethdevice.empty())
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    auto channelConf = getChannelConfig(channel);
+
     switch (reqptr->parameter)
     {
         case LAN_PARM_IP:
@@ -372,8 +403,7 @@
                      reqptr->data[0], reqptr->data[1],
                      reqptr->data[2], reqptr->data[3]);
 
-            channelConfig.ipaddr.assign(ipaddr);
-
+            channelConf->ipaddr.assign(ipaddr);
         }
         break;
 
@@ -381,7 +411,7 @@
         {
             uint8_t ipsrc{};
             memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE);
-            channelConfig.ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
+            channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
         }
         break;
 
@@ -401,7 +431,7 @@
                                      bus,
                                      ipmi::network::MAC_INTERFACE,
                                      ipmi::network::ROOT,
-                                     ipmi::network::INTERFACE);
+                                     ethdevice);
 
             ipmi::setDbusProperty(bus,
                                   macObjectInfo.second,
@@ -410,8 +440,7 @@
                                   "MACAddress",
                                   std::string(mac));
 
-            channelConfig.macAddress = mac;
-
+            channelConf->macAddress = mac;
         }
         break;
 
@@ -420,7 +449,7 @@
             snprintf(netmask, INET_ADDRSTRLEN, ipmi::network::IP_ADDRESS_FORMAT,
                      reqptr->data[0], reqptr->data[1],
                      reqptr->data[2], reqptr->data[3]);
-            channelConfig.netmask.assign(netmask);
+            channelConf->netmask.assign(netmask);
         }
         break;
 
@@ -429,8 +458,7 @@
             snprintf(gateway, INET_ADDRSTRLEN, ipmi::network::IP_ADDRESS_FORMAT,
                      reqptr->data[0], reqptr->data[1],
                      reqptr->data[2], reqptr->data[3]);
-            channelConfig.gateway.assign(gateway);
-
+            channelConf->gateway.assign(gateway);
         }
         break;
 
@@ -442,7 +470,7 @@
             // We assume that ipmitool always send enable
             // bit as 1.
             vlan = le16toh(vlan);
-            channelConfig.vlanID = vlan;
+            channelConf->vlanID = vlan;
         }
         break;
 
@@ -450,22 +478,20 @@
         {
             if (reqptr->data[0] == SET_COMPLETE)
             {
-                channelConfig.lan_set_in_progress = SET_COMPLETE;
+                channelConf->lan_set_in_progress = SET_COMPLETE;
 
                 log<level::INFO>("Network data from Cache",
-                                 entry("PREFIX=%s", channelConfig.netmask.c_str()),
-                                 entry("ADDRESS=%s", channelConfig.ipaddr.c_str()),
-                                 entry("GATEWAY=%s", channelConfig.gateway.c_str()),
-                                 entry("VLAN=%d", channelConfig.vlanID));
+                                 entry("PREFIX=%s", channelConf->netmask.c_str()),
+                                 entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
+                                 entry("GATEWAY=%s", channelConf->gateway.c_str()),
+                                 entry("VLAN=%d", channelConf->vlanID));
 
                 log<level::INFO>("Use Set Channel Access command to apply");
-
             }
             else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
             {
-                channelConfig.lan_set_in_progress = SET_IN_PROGRESS;
+                channelConf->lan_set_in_progress = SET_IN_PROGRESS;
             }
-
         }
         break;
 
@@ -473,7 +499,6 @@
         {
             rc = IPMI_CC_PARM_NOT_SUPPORTED;
         }
-
     }
 
     return rc;
@@ -499,6 +524,8 @@
     const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0
 
     get_lan_t *reqptr = (get_lan_t*) request;
+    // channel number is the lower nibble
+    int channel = reqptr->rev_channel & CHANNEL_MASK;
 
     if (reqptr->rev_channel & 0x80) // Revision is bit 7
     {
@@ -508,9 +535,16 @@
         return IPMI_CC_OK;
     }
 
+    auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
+    if (ethdevice.empty())
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    auto channelConf = getChannelConfig(channel);
+
     if (reqptr->parameter == LAN_PARM_INPROGRESS)
     {
-        uint8_t buf[] = {current_revision, channelConfig.lan_set_in_progress};
+        uint8_t buf[] = {current_revision, channelConf->lan_set_in_progress};
         *data_len = sizeof(buf);
         memcpy(response, &buf, *data_len);
     }
@@ -536,7 +570,7 @@
         *data_len = sizeof(current_revision);
         memcpy(buf, &current_revision, *data_len);
 
-        if (getNetworkData(reqptr->parameter, &buf[1]) == IPMI_CC_OK)
+        if (getNetworkData(reqptr->parameter, &buf[1], channel) == IPMI_CC_OK)
         {
             if (reqptr->parameter == LAN_PARM_MAC)
             {
@@ -559,7 +593,7 @@
 
         *data_len = sizeof(current_revision);
         memcpy(buf, &current_revision, *data_len);
-        if (getNetworkData(reqptr->parameter, &buf[1]) == IPMI_CC_OK)
+        if (getNetworkData(reqptr->parameter, &buf[1], channel) == IPMI_CC_OK)
         {
             *data_len = sizeof(buf);
             memcpy(response, &buf, *data_len);
@@ -570,7 +604,7 @@
         uint8_t buff[ipmi::network::IPSRC_SIZE_BYTE + 1] = {};
         *data_len = sizeof(current_revision);
         memcpy(buff, &current_revision, *data_len);
-        if (getNetworkData(reqptr->parameter, &buff[1]) == IPMI_CC_OK)
+        if (getNetworkData(reqptr->parameter, &buff[1], channel) == IPMI_CC_OK)
         {
             *data_len = sizeof(buff);
             memcpy(response, &buff, *data_len);