/*
*
* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <stdint.h>
#include <sched.h>
#include <dlfcn.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/file.h>
#include <pwd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fnmatch.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>

#include <rte_config.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <rte_eth_bond.h>
#include "nsfw_init.h"
#include "common_mem_mbuf.h"
#include "common_mem_mempool.h"
#include "common_func.h"
#include "hal.h"
#include "nstack_securec.h"
#include <rte_ethdev_driver.h>

#define DPDK_NON_ROOT_USER_NAME "paas"
#define DPDK_TOOL_ENV "DPDK_TOOL_DIR"
#define DPDK_NIC_LIST_FILE   "%s/ip_module/.nstack_dpdk_nic_list"
#define SOCKET_ID_0 0

NSTACK_STATIC struct passwd *dpdk_non_root_user;
NSTACK_STATIC char dpdk_tool_path[HAL_MAX_PATH_LEN] = { 0 };

/* Default configuration for rx and tx thresholds etc. */
NSTACK_STATIC const struct rte_eth_rxconf rx_conf_default_igb = {
  .rx_thresh = {
                .pthresh = 8,
                .hthresh = 8,
                .wthresh = 1,   //not bigger than 1
                },
};

/*
 * These default values are optimized for use with the Intel(R) 82576 1 GbE
 * Controller and the DPDK e1000 PMD. Consider using other values for other
 * network controllers and/or network drivers.
 */
NSTACK_STATIC const struct rte_eth_txconf tx_conf_default_igb = {
  .tx_thresh = {
                .pthresh = 8,
                .hthresh = 1,
                .wthresh = 16,
                },
  .tx_free_thresh = 0,          /* Use PMD default values */
  .tx_rs_thresh = 0,            /* Use PMD default values */
};

/*
 * RX and TX Prefetch, Host, and Write-back threshold values should be
 * carefully set for optimal performance. Consult the network
 * controller's datasheet and supporting DPDK documentation for guidance
 * on how these parameters should be set.
 */

/* Default configuration for rx and tx thresholds etc. */
NSTACK_STATIC const struct rte_eth_rxconf rx_conf_default_ixgbe = {
  .rx_thresh = {
                .pthresh = 8,
                .hthresh = 8,
                .wthresh = 4,
                },
  .rx_free_thresh = 0,
};

/*
 * These default values are optimized for use with the Intel(R) 82599 10 GbE
 * Controller and the DPDK ixgbe PMD. Consider using other values for other
 * network controllers and/or network drivers.
 */
NSTACK_STATIC const struct rte_eth_txconf tx_conf_default_ixgbe = {
  .tx_thresh = {
                .pthresh = 36,
                .hthresh = 0,
                .wthresh = 0,
                },
  .tx_free_thresh = 0,          /* Use PMD default values */
  .tx_rs_thresh = 0,            /* Use PMD default values */
  .txq_flags = 0,
};

/* the port configuration of normal port */
NSTACK_STATIC struct rte_eth_conf port_conf_default_normal = {
  .rxmode = {
             .mq_mode = ETH_RSS,
             .max_rx_pkt_len = ETHER_MAX_LEN,
             .split_hdr_size = 0,
             .header_split = 0,
             .hw_ip_checksum = 1,
             .hw_vlan_filter = 1,
             .hw_vlan_strip = 1,
             .jumbo_frame = 0,
             .hw_strip_crc = 0,
             },
  .rx_adv_conf = {
                  .rss_conf = {
                               .rss_key = NULL,
                               .rss_hf = (ETH_RSS_IPV4 | ETH_RSS_NONFRAG_IPV4_TCP | ETH_RSS_NONFRAG_IPV4_UDP),  //rss hash key
                               },
                  },
  .txmode = {
             .mq_mode = ETH_DCB_NONE,
             },
  .intr_conf = {
                .lsc = 0,
                },
};

/* the port configuration of virtio port */
NSTACK_STATIC struct rte_eth_conf port_conf_default_virtio = {
  .rxmode = {
             .mq_mode = ETH_RSS,
             .max_rx_pkt_len = ETHER_MAX_LEN,
             .split_hdr_size = 0,
             .header_split = 0,
             .hw_ip_checksum = 0,       /* Virtio NIC doesn't support HW IP CheckSUM */
             .hw_vlan_filter = 1,
             .hw_vlan_strip = 1,
             .jumbo_frame = 0,
             .hw_strip_crc = 0,
             },
  .rx_adv_conf = {
                  .rss_conf = {
                               .rss_key = NULL,
                               .rss_hf = (ETH_RSS_IPV4 | ETH_RSS_NONFRAG_IPV4_TCP | ETH_RSS_NONFRAG_IPV4_UDP),  //rss hash key
                               },
                  },
  .txmode = {
             .mq_mode = ETH_DCB_NONE,
             },
  .intr_conf = {
                .lsc = 0,
                },
};

/* the port configuration of bond port */
NSTACK_STATIC struct rte_eth_conf port_conf_default_bond = {
  .rxmode = {
             .mq_mode = ETH_MQ_RX_NONE,
             .max_rx_pkt_len = ETHER_MAX_LEN,
             .split_hdr_size = 0,
             .header_split = 0,
                             /**< Header Split disabled */
             .hw_ip_checksum = 0,
                             /**< IP checksum offload enabled */
             .hw_vlan_filter = 1,
                             /**< VLAN filtering enabled */
             .hw_vlan_strip = 1,
             .jumbo_frame = 0,
                             /**< Jumbo Frame Support disabled */
             .hw_strip_crc = 0,
                             /**< CRC stripped by hardware */
             },
  .rx_adv_conf = {
                  .rss_conf = {
                               .rss_key = NULL,
                               .rss_hf = ETH_RSS_IP,
                               },
                  },
  .txmode = {
             .mq_mode = ETH_MQ_TX_NONE,
             },
};

/*****************************************************************************
*   Prototype    : dpdk_mbuf_to_file
*   Description  : write the packet data into a file
*   Input        : uint16_t pkt_number
*                  struct rte_mbuf **pkts
*   Output       : None
*   Return Value :
*   Calls        :
*   Called By    :
*
*****************************************************************************/
NSTACK_STATIC void
dpdk_mbuf_to_file (uint16_t pkt_number, struct rte_mbuf **pkts)
{
  char line[100] = { 0 };
  FILE *f = NULL;
  struct rte_mbuf *p = NULL;
  uint16_t len = 0, offset, i;
  uint16_t pktlen = 0;
  uint16_t start = 0;
  uint16_t number = 0;
  unsigned char *data = NULL;

  f = fopen ("/var/log/nStack/packet.txt", "a+");
  if (f == NULL)
    {
      NSHAL_LOGERR ("can not open the file:%s", "packet.txt");
      return;
    }

  for (i = 0; i < pkt_number; i++)
    {
      pktlen = 0;
      p = pkts[i];
      while (p)
        {
          len = 0;
          data = rte_pktmbuf_mtod (p, unsigned char *);
          while (len < p->data_len)
            {
              start = pktlen % 16;      /* start of the line */
              if (start == 0)
                {
                  number = SNPRINTF_S (line, sizeof (line), sizeof (line) - 1,
                                       "%08X", len);
                }

              for (offset = 0;
                   ((offset + start) < 16) && ((len + offset) < p->data_len);
                   offset++)
                {
                  number +=
                    SNPRINTF_S (line + number, sizeof (line),
                                sizeof (line) - 1, " %02X",
                                data[len + offset]);
                }

              fprintf (f, "%s", line);
              if ((offset + start) == 16)
                fprintf (f, "\n");

              len += offset;
              pktlen += offset;
              (void) MEMSET_S (line, sizeof (line), 0, sizeof (line));
            }

          p = p->next;

        }
      fprintf (f, "\n");
    }

  fclose (f);
  return;
}

/*****************************************************************************
*   Prototype    : hal_rte_eth_rx_burst
*   Description  : a copy of rte_eth_rx_burst, because this function invokes
                   a global(rte_eth_devices), which cannt be access by dlsym
                   symbols
*   Input        : uint8_t port_id
*                  uint16_t queue_id
*                  struct rte_mbuf **rx_pkts
*                  const uint16_t nb_pkts
*   Output       : None
*   Return Value :
*   Calls        :
*   Called By    :
*
*****************************************************************************/
NSTACK_STATIC inline uint16_t
hal_rte_eth_rx_burst (uint8_t port_id, uint16_t queue_id,
                      struct rte_mbuf ** rx_pkts, const uint16_t nb_pkts)
{
#ifdef RTE_ETHDEV_RXTX_CALLBACKS
  struct rte_eth_rxtx_callback *cb;
#endif
  int16_t nb_rx;
  char *pst_capture_packet = NULL;
  struct rte_eth_dev *dev = &rte_eth_devices[port_id];

