SetLan: Move the setChannel access function

Move the function from app/channel.cpp to the
transporthandler.cpp

setChannel access is disabled now as the functionality
to apply the network changes would be done through timer
based approach.

In later commit we would create this function as callback
when the network timer expires.

Change-Id: Ic128a47237b2ecdbc56267227105909f8886a862
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/transporthandler.cpp b/transporthandler.cpp
index f4326c1..bbc6ae5 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -22,7 +22,19 @@
 #include <mapper.h>
 #endif
 
+/** @struct SetChannelAccessRequest
+ *
+ * IPMI payload for Set Channel access command request.
+ */
+struct SetChannelAccessRequest
+{
+    uint8_t channelNumber;       //!< Channel number.
+    uint8_t setting;             //!< The setting values.
+    uint8_t privilegeLevelLimit; //!< The Privilege Level Limit
+} __attribute__((packed));
+
 const int SIZE_MAC = 18; //xx:xx:xx:xx:xx:xx
+constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
 
 std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig;
 
@@ -620,6 +632,319 @@
     return rc;
 }
 
+ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn,
+                                   ipmi_cmd_t cmd,
+                                   ipmi_request_t request,
+                                   ipmi_response_t response,
+                                   ipmi_data_len_t data_len,
+                                   ipmi_context_t context)
+{
+    ipmi_ret_t rc = IPMI_CC_OK;
+
+    std::string ipaddress;
+    std::string gateway;
+    uint8_t prefix {};
+    uint32_t vlanID {};
+    std::string networkInterfacePath;
+    ipmi::DbusObjectInfo ipObject;
+    ipmi::DbusObjectInfo systemObject;
+
+    if (*data_len < sizeof(SetChannelAccessRequest))
+    {
+        return IPMI_CC_INVALID;
+    }
+
+    auto requestData = reinterpret_cast<const SetChannelAccessRequest*>
+                   (request);
+    int channel = requestData->channelNumber & CHANNEL_MASK;
+
+    auto ethdevice = ipmi::network::ChanneltoEthernet(channel);
+    if (ethdevice.empty())
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+    auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE;
+    auto channelConf = getChannelConfig(channel);
+
+    // Todo: parse the request data if needed.
+    // Using Set Channel cmd to apply changes of Set Lan Cmd.
+    try
+    {
+        sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+
+        log<level::INFO>("Network data from Cache",
+                         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),
+                         entry("IPSRC=%d", channelConf->ipsrc));
+        if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
+        {
+            //get the first twelve bits which is vlan id
+            //not interested in rest of the bits.
+            channelConf->vlanID = le32toh(channelConf->vlanID);
+            vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
+        }
+
+        // if the asked ip src is DHCP then not interested in
+        // any given data except vlan.
+        if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
+        {
+            // always get the system object
+            systemObject = ipmi::getDbusObject(
+                    bus,
+                    ipmi::network::SYSTEMCONFIG_INTERFACE,
+                    ipmi::network::ROOT);
+
+            // the below code is to determine the mode of the interface
+            // as the handling is same, if the system is configured with
+            // DHCP or user has given all the data.
+            try
+            {
+                ipmi::ObjectTree ancestorMap;
+
+                ipmi::InterfaceList interfaces {
+                    ipmi::network::ETHERNET_INTERFACE };
+
+                // if the system is having ip object,then
+                // get the IP object.
+                ipObject = ipmi::getDbusObject(bus,
+                                               ipmi::network::IP_INTERFACE,
+                                               ipmi::network::ROOT,
+                                               ethIp);
+
+                // Get the parent interface of the IP object.
+                try
+                {
+                    ancestorMap = ipmi::getAllAncestors(bus,
+                                                        ipObject.first,
+                                                        std::move(interfaces));
+                }
+                catch (InternalFailure& e)
+                {
+                    // if unable to get the parent interface
+                    // then commit the error and return.
+                    log<level::ERR>("Unable to get the parent interface",
+                                    entry("PATH=%s", ipObject.first.c_str()),
+                                    entry("INTERFACE=%s",
+                                          ipmi::network::ETHERNET_INTERFACE));
+                    commit<InternalFailure>();
+                    rc = IPMI_CC_UNSPECIFIED_ERROR;
+                    channelConf->clear();
+                    return rc;
+                }
+
+                networkInterfacePath = ancestorMap.begin()->first;
+            }
+            catch (InternalFailure& e)
+            {
+                // TODO Currently IPMI supports single interface,need to handle
+                // Multiple interface through
+                // https://github.com/openbmc/openbmc/issues/2138
+
+                // if there is no ip configured on the system,then
+                // get the network interface object.
+                auto networkInterfaceObject = ipmi::getDbusObject(
+                        bus,
+                        ipmi::network::ETHERNET_INTERFACE,
+                        ipmi::network::ROOT,
+                        ethdevice);
+
+                networkInterfacePath = std::move(networkInterfaceObject.first);
+            }
+
+            // get the configured mode on the system.
+            auto enableDHCP = ipmi::getDbusProperty(
+                    bus,
+                    ipmi::network::SERVICE,
+                    networkInterfacePath,
+                    ipmi::network::ETHERNET_INTERFACE,
+                    "DHCPEnabled").get<bool>();
+
+            // if ip address source is not given then get the ip source mode
+            // from the system so that it can be applied later.
+            if (channelConf->ipsrc == ipmi::network::IPOrigin::UNSPECIFIED)
+            {
+                channelConf->ipsrc = (enableDHCP) ?
+                    ipmi::network::IPOrigin::DHCP :
+                    ipmi::network::IPOrigin::STATIC;
+            }
+
+            // check whether user has given all the data
+            // or the configured system interface is dhcp enabled,
+            // in both of the cases get the values from the cache.
+            if ((!channelConf->ipaddr.empty() &&
+                 !channelConf->netmask.empty() &&
+                 !channelConf->gateway.empty()) ||
+                (enableDHCP)) // configured system interface mode = DHCP
+            {
+                //convert mask into prefix
+                ipaddress = channelConf->ipaddr;
+                prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
+                gateway = channelConf->gateway;
+            }
+            else // asked ip src = static and configured system src = static
+                // or partially given data.
+            {
+                // We have partial filled cache so get the remaining
+                // info from the system.
+
+                // Get the network data from the system as user has
+                // not given all the data then use the data fetched from the
+                // system but it is implementation dependent,IPMI spec doesn't
+                // force it.
+
+                // if system is not having any ip object don't throw error,
+                try
+                {
+                    auto properties = ipmi::getAllDbusProperties(
+                            bus,
+                            ipObject.second,
+                            ipObject.first,
+                            ipmi::network::IP_INTERFACE);
+
+                    ipaddress = channelConf->ipaddr.empty() ?
+                        properties["Address"].get<std::string>() :
+                        channelConf->ipaddr;
+
+                    prefix = channelConf->netmask.empty() ?
+                        properties["PrefixLength"].get<uint8_t>() :
+                        ipmi::network::toPrefix(AF_INET,
+                                channelConf->netmask);
+               }
+                catch (InternalFailure& e)
+                {
+                    log<level::INFO>("Failed to get IP object which matches",
+                            entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
+                            entry("MATCH=%s", ethIp));
+                }
+
+                auto systemProperties = ipmi::getAllDbusProperties(
+                        bus,
+                        systemObject.second,
+                        systemObject.first,
+                        ipmi::network::SYSTEMCONFIG_INTERFACE);
+
+                gateway = channelConf->gateway.empty() ?
+                        systemProperties["DefaultGateway"].get<std::string>() :
+                        channelConf->gateway;
+            }
+        }
+
+        // Currently network manager doesn't support purging of all the
+        // ip addresses and the vlan interfaces from the parent interface,
+        // TODO once the support is there, will make the change here.
+        // https://github.com/openbmc/openbmc/issues/2141.
+
+        // TODO Currently IPMI supports single interface,need to handle
+        // Multiple interface through
+        // https://github.com/openbmc/openbmc/issues/2138
+
+        // instead of deleting all the vlan interfaces and
+        // all the ipv4 address,we will call reset method.
+        //delete all the vlan interfaces
+
+        ipmi::deleteAllDbusObjects(bus,
+                                   ipmi::network::ROOT,
+                                   ipmi::network::VLAN_INTERFACE);
+
+        // set the interface mode  to static
+        auto networkInterfaceObject = ipmi::getDbusObject(
+                bus,
+                ipmi::network::ETHERNET_INTERFACE,
+                ipmi::network::ROOT,
+                ethdevice);
+
+        // setting the physical interface mode to static.
+        ipmi::setDbusProperty(bus,
+                              ipmi::network::SERVICE,
+                              networkInterfaceObject.first,
+                              ipmi::network::ETHERNET_INTERFACE,
+                              "DHCPEnabled",
+                              false);
+
+        networkInterfacePath = networkInterfaceObject.first;
+
+        //delete all the ipv4 addresses
+        ipmi::deleteAllDbusObjects(bus,
+                                   ipmi::network::ROOT,
+                                   ipmi::network::IP_INTERFACE,
+                                   ethIp);
+
+        if (vlanID)
+        {
+            ipmi::network::createVLAN(bus,
+                                      ipmi::network::SERVICE,
+                                      ipmi::network::ROOT,
+                                      ethdevice,
+                                      vlanID);
+
+            auto networkInterfaceObject = ipmi::getDbusObject(
+                    bus,
+                    ipmi::network::VLAN_INTERFACE,
+                    ipmi::network::ROOT);
+
+           networkInterfacePath = networkInterfaceObject.first;
+        }
+
+        if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
+        {
+            ipmi::setDbusProperty(bus,
+                                  ipmi::network::SERVICE,
+                                  networkInterfacePath,
+                                  ipmi::network::ETHERNET_INTERFACE,
+                                  "DHCPEnabled",
+                                  true);
+        }
+        else
+        {
+            //change the mode to static
+            ipmi::setDbusProperty(bus,
+                                  ipmi::network::SERVICE,
+                                  networkInterfacePath,
+                                  ipmi::network::ETHERNET_INTERFACE,
+                                  "DHCPEnabled",
+                                  false);
+
+            if (!ipaddress.empty())
+            {
+                ipmi::network::createIP(bus,
+                                        ipmi::network::SERVICE,
+                                        networkInterfacePath,
+                                        ipv4Protocol,
+                                        ipaddress,
+                                        prefix);
+            }
+
+            if (!gateway.empty())
+            {
+                ipmi::setDbusProperty(bus,
+                                      systemObject.second,
+                                      systemObject.first,
+                                      ipmi::network::SYSTEMCONFIG_INTERFACE,
+                                      "DefaultGateway",
+                                      std::string(gateway));
+            }
+        }
+
+    }
+    catch (InternalFailure& e)
+    {
+        log<level::ERR>("Failed to set network data",
+                        entry("PREFIX=%d", prefix),
+                        entry("ADDRESS=%s", ipaddress.c_str()),
+                        entry("GATEWAY=%s", gateway.c_str()),
+                        entry("VLANID=%d", vlanID),
+                        entry("IPSRC=%d", channelConf->ipsrc));
+
+        commit<InternalFailure>();
+        rc = IPMI_CC_UNSPECIFIED_ERROR;
+    }
+
+    channelConf->clear();
+    return rc;
+}
+
 void register_netfn_transport_functions()
 {
     // <Wildcard Command>