ncsi: implement the getinfo function

This function is used to dump all the info
of the package and the channels underlying
the package.
This function talks with the NCSI driver over
netlink messages.

Change-Id: Ie0aa8924049a6d3b32426627e025f653f84cfbe9
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/ncsi_util.cpp b/ncsi_util.cpp
index f05cc0b..bf19ee3 100644
--- a/ncsi_util.cpp
+++ b/ncsi_util.cpp
@@ -19,17 +19,182 @@
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
-namespace internal
-{
-
 constexpr auto DEFAULT_VALUE = -1;
 constexpr auto NONE = 0;
+using CallBack = int(*)(struct nl_msg* msg, void* arg);
+
+namespace internal
+{
 
 using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
 using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
 
+CallBack infoCallBack =  [](struct nl_msg* msg, void* arg)
+{
+    using namespace phosphor::network::ncsi;
+    auto nlh = nlmsg_hdr(msg);
+
+    struct nlattr* tb[NCSI_ATTR_MAX + 1] = { nullptr };
+    struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] =
+    {
+        { type: NLA_UNSPEC },
+        { type: NLA_U32 },
+        { type: NLA_NESTED },
+        { type: NLA_U32 },
+        { type: NLA_U32 },
+    };
+
+    struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = { nullptr };
+    struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] =
+    {
+        { type: NLA_UNSPEC },
+        { type: NLA_NESTED },
+        { type: NLA_U32 },
+        { type: NLA_FLAG },
+        { type: NLA_NESTED },
+    };
+
+    struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = { nullptr };
+    struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] =
+    {
+        { type: NLA_UNSPEC },
+        { type: NLA_NESTED },
+        { type: NLA_U32 },
+        { type: NLA_FLAG },
+        { type: NLA_NESTED },
+        { type: NLA_UNSPEC},
+    };
+
+    auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
+    if (!tb[NCSI_ATTR_PACKAGE_LIST])
+    {
+        log<level::ERR>("No Packages");
+        return -1;
+    }
+
+    auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
+    if (!attrTgt)
+    {
+        log<level::ERR>("Package list attribute is null");
+        return -1;
+    }
+
+    auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
+    nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
+    {
+        ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
+                               packagePolicy);
+        if (ret < 0)
+        {
+            log<level::ERR>("Failed to parse package nested");
+            return -1;
+        }
+
+        if (packagetb[NCSI_PKG_ATTR_ID])
+        {
+            auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
+            log<level::DEBUG>("Package has id",
+                              entry("ID=%x", attrID));
+        }
+        else
+        {
+            log<level::DEBUG>("Package with no id\n");
+        }
+
+        if (packagetb[NCSI_PKG_ATTR_FORCED])
+        {
+            log<level::DEBUG>("This package is forced\n");
+        }
+
+        auto channelListTarget = static_cast<nlattr*>(
+                nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
+
+        auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
+        nla_for_each_nested(channelListTarget,
+                            packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
+        {
+            ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
+                                   channelListTarget, channelPolicy);
+            if (ret < 0)
+            {
+                log<level::ERR>("Failed to parse channel nested");
+                return -1;
+            }
+
+            if (channeltb[NCSI_CHANNEL_ATTR_ID])
+            {
+                auto channel = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
+                if (channeltb[NCSI_CHANNEL_ATTR_ACTIVE])
+                {
+                    log<level::DEBUG>("Channel Active",
+                            entry("CHANNEL=%x", channel));
+                }
+                else
+                {
+                    log<level::DEBUG>("Channel not Active",
+                            entry("CHANNEL=%x", channel));
+
+                }
+
+                if (channeltb[NCSI_CHANNEL_ATTR_FORCED])
+                {
+                    log<level::DEBUG>("Channel is forced");
+                }
+
+            }
+            else
+            {
+                log<level::DEBUG>("Channel with no ID");
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
+            {
+                auto major = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
+                log<level::DEBUG>("Channel Major Version",
+                                  entry("VERSION=%x", major));
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
+            {
+                auto minor = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
+                log<level::DEBUG>("Channel Minor Version",
+                                  entry("VERSION=%x", minor));
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
+            {
+                auto str = nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
+                log<level::DEBUG>("Channel Version Str",
+                                  entry("VERSION=%s", str));
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
+            {
+                auto link = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
+                log<level::DEBUG>("Channel Link State",
+                                  entry("STATE=%d", link));
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
+            {
+                log<level::DEBUG>("Active Vlan ids");
+                auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
+                auto vid = static_cast<nlattr*>(nla_data(vids));
+                auto len = nla_len(vids);
+                while (nla_ok(vid, len))
+                {
+                    auto id = nla_get_u16(vid);
+                    log<level::DEBUG>("VID",
+                                      entry("VID=%d", id));
+
+                    vid = nla_next(vid, &len);
+                }
+            }
+
+        }
+
+    }
+    return (int)NL_SKIP;
+};
+
 int applyCmd(int ifindex, int cmd, int package = DEFAULT_VALUE,
-             int channel = DEFAULT_VALUE, int flags = NONE)
+             int channel = DEFAULT_VALUE, int flags = NONE,
+             CallBack function = nullptr)
 {
     nlSocketPtr socket(nl_socket_alloc(),&::nl_socket_free);
     auto ret = genl_connect(socket.get());
@@ -94,6 +259,12 @@
         return ret;
     }
 
+    if (function)
+    {
+        // Add a callback function to the socket
+        nl_socket_modify_cb(socket.get(), NL_CB_VALID, NL_CB_CUSTOM,
+                            function, nullptr);
+    }
 
     ret = nl_send_auto(socket.get(), msg.get());
     if (ret < 0)
@@ -126,6 +297,22 @@
                               ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
 }
 
+int getInfo(int ifindex, int package)
+{
+    if (package == DEFAULT_VALUE)
+    {
+        return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
+                                  package, DEFAULT_VALUE, NLM_F_DUMP,
+                                  internal::infoCallBack);
+    }
+    else
+    {
+        return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
+                                  package, DEFAULT_VALUE, NONE,
+                                  internal::infoCallBack);
+    }
+}
+
 }//namespace ncsi
 }//namespace network
 }//namespace phosphor
diff --git a/ncsi_util.hpp b/ncsi_util.hpp
index c519cb4..570152b 100644
--- a/ncsi_util.hpp
+++ b/ncsi_util.hpp
@@ -27,6 +27,15 @@
  */
 int clearInterface(int ifindex);
 
+/* @brief  This function is used to dump all the info
+ *         of the package and the channels underlying
+ *         the package.
+ * @param[in] ifindex - Interface Index.
+ * @param[in] package - NCSI Package.
+ * @returns 0 on success and negative value for failure.
+ */
+int getInfo(int ifindex, int package);
+
 }//namespace ncsi
 }//namespace network
 }//namespace phosphor