  if (NULL == dev->rx_pkt_burst)
    {
      NSHAL_LOGERR ("dev->rx_pkt_burst is NULL,dev=%p", dev);
      return 0;
    }

#ifdef RTE_LIBRTE_ETHDEV_DEBUG
  RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, 0);
  RTE_FUNC_PTR_OR_ERR_RET (*dev->rx_pkt_burst, 0);

  if (queue_id >= dev->data->nb_rx_queues)
    {
      RTE_PMD_DEBUG_TRACE ("Invalid RX queue_id=%d\n", queue_id);
      return 0;
    }
#endif
  nb_rx = (*dev->rx_pkt_burst) (dev->data->rx_queues[queue_id],
                                rx_pkts, nb_pkts);

#ifdef RTE_ETHDEV_RXTX_CALLBACKS
  cb = dev->post_rx_burst_cbs[queue_id];

  if (unlikely (cb != NULL))
    {
      do
        {
          nb_rx = cb->fn.rx (port_id, queue_id, rx_pkts, nb_rx,
                             nb_pkts, cb->param);
          cb = cb->next;
        }
      while (cb != NULL);
    }
#endif

  //pst_capture_packet = getenv ("NSTACK_CAPTURE_PACKET");
  if (pst_capture_packet && strcmp (pst_capture_packet, "1") == 0)
    {
      dpdk_mbuf_to_file (nb_rx, rx_pkts);
    }
  return (uint16_t) nb_rx;
}

/*****************************************************************************
*   Prototype    : hal_rte_eth_tx_burst
*   Description  : a copy of rte_eth_tx_burst, because this function invokes

*   Input        : uint8_t port_id
*                  uint16_t queue_id
*                  struct rte_mbuf **tx_pkts
*                  uint16_t nb_pkts
*   Output       : None
*   Return Value :
*   Calls        :
*   Called By    :
*
*****************************************************************************/
NSTACK_STATIC inline uint16_t
hal_rte_eth_tx_burst (uint8_t port_id, uint16_t queue_id,
                      struct rte_mbuf ** tx_pkts, uint16_t nb_pkts)
{
#ifdef RTE_ETHDEV_RXTX_CALLBACKS
  struct rte_eth_rxtx_callback *cb;
#endif
  int16_t nb_tx = 0;
  char *pst_capture_packet = NULL;
  struct rte_eth_dev *dev = &rte_eth_devices[port_id];

  if (NULL == dev->tx_pkt_burst)
    {
      NSHAL_LOGERR ("dev->tx_pkt_burst is NULL");
      return 0;
    }

#ifdef RTE_LIBRTE_ETHDEV_DEBUG
  RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, 0);
  RTE_FUNC_PTR_OR_ERR_RET (*dev->tx_pkt_burst, 0);

  if (queue_id >= dev->data->nb_tx_queues)
    {
      RTE_PMD_DEBUG_TRACE ("Invalid TX queue_id=%d\n", queue_id);
      return 0;
    }
#endif

#ifdef RTE_ETHDEV_RXTX_CALLBACKS
  cb = dev->pre_tx_burst_cbs[queue_id];

  if (unlikely (cb != NULL))
    {
      do
        {
          nb_pkts = cb->fn.tx (port_id, queue_id, tx_pkts, nb_pkts,
                               cb->param);
          cb = cb->next;
        }
      while (cb != NULL);
    }
#endif

  nb_tx = (*dev->tx_pkt_burst) (dev->data->tx_queues[queue_id], tx_pkts,
                                nb_pkts);

  //pst_capture_packet = getenv ("NSTACK_CAPTURE_PACKET");
  if (pst_capture_packet && strcmp (pst_capture_packet, "1") == 0)
    {
      dpdk_mbuf_to_file (nb_tx, tx_pkts);
    }

  return nb_tx;
}

/*****************************************************************************
 Prototype    : dpdk_get_hugepage_size
 Description  : get the free hugepage size
 Input        : the dir of the nstack hugepage
 Output       : free hugepage size
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
int
dpdk_read_hugepage_size (int *freehuge)
{
  int fd_huge;
  int len;
  char buf[5] = { '\0' };
  fd_huge =
    open ("/sys/kernel/mm/hugepages/hugepages-1048576kB/free_hugepages",
          O_RDONLY);
  if (fd_huge < 0)
    {
      NSHAL_LOGERR ("errno=%d", errno);
      return -1;
    }

  len = read (fd_huge, buf, sizeof (buf));
  if (len < 0)
    {
      NSHAL_LOGERR ("errno=%d", errno);
      close (fd_huge);          //fix codeDEX 124547
      return -1;
    }
  *freehuge = buf[0] - '0';
  NSHAL_LOGINF ("hugepage size=%d", *freehuge);
  close (fd_huge);

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_clear_hugedir
 Description  : clear the hugepage which is used by dpdk
 Input        : the dir of the nstack hugepage
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_clear_hugedir (const char *hugedir)
{
  DIR *dir;
  struct dirent *dirent_dpdk;
  int dir_fd, fd, lck_result, lk_result;
  const char filter[] = "*mapns*";      /* matches hugepage files */

  /* open directory */
  dir = opendir (hugedir);
  if (!dir)
    {
      NSHAL_LOGERR ("the path %s is not exist, errno = %d", hugedir, errno);
      goto error;
    }
  dir_fd = dirfd (dir);

  dirent_dpdk = readdir (dir);
  if (!dirent_dpdk)
    {
      NSHAL_LOGERR ("the dir %s can not read, errno = %d", hugedir, errno);
      goto error;
    }

  while (dirent_dpdk != NULL)
    {
      /* skip files that don't match the hugepage pattern */
      if (fnmatch (filter, dirent_dpdk->d_name, 0) > 0)
        {
          NSHAL_LOGWAR ("the file name %s is not match mapns, errno = %d",
                        dirent_dpdk->d_name, errno);
          dirent_dpdk = readdir (dir);
          continue;
        }

      /* try and lock the file */
      fd = openat (dir_fd, dirent_dpdk->d_name, O_RDONLY);

      /* skip to next file */
      if (fd == -1)
        {
          NSHAL_LOGERR ("the file name %s can not be lock, errno = %d",
                        dirent_dpdk->d_name, errno);
          dirent_dpdk = readdir (dir);
          continue;
        }

      /* non-blocking lock */
      lck_result = flock (fd, LOCK_EX | LOCK_NB);

      /* if lock succeeds, unlock and remove the file */
      if (lck_result != -1)
        {
          NSHAL_LOGWAR
            ("the file name %s can be lock and will delete, errno = %d",
             dirent_dpdk->d_name, errno);
          lck_result = flock (fd, LOCK_UN);
          if (-1 == lck_result)
            NSHAL_LOGERR ("the file name %s unlock fail, errno = %d",
                          dirent_dpdk->d_name, errno);
          lk_result = unlinkat (dir_fd, dirent_dpdk->d_name, 0);
          if (-1 == lk_result)
            NSHAL_LOGERR ("the file name %s is unlinkat fail, errno = %d",
                          dirent_dpdk->d_name, errno);
        }
      close (fd);
      dirent_dpdk = readdir (dir);
    }

  (void) closedir (dir);
  return 0;

error:
  if (dir)
    (void) closedir (dir);

  return -1;
}

