text: /*
   *
   * Copyright (C) 2011, 2015 Cisco Systems, Inc.
   * Copyright (C) 2015 CBA research group, Technical University of Catalonia.
   *
   * 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 "netm_vpp.h"
  #include "../net_mgr.h"
  #include "../net_mgr_proc_fc.h"
  #include "../../iface_list.h"
  #include "../../lib/oor_log.h"
  #include "../../lib/prefixes.h"
  #include "../../lib/sockets.h"
  #include "../../lib/util.h"
  #include <vpp/api/vpe_msg_enum.h>
  #include "../../lib/vpp_api/vpp_api_requests.h"
  
  #define vl_typedefs
    #include <vpp/api/vpe_all_api_h.h>
  #undef vl_typedefs
  
  /* declare message handlers for each api */
  
  #define vl_endianfun        /* define message structures */
    #include <vpp/api/vpe_all_api_h.h>
  #undef vl_endianfun
  
  
  #include <zmq.h>
  
  int vpp_netm_init();
  void vpp_netm_uninit();
  glist_t * vpp_get_ifaces_names();
  glist_t * vpp_get_iface_addr_list(char *iface_name, int afi);
  lisp_addr_t * vpp_get_src_addr_to(lisp_addr_t *addr);
  lisp_addr_t * vpp_get_iface_gw(char *iface_name, int afi);
  uint8_t vpp_get_iface_status(char *iface_name);
  int vpp_get_iface_index(char *iface_name);
  void vpp_get_iface_mac_addr(char *iface_name, uint8_t *mac);
  char * vpp_get_iface_name_associated_with_prefix(lisp_addr_t * pref);
  int vpp_reload_routes(uint32_t table, int afi);
  shash_t * vpp_build_addr_to_if_name_hasht();
  inline static netm_vpp_data_t * netm_vpp_data_new();
  inline static void netm_vpp_data_free(netm_vpp_data_t * vpp_data);
  int process_vpp_link_msg(struct sock *sl);
  void vpp_process_link_change(vpp_nl_link_info *link_info, uint8_t status);
  void vpp_process_addr_change(vpp_nl_addr_info *addr_info,uint8_t act);
  
  net_mgr_class_t netm_vpp = {
          .netm_init = vpp_netm_init,
          .netm_uninit = vpp_netm_uninit,
          .netm_get_ifaces_names = vpp_get_ifaces_names,
          .netm_get_iface_index = vpp_get_iface_index,
          .netm_get_iface_addr_list = vpp_get_iface_addr_list,
          .netm_get_src_addr_to = vpp_get_src_addr_to,
          .netm_get_iface_gw = vpp_get_iface_gw,
          .netm_get_first_ipv6_addr_from_iface_with_scope = vpp_get_first_ipv6_addr_from_iface_with_scope,
          .netm_get_iface_status = vpp_get_iface_status,
          .netm_get_iface_mac_addr = vpp_get_iface_mac_addr,
          .netm_reload_routes = vpp_reload_routes,
          .netm_build_addr_to_if_name_hasht = vpp_build_addr_to_if_name_hasht,
          .netm_get_iface_associated_with_pref = vpp_get_iface_name_associated_with_prefix,
          .data = NULL
  };
  
  int
  vpp_netm_init()
  {
      netm_vpp_data_t *data = netm_vpp_data_new();
      int error;
      int fd,rc;
      size_t fd_size;
  
      data->zmq_context = zmq_ctx_new();
      data->zmq_socket = zmq_socket(data->zmq_context, ZMQ_PULL);
      error = zmq_bind(data->zmq_socket, VPP_IPC_FILE);
  
      if (error != 0){
          OOR_LOG(LDBG_1,"vpp_netm_init: Error while ZMQ binding on server: %s\n",zmq_strerror (errno));
          netm_vpp_data_free(data);
          return (BAD);
      }
      netm_vpp.data = data;
      OOR_LOG(LDBG_1,"vpp_netm_init: VPP network notification socket initiated using ZMQ\n");
  
      /* Get socket FD. When we receive an event in this socket, we should double check by checking
       * ZMQ_EVENTS option */
  
      fd_size = sizeof(int);
      rc = zmq_getsockopt(data->zmq_socket, ZMQ_FD, &fd, &fd_size);
      if (rc == -1){
          OOR_LOG(LDBG_1,"vpp_netm_init: Error while getting the fd of the ZMQ socket: %s\n",zmq_strerror (errno));
      }
  
      sockmstr_register_read_listener(smaster, process_vpp_link_msg, NULL,fd);
  
      return (GOOD);
  }
  
  void
  vpp_netm_uninit()
  {
      netm_vpp_data_t *data = (netm_vpp_data_t *)netm_vpp.data;
      if (!data){
          return;
      }
      zmq_close (data->zmq_socket);
      zmq_ctx_destroy (data->zmq_context);
      netm_vpp_data_free(data);
  }
  
  
  int
  process_vpp_link_msg(struct sock *sl){
      uint8_t *buffer;
      int nbytes,rc;
      buffer = xzalloc(4096);
      netm_vpp_data_t *data = (netm_vpp_data_t *)netm_vpp.data;
      uint32_t events_flag;
      size_t fd_size;
      vpp_nl_msg *nl_msg;
  
      /* Check socket is ready to read */
      fd_size = sizeof(uint32_t);
      rc = zmq_getsockopt(data->zmq_socket, ZMQ_EVENTS, &events_flag, &fd_size);
      if (rc == -1){
          OOR_LOG(LDBG_1,"process_vpp_link_msg: Error while processing event type of ZMQ socket: %s\n",zmq_strerror (errno));
      }
      if (!(events_flag & ZMQ_POLLIN)){
          OOR_LOG(LDBG_3,"process_vpp_link_msg: ZMQ socket not ready to be read");
          return (GOOD);
      }
      /* Process the packet */
      nbytes = zmq_recv(data->zmq_socket, buffer, 4096, ZMQ_DONTWAIT);
      if (nbytes == -1){
          OOR_LOG(LERR,"process_vpp_link_msg: Error while ZMQ receiving: %s\n",zmq_strerror (errno));
          return (BAD);
      }
  
      nl_msg = (vpp_nl_msg *)buffer;
  
  
      switch(nl_msg->type){
      case VPP_NEWLINK:
          vpp_process_link_change((vpp_nl_link_info *)(buffer+sizeof(vpp_nl_msg)), UP);
          break;
      case VPP_DELLINK:
          vpp_process_link_change((vpp_nl_link_info *)(buffer+sizeof(vpp_nl_msg)), DOWN);
          break;
      case VPP_NEWADDR:
          vpp_process_addr_change((vpp_nl_addr_info *)(buffer+sizeof(vpp_nl_msg)), ADD);
          break;
      case VPP_DELADDR:
          vpp_process_addr_change((vpp_nl_addr_info *)(buffer+sizeof(vpp_nl_msg)), RM);
          break;
      default:
          OOR_LOG(LERR,"process_vpp_link_msg: Unknown message type\n");
      }
  
      return (GOOD);
  }
  
  void
  vpp_process_link_change(vpp_nl_link_info *link_info, uint8_t status)
  {
      OOR_LOG(LDBG_2, "vpp_process_link_change: Status of interface %d has changed to %s",
              link_info->ifi_index, status == UP ? "UP":"DOWN");
  
      nm_process_link_change(link_info->ifi_index, link_info->ifi_index, status);
  }
  
  void
  vpp_process_addr_change(vpp_nl_addr_info *addr_info, uint8_t act)
  {
      lisp_addr_t new_addr = { .lafi = LM_AFI_IP };
  
      switch (addr_info->ifa_family){
      case AF_INET:
          lisp_addr_ip_init(&new_addr, CO(addr_info,sizeof(vpp_nl_addr_info)), AF_INET);
          break;
      case AF_INET6:
          lisp_addr_ip_init(&new_addr, CO(addr_info,sizeof(vpp_nl_addr_info)), AF_INET6);
      }
  
      nm_process_address_change(act,addr_info->ifa_index, &new_addr);
  }
  
  
  
  glist_t *
  vpp_get_ifaces_names()
  {
      glist_t *iface_names = glist_new_managed((glist_del_fct)free);
      vpp_api_main_t * vam = vpp_api_main_get();
      vl_api_sw_interface_dump_t *mp;
      glist_entry_t *iface_it;
      vpp_api_iface_t *iface_info;
  
      glist_remove_all(vam->iface_list);
      /* Get list of ethernets */
      MSG (SW_INTERFACE_DUMP, sw_interface_dump);
      mp->name_filter_valid = 1;
      strncpy ((char *) mp->name_filter, "Ether", sizeof (mp->name_filter) - 1);
      VPP_SEND;
  
      /* Use a control ping for synchronization */
      {
          vl_api_control_ping_t *mp;
          MSG (CONTROL_PING, control_ping);
          VPP_SEND;
      }
  
      if (vpp_wait(vam) == ERR_NO_REPLY){
          OOR_LOG(LERR,"vpp_get_ifaces_names: Couldn't obtain the list of interfaces");
          glist_destroy(iface_names);
          return (NULL);
      }
  
      glist_for_each_entry(iface_it,vam->iface_list){
          iface_info = (vpp_api_iface_t *)glist_entry_data(iface_it);
          glist_add(strdup(iface_info->iface_name),iface_names);
      }
      glist_remove_all(vam->iface_list);
  
      return (iface_names);
  }
  
  glist_t *
  vpp_get_iface_addr_list(char *iface_name, int afi)
  {
      glist_t *addr_list = glist_new_managed((glist_del_fct)lisp_addr_del);
      vl_api_ip_address_dump_t *mp;
      vpp_api_main_t * vam = vpp_api_main_get();
      lisp_addr_t *addr;
      iface_t * iface;
      uint32_t iface_index;
      glist_entry_t *addr_it;
  
      glist_remove_all(vam->ip_addr_lst);
      iface = get_interface(iface_name);
      if (!iface){
          iface_index = vpp_get_iface_index(iface_name);
          if (iface_index == 0){
              OOR_LOG(LDBG_1,"vpp_get_iface_addr_list: Unknown interface %s",iface_name);
              return (addr_list);
          }
      }else{
          iface_index = iface->iface_index;
      }
  
      MSG (IP_ADDRESS_DUMP, ip_address_dump);
      mp->sw_if_index = ntohl (iface_index);
      if (afi == AF_INET6){
          mp->is_ipv6 = 1;
      }
      vam->requested_ip_afi = afi;
      VPP_SEND;
  
      /* Use a control ping for synchronization */
      {
        vl_api_control_ping_t *mp;
        MSG (CONTROL_PING, control_ping);
        VPP_SEND;
      }
      if (vpp_wait(vam) == ERR_NO_REPLY){
          return (addr_list);
      }
  
      glist_for_each_entry(addr_it,vam->ip_addr_lst){
          addr = (lisp_addr_t *)glist_entry_data(addr_it);
          lisp_addr_set_lafi(addr,LM_AFI_IP);
          if (ip_addr_is_link_local(lisp_addr_ip(addr)) == TRUE) {
              OOR_LOG(LDBG_2, "vpp_get_iface_addr_list: interface address from "
                      "%s discarded (%s)", iface_name, lisp_addr_to_char(addr));
          }else {
              glist_add (lisp_addr_clone(addr), addr_list);
          }
      }
  
      if (glist_size(addr_list) == 0){
          OOR_LOG(LDBG_2, "vpp_get_iface_addr_list: No %s RLOC configured for interface "
                  "%s\n", (afi == AF_INET) ? "IPv4" : "IPv6", iface_name);
      }
      glist_remove_all(vam->ip_addr_lst);
  
      return(addr_list);
  }
  
  /* This function only works if the addr is directly connected to OOR */
  lisp_addr_t *
  vpp_get_src_addr_to(lisp_addr_t *addr)
  {
      lisp_addr_t *src_addr = NULL, *pref, *iface_addr;
      glist_t *addr_list, *net_pref_list;
      glist_entry_t *add_list_it, *net_pref_list_it;
      int afi = lisp_addr_ip_afi(addr);
      uint8_t pref_found = FALSE;
      shash_t *addr_to_iface;
  
      net_pref_list = vpp_ip_fib_prefixs(afi);
      glist_for_each_entry(net_pref_list_it, net_pref_list){
          pref = (lisp_addr_t *)glist_entry_data(net_pref_list_it);
          if (pref_is_addr_part_of_prefix(addr,pref)){
              pref_found = TRUE;
              break;
          }
      }
      if (!pref_found){
          OOR_LOG(LDBG_1,"vpp_get_src_addr_to: netm_get_src_addr_to should only be used with"
                  "directly connected addresses (%s)",lisp_addr_to_char(addr));
          goto end;
      }
      addr_to_iface = vpp_build_addr_to_if_name_hasht();
      addr_list = shash_keys(addr_to_iface);
      iface_addr = lisp_addr_new();
      glist_for_each_entry(add_list_it,addr_list){
          lisp_addr_ip_from_char((char *)glist_entry_data(add_list_it), iface_addr);
          if (pref_is_addr_part_of_prefix(iface_addr,pref) == TRUE){
              src_addr = lisp_addr_clone(iface_addr);
              goto end;
          }
      }
      OOR_LOG(LDBG_1,"netm_get_src_addr_to: No src address slected to reach %s. It should never happen",
              lisp_addr_to_char(addr));
  
      end:
      glist_destroy(net_pref_list);
      glist_destroy(addr_list);
      shash_destroy(addr_to_iface);
      lisp_addr_del(iface_addr);
      return (src_addr);
  }
  
  lisp_addr_t *
  vpp_get_iface_gw(char *iface_name, int afi)
  {
      /* We only support one default gateway */
      lisp_addr_t *gw;
      gw = vpp_oor_pkt_miss_get_default_route(afi);
  
      return (lisp_addr_clone(gw));
  }
  
  lisp_addr_t *
  vpp_get_first_ipv6_addr_from_iface_with_scope()
  {
      OOR_LOG(LDBG_1,"vpp_get_first_ipv6_addr_from_iface_with_scope: VPP without IPv6 support");
      return (NULL);
  }
  
  
  uint8_t
  vpp_get_iface_status(char *iface_name)
  {
      vpp_api_main_t * vam = vpp_api_main_get();
      vl_api_sw_interface_dump_t *mp;
      vpp_api_iface_t *iface_info;
  
      glist_remove_all(vam->iface_list);
      /* Get list of ethernets */
      MSG (SW_INTERFACE_DUMP, sw_interface_dump);
      mp->name_filter_valid = 1;
      strncpy ((char *) mp->name_filter, iface_name, sizeof (mp->name_filter) - 1);
      VPP_SEND;
  
      /* Use a control ping for synchronization */
      {
          vl_api_control_ping_t *mp;
          MSG (CONTROL_PING, control_ping);
          VPP_SEND;
      }
  
      vpp_wait(vam);
      if (glist_size(vam->iface_list) == 0){
          OOR_LOG(LDBG_1,"vpp_get_iface_index: Unknown interface %s",iface_name);
          return (ERR_NO_EXIST);
      }
      iface_info = (vpp_api_iface_t *)glist_first_data(vam->iface_list);
  
      return (iface_info->status);
  }
  
  int
  vpp_get_iface_index(char *iface_name)
  {
      vpp_api_main_t * vam = vpp_api_main_get();
      vl_api_sw_interface_dump_t *mp;
      vpp_api_iface_t *iface_info;
  
      glist_remove_all(vam->iface_list);
      /* Get list of ethernets */
      MSG (SW_INTERFACE_DUMP, sw_interface_dump);
      mp->name_filter_valid = 1;
      strncpy ((char *) mp->name_filter, iface_name, sizeof (mp->name_filter) - 1);
      VPP_SEND;
  
      /* Use a control ping for synchronization */
      {
          vl_api_control_ping_t *mp;
          MSG (CONTROL_PING, control_ping);
          VPP_SEND;
      }
  
      vpp_wait(vam);
      if (glist_size(vam->iface_list) == 0){
          OOR_LOG(LDBG_1,"vpp_get_iface_index: Unknown interface %s",iface_name);
          return (0);
      }
      iface_info = (vpp_api_iface_t *)glist_first_data(vam->iface_list);
  
      return (iface_info->iface_index);
  }
  
  void
  vpp_get_iface_mac_addr(char *iface_name, uint8_t *mac)
  {
      vpp_api_main_t * vam = vpp_api_main_get();
      vl_api_sw_interface_dump_t *mp;
      vpp_api_iface_t *iface_info;
  
      glist_remove_all(vam->iface_list);
      /* Get list of ethernets */
      MSG (SW_INTERFACE_DUMP, sw_interface_dump);
      mp->name_filter_valid = 1;
      strncpy ((char *) mp->name_filter, iface_name, sizeof (mp->name_filter) - 1);
      VPP_SEND;
  
      /* Use a control ping for synchronization */
      {
          vl_api_control_ping_t *mp;
          MSG (CONTROL_PING, control_ping);
          VPP_SEND;
      }
  
      vpp_wait(vam);
      if (glist_size(vam->iface_list) == 0){
          OOR_LOG(LDBG_1,"vpp_get_iface_index: Unknown interface %s",iface_name);
          return;
      }
      iface_info = (vpp_api_iface_t *)glist_first_data(vam->iface_list);
      memcpy(mac, iface_info->l2_address, 6*sizeof(uint8_t));
  
      return;
  }
  
  char *
  vpp_get_iface_name_associated_with_prefix(lisp_addr_t * pref)
  {
      shash_t *addr_to_iface;
      glist_t *iface_addr_lst;
      glist_entry_t *addr_it;
      lisp_addr_t *iface_addr = lisp_addr_new();
      lisp_addr_t *ip_pref_addr = lisp_addr_get_ip_pref_addr(pref);
      char * iface_name = NULL;
  
  
      //XXX This process is not optim. VPP API not offer to obtain interface from IP
      addr_to_iface = vpp_build_addr_to_if_name_hasht();
      iface_addr_lst = shash_keys(addr_to_iface);
      glist_for_each_entry(addr_it,iface_addr_lst){
          lisp_addr_ip_from_char((char *)glist_entry_data(addr_it), iface_addr);
          if (pref_is_addr_part_of_prefix(iface_addr,ip_pref_addr) == TRUE){
              iface_name = strdup(shash_lookup(addr_to_iface, lisp_addr_to_char(iface_addr)));
              goto end;
          }
      }
      OOR_LOG(LERR,"VPP: Not found interface associated with prefix %s",lisp_addr_to_char(pref));
  
  end:
     glist_destroy(iface_addr_lst);
     shash_destroy(addr_to_iface);
     lisp_addr_del(iface_addr);
     return (iface_name);
  }
  
  
  /*
   * Request to the kernel the routing table with the selected afi
   */
  int
  vpp_reload_routes(uint32_t table, int afi)
  {
  
      return (GOOD);
  }
  
  shash_t *
  vpp_build_addr_to_if_name_hasht()
  {
      shash_t *ht;
      glist_t *iface_name_list, *addr_list;
      glist_entry_t *iface_name_it, *addr_it;
      char *iface_name;
      lisp_addr_t *addr;
      OOR_LOG(LDBG_2, "Building address to interface hash table");
  
      iface_name_list = vpp_get_ifaces_names();
      if (!iface_name_list){
          return (NULL);
      }
  
      ht = shash_new_managed((free_value_fn_t)free);
  
      glist_for_each_entry(iface_name_it,iface_name_list){
          iface_name = (char *)glist_entry_data(iface_name_it);
          addr_list = vpp_get_iface_addr_list(iface_name, AF_INET);
          glist_for_each_entry(addr_it, addr_list){
              addr = (lisp_addr_t *)glist_entry_data(addr_it);
              shash_insert(ht, strdup(lisp_addr_to_char(addr)), strdup(iface_name));
              OOR_LOG(LDBG_2, "Found interface %s with address %s", iface_name,
                                  lisp_addr_to_char(addr));
          }
          glist_destroy (addr_list);
  
          addr_list = vpp_get_iface_addr_list(iface_name, AF_INET6);
          glist_for_each_entry(addr_it, addr_list){
              addr = (lisp_addr_t *)glist_entry_data(addr_it);
              shash_insert(ht, strdup(lisp_addr_to_char(addr)), strdup(iface_name));
              OOR_LOG(LDBG_2, "Found interface %s with address %s", iface_name,
                      lisp_addr_to_char(addr));
          }
          glist_destroy (addr_list);
      }
  
      return (ht);
  }
  
  inline static netm_vpp_data_t *
  netm_vpp_data_new()
  {
      return (xzalloc(sizeof(netm_vpp_data_t)));
  }
  
  inline static void
  netm_vpp_data_free(netm_vpp_data_t * vpp_data)
  {
      free(vpp_data);
  }
  
  
  