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 <stdarg.h>
  #include <netdb.h>
  
  #include "ios.h"
  #include "ios_input.h"
  #include "ios_output.h"
  #include "../../data-plane.h"
  #include "../../encapsulations/vxlan-gpe.h"
  #include "../../../iface_list.h"
  #include "../../../oor_external.h"
  #include "../../../fwd_policies/fwd_policy.h"
  #include "../../../lib/oor_log.h"
  #include "../../../lib/util.h"
  #include "../../../net_mgr/net_mgr.h"
  
  int ios_init(oor_dev_type_e dev_type, oor_encap_t encap_type,...);
  void ios_uninit();
  int ios_add_datap_iface_addr(iface_t *iface, int afi);
  int ios_add_datap_iface_gw(iface_t *iface, int afi);
  int ios_register_lcl_mapping(oor_dev_type_e dev_type, mapping_t *map);
  int ios_deregister_lcl_mapping(oor_dev_type_e dev_type, mapping_t *map);
  int ios_updated_route(int command, iface_t *iface, lisp_addr_t *src_pref,
                           lisp_addr_t *dst_pref, lisp_addr_t *gw);
  void ios_process_new_gateway(iface_t *iface,lisp_addr_t *gateway);
  int ios_updated_addr(iface_t *iface,lisp_addr_t *old_addr,lisp_addr_t *new_addr);
  int ios_update_link(iface_t *iface, int old_iface_index, int new_iface_index,
                         int status);
  int ios_reset_socket(int fd, int afi);
  int ios_rm_fwd_from_entry(lisp_addr_t *eid_prefix, uint8_t is_local);
  ios_data_t * ios_data_new_init(oor_encap_t encap_type, int tun_socket,
                                       int ipv4_data_socket, int ipv6_data_socket);
  void ios_data_free(ios_data_t *data);
  
  data_plane_struct_t dplane_apple = {
      .datap_init = ios_init,
      .datap_uninit = ios_uninit,
      .datap_add_iface_addr = ios_add_datap_iface_addr,
      .datap_add_iface_gw = ios_add_datap_iface_gw,
      .datap_register_lcl_mapping = ios_register_lcl_mapping,
      .datap_deregister_lcl_mapping = ios_deregister_lcl_mapping,
      .datap_input_packet = ios_process_input_packet,
      .datap_rtr_input_packet = ios_rtr_process_input_packet,
      .datap_output_packet = ios_output_recv2,
      .datap_updated_route = ios_updated_route,
      .datap_updated_addr = ios_updated_addr,
      .datap_update_link = ios_update_link,
      .datap_rm_fwd_from_entry = ios_rm_fwd_from_entry,
      .datap_reset_all_fwd = ios_reset_all_fwd,
      .datap_data = NULL
  };
  
  inline ios_data_t *
  ios_get_datap_data()
  {
      return ((ios_data_t *)dplane_apple.datap_data);
  }
  
  int
  ios_init(oor_dev_type_e dev_type, oor_encap_t encap_type,...)
  {
      int (*cb_func)(sock_t *) = NULL;
      int tun_socket, ipv4_data_socket, ipv6_data_socket;
      int data_port;
      
      //open socket to connect with TunnelProvider
      tun_socket = open_data_datagram_input_socket(AF_INET, 10001);
      sockmstr_register_read_listener(smaster, ios_output_recv2, NULL,tun_socket);
      
      switch (dev_type){
          case MN_MODE:
          case xTR_MODE:
              cb_func = ios_process_input_packet;
              break;
          case RTR_MODE:
              cb_func = ios_rtr_process_input_packet;
              break;
          default:
              return (BAD);
      }
      
      switch (encap_type){
          case ENCP_LISP:
              data_port = LISP_DATA_PORT;
              break;
          case ENCP_VXLAN_GPE:
              data_port = VXLAN_GPE_DATA_PORT;
              break;
      }
      
      if (default_rloc_afi != AF_INET6){
          ipv4_data_socket = open_data_datagram_input_socket(AF_INET, data_port);
          sockmstr_register_read_listener(smaster, cb_func, NULL,ipv4_data_socket);
      }else {
          ipv4_data_socket = ERR_SOCKET;
      }
      
      if (default_rloc_afi != AF_INET){
          ipv6_data_socket = open_data_datagram_input_socket(AF_INET6, data_port);
          sockmstr_register_read_listener(smaster, cb_func, NULL,ipv6_data_socket);
      }else {
          ipv6_data_socket = ERR_SOCKET;
      }
      
      dplane_apple.datap_data = (void *)ios_data_new_init(encap_type, tun_socket,
                                                              ipv4_data_socket, ipv6_data_socket);
      if (!(dplane_apple.datap_data)){
          return (BAD);
      }
      
      return (GOOD);
  }
  
  void
  ios_uninit()
  {
      ios_data_free(dplane_apple.datap_data);
  }
  
  int
  ios_add_datap_iface_addr(iface_t *iface, int afi)
  {
      return (GOOD);
  }
  
  int
  ios_add_datap_iface_gw(iface_t *iface, int afi)
  {
      return (GOOD);
  }
  
  int
  ios_register_lcl_mapping(oor_dev_type_e dev_type, mapping_t *map)
  {
      return (GOOD);
  }
  
  int
  ios_deregister_lcl_mapping(oor_dev_type_e dev_type, mapping_t *map)
  {
      return (GOOD);
  }
  
  int
  ios_updated_route(int command, iface_t *iface, lisp_addr_t *src_pref,
                       lisp_addr_t *dst_pref, lisp_addr_t *gateway)
  {
      if (lisp_addr_ip_afi(gateway) != LM_AFI_NO_ADDR
          && (lisp_addr_ip_afi(dst_pref) == LM_AFI_NO_ADDR ||
          laddr_is_full_space_pref(dst_pref))) {
          
          // Process the new gateway
          OOR_LOG(LDBG_1,  "ios_updated_route: Process new gateway "
                  "associated to the interface %s:  %s", iface->iface_name,
                  lisp_addr_to_char(gateway));
          ios_process_new_gateway(iface,gateway);
      }
      return (GOOD);
  }
  
  void
  ios_process_new_gateway(iface_t *iface,lisp_addr_t *gateway)
  {
      lisp_addr_t **gw_addr    = NULL;
      int afi;
      ios_data_t *data;
      afi = lisp_addr_ip_afi(gateway);
      
      switch(afi){
          case AF_INET:
              gw_addr = &(iface->ipv4_gateway);
              break;
          case AF_INET6:
              gw_addr = &(iface->ipv6_gateway);
              break;
          default:
              return;
      }
      if (*gw_addr == NULL || lisp_addr_is_no_addr(*gw_addr)) { // The default gateway of this interface is not deffined yet
          lisp_addr_del (*gw_addr);
          *gw_addr = lisp_addr_new();
      }
      lisp_addr_copy(*gw_addr,gateway);
      
      if (iface->status != UP){
          OOR_LOG(LDBG_1,"ios_process_new_gateway: Probably the interface %s is UP "
                  "but we didn't receive netlink indicating this. Checking it",
                  iface->iface_name,iface->iface_name);
          iface->status = net_mgr->netm_get_iface_status(iface->iface_name);
      }
      
      data = (ios_data_t *)dplane_apple.datap_data;
      
      /* Recreate sockets */
      if (afi == AF_INET){
          ios_reset_socket(data->ipv4_data_socket,AF_INET);
      }else{
          ios_reset_socket(data->ipv6_data_socket,AF_INET6);
      }
  }
  
  int
  ios_updated_addr(iface_t *iface,lisp_addr_t *old_addr,lisp_addr_t *new_addr)
  {
      int new_addr_ip_afi;
      ios_data_t * data;
  
      data = (ios_data_t *)dplane_apple.datap_data;
      new_addr_ip_afi = lisp_addr_ip_afi(new_addr);
      
      /* Check if the detected change of address id the same. */
      if (lisp_addr_cmp(old_addr, new_addr) == 0) {
          OOR_LOG(LDBG_2, "ios_updated_addr: The change of address detected "
                  "for interface %s doesn't affect", iface->iface_name);
          
          return (GOOD);
      };
      
      switch (new_addr_ip_afi){
          case AF_INET:
              ios_reset_socket(data->ipv4_data_socket, AF_INET);
              break;
          case AF_INET6:
              ios_reset_socket(data->ipv6_data_socket, AF_INET6);
              break;
          default:
              return (BAD);
      }
      
      return (GOOD);
  }
  
  int
  ios_update_link(iface_t *iface, int old_iface_index, int new_iface_index, int status)
  {
      ios_data_t * data;
      
      data = (ios_data_t *)dplane_apple.datap_data;
      
      if (default_rloc_afi != AF_INET6){
          ios_reset_socket(data->ipv4_data_socket, AF_INET);
      }
      if (default_rloc_afi != AF_INET){
          ios_reset_socket(data->ipv6_data_socket, AF_INET6);
      }
      return (GOOD);
  }
  
  int
  ios_reset_socket(int fd, int afi)
  {
      sock_t *old_sock;
      int new_fd;
      ios_data_t * data;
      int src_port;
      
      data = (ios_data_t *)dplane_apple.datap_data;
      old_sock = sockmstr_register_get_by_fd(smaster,fd);
      sockmstr_unregister_read_listenedr(smaster,old_sock);
  
      switch (data->encap_type){
          case ENCP_LISP:
              src_port = LISP_DATA_PORT;
              break;
          case ENCP_VXLAN_GPE:
              src_port = VXLAN_GPE_DATA_PORT;
              break;
      }
      
      switch (afi){
          case AF_INET:
              OOR_LOG(LDBG_2,"reset_socket: Reset IPv4 data socket");
              new_fd = open_data_datagram_input_socket(AF_INET,src_port);
              if (new_fd == ERR_SOCKET){
                  OOR_LOG(LDBG_2,"ios_reset_socket: Error recreating the socket");
                  return (BAD);
              }
              data->ipv4_data_socket = new_fd;
              break;
          case AF_INET6:
              OOR_LOG(LDBG_2,"reset_socket: Reset IPv6 data socket");
              new_fd = open_data_datagram_input_socket(AF_INET6,src_port);
              if (new_fd == ERR_SOCKET){
                  OOR_LOG(LDBG_2,"ios_reset_socket: Error recreating the socket");
                  return (BAD);
              }
              data->ipv6_data_socket = new_fd;
              break;
          default:
              return (BAD);
      }
      
      sockmstr_register_read_listener(smaster,ios_process_input_packet,NULL,new_fd);
      
      return (GOOD);
  }
  
  int
  ios_rm_fwd_from_entry(lisp_addr_t *eid_prefix, uint8_t is_local)
  {
      char * eid_prefix_char = lisp_addr_to_char(eid_prefix);
      glist_t *fwd_tpl_list, *pxtr_fwd_tpl_list;
      glist_entry_t *tpl_it;
      fwd_info_t *fi;
      ios_data_t *data = (ios_data_t *)dplane_apple.datap_data;
      packet_tuple_t *tpl;
      
      
      if (is_local){
          return (ios_reset_all_fwd());
      }
      
      if (strcmp(eid_prefix_char,FULL_IPv4_ADDRESS_SPACE) == 0){ // Update of the PeTR list for IPv4 EIDs or RTR list
          OOR_LOG(LDBG_3, "ios_rm_fwd_from_entry: Removing all the forwading entries association with the PeTRs for IPv4 EIDs");
          pxtr_fwd_tpl_list = (glist_t *)shash_lookup(data->eid_to_dp_entries,FULL_IPv4_ADDRESS_SPACE);
          if (!pxtr_fwd_tpl_list){
              return (GOOD);
          }
          /* Remove all the entries associated with the PxTR */
          
          while (glist_size(pxtr_fwd_tpl_list) > 0){
              tpl = (packet_tuple_t *)glist_first_data(pxtr_fwd_tpl_list);
              fi = ttable_lookup(&(data->ttable), tpl);
              // When we recurively call this function using the associated_entry we will execute "else" statement where we also
              // update the list of entries associated with PxTR.
              ios_rm_fwd_from_entry(fi->associated_entry,is_local);
          }
      }else if(strcmp(eid_prefix_char,FULL_IPv6_ADDRESS_SPACE) == 0){ // Update of the PeTR list for IPv6 EIDs or RTR list
          OOR_LOG(LDBG_3, "ios_rm_fwd_from_entry: Removing all the forwading entries association with the PeTRs for IPv6 EIDs");
          pxtr_fwd_tpl_list = (glist_t *)shash_lookup(data->eid_to_dp_entries,FULL_IPv6_ADDRESS_SPACE);
          if (!pxtr_fwd_tpl_list){
              return (GOOD);
          }
          /* Remove all the entries associated with the PxTR */
          
          while (glist_size(pxtr_fwd_tpl_list) > 0){
              tpl = (packet_tuple_t *)glist_first_data(pxtr_fwd_tpl_list);
              fi = ttable_lookup(&(data->ttable), tpl);
              // When we recurively call this function using the associated_entry we will execute "else" statement where we also
              // update the list of entries associated with PxTR.
              ios_rm_fwd_from_entry(fi->associated_entry,is_local);
          }
      }else{
          OOR_LOG(LDBG_3, "ios_rm_fwd_from_entry: Removing all the forwading entries association with the EID %s",eid_prefix_char);
          fwd_tpl_list = (glist_t *)shash_lookup(data->eid_to_dp_entries,eid_prefix_char);
          if (!fwd_tpl_list){
              OOR_LOG(LDBG_1, "ios_rm_fwd_from_entry: Entry %s not found in the shasht!",eid_prefix_char);
              return (BAD);
          }
          /* Check if it is a negative entry in order to remove also from PxTRs list */
          tpl = (packet_tuple_t *)glist_first_data(fwd_tpl_list);
          fi = ttable_lookup(&(data->ttable), tpl);
          if (fi->neg_map_reply_act == ACT_NATIVE_FWD){ //negative mapping
              switch (lisp_addr_ip_afi(fi->associated_entry)){
                  case AF_INET:
                      pxtr_fwd_tpl_list = (glist_t *)shash_lookup(data->eid_to_dp_entries,FULL_IPv4_ADDRESS_SPACE);
                      break;
                  case AF_INET6:
                      pxtr_fwd_tpl_list = (glist_t *)shash_lookup(data->eid_to_dp_entries,FULL_IPv6_ADDRESS_SPACE);
                      break;
                  default:
                      OOR_LOG(LDBG_1, "ios_rm_fwd_from_entry: Associated entry is not IP");
                      return (BAD);
              }
              glist_for_each_entry(tpl_it,fwd_tpl_list){
                  tpl = (packet_tuple_t *)glist_entry_data(tpl_it);
                  glist_remove_obj(tpl,pxtr_fwd_tpl_list);
              }
          }
          /* Remove associated entry from eid_to_dp_entries */
          shash_remove(data->eid_to_dp_entries, eid_prefix_char);
      }
      
      return (GOOD);
  }
  
  
  /* Remove all the fwd programmed in the data plane
   * Used when a change is produced in the local mappings */
  
  int
  ios_reset_all_fwd()
  {
      ios_data_t *data = (ios_data_t *)dplane_apple.datap_data;
      
      shash_destroy(data->eid_to_dp_entries);
      data->eid_to_dp_entries = shash_new_managed((free_value_fn_t)glist_destroy);
      return (GOOD);
  }
  
  ios_data_t *
  ios_data_new_init(oor_encap_t encap_type, int tun_socket,
                       int ipv4_data_socket, int ipv6_data_socket)
  {
      ios_data_t * data;
      data = xmalloc(sizeof(ios_data_t));
      if (!data){
          return (NULL);
      }
      data->encap_type = encap_type;
      data->tun_socket = tun_socket;
      data->ipv4_data_socket = ipv4_data_socket;
      data->ipv6_data_socket = ipv6_data_socket;
      data->eid_to_dp_entries = shash_new_managed((free_value_fn_t)glist_destroy);
      
      ttable_init(&(data->ttable));
      
      return (data);
  }
  
  void
  ios_data_free(ios_data_t *data)
  {
      if (!data){
          return;
      }
      shash_destroy(data->eid_to_dp_entries);
      ttable_uninit(&(data->ttable));
      free(data);
  }
  