/*****************************************************************************
 Prototype    : dpdk_init_global
 Description  : DPDK global init
 Input        : int argc
                char** argv
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_init_global (int argc, char **argv)
{
  //int ret;
  const char hugepath[] = "/mnt/nstackhuge";
  //int freeHuge = 0;
  //int retryCount = 10;

  if (-1 == dpdk_clear_hugedir (hugepath))
    {
      NSHAL_LOGERR ("clear hugedir fail, try again!");
      sys_sleep_ns (0, 100000000);
      (void) dpdk_clear_hugedir (hugepath);
    }
  NSHAL_LOGINF ("init global succ");

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_init_env
 Description  : init dpdk run env
 Input        : void
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_init_env (void)
{
  int ret;
  size_t len_size;
  char *dpdk_env;
  char *dpdk_path;

  /* Get dpdk_tool_path */
  dpdk_env = getenv (DPDK_TOOL_ENV);
  if (NULL == dpdk_env)
    {
      NSHAL_LOGERR ("please set enviroment:%s before start this stack"
                    "\nthe value of the %s must be the path of dpdk tools",
                    DPDK_TOOL_ENV, DPDK_TOOL_ENV);
      return -1;
    }

  /* modify ugly len_size judgement and strcpy */
  /* check len_size for malloc */
  len_size = strlen (dpdk_env);
  if (0 == len_size || len_size >= HAL_MAX_PATH_LEN)
    {
      NSHAL_LOGERR ("fail to dpdk_env strlen(DPDK_TOOL_ENV)");
      return -1;
    }

  /* DPDK_TOOL_ENV's value will be use as popen's paramter,we need check's validity */
  dpdk_path = realpath (dpdk_env, NULL);
  if (NULL == dpdk_path)
    {
      NSHAL_LOGERR ("env:%s value incorrect]value=%s,errno=%d", DPDK_TOOL_ENV,
                    dpdk_env, errno);
      return -1;
    }

  len_size = strlen (dpdk_path);
  if (0 == len_size || len_size >= HAL_MAX_PATH_LEN)
    {
      NSHAL_LOGERR ("fail to dpdk_path strlen(DPDK_TOOL_ENV)");
      return -1;
    }

  ret = STRCPY_S (dpdk_tool_path, HAL_MAX_PATH_LEN, dpdk_path);
  if (EOK != ret)
    {
      NSHAL_LOGERR ("STRCPY_S failed]ret=%d", ret);
      return -1;
    }

  if (!hal_is_script_valid (dpdk_tool_path))
    {
      NSHAL_LOGERR ("dpdk_tool_path is invalid]dpdk_tool_path=%s",
                    dpdk_tool_path);
      return -1;
    }

  /* get non-root user's id */
  dpdk_non_root_user = getpwnam (DPDK_NON_ROOT_USER_NAME);
  if (dpdk_non_root_user)
    {
      NSHAL_LOGINF ("non-root]name=%s,uid=%u,gid=%u,errno=%d",
                    dpdk_non_root_user->pw_name, dpdk_non_root_user->pw_uid,
                    dpdk_non_root_user->pw_gid, errno);
    }
  else
    {
      NSHAL_LOGERR ("non-root]cannot find user %s", DPDK_NON_ROOT_USER_NAME);
    }

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_init_local
 Description  : DPDK local init
 Input        : void
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_init_local (void)
{
  int ret;

  ret = dpdk_init_env ();

  if (ret < 0)
    {
      NSHAL_LOGERR ("dpdk_init_env failed");
      return -1;
    }

  NSHAL_LOGINF ("init local succ");

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_set_port
 Description  : check and save the port num
 Input        : netif_inst_t* inst
                int port
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_set_port (netif_inst_t * inst, uint8_t port)
{
  if (port >= rte_eth_dev_count ())
    {
      NSHAL_LOGERR ("the number of port=%d is more than rte_eth_dev_count=%d",
                    port, rte_eth_dev_count ());
      return -1;
    }

  inst->data.dpdk_if.port_id = port;

  return 0;

}

/*****************************************************************************
 Prototype    : dpdk_set_nic_name
 Description  : check and save nic name
 Input        : netif_inst_t* inst
                const char* name
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_set_nic_name (netif_inst_t * inst, const char *name)
{
  int ret;

  /* sizeof(pointer) always = 8 in 64 bit system */
  ret =
    STRCPY_S (inst->data.dpdk_if.nic_name,
              sizeof (inst->data.dpdk_if.nic_name), name);
  if (EOK != ret)
    {
      NSHAL_LOGERR ("STRCPY_S set nic_name failed]ret=%d", ret);
      return -1;
    }

  if (!hal_is_script_valid (inst->data.dpdk_if.nic_name))
    {
      NSHAL_LOGERR ("nic_name is invalid");
      return -1;
    }

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_get_driver_name
 Description  : get and save driver name
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_get_driver_name (netif_inst_t * inst)
{
  int ret_len, ret;
  char script_cmmd[HAL_SCRIPT_LENGTH];
  char driver_name[HAL_SCRIPT_LENGTH] = { 0 };

  ret = hal_snprintf (script_cmmd, sizeof (script_cmmd),
                      "readlink -f /sys/class/net/" "%s"
                      "/device/driver| awk -F'/' '{print $6}'",
                      inst->data.dpdk_if.nic_name);
  if (-1 == ret)
    {
      NSHAL_LOGERR ("hal_snprintf failed");
      return -1;
    }

  ret_len =
    hal_run_script (script_cmmd, driver_name, sizeof (driver_name) - 1);

  if (ret_len > HAL_MAX_DRIVER_NAME_LEN)
    {
      ret_len = HAL_MAX_DRIVER_NAME_LEN;
    }

  if (ret_len <= 0)
    {
      NSHAL_LOGERR ("%s does't have a driver", driver_name);

      ret =
        STRNCPY_S (inst->data.dpdk_if.driver_name,
                   sizeof (inst->data.dpdk_if.driver_name), "NULL",
                   sizeof ("NULL"));

      if (EOK != ret)
        {
          NSHAL_LOGERR ("STRNCPY_S failed]ret=%d.", ret);
          return -1;
        }

      return -1;
    }
  else
    {
      ret =
        STRNCPY_S (inst->data.dpdk_if.driver_name,
                   sizeof (inst->data.dpdk_if.driver_name), driver_name,
                   ret_len);

      if (EOK != ret)
        {
          NSHAL_LOGERR ("STRNCPY_S failed]ret=%d.", ret);
          return -1;
        }

      inst->data.dpdk_if.driver_name[(ret_len - 1)] = '\0';

      return 0;
    }
}

/*****************************************************************************
 Prototype    : dpdk_set_pci_permission
 Description  : set pci permission
 Input        : char *pci_addr
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_set_pci_permission (char *pci_addr)
{
  DIR *dir_desc;
  char file_path[HAL_SCRIPT_LENGTH] = { 0 }, dir_path[HAL_SCRIPT_LENGTH] =
  {
  0};
  struct dirent *ent;
  struct stat statbuf;
  int ret;

  ret =
    SNPRINTF_S (dir_path, sizeof (dir_path), sizeof (dir_path) - 1,
                "/sys/bus/pci/devices/%s", pci_addr);
  if (ret < 0)
    {
      NSHAL_LOGERR ("SNPRINTF_S fail");
      return -1;
    }

  if ((dir_desc = opendir (dir_path)) == NULL)
    {
      NSHAL_LOGERR ("opendir fail:errno=%d", errno);
      return -1;
    }

  while ((ent = readdir (dir_desc)) != NULL)
    {
      if (strstr (ent->d_name, "resource"))
        {
          ret =
            SNPRINTF_S (file_path, sizeof (file_path), sizeof (file_path) - 1,
                        "%s/%s", dir_path, ent->d_name);
          if (ret < 0)
            {
              NSHAL_LOGERR ("SNPRINTF_S fail");
              (void) closedir (dir_desc);
              return -1;
            }

          if (!lstat (file_path, &statbuf) && !S_ISDIR (statbuf.st_mode))
            {
              ret =
                chown (file_path, dpdk_non_root_user->pw_uid,
                       dpdk_non_root_user->pw_gid);
              if (ret < 0)
                {
                  NSHAL_LOGERR ("chown fail]file_path=%s,ret=%d,errno=%d",
                                file_path, ret, errno);
                  (void) closedir (dir_desc);
                  return -1;
                }
              NSHAL_LOGWAR ("chown succ]file_path=%s,ret=%d", file_path, ret);
              ret = chmod (file_path, 0640);
              if (ret < 0)
                {
                  NSHAL_LOGERR ("chmod fail]file_path=%s,ret=%d,errno=%d",
                                file_path, ret, errno);
                  (void) closedir (dir_desc);
                  return -1;
                }
              NSHAL_LOGWAR ("chmod succ]file_path=%s,ret=%d", file_path, ret);
            }
        }
    }

  (void) closedir (dir_desc);
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_get_pci_addr
 Description  : get and save pci addr
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_get_pci_addr (netif_inst_t * inst)
{
  int ret, pci_len;
  struct ethtool_drvinfo edata = { 0 };
  struct ifreq ifr;
  int fd = -1;

  /* use ioctl to get pci address instead of call dpdk-devbind.py to reduce time cost */
  ret = MEMSET_S (&ifr, sizeof (ifr), 0, sizeof (ifr));
  if (EOK != ret)
    {
      NSHAL_LOGERR ("MEMSET_S fail");
      return -1;
    }
  edata.cmd = ETHTOOL_GDRVINFO;
  ret =
    STRCPY_S (ifr.ifr_name, sizeof (ifr.ifr_name),
              inst->data.dpdk_if.nic_name);
  if (EOK != ret)
    {
      NSHAL_LOGERR ("STRCPY_S fail");
      return -1;
    }

  ifr.ifr_data = (char *) (&edata);
  if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
      NSHAL_LOGERR ("cannot init socket, errno=%d", errno);
      return -1;
    }
  if (ioctl (fd, SIOCETHTOOL, &ifr) < 0)
    {
      NSHAL_LOGERR ("ioctl to the device %s err, errno=%d",
                    inst->data.dpdk_if.nic_name, errno);
      close (fd);
      return -1;
    }
  close (fd);

  pci_len = strlen (edata.bus_info);
  if (pci_len == 0
      || pci_len > (int) sizeof (inst->data.dpdk_if.pci_addr) - 1)
    {
      NSHAL_LOGERR ("does't have a pci_addr");
      inst->data.dpdk_if.pci_addr[0] = '\0';
      return -1;
    }

  NSHAL_LOGINF ("nic_name=%s,nic_pci_addr=%s", inst->data.dpdk_if.nic_name,
                edata.bus_info);

  ret =
    STRNCPY_S (inst->data.dpdk_if.pci_addr,
               sizeof (inst->data.dpdk_if.pci_addr), edata.bus_info, pci_len);
  if (EOK != ret)
    {
      NSHAL_LOGERR ("STRNCPY_S failed]ret=%d.", ret);
      return -1;
    }

  if (!hal_is_script_valid (inst->data.dpdk_if.pci_addr))
    {
      NSHAL_LOGERR ("pci_addr is invalid]pci_addr=%s",
                    inst->data.dpdk_if.pci_addr);
      return -1;
    }

  if (dpdk_non_root_user && getuid ())
    {
      ret = dpdk_set_pci_permission (inst->data.dpdk_if.pci_addr);
      if (ret < 0)
        {
          NSHAL_LOGERR ("dpdk_set_pci_permission fail");
          return -1;
        }
    }

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_mlx_linkup
 Description  : linkup the port for mlx
                In bonding mode, mlx4 NICs should be set up manually,
                in order to make bond port up
 Input        : char* name
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_mlx_linkup (char *name)
{
  struct ifreq st_ifreq;
  int sock;
  int retVal;

  if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
    {
      NSHAL_LOGERR ("socket fail]errno=%d", errno);
      return 2;
    }

  retVal = STRCPY_S (st_ifreq.ifr_name, sizeof (st_ifreq.ifr_name), name);

  if (EOK != retVal)
    {
      NSHAL_LOGERR ("STRCPY_S fail]");
      close (sock);
      return 1;
    }

  if (ioctl (sock, (uint64_t) SIOCGIFFLAGS, &st_ifreq) < 0)
    {
      NSHAL_LOGERR ("ioctl SIOCGIFFLAGS fail]errno=%d", errno);
      close (sock);
      return 3;
    }

  st_ifreq.ifr_flags |= IFF_UP;
  st_ifreq.ifr_flags |= IFF_RUNNING;

  if (ioctl (sock, (uint64_t) SIOCSIFFLAGS, &st_ifreq) < 0)
    {
      NSHAL_LOGERR ("ioctl SIOCSIFFLAGS fail]errno=%d", errno);
      close (sock);
      return 3;
    }

  close (sock);
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_nonmlx_linkdown
 Description  : linkdown the port for nonmlx
 Input        : char* name
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_nonmlx_linkdown (char *name)
{
  int ret, ret_len;
  char script_cmmd[HAL_SCRIPT_LENGTH];
  char result_buf[HAL_SCRIPT_LENGTH];

  ret =
    hal_snprintf (script_cmmd, sizeof (script_cmmd), "sudo ifconfig %s down",
                  name);
  if (-1 == ret)
    {
      NSHAL_LOGERR ("spl_snprintf failed]");
      return -1;
    }

  ret_len = hal_run_script (script_cmmd, result_buf, sizeof (result_buf));
  NSHAL_LOGINF ("ifconfig]script_cmmd=%s,ret_len=%d", script_cmmd, ret_len);
  if (0 > ret_len)
    {
      NSHAL_LOGERR ("cannot able to ifconfig %s down", name);
      return -1;
    }

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_get_uio_by_pci_addr
 Description  : get uio
 Input        : char *pci_addr
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_get_uio_by_pci_addr (char *pci_addr)
{
  int i, ret, ret_len;
  char script_cmmd[HAL_SCRIPT_LENGTH];
  char result_buf[HAL_SCRIPT_LENGTH];

  for (i = 0; i < RTE_MAX_ETHPORTS; i++)
    {
      ret = hal_snprintf (script_cmmd, sizeof (script_cmmd),
                          "readlink ls /sys/class/uio/uio%d/device | awk -F '/' '{print $4}'",
                          i);
      if (-1 == ret)
        {
          NSHAL_LOGERR ("hal_snprintf fail]pci=%s,i=%d", pci_addr, i);
          return -1;
        }

      ret_len = hal_run_script (script_cmmd, result_buf, sizeof (result_buf));
      if (0 > ret_len)
        {
          NSHAL_LOGERR ("hal_run_script fail]pci=%s,i=%d", pci_addr, i);
          return -1;
        }
      if (strcmp (result_buf, pci_addr) == 0)
        return i;
    }

  return -1;
}

/*****************************************************************************
 Prototype    : dpdk_set_uio_permission
 Description  : set uio permission
 Input        : char *pci_addr
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_set_uio_permission (char *pci_addr)
{
  int ret, uio_idx;
  char file_path[HAL_SCRIPT_LENGTH / 2] = { 0 };

  /* get /dev/uio by pci addr */
  uio_idx = dpdk_get_uio_by_pci_addr (pci_addr);
  if (uio_idx < 0)
    {
      NSHAL_LOGERR ("dpdk_get_uio_by_pci_addr fail]pci_addr=%s", pci_addr);
      return -1;
    }

  NSHAL_LOGINF ("uio_idx]pci=%s,uio%d", pci_addr, uio_idx);

  /* change /dev/uio%u permission */
  if (SNPRINTF_S
      (file_path, sizeof (file_path), sizeof (file_path) - 1, "/dev/uio%d",
       uio_idx) < 0)
    {
      NSHAL_LOGERR ("SNPRINTF_S failed]uio%d", uio_idx);
      return -1;
    }
  sys_sleep_ns (0, 500000000);
  ret =
    chown (file_path, dpdk_non_root_user->pw_uid, dpdk_non_root_user->pw_gid);
  if (ret < 0)
    {
      NSHAL_LOGERR ("chown fail]file_path=%s,ret=%d,errno=%d", file_path, ret,
                    errno);
      return -1;
    }
  NSHAL_LOGWAR ("chown succ]file_path=%s,ret=%d", file_path, ret);

  ret = chmod (file_path, 0640);
  if (ret < 0)
    {
      NSHAL_LOGERR ("chmod fail]file_path=%s,ret=%d,errno=%d", file_path, ret,
                    errno);
      return -1;
    }
  NSHAL_LOGWAR ("chmod succ]file_path=%s,ret=%d", file_path, ret);

  /* change /sys/class/uio/uio%u/device/config permission */
  if (SNPRINTF_S
      (file_path, sizeof (file_path), sizeof (file_path) - 1,
       "/sys/class/uio/uio%d/device/config", uio_idx) < 0)
    {
      NSHAL_LOGERR ("SNPRINTF_S failed]uio%d", uio_idx);
      return -1;
    }

  ret =
    chown (file_path, dpdk_non_root_user->pw_uid, dpdk_non_root_user->pw_gid);
  if (ret < 0)
    {
      NSHAL_LOGERR ("chown fail]file_path=%s,ret=%d,errno=%d", file_path, ret,
                    errno);
      return -1;
    }
  NSHAL_LOGWAR ("chown succ]file_path=%s,ret=%d", file_path, ret);

  ret = chmod (file_path, 0640);
  if (ret < 0)
    {
      NSHAL_LOGERR ("chmod fail]file_path=%s,ret=%d,errno=%d", file_path, ret,
                    errno);
      return -1;
    }
  NSHAL_LOGWAR ("chmod succ]file_path=%s,ret=%d", file_path, ret);

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_get_nic_list_file
 Description  : get dpdk bind nic list file
 Input        : void
 Output       : char*
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC char *
dpdk_get_nic_list_file (void)
{
  int ret;
  static char buffer[HAL_MAX_PATH_LEN]; /* static so auto-zeroed */
  const char *directory = "/var/run";
  const char *home_dir = getenv ("HOME");

  if (getuid () != 0 && home_dir != NULL)
    directory = home_dir;

  ret =
    SNPRINTF_S (buffer, sizeof (buffer), sizeof (buffer) - 1,
                DPDK_NIC_LIST_FILE, directory);
  if (-1 == ret)
    {
      NSCOMM_LOGERR ("SNPRINTF_S failed]ret=%d", ret);
      return NULL;
    }

  return buffer;
}

/*****************************************************************************
 Prototype    : dpdk_bind_uio
 Description  : bind uio
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_bind_uio (netif_inst_t * inst)
{
  char script_cmmd[HAL_SCRIPT_LENGTH];
  char result_buf[HAL_SCRIPT_LENGTH];
  int ret, ret_len;

  result_buf[0] = '\0';

  if (strncmp ("mlx4_co", inst->data.dpdk_if.driver_name, (size_t) 7) == 0)
    {
      /*For MLX4: NIC should be set link up before rte_eth_dev_start() is called. */
      ret = dpdk_mlx_linkup (inst->data.dpdk_if.nic_name);
      if (0 != ret)
        {
          NSHAL_LOGERR ("set mlx linkup fail]nic_name=%s,ret=%d",
                        inst->data.dpdk_if.nic_name, ret);

          return -1;
        }
    }
  else
    {
      /*For other drivers: NIC should be set link down before bind uio. */
      ret = dpdk_nonmlx_linkdown (inst->data.dpdk_if.nic_name);
      if (-1 == ret)
        {
          NSHAL_LOGERR ("dpdk_nonmlx_linkdown fail]nic_name=%s",
                        inst->data.dpdk_if.nic_name);
          return -1;
        }

      /* save binded VF list to file /var/run/ip_module/.nstack_dpdk_nic_list */
      ret = hal_snprintf (script_cmmd, sizeof (script_cmmd),
                          "sudo %s"
                          "/dpdk-devbind.py -s | grep `ethtool -i %s| grep bus-info |awk '{print $2}'` >> %s 2>&1",
                          dpdk_tool_path, inst->data.dpdk_if.nic_name,
                          dpdk_get_nic_list_file ());

      if (-1 == ret)
        {
          NSHAL_LOGERR ("hal_snprintf fail]nic_name=%s",
                        inst->data.dpdk_if.nic_name);
          return -1;
        }

      ret_len =
        hal_run_script (script_cmmd, result_buf, sizeof (result_buf) - 1);
      NSHAL_LOGINF ("bind]script_cmmd=%s,ret_len=%d", script_cmmd, ret_len);
      if (0 > ret_len)
        {
          NSHAL_LOGERR ("hal_run_script fail]script_cmmd=%s", script_cmmd);
          return -1;
        }

      if (strncmp ("virtio", inst->data.dpdk_if.driver_name, (size_t) 6) == 0)
        {
          /* For Virtio NIC, should call "./devbind.sh ethX" to bind the VF */
          ret = hal_snprintf (script_cmmd, sizeof (script_cmmd),
                              "sudo %s"
                              "/dpdk-devbind.py  --bind=igb_uio `ethtool -i %s| grep bus-info |awk '{print $2}'`",
                              dpdk_tool_path, inst->data.dpdk_if.nic_name);
          //"sudo %s" "/devbind.sh " "%s", dpdk_tool_path, inst->data.dpdk_if.nic_name);

        }
      else
        {
          ret = hal_snprintf (script_cmmd, sizeof (script_cmmd),
                              "sudo %s" "/dpdk-devbind.py  --bind=igb_uio "
                              "%s", dpdk_tool_path,
                              inst->data.dpdk_if.nic_name);
        }

      if (-1 == ret)
        {
          NSHAL_LOGERR ("hal_snprintf failed");
          return -1;
        }

      ret_len =
        hal_run_script (script_cmmd, result_buf, sizeof (result_buf) - 1);
      NSHAL_LOGINF ("bind]script_cmmd=%s,retlen=%d", script_cmmd, ret_len);
      if (0 > ret_len)
        {
          NSHAL_LOGERR ("hal_run_script fail]script_cmmd=%s", script_cmmd);
          return -1;
        }

      if (dpdk_non_root_user && getuid ())
        {
          ret = dpdk_set_uio_permission (inst->data.dpdk_if.pci_addr);

          if (ret < 0)
            {
              NSHAL_LOGERR ("set_uio_permission fail]nic_name=%s",
                            inst->data.dpdk_if.nic_name);
              return -1;
            }
        }
    }

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_probe_pci
 Description  : probe pci
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_probe_pci (netif_inst_t * inst)
{
  int ret;
  uint16_t port_id;
  struct rte_eth_dev *eth_dev;
  char *pci_addr = inst->data.dpdk_if.pci_addr;

  ret = rte_eal_iopl_init ();
  if (0 != ret)
    {
      NSHAL_LOGERR ("rte_eal_iopl_init fail]pci_addr=%s,ret=%d", pci_addr,
                    ret);
      return -1;
    }

  ret = rte_eth_dev_attach (pci_addr, &port_id);
  if (0 != ret)
    {
      NSHAL_LOGWAR
        ("pci attach to DPDK fail, the pci may have attached, try to get port id]pci_addr=%s,ret=%d",
         pci_addr, ret);

      eth_dev = rte_eth_dev_allocated (inst->data.dpdk_if.nic_name);
      if (NULL != eth_dev && NULL != eth_dev->data)
        {
          port_id = eth_dev->data->port_id;
          ret = 0;
        }
    }

  if (!ret)
    {
      ret = dpdk_set_port (inst, port_id);

      if (0 == ret)
        {
          NSHAL_LOGINF ("set port success]pci_addr=%s,port_id=%u", pci_addr,
                        port_id);
        }
      else
        {
          NSHAL_LOGERR ("set port fail]pci_addr=%s,port_id=%u", pci_addr,
                        port_id);
          return -1;
        }
    }
  else
    {
      NSHAL_LOGERR ("get port fail]pci_addr=%s,ret=%d", pci_addr, ret);
      return -1;
    }

  /*[TA33635][2017-04-24][l00408818] Start: support bond mode */
  ret = rte_eal_devargs_add (RTE_DEVTYPE_WHITELISTED_PCI, pci_addr);
  if (!ret)
    {
      NSHAL_LOGINF ("pci attach to whitelist success]pci_addr=%s", pci_addr);
    }
  else
    {
      NSHAL_LOGERR ("pci attach to whitelist fail]pci_addr=%s", pci_addr);
      return -1;
    }
  /*[TA33635][2017-04-24][l00408818] End */

  return 0;
}

/*nic_name->driver_name->pci_addr->get port*/
/*****************************************************************************
 Prototype    : dpdk_open
 Description  : open the port
 Input        : netif_inst_t* inst
                const char* name
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_open (netif_inst_t * inst, const char *name)
{
  int ret;

  ret = dpdk_set_nic_name (inst, name);

  if (0 != ret)
    {
      NSHAL_LOGERR ("dpdk_set_nic_name fail]nic_name=%s, ret=%d", name, ret);
      return -1;
    }

  ret = dpdk_get_driver_name (inst);

  if (0 != ret)
    {
      NSHAL_LOGERR ("dpdk_get_driver_name fail]nic_name=%s, ret=%d", name,
                    ret);
      return -1;
    }

  ret = dpdk_get_pci_addr (inst);

  if (0 != ret)
    {
      NSHAL_LOGERR ("dpdk_get_pci_addr fail]nic_name=%s, ret=%d", name, ret);
      return -1;
    }

  ret = dpdk_bind_uio (inst);

  if (0 != ret)
    {
      NSHAL_LOGERR ("dpdk_bind_uio fail]nic_name=%s, ret=%d", name, ret);
      return -1;
    }

  ret = dpdk_probe_pci (inst);

  if (0 != ret)
    {
      NSHAL_LOGERR ("dpdk_probe_pci fail]nic_name=%s, ret=%d", name, ret);
      return -1;
    }

  NSHAL_LOGINF ("open port succ]port_id=%u, nic_name=%s",
                inst->data.dpdk_if.port_id, inst->data.dpdk_if.nic_name);

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_close
 Description  : close the port
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_close (netif_inst_t * inst)
{
  int i;

  /* close slave NIC first */
  for (i = 0; i < inst->data.dpdk_if.slave_num; i++)
    {
      rte_eth_dev_close (inst->data.dpdk_if.slave_port[i]);
      NSHAL_LOGINF ("close slave port succ]port_id=%u, nic_name=%s",
                    inst->data.dpdk_if.slave_port[i],
                    inst->data.dpdk_if.nic_name);
    }

  rte_eth_dev_close (inst->data.dpdk_if.port_id);
  NSHAL_LOGINF ("close port succ]port_id=%u, nic_name=%s",
                inst->data.dpdk_if.port_id, inst->data.dpdk_if.nic_name);
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_get_queue_conf
 Description  : get the port queue configure
 Input        : netif_inst_t* inst
                struct rte_eth_rxconf** rx_conf
                struct rte_eth_txconf** tx_conf
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_get_queue_conf (netif_inst_t * inst,
                     struct rte_eth_rxconf **rx_conf,
                     struct rte_eth_txconf **tx_conf)
{
  static struct rte_eth_dev_info slave_dev_info;

  if (strncmp ("igb", inst->data.dpdk_if.driver_name, (size_t) 3) == 0)
    {
      *rx_conf = (struct rte_eth_rxconf *) &rx_conf_default_igb;
      *tx_conf = (struct rte_eth_txconf *) &tx_conf_default_igb;
      NSHAL_LOGINF ("igb config is enable]port_id=%u",
                    inst->data.dpdk_if.port_id);
    }
  else if (strncmp ("ixgbe", inst->data.dpdk_if.driver_name, (size_t) 5) == 0)
    {
      *rx_conf = (struct rte_eth_rxconf *) &rx_conf_default_ixgbe;
      *tx_conf = (struct rte_eth_txconf *) &tx_conf_default_ixgbe;
      NSHAL_LOGINF ("igxbe config is enable]port_id=%u",
                    inst->data.dpdk_if.port_id);
    }
  else if (strncmp ("bond", inst->data.dpdk_if.driver_name, (size_t) 4) == 0)
    {
      *rx_conf = NULL;
      rte_eth_dev_info_get (inst->data.dpdk_if.slave_port[0],
                            &slave_dev_info);
      *tx_conf = &(slave_dev_info.default_txconf);
      NSHAL_LOGINF ("bond config is enable]port_id=%u",
                    inst->data.dpdk_if.port_id);
    }
  else
    {
      *rx_conf = NULL;
      *tx_conf = NULL;
      NSHAL_LOGINF ("default config is enable]port_id=%u",
                    inst->data.dpdk_if.port_id);
    }
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_get_port_conf
 Description  : get the port configure
 Input        : netif_inst_t* inst
                struct rte_eth_conf** port_conf
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC void
dpdk_get_port_conf (netif_inst_t * inst, struct rte_eth_conf **port_conf)
{
  if (strncmp ("virtio", inst->data.dpdk_if.driver_name, (size_t) 6) == 0)
    {
      *port_conf = &port_conf_default_virtio;
    }
  else if (strncmp ("bond", inst->data.dpdk_if.driver_name, (size_t) 4) == 0)
    {
      *port_conf = &port_conf_default_bond;
    }
  else
    {
      *port_conf = &port_conf_default_normal;
    }

  (*port_conf)->rxmode.hw_vlan_filter = inst->data.dpdk_if.hw_vlan_filter;
  (*port_conf)->rxmode.hw_vlan_strip = inst->data.dpdk_if.hw_vlan_strip;
}

/*****************************************************************************
 Prototype    : dpdk_setup_port
 Description  : setup the port
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_setup_port (netif_inst_t * inst)
{
  int ret;
  uint32_t i;
  struct rte_eth_conf *port_conf;
  struct rte_eth_rxconf *rx_conf;
  struct rte_eth_txconf *tx_conf;

  uint8_t port_id = inst->data.dpdk_if.port_id;
  struct rte_mempool **mp =
    (struct rte_mempool **) inst->data.dpdk_if.rx_pool;
  uint32_t *rx_ring_size = inst->data.dpdk_if.rx_ring_size;
  uint32_t *tx_ring_size = inst->data.dpdk_if.tx_ring_size;
  uint32_t rx_queue_num = inst->data.dpdk_if.rx_queue_num;
  uint32_t tx_queue_num = inst->data.dpdk_if.tx_queue_num;

  dpdk_get_port_conf (inst, &port_conf);

  ret =
    rte_eth_dev_configure (port_id, rx_queue_num, tx_queue_num, port_conf);
  if (ret < 0)
    {
      NSHAL_LOGERR ("rte_eth_dev_configure]port_id=%u,ret=%d", port_id, ret);
      return ret;
    }

  if (dpdk_get_queue_conf (inst, &rx_conf, &tx_conf) < 0)
    {
      NSHAL_LOGERR ("dpdk_get_queue_conf failed]inst=%p,rx_conf=%p", inst,
                    rx_conf);
      return -1;
    }
  /* fix "FORTIFY.Out-of-Bounds_Read" type codedex issue CID 33436 */
  if (rx_queue_num > HAL_ETH_MAX_QUEUE_NUM
      || tx_queue_num > HAL_ETH_MAX_QUEUE_NUM)
    {
      NSHAL_LOGERR
        ("queue num error]rx_queue_num=%u, tx_queue_num=%u, HAL_ETH_MAX_QUEUE_NUM=%d",
         rx_queue_num, tx_queue_num, HAL_ETH_MAX_QUEUE_NUM);

      return -1;
    }

  for (i = 0; i < rx_queue_num; i++)
    {
      ret =
        rte_eth_rx_queue_setup (port_id, i, rx_ring_size[i], SOCKET_ID_ANY,
                                rx_conf, mp[i]);
      if (ret < 0)
        {
          NSHAL_LOGERR ("rx queue setup fail]index=%u,port_id=%u,ret=%d", i,
                        port_id, ret);
          return ret;
        }
    }

  for (i = 0; i < tx_queue_num; i++)
    {
      ret =
        rte_eth_tx_queue_setup (port_id, i, tx_ring_size[i], SOCKET_ID_ANY,
                                tx_conf);

      if (ret < 0)
        {
          NSHAL_LOGERR ("tx queue setup fail]q=%u,ret=%d", i, ret);
          return ret;
        }
    }

  rte_eth_promiscuous_enable (port_id);

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_start
 Description  : start the port
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_start (netif_inst_t * inst)
{
  int ret;
  struct ether_addr eth_addr;

  ret = dpdk_setup_port (inst);

  if (ret < 0)
    {
      NSHAL_LOGERR ("call dpdk_setup_port fail]ret=%d", ret);
      return ret;
    }

  ret = rte_eth_dev_start (inst->data.dpdk_if.port_id);
  if (ret < 0)
    {
      NSHAL_LOGERR ("rte_eth_dev_start fail]ret=%d", ret);
      return ret;
    }

  rte_eth_macaddr_get (inst->data.dpdk_if.port_id, &eth_addr);
  NSHAL_LOGINF ("port_id=%u,nic_name=%s,mac=%02X:%02X:%02X:%02X:%02X:%02X",
                inst->data.dpdk_if.port_id,
                inst->data.dpdk_if.nic_name,
                eth_addr.addr_bytes[0],
                eth_addr.addr_bytes[1],
                eth_addr.addr_bytes[2],
                eth_addr.addr_bytes[3],
                eth_addr.addr_bytes[4], eth_addr.addr_bytes[5]);

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_stop
 Description  : stop the port
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_stop (netif_inst_t * inst)
{
  int i;

  /* stop slave NIC first */
  for (i = 0; i < inst->data.dpdk_if.slave_num; i++)
    {
      rte_eth_dev_stop (inst->data.dpdk_if.slave_port[i]);
      NSHAL_LOGINF ("stop slave port succ]port_id=%u, nic_name=%s",
                    inst->data.dpdk_if.slave_port[i],
                    inst->data.dpdk_if.nic_name);
    }
  rte_eth_dev_stop (inst->data.dpdk_if.port_id);

  NSHAL_LOGINF ("stop port succ]port_id=%u, nic_name=%s",
                inst->data.dpdk_if.port_id, inst->data.dpdk_if.nic_name);
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_get_mtu
 Description  : get the port mtu
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC uint32_t
dpdk_get_mtu (netif_inst_t * inst)
{
  uint32_t mtu;

  if (rte_eth_dev_get_mtu (inst->data.dpdk_if.port_id, (uint16_t *) & mtu))
    {
      return 0;
    }

  return mtu;
}

/*****************************************************************************
 Prototype    : dpdk_get_macaddr
 Description  : get the port mac addr
 Input        : netif_inst_t* inst
                void* mac_addr
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_get_macaddr (netif_inst_t * inst, void *mac_addr)
{
  /*bond port */
  int primary_port = rte_eth_bond_primary_get (inst->data.dpdk_if.port_id);
  if (0 <= primary_port)
    {
      rte_eth_macaddr_get ((uint8_t) primary_port,
                           (struct ether_addr *) mac_addr);

      NSHAL_LOGDBG
        ("primary_port_id=%u,nic_name=%s,mac=%02X:%02X:%02X:%02X:%02X:%02X",
         primary_port, inst->data.dpdk_if.nic_name,
         ((struct ether_addr *) mac_addr)->addr_bytes[0],
         ((struct ether_addr *) mac_addr)->addr_bytes[1],
         ((struct ether_addr *) mac_addr)->addr_bytes[2],
         ((struct ether_addr *) mac_addr)->addr_bytes[3],
         ((struct ether_addr *) mac_addr)->addr_bytes[4],
         ((struct ether_addr *) mac_addr)->addr_bytes[5]);
    }
  /*normal port */
  else
    {
      rte_eth_macaddr_get (inst->data.dpdk_if.port_id,
                           (struct ether_addr *) mac_addr);

      NSHAL_LOGDBG
        ("normal_port_id=%u,nic_name=%s,mac=%02X:%02X:%02X:%02X:%02X:%02X",
         inst->data.dpdk_if.port_id, inst->data.dpdk_if.nic_name,
         ((struct ether_addr *) mac_addr)->addr_bytes[0],
         ((struct ether_addr *) mac_addr)->addr_bytes[1],
         ((struct ether_addr *) mac_addr)->addr_bytes[2],
         ((struct ether_addr *) mac_addr)->addr_bytes[3],
         ((struct ether_addr *) mac_addr)->addr_bytes[4],
         ((struct ether_addr *) mac_addr)->addr_bytes[5]);
    }

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_capa_convert
 Description  : convert format from dpdk to hal
 Input        : const struct rte_eth_dev_info* dev_info
                hal_netif_capa_t* capa
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC inline void
dpdk_capa_convert (const struct rte_eth_dev_info *dev_info,
                   hal_netif_capa_t * capa)
{
  int retVal =
    MEMSET_S (capa, sizeof (hal_netif_capa_t), 0, sizeof (hal_netif_capa_t));
  if (EOK != retVal)
    {
      NSHAL_LOGERR ("MEMSET_S fail]retVal=%d", retVal);
    }

  capa->tx_offload_capa = dev_info->tx_offload_capa;
}

/*****************************************************************************
 Prototype    : dpdk_get_capability
 Description  : get the port capability
 Input        : netif_inst_t* inst
                hal_netif_capa_t* capa
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_get_capability (netif_inst_t * inst, hal_netif_capa_t * capa)
{
  struct rte_eth_dev_info dev_info;

  rte_eth_dev_info_get (inst->data.dpdk_if.port_id, &dev_info);
  dpdk_capa_convert (&dev_info, capa);
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_recv
 Description  : recv packet from the port
 Input        : netif_inst_t* inst
                uint16_t queue_id
                struct common_mem_mbuf** rx_pkts
                uint16_t nb_pkts
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC uint16_t
dpdk_recv (netif_inst_t * inst, uint16_t queue_id,
           hal_mbuf_t ** rx_pkts, uint16_t nb_pkts)
{
  return hal_rte_eth_rx_burst (inst->data.dpdk_if.port_id, queue_id,
                               (struct rte_mbuf **) rx_pkts, nb_pkts);
}

/*****************************************************************************
 Prototype    : dpdk_send
 Description  : send packet to the port
 Input        : netif_inst_t* inst
                uint16_t queue_id
                struct common_mem_mbuf** tx_pkts
                uint16_t nb_pkts
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC uint16_t
dpdk_send (netif_inst_t * inst, uint16_t queue_id,
           hal_mbuf_t ** tx_pkts, uint16_t nb_pkts)
{
  return hal_rte_eth_tx_burst (inst->data.dpdk_if.port_id, queue_id,
                               (struct rte_mbuf **) tx_pkts, nb_pkts);
}

/*****************************************************************************
 Prototype    : dpdk_link_status
 Description  : get link status form the port
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC uint32_t
dpdk_link_status (netif_inst_t * inst)
{
  struct rte_eth_link eth_link;

  /* add log output when failed */
  int retVal = MEMSET_S (&eth_link, sizeof (struct rte_eth_link), 0,
                         sizeof (struct rte_eth_link));
  if (EOK != retVal)
    {
      NSHAL_LOGERR ("MEMSET_S fail]retVal=%d", retVal);
    }

  rte_eth_link_get (inst->data.dpdk_if.port_id, &eth_link);

  return eth_link.link_status;
}

/*****************************************************************************
 Prototype    : dpdk_stats_convert
 Description  : convert format from dpdk to hal
 Input        : const struct rte_eth_stats* rte_stats
                hal_netif_stats_t* stats
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC inline void
dpdk_stats_convert (const struct rte_eth_stats *rte_stats,
                    hal_netif_stats_t * stats)
{
  int i;

  /* give fail error number when failed */
  int retVal = MEMSET_S (stats, sizeof (hal_netif_stats_t), 0,
                         sizeof (hal_netif_stats_t));
  if (EOK != retVal)
    {
      NSHAL_LOGERR ("MEMSET_S fail]retVal=%d", retVal);
    }

  stats->ipackets = rte_stats->ipackets;
  stats->opackets = rte_stats->opackets;
  stats->ibytes = rte_stats->ibytes;
  stats->obytes = rte_stats->obytes;
  stats->imissed = rte_stats->imissed;
  stats->ierrors = rte_stats->ierrors;
  stats->oerrors = rte_stats->oerrors;
  stats->rx_nombuf = rte_stats->rx_nombuf;

  for (i = 0; i < HAL_ETH_QUEUE_STAT_CNTRS; i++)
    {
      stats->q_ipackets[i] = rte_stats->q_ipackets[i];
      stats->q_opackets[i] = rte_stats->q_opackets[i];
      stats->q_ibytes[i] = rte_stats->q_ibytes[i];
      stats->q_obytes[i] = rte_stats->q_obytes[i];
      stats->q_errors[i] = rte_stats->q_errors[i];
    }
}

/*****************************************************************************
 Prototype    : dpdk_stats
 Description  : get stats form the port
 Input        : netif_inst_t* inst
                hal_netif_stats_t* stats
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_stats (netif_inst_t * inst, hal_netif_stats_t * stats)
{
  int ret;
  struct rte_eth_stats rte_stats;

  ret = rte_eth_stats_get (inst->data.dpdk_if.port_id, &rte_stats);
  if (ret == 0)
    {
      dpdk_stats_convert (&rte_stats, stats);
      return 0;
    }

  return -1;
}

/*****************************************************************************
 Prototype    : dpdk_stats_reset
 Description  : reset stats to the port
 Input        : netif_inst_t* inst
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_stats_reset (netif_inst_t * inst)
{
  rte_eth_stats_reset (inst->data.dpdk_if.port_id);
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_config
 Description  : config the port queue and ring
 Input        : netif_inst_t* inst
                hal_netif_config_t* conf
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_config (netif_inst_t * inst, hal_netif_config_t * conf)
{
  uint32_t i;

  inst->data.dpdk_if.hw_vlan_filter = conf->bit.hw_vlan_filter;
  inst->data.dpdk_if.hw_vlan_strip = conf->bit.hw_vlan_strip;

  inst->data.dpdk_if.rx_queue_num = conf->rx.queue_num;
  /* fix Buffer Overflow type code-dex issue */
  if (inst->data.dpdk_if.rx_queue_num > HAL_ETH_MAX_QUEUE_NUM)
    {
      NSHAL_LOGERR
        ("rx queue num error]rx_queue_num=%u, HAL_ETH_MAX_QUEUE_NUM=%d",
         inst->data.dpdk_if.rx_queue_num, HAL_ETH_MAX_QUEUE_NUM);

      return -1;
    }

  for (i = 0; i < inst->data.dpdk_if.rx_queue_num; i++)
    {
      inst->data.dpdk_if.rx_ring_size[i] = conf->rx.ring_size[i];
      inst->data.dpdk_if.rx_pool[i] =
        (struct rte_mempool *) conf->rx.ring_pool[i];
    }

  inst->data.dpdk_if.tx_queue_num = conf->tx.queue_num;
  /* fix "FORTIFY.Out-of-Bounds_Read--Off-by-One" type codedex issue */
  if (inst->data.dpdk_if.tx_queue_num > HAL_ETH_MAX_QUEUE_NUM)
    {
      NSHAL_LOGERR
        ("tx queue num error]rx_queue_num=%u, HAL_ETH_MAX_QUEUE_NUM=%d",
         inst->data.dpdk_if.tx_queue_num, HAL_ETH_MAX_QUEUE_NUM);

      return -1;
    }
  for (i = 0; i < inst->data.dpdk_if.tx_queue_num; i++)
    {
      inst->data.dpdk_if.tx_ring_size[i] = conf->tx.ring_size[i];
    }

  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_bond_config
 Description  : config the port for bond mode
 Input        : netif_inst_t* inst
                uint8_t slave_num
                netif_inst_t* slave_inst[]
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_bond_config (netif_inst_t * inst, uint8_t slave_num,
                  netif_inst_t * slave_inst[])
{
  int ret;
  uint32_t i, queue;

  inst->data.dpdk_if.slave_num = slave_num;

  for (i = 0; i < slave_num; i++)
    {
      inst->data.dpdk_if.slave_port[i] = slave_inst[i]->data.dpdk_if.port_id;

      if (0 == i)
        {
          inst->data.dpdk_if.hw_vlan_filter =
            slave_inst[i]->data.dpdk_if.hw_vlan_filter;
          inst->data.dpdk_if.hw_vlan_strip =
            slave_inst[i]->data.dpdk_if.hw_vlan_strip;
          inst->data.dpdk_if.rx_queue_num =
            slave_inst[i]->data.dpdk_if.rx_queue_num;
          inst->data.dpdk_if.tx_queue_num =
            slave_inst[i]->data.dpdk_if.tx_queue_num;

          /*will be used in function dpdk_get_queue_conf and dpdk_get_port_conf */
          ret =
            STRCPY_S (inst->data.dpdk_if.driver_name,
                      sizeof (inst->data.dpdk_if.driver_name), "bond");

          if (EOK != ret)
            {
              NSHAL_LOGERR ("STRCPY_S failed]ret=%d.", ret);
              return -1;
            }

          for (queue = 0; queue < HAL_ETH_MAX_QUEUE_NUM; queue++)
            {
              inst->data.dpdk_if.rx_pool[queue] =
                slave_inst[i]->data.dpdk_if.rx_pool[queue];
              inst->data.dpdk_if.rx_ring_size[queue] =
                slave_inst[i]->data.dpdk_if.rx_ring_size[queue];
              inst->data.dpdk_if.tx_ring_size[queue] =
                slave_inst[i]->data.dpdk_if.tx_ring_size[queue];
            }
        }
    }

  return 0;

}

/*****************************************************************************
 Prototype    : dpdk_bond
 Description  : bond port
 Input        : netif_inst_t* inst
                const char* bond_name
                uint8_t slave_num
                netif_inst_t* slave_inst[]
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_bond (netif_inst_t * inst, const char *bond_name, uint8_t slave_num,
           netif_inst_t * slave_inst[])
{
  int i, ret;
  int port;
  struct ether_addr eth_addr;

  /* check if all the slaves' drivers are same, not support different drivers */
  for (i = 1; i < slave_num; i++)
    {
      if (strncmp
          (slave_inst[0]->data.dpdk_if.driver_name,
           slave_inst[i]->data.dpdk_if.driver_name,
           strlen (slave_inst[0]->data.dpdk_if.driver_name)))
        {
          NSHAL_LOGERR
            ("dpdk does not support different types of network card]slave[0]=%s, slave[%i]=%s",
             slave_inst[0]->data.dpdk_if.driver_name, i,
             slave_inst[i]->data.dpdk_if.driver_name);
          return -1;
        }
    }

  ret = dpdk_set_nic_name (inst, bond_name);

  if (0 != ret)
    {
      NSHAL_LOGERR ("dpdk_set_nic_name fail]ret=%d", ret);
      return -1;
    }

  port =
    rte_eth_bond_create (bond_name, BONDING_MODE_ACTIVE_BACKUP, SOCKET_ID_0);
  if (port < 0)
    {
      NSHAL_LOGERR ("rte_eth_bond_create fail]ret=%i", ret);
      return -1;
    }

  ret = dpdk_set_port (inst, (uint8_t) port);

  if (ret < 0)
    {
      NSHAL_LOGERR ("dpdk_set_port fail]ret=%i", ret);
      return ret;
    }

  ret = dpdk_bond_config (inst, slave_num, slave_inst);

  if (ret < 0)
    {
      NSHAL_LOGERR ("dpdk_bond_config fail]ret=%i", ret);
      return ret;
    }

  ret = dpdk_setup_port (inst);

  if (ret < 0)
    {
      NSHAL_LOGERR ("dpdk_setup_port fail]ret=%i", ret);
      return ret;
    }

  for (i = 0; i < slave_num; i++)
    {
      NSHAL_LOGINF ("add slave port_id=%u, nic_name=%s",
                    slave_inst[i]->data.dpdk_if.port_id,
                    slave_inst[i]->data.dpdk_if.nic_name);

      if (rte_eth_bond_slave_add
          ((uint8_t) port, slave_inst[i]->data.dpdk_if.port_id) == -1)
        {
          NSHAL_LOGERR ("adding slave (%u) to bond (%u) failed]",
                        slave_inst[i]->data.dpdk_if.port_id, port);
          return -1;
        }
    }

  rte_eth_macaddr_get (slave_inst[0]->data.dpdk_if.port_id, &eth_addr);

  ret = rte_eth_bond_mac_address_set ((uint8_t) port, &eth_addr);
  if (ret < 0)
    {
      NSHAL_LOGERR ("rte_eth_bond_mac_address_set fail]ret=%i", ret);
      return ret;
    }

  ret = rte_eth_dev_start (inst->data.dpdk_if.port_id);
  if (ret < 0)
    {
      NSHAL_LOGERR ("rte_eth_dev_start fail]ret=%i, port_id=%d", ret,
                    inst->data.dpdk_if.port_id);
      return ret;
    }

  NSHAL_LOGINF ("port_id=%d,nic_name=%s,mac=%02X:%02X:%02X:%02X:%02X:%02X",
                port,
                inst->data.dpdk_if.nic_name,
                eth_addr.addr_bytes[0],
                eth_addr.addr_bytes[1],
                eth_addr.addr_bytes[2],
                eth_addr.addr_bytes[3],
                eth_addr.addr_bytes[4], eth_addr.addr_bytes[5]);
  return 0;
}

/*****************************************************************************
 Prototype    : dpdk_add_mcastaddr
 Description  : add mcastaddr to the port
 Input        : netif_inst_t* inst
                void* mc_addr_set
                void* mc_addr
                uint32_t nb_mc_addr
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_set_mcastaddr (netif_inst_t * inst, void *mc_addr_set,
                    void *mc_addr, uint32_t nb_mc_addr)
{
  uint8_t port = inst->data.dpdk_if.port_id;
  int iRetVal;

  NSHAL_LOGINF ("mc_addr_set number=%u", nb_mc_addr);
  iRetVal = rte_eth_dev_set_mc_addr_list (port, mc_addr_set, nb_mc_addr);
  if (iRetVal != 0)
    {
      NSHAL_LOGWAR ("fail to set_mc_addr_list]port=%u,ret=%d", port, iRetVal);
    }

  return iRetVal;
}

/*****************************************************************************
 Prototype    : dpdk_add_mac_addr
 Description  : add mcastaddr to the port
 Input        : netif_inst_t* inst
                void* mc_addr
 Output       : None
 Return Value : NSTACK_STATIC int
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_add_mac_addr (netif_inst_t * inst, void *mc_addr)
{
  uint8_t port = inst->data.dpdk_if.port_id;
  int iRetVal;

  /* for MLX: set_mc_addr_list() is not callback, so call mac_addr_add() instead */
  iRetVal = rte_eth_dev_mac_addr_add (port, mc_addr, 0);
  if (0 != iRetVal)
    {
      NSHAL_LOGWAR ("fail to add_mac_addr]port=%u,ret=%d", port, iRetVal);
    }

  return iRetVal;
}

/*****************************************************************************
 Prototype    : dpdk_rmv_mac_addr
 Description  : remove mcastaddr to the port
 Input        : netif_inst_t* inst
                void* mc_addr
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_rmv_mac_addr (netif_inst_t * inst, void *mc_addr)
{
  uint8_t port = inst->data.dpdk_if.port_id;
  int iRetVal;

  /* for MLX: set_mc_addr_list() is not callback, so call mac_addr_remove() instead */
  iRetVal = rte_eth_dev_mac_addr_remove (port, mc_addr);
  if (0 != iRetVal)
    {
      NSHAL_LOGWAR ("fail to rmv_mac_addr]port=%u,ret=%d", port, iRetVal);
    }

  return iRetVal;
}

/*****************************************************************************
 Prototype    : dpdk_allmcast
 Description  : set allmcast mode to the port
 Input        : netif_inst_t* inst
                uint8_t enable
 Output       : None
 Return Value : NSTACK_STATIC
 Calls        :
 Called By    :

*****************************************************************************/
NSTACK_STATIC int
dpdk_allmcast (netif_inst_t * inst, uint8_t enable)
{
  if (enable)
    {
      rte_eth_allmulticast_enable (inst->data.dpdk_if.port_id);
    }
  else
    {
      rte_eth_allmulticast_disable (inst->data.dpdk_if.port_id);
    }

  return 0;
}

const netif_ops_t dpdk_netif_ops = {
  .name = "dpdk",
  .init_global = dpdk_init_global,
  .init_local = dpdk_init_local,
  .open = dpdk_open,
  .close = dpdk_close,
  .start = dpdk_start,
  .stop = dpdk_stop,
  .bond = dpdk_bond,
  .mtu = dpdk_get_mtu,
  .macaddr = dpdk_get_macaddr,
  .capability = dpdk_get_capability,
  .recv = dpdk_recv,
  .send = dpdk_send,
  .link_status = dpdk_link_status,
  .stats = dpdk_stats,
  .stats_reset = dpdk_stats_reset,
  .config = dpdk_config,
  .mcastaddr = dpdk_set_mcastaddr,
  .add_mac = dpdk_add_mac_addr,
  .rmv_mac = dpdk_rmv_mac_addr,
  .allmcast = dpdk_allmcast
};

HAL_IO_REGISTER (dpdk, &dpdk_netif_ops);