/*
 *
 * 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 <unistd.h>
#include "../lib/iface_locators.h"
#include "../lib/sockets.h"
#include "../lib/mem_util.h"
#include "../lib/oor_log.h"
#include "../lib/timers_utils.h"
#include "../lib/util.h"
#include "lisp_xtr.h"


typedef struct _timer_map_reg_argument {
    map_local_entry_t  *mle;
    map_server_elt     *ms;
} timer_map_reg_argument;

typedef struct _timer_encap_map_reg_argument {
    map_local_entry_t  *mle;
    map_server_elt     *ms;
    locator_t          *src_loct;
    lisp_addr_t        *rtr_rloc;
} timer_encap_map_reg_argument;

typedef struct _timer_inf_req_argument {
    map_local_entry_t *mle;
    locator_t *loct;
    map_server_elt *ms;
}timer_inf_req_argument;


static oor_ctrl_dev_t *xtr_ctrl_alloc();
static int xtr_ctrl_construct(oor_ctrl_dev_t *dev);
static void xtr_ctrl_dealloc(oor_ctrl_dev_t *dev);
static void xtr_ctrl_destruct(oor_ctrl_dev_t *dev);
static void xtr_run(oor_ctrl_dev_t *dev);
static int xtr_recv_msg(oor_ctrl_dev_t *dev, lbuf_t *msg, uconn_t *uc);
static int xtr_if_link_update(oor_ctrl_dev_t *dev, char *iface_name, uint8_t status);
int xtr_if_addr_update(oor_ctrl_dev_t *dev, char *iface_name, lisp_addr_t *old_addr,
        lisp_addr_t *new_addr, uint8_t status);
int xtr_route_update(oor_ctrl_dev_t *dev, int command, char *iface_name ,lisp_addr_t *src_pref,
        lisp_addr_t *dst_pref, lisp_addr_t *gateway);
static fwd_info_t * xtr_get_forwarding_entry(oor_ctrl_dev_t *dev, packet_tuple_t *tuple);
/*************************** PROCESS MESSAGES ********************************/
static int xtr_recv_enc_ctrl_msg(lisp_xtr_t *xtr, lbuf_t *msg, void **ecm_hdr, uconn_t *int_uc);
static int xtr_recv_map_request(lisp_xtr_t *xtr, lbuf_t *buf,  void *ecm_hdr, uconn_t *int_uc, uconn_t *ext_uc);
static inline int xtr_recv_map_reply(lisp_xtr_t *xtr, lbuf_t *buf, uconn_t *uc);
static int xtr_recv_map_notify(lisp_xtr_t *xtr, lbuf_t *buf);
static int xtr_recv_info_nat(lisp_xtr_t *xtr, lbuf_t *buf, uconn_t *uc);
static int xtr_build_and_send_map_reg(lisp_xtr_t * xtr, mapping_t * m, map_server_elt *ms,
        uint64_t nonce);
static int xtr_build_and_send_encap_map_reg(lisp_xtr_t * xtr, mapping_t * m, map_server_elt *ms,
        lisp_addr_t *etr_addr, lisp_addr_t *rtr_addr, uint64_t nonce);
static int xtr_build_and_send_smr_mreq(lisp_xtr_t *xtr, mapping_t *smap,
        lisp_addr_t *deid, lisp_addr_t *drloc);
static int xtr_build_and_send_info_req(lisp_xtr_t * xtr, mapping_t * m, locator_t *loct,
        map_server_elt *ms, uint64_t nonce);
/**************************** LOGICAL PROCESSES ******************************/
/****************************** Map Register *********************************/
static int xtr_program_map_register_for_mapping(lisp_xtr_t *xtr, map_local_entry_t *mle);
static int xtr_map_register_cb(oor_timer_t *timer);
/****************************** Encap Map Register ***************************/
static int xtr_encap_map_register_cb(oor_timer_t *timer);
static int xtr_program_encap_map_reg_of_loct_for_map(lisp_xtr_t *xtr, map_local_entry_t *mle,
        locator_t *src_loct);
/*********************************** SMR *************************************/
static void xtr_smr_process_start(lisp_xtr_t *xtr);
static int xtr_smr_notify_mcache_entry(lisp_xtr_t  *xtr, mapping_t *src_map,
        mapping_t *dst_map);
static int xtr_smr_process_start_cb(oor_timer_t *timer);
static int xtr_program_smr(lisp_xtr_t *xtr, int time);
static glist_t * xtr_get_map_local_entry_to_smr(lisp_xtr_t *xtr);
/****************************** Info Request *********************************/
static int xtr_program_initial_info_request_process(lisp_xtr_t *xtr);
static int xtr_program_info_req_per_loct(lisp_xtr_t *xtr, map_local_entry_t *mle, locator_t *loct);
static int xtr_info_request_cb(oor_timer_t *timer);
/**************************** AUXILIAR FUNCTIONS *****************************/
static int xtr_iface_event_signaling(lisp_xtr_t * xtr, iface_locators * if_loct);
/****************************** NAT traversal ********************************/
static int xtr_update_nat_info(lisp_xtr_t *xtr, map_local_entry_t *mle, locator_t *loct,
        glist_t *rtr_list);
static void xtr_update_rtrs_caches(lisp_xtr_t *xtr);
static void xtr_update_rtrs_cache_afi(lisp_xtr_t *xtr, int afi);
static glist_t * nat_select_rtrs(glist_t * rtr_list);
/*****************************************************************************/
static map_local_entry_t * get_map_loc_ent_containing_loct_ptr(local_map_db_t *local_db, locator_t *locator);
/******************************* TIMERS **************************************/
static timer_map_reg_argument * timer_map_reg_argument_new_init(map_local_entry_t *mle,
        map_server_elt *ms);
static void timer_map_reg_arg_free(timer_map_reg_argument * timer_arg);
static timer_encap_map_reg_argument * timer_encap_map_reg_argument_new_init(map_local_entry_t *mle,
        map_server_elt *ms, locator_t *src_loct, lisp_addr_t *rtr_addr);
static void timer_encap_map_reg_arg_free(timer_encap_map_reg_argument * timer_arg);
static void timer_encap_map_reg_stop_using_locator(map_local_entry_t *mle, locator_t *loct);
static timer_inf_req_argument * timer_inf_req_argument_new_init(map_local_entry_t *mle, locator_t *loct,
        map_server_elt *ms);
static void timer_inf_req_arg_free(timer_inf_req_argument * timer_arg);
static void timer_inf_req_stop_using_locator(map_local_entry_t *mle, locator_t *loct);



/* implementation of ctrl base functions */
ctrl_dev_class_t xtr_ctrl_class = {
        .alloc = xtr_ctrl_alloc,
        .construct = xtr_ctrl_construct,
        .dealloc = xtr_ctrl_dealloc,
        .destruct = xtr_ctrl_destruct,
        .run = xtr_run,
        .recv_msg = xtr_recv_msg,
        .if_link_update = xtr_if_link_update,
        .if_addr_update = xtr_if_addr_update,
        .route_update = xtr_route_update,
        .get_fwd_entry = xtr_get_forwarding_entry
};


static oor_ctrl_dev_t *
xtr_ctrl_alloc()
{
    lisp_xtr_t *xtr;
    xtr = xzalloc(sizeof(lisp_xtr_t));
    return(&xtr->super);
}

static int
xtr_ctrl_construct(oor_ctrl_dev_t *dev)
{
    lisp_xtr_t *xtr = lisp_xtr_cast(dev);
    lisp_addr_t addr;
    mapping_t *pxtr_4_map, *pxtr_6_map;
    mcache_entry_t *def_ipv4_mc, *def_ipv6_mc;


    lisp_tr_init(&xtr->tr);

    /* set up databases */
    xtr->local_mdb = local_map_db_new();
    xtr->map_servers = glist_new_managed((glist_del_fct)map_server_elt_del);
    xtr->pitrs = glist_new_managed((glist_del_fct)lisp_addr_del);
    def_ipv4_mc = mcache_entry_new();
    def_ipv6_mc = mcache_entry_new();

    if (!xtr->local_mdb || !xtr->map_servers || !xtr->pitrs ||
            !def_ipv4_mc || !def_ipv6_mc) {
        return(BAD);
    }

    /* Add entries used to add the PeTR or RTRs in case of NAT */

    lisp_addr_ippref_from_char(FULL_IPv4_ADDRESS_SPACE, &addr);
    pxtr_4_map = mapping_new_init(&addr);
    mapping_set_action(pxtr_4_map,ACT_NATIVE_FWD);
    mcache_entry_init_static(def_ipv4_mc, pxtr_4_map);
    mcache_add_entry(xtr->tr.map_cache,mcache_entry_eid(def_ipv4_mc),def_ipv4_mc);

    lisp_addr_ippref_from_char(FULL_IPv6_ADDRESS_SPACE, &addr);
    pxtr_6_map = mapping_new_init(&addr);
    mapping_set_action(pxtr_6_map,ACT_NATIVE_FWD);
    mcache_entry_init_static(def_ipv6_mc, pxtr_6_map);
    mcache_add_entry(xtr->tr.map_cache,mcache_entry_eid(def_ipv6_mc),def_ipv6_mc);

    OOR_LOG(LDBG_1, "Finished constructing xTR");

    return(GOOD);
}

static void
xtr_ctrl_dealloc(oor_ctrl_dev_t *dev) {
    lisp_xtr_t *xtr = lisp_xtr_cast(dev);
    free(xtr);
    OOR_LOG(LDBG_1, "Freed xTR ...");
}

static void
xtr_ctrl_destruct(oor_ctrl_dev_t *dev)
{
    map_local_entry_t * map_loc_e;
    void *it;
    lisp_xtr_t *xtr = lisp_xtr_cast(dev);
    lisp_tr_uninit(&xtr->tr);

    local_map_db_foreach_entry(xtr->local_mdb, it) {
        map_loc_e = (map_local_entry_t *)it;
        ctrl_unregister_mapping_dp(dev,map_local_entry_mapping(map_loc_e));
    } local_map_db_foreach_end;

    local_map_db_del(xtr->local_mdb);

    glist_destroy(xtr->pitrs);
    glist_destroy(xtr->map_servers);
    oor_timer_stop(xtr->smr_timer);
    OOR_LOG(LDBG_1,"xTR device destroyed");
}

static void
xtr_run(oor_ctrl_dev_t *dev)
{
    lisp_xtr_t *xtr = lisp_xtr_cast(dev);
    map_local_entry_t *map_loc_e;
    mcache_entry_t *ipv4_petrs_mc,*ipv6_petrs_mc;
    locator_t *loct;
    void *it;
    int num_eids = 0;


    if (xtr->super.mode == MN_MODE){
        OOR_LOG(LDBG_1, "\nStarting xTR MN ...\n");
    }
    if (xtr->super.mode == xTR_MODE){
        OOR_LOG(LDBG_1, "\nStarting xTR ...\n");
    }

    if (glist_size(xtr->map_servers) == 0) {
        OOR_LOG(LWRN, "**** NO MAP SERVER CONFIGURED. Your EID will not be registered in the Mapping System.");
        oor_timer_sleep(2);
    }

    if (glist_size(xtr->tr.map_resolvers) == 0) {
        OOR_LOG(LCRIT, "**** NO MAP RESOLVER CONFIGURED. You can not request mappings from the mapping system");
        oor_timer_sleep(2);
    }

    ipv4_petrs_mc = get_proxy_etrs_for_afi(&xtr->tr, AF_INET);
    ipv6_petrs_mc = get_proxy_etrs_for_afi(&xtr->tr, AF_INET6);;
    if (mcache_has_locators(ipv4_petrs_mc) == FALSE && mcache_has_locators(ipv6_petrs_mc) == FALSE) {
        OOR_LOG(LWRN, "No Proxy-ETR defined. Packets to non-LISP destinations "
                "will be forwarded natively (no LISP encapsulation). This "
                "may prevent mobility in some scenarios.");
        oor_timer_sleep(2);
    } else {
        xtr->tr.fwd_policy->updated_map_cache_inf(xtr->tr.fwd_policy_dev_parm,ipv4_petrs_mc);
        notify_datap_rm_fwd_from_entry(&(xtr->super),mcache_entry_eid(ipv4_petrs_mc),FALSE);
        xtr->tr.fwd_policy->updated_map_cache_inf(xtr->tr.fwd_policy_dev_parm,ipv6_petrs_mc);
        notify_datap_rm_fwd_from_entry(&(xtr->super),mcache_entry_eid(ipv6_petrs_mc),FALSE);
    }

    /* Check configured parameters when NAT-T activated. */
    if (xtr->nat_aware == TRUE) {
        if (glist_size(xtr->map_servers) > 1) {
            OOR_LOG(LERR, "NAT aware on -> This version of OOR is limited to one Map Server.");
            exit_cleanup();
        }

        if (glist_size(xtr->map_servers) == 1 &&
                lisp_addr_ip_afi(((map_server_elt *)glist_first_data(xtr->map_servers))->address) != AF_INET) {
            OOR_LOG(LERR, "NAT aware on -> This version of OOR is limited to IPv4 Map Server.");
            exit_cleanup();
        }

        if (glist_size(xtr->tr.map_resolvers) > 0) {
            OOR_LOG(LINF, "NAT aware on -> No Map Resolver will be used.");
            glist_remove_all(xtr->tr.map_resolvers);
        }
        if (xtr->tr.probe_interval > 0) {
            xtr->tr.probe_interval = 0;
            OOR_LOG(LINF, "NAT aware on -> disabling RLOC probing");
        }
        /* Set local locators to unreachable*/
        local_map_db_foreach_entry(xtr->local_mdb, it) {
            map_loc_e = (map_local_entry_t *)it;
            num_eids++;
            if (num_eids > 1){
                OOR_LOG(LERR, "NAT aware on -> Only one EID prefix supported.");
                exit_cleanup();
            }
            mapping_foreach_locator(map_local_entry_mapping(map_loc_e),loct){
                locator_set_R_bit(loct,0);
                /* We don't support LCAF in NAT */
                if (lisp_addr_lafi(locator_addr(loct)) == LM_AFI_LCAF){
                    OOR_LOG(LERR, "NAT aware on -> This version of OOR doesn't support LCAF when NAT is enabled.");
                    exit_cleanup();
                }
            }mapping_foreach_locator_end;
            OOR_LOG(LERR, "NAT aware on -> Removing PETRs");
            /* Remove PeTR. The locators will be used for RTRs */
            mapping_remove_locators(mcache_entry_mapping(ipv4_petrs_mc));
            mapping_remove_locators(mcache_entry_mapping(ipv6_petrs_mc));
        } local_map_db_foreach_end;
    }

    if (xtr->super.mode == MN_MODE){
        /* Check number of EID prefixes */

        if (local_map_db_num_ip_eids(xtr->local_mdb, AF_INET) > 1) {
            OOR_LOG(LERR, "OOR in mobile node mode only supports one IPv4 EID "
                    "prefix and one IPv6 EID prefix");
            exit_cleanup();
        }
        if (local_map_db_num_ip_eids(xtr->local_mdb, AF_INET6) > 1) {
            OOR_LOG(LERR, "OOR in mobile node mode only supports one IPv4 EID "
                    "prefix and one IPv6 EID prefix");
            exit_cleanup();
        }
    }

    OOR_LOG(LDBG_1, "\n");
    OOR_LOG(LDBG_1, "    ****** Summary of the xTR configuration ******\n");
    local_map_db_dump(xtr->local_mdb, LDBG_1);
    mcache_dump_db(xtr->tr.map_cache, LDBG_1);

    map_servers_dump(xtr, LDBG_1);
    OOR_LOG(LDBG_1, "************* %13s ***************", "Map Resolvers");
    glist_dump(xtr->tr.map_resolvers, (glist_to_char_fct)lisp_addr_to_char, LDBG_1);
    OOR_LOG(LDBG_1, "*******************************************\n");

    proxy_etrs_dump(xtr, LDBG_1);

    OOR_LOG(LDBG_1, "************* %13s ***************", "Proxy-ITRs");
    glist_dump(xtr->pitrs, (glist_to_char_fct)lisp_addr_to_char, LDBG_1);
    OOR_LOG(LDBG_1, "*******************************************\n");

    if (glist_size(xtr->tr.allowed_dst_eids) != 0){
        OOR_LOG(LDBG_1, "************* %13s ***************", "Allowed dst. EIDs");
        glist_dump(xtr->tr.allowed_dst_eids, (glist_to_char_fct)lisp_addr_to_char, LDBG_1);
        OOR_LOG(LDBG_1, "*******************************************\n");
    }

    local_map_db_foreach_entry(xtr->local_mdb, it) {
        /* Register EID prefix to control */
        map_loc_e = (map_local_entry_t *)it;

        if (ctrl_register_mapping_dp(&(xtr->super),map_local_entry_mapping(map_loc_e))!=GOOD){
            OOR_LOG(LERR, "Couldn't register the mapping %s in the data plane",
                    lisp_addr_to_char(map_local_entry_eid(map_loc_e)));
            exit_cleanup();
        }
        /* Update forwarding info of the local mappings. When it is created during conf file process,
         * the local rlocs are not set. For this reason should be calculated again. It can not be removed
         * from the conf file process -> In future could appear fwd_map_info parameters*/
        xtr->tr.fwd_policy->updated_map_loc_inf(xtr->tr.fwd_policy_dev_parm,map_loc_e);

    } local_map_db_foreach_end;


    if (xtr->nat_aware){
        xtr_program_initial_info_request_process(xtr);
    }else{
        /* SMR proxy-ITRs list to be updated with new mappings.
         * Initial Map register is sent during this process */
        xtr_program_smr(xtr, 1);
    }

    /* RLOC Probing proxy ETRs */
    tr_program_mce_rloc_probing(&xtr->tr, ipv4_petrs_mc);
    tr_program_mce_rloc_probing(&xtr->tr, ipv6_petrs_mc);
}


static int
xtr_recv_msg(oor_ctrl_dev_t *dev, lbuf_t *msg, uconn_t *uc)
{
    int ret = 0;
    lisp_msg_type_e type;
    lisp_xtr_t *xtr = lisp_xtr_cast(dev);
    void *ecm_hdr = NULL;
    uconn_t *int_uc, *ext_uc = NULL, aux_uc;

    type = lisp_msg_type(msg);

    if (type == LISP_ENCAP_CONTROL_TYPE) {
        if (xtr_recv_enc_ctrl_msg(xtr, msg, &ecm_hdr, &aux_uc)!=GOOD){
            return (BAD);
        }
        type = lisp_msg_type(msg);
        ext_uc = uc;
        int_uc = &aux_uc;
        OOR_LOG(LDBG_1, "xTR: Received Encapsulated %s", lisp_msg_hdr_to_char(msg));
    }else{
        int_uc = uc;
    }

    switch (type) {
    case LISP_MAP_REQUEST:
        ret = xtr_recv_map_request(xtr, msg, ecm_hdr, int_uc, ext_uc);
        break;
    case LISP_MAP_REPLY:
        ret = xtr_recv_map_reply(xtr, msg, int_uc);
        break;
    case LISP_MAP_REGISTER:
        break;
    case LISP_MAP_NOTIFY:
        ret = xtr_recv_map_notify(xtr, msg);
        break;
    case LISP_INFO_NAT:
        ret = xtr_recv_info_nat(xtr, msg, int_uc);
        break;
    default:
        OOR_LOG(LDBG_1, "xTR: Unidentified type (%d) control message received",
                type);
        ret = BAD;
        break;
    }

    if (ret != GOOD) {
        OOR_LOG(LDBG_1,"xTR: Failed to process LISP control message");
        return (BAD);
    } else {
        OOR_LOG(LDBG_3, "xTR: Completed processing of LISP control message");
        return (ret);
    }
}

static int
xtr_if_link_update(oor_ctrl_dev_t *dev, char *iface_name, uint8_t status)
{
    lisp_xtr_t * xtr = lisp_xtr_cast(dev);
    iface_locators * if_loct = NULL;
    locator_t * locator = NULL;
    map_local_entry_t * map_loc_e = NULL;
    glist_entry_t * it = NULL;
    glist_entry_t * it_m = NULL;

    if_loct = (iface_locators *)shash_lookup(xtr->tr.iface_locators_table,iface_name);
    if (if_loct  == NULL){
        OOR_LOG(LDBG_2, "xtr_if_status_change: Iface %s not found in the list of ifaces for xTR device",
                iface_name);
        return (BAD);
    }
    /* Change the status of the affected locators */
    glist_for_each_entry(it,if_loct->ipv4_locators){
        locator = (locator_t *)glist_entry_data(it);
        locator_set_state(locator,status);
    }
    glist_for_each_entry(it,if_loct->ipv6_locators){
        locator = (locator_t *)glist_entry_data(it);
        locator_set_state(locator,status);
    }
    /* Transition check */
    if (if_loct->status_changed == TRUE){
        if_loct->status_changed = FALSE;
    }else{
        if_loct->status_changed = TRUE;
    }
    /* Recalculate forwarding info of the affected mappings */
    glist_for_each_entry(it_m, if_loct->map_loc_entries){
        map_loc_e = (map_local_entry_t *)glist_entry_data(it_m);
        xtr->tr.fwd_policy->updated_map_loc_inf(xtr->tr.fwd_policy_dev_parm,map_loc_e);
        notify_datap_rm_fwd_from_entry(&(xtr->super),map_local_entry_eid(map_loc_e),TRUE);
    }

    xtr_iface_event_signaling(xtr, if_loct);

    return (GOOD);
}

int
xtr_if_addr_update(oor_ctrl_dev_t *dev, char *iface_name, lisp_addr_t *old_addr,
        lisp_addr_t *new_addr, uint8_t status)
{
    lisp_xtr_t * xtr = lisp_xtr_cast(dev);
    iface_locators * if_loct = NULL;
    glist_t * loct_list = NULL;
    glist_t * locators = NULL;
    locator_t * locator = NULL;
    map_local_entry_t * map_loc_e = NULL;
    mapping_t * mapping = NULL;
    int afi = AF_UNSPEC;
    glist_entry_t * it = NULL;
    glist_entry_t * it_aux = NULL;
    glist_entry_t * it_m = NULL;
    lisp_addr_t ** prev_addr = NULL;

    if_loct = (iface_locators *)shash_lookup(xtr->tr.iface_locators_table,iface_name);
    if (if_loct  == NULL){
        OOR_LOG(LDBG_2, "xtr_if_addr_update: Iface %s not found in the list of ifaces for xTR device",
                iface_name);
        return (BAD);
    }

    if (lisp_addr_cmp(old_addr, new_addr) == 0){
        return (GOOD);
    }

    /*Check if the address has been removed*/
    if (lisp_addr_is_no_addr(new_addr)){
        /* Process removed address */
        if (lisp_addr_lafi(old_addr) == AF_INET){
            locators = if_loct->ipv4_locators;
            prev_addr = &(if_loct->ipv4_prev_addr);
        }else{
            locators = if_loct->ipv6_locators;
            prev_addr = &(if_loct->ipv6_prev_addr);
        }
        glist_for_each_entry_safe(it,it_aux,locators){
            locator = (locator_t *)glist_entry_data(it);
            if (!lisp_addr_is_ip(locator_addr(locator))){
                OOR_LOG(LERR,"OOR doesn't support change of non IP locator!!!");
            }
            map_loc_e = get_map_loc_ent_containing_loct_ptr(xtr->local_mdb,locator);
            if(map_loc_e == NULL){
                continue;
            }
            mapping = map_local_entry_mapping(map_loc_e);
            mapping_desactivate_locator(mapping,locator);
            /* Recalculate forwarding info of the mappings with activated locators */
            xtr->tr.fwd_policy->updated_map_loc_inf(xtr->tr.fwd_policy_dev_parm,map_loc_e);
            notify_datap_rm_fwd_from_entry(&(xtr->super),map_local_entry_eid(map_loc_e),TRUE);
        }

        /* prev_addr is the previous address before starting the transition process */
        if (*prev_addr == NULL){
            *prev_addr = lisp_addr_clone(old_addr);
        }

        goto done;
    }
    /* Process new address */
    afi = lisp_addr_lafi(new_addr) == LM_AFI_IP ? lisp_addr_ip_afi(new_addr)  : AF_UNSPEC;
    switch(afi){
    case AF_INET:
        locators = if_loct->ipv4_locators;
        prev_addr = &(if_loct->ipv4_prev_addr);
        break;
    case AF_INET6:
        locators = if_loct->ipv6_locators;
        prev_addr = &(if_loct->ipv6_prev_addr);
        break;
    default:
        OOR_LOG(LDBG_2, "xtr_if_addr_update: AFI of the new address not known");
        return (BAD);
    }
    /* Update the address of the affected locators */
    glist_for_each_entry_safe(it,it_aux,locators){
        locator = (locator_t *)glist_entry_data(it);
        /* The locator was not active during init process */
        if (lisp_addr_is_no_addr(locator_addr(locator))==TRUE){

            /* If locator was not active, activate it */
            map_loc_e = get_map_loc_ent_containing_loct_ptr(xtr->local_mdb,locator);
            if(map_loc_e == NULL){
                continue;
            }
            /* Check if exists an active locator with the same address.
             * If it exists, remove not activated locator: Duplicated */
            mapping = map_local_entry_mapping(map_loc_e);
            if (mapping_get_loct_with_addr(mapping,new_addr) != NULL){
                OOR_LOG(LDBG_2, "xtr_if_addr_change: A non active locator is duplicated. Removing it");
                loct_list = mapping_get_loct_lst_with_afi(mapping,LM_AFI_NO_ADDR,0);
                iface_locators_unattach_locator(xtr->tr.iface_locators_table,locator);
                glist_remove_obj_with_ptr(locator,loct_list);
                continue;
            }
            /* Activate locator */
            mapping_activate_locator(mapping,locator,new_addr);
            /* Recalculate forwarding info of the mappings with activated locators */
            xtr->tr.fwd_policy->updated_map_loc_inf(xtr->tr.fwd_policy_dev_parm,map_loc_e);
            notify_datap_rm_fwd_from_entry(&(xtr->super),map_local_entry_eid(map_loc_e),TRUE);
        }else{
            if (!lisp_addr_is_ip(locator_addr(locator))){
                OOR_LOG(LERR,"OOR doesn't support change of non IP locator!!!");
            }
            locator_clone_addr(locator,new_addr);
        }

    }
    /* Transition check */
    /* prev_addr is the previous address before starting the transition process */
    if (*prev_addr != NULL){
        if (lisp_addr_cmp(*prev_addr, new_addr) == 0){
            lisp_addr_del(*prev_addr);
            *prev_addr = NULL;
        }
    }else{
        if (old_addr != NULL){
            *prev_addr = lisp_addr_clone(old_addr);
        }else{
            *prev_addr = lisp_addr_new_lafi(LM_AFI_NO_ADDR);
        }
    }
    /* Reorder locators */
    glist_for_each_entry(it_m, if_loct->map_loc_entries){
        map_loc_e = (map_local_entry_t *)glist_entry_data(it_m);
        mapping = map_local_entry_mapping(map_loc_e);
        mapping_sort_locators(mapping, new_addr);
    }

done:
    xtr_iface_event_signaling(xtr, if_loct);

    return (GOOD);
}

int
xtr_route_update(oor_ctrl_dev_t *dev, int command, char *iface_name ,lisp_addr_t *src_pref,
        lisp_addr_t *dst_pref, lisp_addr_t *gateway)
{
    lisp_xtr_t * xtr = lisp_xtr_cast(dev);
    iface_locators * if_loct = NULL;

    if_loct = (iface_locators *)shash_lookup(xtr->tr.iface_locators_table,iface_name);
    xtr_iface_event_signaling(xtr, if_loct);
    return (GOOD);
}

static fwd_info_t *
xtr_get_forwarding_entry(oor_ctrl_dev_t *dev, packet_tuple_t *tuple)
{
    lisp_xtr_t *xtr = lisp_xtr_cast(dev);
    fwd_info_t  *fwd_info;
    mcache_entry_t *mce = NULL, *mce_petrs = NULL;
    map_local_entry_t *map_loc_e = NULL;
    lisp_addr_t *eid, *simple_eid;
    lisp_addr_t *src_eid, *dst_eid;
    int iidmlen;
    uint8_t native_fwd = FALSE;

    fwd_info = fwd_info_new();
    if(fwd_info == NULL){
        OOR_LOG(LWRN, "tr_get_fwd_entry: Couldn't allocate memory for fwd_info_t");
        return (NULL);
    }

    /* lookup local mapping for source EID */
    map_loc_e = local_map_db_lookup_eid(xtr->local_mdb, &tuple->src_addr, FALSE);
    if (!map_loc_e){
        // In VPP we should continue with the pocess in order to crete a route to the gatway address
#ifndef VPP
        OOR_LOG(LDBG_3, "The source address %s is not a local EID. This should never happen", lisp_addr_to_char(&tuple->src_addr));
        return (NULL);
#else
        native_fwd = TRUE;
        fwd_info->neg_map_reply_act = ACT_NATIVE_FWD;
        eid = &tuple->src_addr;
        simple_eid = eid;
#endif
    }else{
        eid = map_local_entry_eid(map_loc_e);
        simple_eid = lisp_addr_get_ip_pref_addr(eid);
        if (lisp_addr_is_iid(eid)){
            tuple->iid = lcaf_iid_get_iid(lisp_addr_get_lcaf(eid));
        }else{
            tuple->iid = 0;
        }
    }


    if (tuple->iid > 0){
        iidmlen = 32;
        src_eid = lisp_addr_new_init_iid(tuple->iid, &tuple->src_addr, iidmlen);
        dst_eid = lisp_addr_new_init_iid(tuple->iid, &tuple->dst_addr, iidmlen);
    }else{
        src_eid = lisp_addr_clone(&tuple->src_addr);
        dst_eid = lisp_addr_clone(&tuple->dst_addr);
    }

    /* Get the mcache entry for destination EID */

    if (xtr->nat_aware){
        // Map cache entry of RTRs
        mce = mcache_get_all_space_entry(xtr->tr.map_cache, lisp_addr_ip_afi( &tuple->dst_addr));
    }else{
        mce = mcache_lookup(xtr->tr.map_cache, dst_eid);
    }
    if (!mce) {
        /* No map cache entry, initiate map cache miss process */
        OOR_LOG(LDBG_1, "No map cache for EID %s. Sending Map-Request!",
                lisp_addr_to_char(dst_eid));
        handle_map_cache_miss(&xtr->tr, dst_eid, src_eid);
        /* Get the temporal mce created */
        mce = mcache_lookup(xtr->tr.map_cache, dst_eid);
        fwd_info->associated_entry = lisp_addr_clone(mcache_entry_eid(mce));
    } else{
        fwd_info->associated_entry = lisp_addr_clone(mcache_entry_eid(mce));
        if (mcache_entry_active(mce) == NOT_ACTIVE) {
            OOR_LOG(LDBG_2, "Already sent Map-Request for %s. Waiting for reply!",
                    lisp_addr_to_char(dst_eid));
        }
    }

    mce_petrs = get_proxy_etrs_for_afi(&xtr->tr, lisp_addr_ip_afi(simple_eid));

    /* native_fwd can be TRUE for VPP if src packet is not an EID */
    if (!native_fwd){
        xtr->tr.fwd_policy->get_fwd_info(xtr->tr.fwd_policy_dev_parm,map_loc_e,mce,mce_petrs,tuple, fwd_info);
    }

    /* Assign encapsulated that should be used */
    fwd_info->encap = xtr->tr.encap_type;
    lisp_addr_del(src_eid);
    lisp_addr_del(dst_eid);
    return (fwd_info);
}

inline lisp_xtr_t *
lisp_xtr_cast(oor_ctrl_dev_t *dev)
{
    /* make sure */
    lm_assert(dev->ctrl_class == &xtr_ctrl_class);
    return(CONTAINER_OF(dev, lisp_xtr_t, super));
}

/*************************** PROCESS MESSAGES ********************************/
static int
xtr_recv_enc_ctrl_msg(lisp_xtr_t *xtr, lbuf_t *msg, void **ecm_hdr, uconn_t *int_uc)
{
    packet_tuple_t inner_tuple;

    *ecm_hdr = lisp_msg_pull_ecm_hdr(msg);

    if (ECM_SECURITY_BIT(*ecm_hdr)){
        switch (lisp_ecm_auth_type(msg)){
        default:
            OOR_LOG(LDBG_2, "Not supported ECM auth type %d",lisp_ecm_auth_type(msg));
            return (BAD);
        }
    }

    if (lisp_msg_parse_int_ip_udp(msg) != GOOD) {
        return (BAD);
    }
    pkt_parse_inner_5_tuple(msg, &inner_tuple);
    uconn_init(int_uc, inner_tuple.dst_port, inner_tuple.src_port, &inner_tuple.dst_addr,&inner_tuple.src_addr);
    *ecm_hdr = lbuf_lisp_hdr(msg);
    return (GOOD);
}
static int
xtr_recv_map_request(lisp_xtr_t *xtr, lbuf_t *buf, void *ecm_hdr, uconn_t *int_uc, uconn_t *ext_uc)
{
    lisp_addr_t *seid = NULL;
    lisp_addr_t *deid = NULL;
    lisp_addr_t *smr_src_eid, *smr_req_eid, *aux_eid;
    map_local_entry_t *map_loc_e = NULL;
    mapping_t *map = NULL;
    glist_t *itr_rlocs = NULL;
    void *mreq_hdr = NULL;
    void *mrep_hdr = NULL;
    int i = 0;
    lbuf_t *mrep = NULL;
    lbuf_t  b;
    uconn_t send_uc;

    /* local copy of the buf that can be modified */
    b = *buf;

    seid = lisp_addr_new();
    deid = lisp_addr_new();

    mreq_hdr = lisp_msg_pull_hdr(&b);

    if (lisp_msg_parse_addr(&b, seid) != GOOD) {
        goto err;
    }


    if (MREQ_RLOC_PROBE(mreq_hdr) && MREQ_REC_COUNT(mreq_hdr) > 1) {
        OOR_LOG(LDBG_1, "More than one EID record in RLOC probe. Discarding!");
        goto err;
    }

    if (MREQ_SMR(mreq_hdr) && MREQ_REC_COUNT(mreq_hdr) > 1) {
        OOR_LOG(LDBG_1, "More than one EID record in SMR request. Discarding!");
        goto err;
    }

    /* Process additional ITR RLOCs */
    itr_rlocs = laddr_list_new();
    lisp_msg_parse_itr_rlocs(&b, itr_rlocs);

    /* Process records and build Map-Reply */
    mrep = lisp_msg_create(LISP_MAP_REPLY);
    for (i = 0; i < MREQ_REC_COUNT(mreq_hdr); i++) {
        if (lisp_msg_parse_eid_rec(&b, deid) != GOOD) {
            goto err;
        }


        OOR_LOG(LDBG_1, " dst-eid: %s", lisp_addr_to_char(deid));

        /* Check the existence of the requested EID */
        map_loc_e = local_map_db_lookup_eid(xtr->local_mdb, deid, TRUE);
        if (!map_loc_e) {
            OOR_LOG(LDBG_1,"EID %s not locally configured!",
                    lisp_addr_to_char(deid));
            goto err;
        }
        map = map_local_entry_mapping(map_loc_e);
        lisp_msg_put_mapping(mrep, map, MREQ_RLOC_PROBE(mreq_hdr)
                ? &int_uc->la: NULL);

        /* If packet is a Solicit Map Request, process it */
        if (lisp_addr_lafi(seid) != LM_AFI_NO_ADDR && MREQ_SMR(mreq_hdr)) {
            /* The req EID of the received msg which is a prefix will be the src EID of the new msg. It is converted to IP */
            aux_eid = lisp_addr_get_ip_pref_addr(deid);
            lisp_addr_set_lafi(aux_eid,LM_AFI_IP); //aux_eid is part of deid-> we convert deid to IP
            smr_src_eid = deid;
            smr_req_eid = seid;
            if(tr_reply_to_smr(&xtr->tr,smr_src_eid,smr_req_eid) != GOOD) {
                goto err;
            }
            /* Return if RLOC probe bit is not set */
            if (!MREQ_RLOC_PROBE(mreq_hdr)) {
                goto done;
            };
        }
    }
    mrep_hdr = lisp_msg_hdr(mrep);
    MREP_RLOC_PROBE(mrep_hdr) = MREQ_RLOC_PROBE(mreq_hdr);
    MREP_NONCE(mrep_hdr) = MREQ_NONCE(mreq_hdr);

    /* SEND MAP-REPLY */
    if (map_reply_fill_uconn(&xtr->super, itr_rlocs, int_uc, ext_uc, &send_uc) != GOOD){
        OOR_LOG(LDBG_1, "Couldn't send Map-Reply, no itr_rlocs reachable");
        goto err;
    }
    OOR_LOG(LDBG_1, "Sending %s", lisp_msg_hdr_to_char(mrep));
    send_msg(&xtr->super, mrep, &send_uc);

done:
    glist_destroy(itr_rlocs);
    lisp_msg_destroy(mrep);
    lisp_addr_del(seid);
    lisp_addr_del(deid);
    return(GOOD);
err:
    glist_destroy(itr_rlocs);
    lisp_msg_destroy(mrep);
    lisp_addr_del(seid);
    lisp_addr_del(deid);
    return(BAD);
}


static inline int
xtr_recv_map_reply(lisp_xtr_t *xtr, lbuf_t *buf, uconn_t *uc)
{
    return (tr_recv_map_reply(&xtr->tr,buf,uc));
}

static int
xtr_recv_map_notify(lisp_xtr_t *xtr, lbuf_t *buf)
{
    lisp_addr_t *eid;
    map_local_entry_t *map_loc_e;
    mapping_t *m;
    void *hdr, *auth_hdr;
    locator_t *probed ;
    map_server_elt *ms;
    nonces_list_t *nonces_lst;
    oor_timer_t *timer;
    timer_map_reg_argument *timer_arg_mn;
    timer_encap_map_reg_argument *timer_arg_emn;
    int i, res = BAD;
    lbuf_t b;

    /* local copy */
    b = *buf;
    hdr = lisp_msg_pull_hdr(&b);

    /* Check NONCE */
    nonces_lst = htable_nonces_lookup(nonces_ht, MNTF_NONCE(hdr));
    if (!nonces_lst){
        OOR_LOG(LDBG_1, "No Map Register sent with nonce: %"PRIx64
                " Discarding message!", htonll(MNTF_NONCE(hdr)));
        return(BAD);
    }
    timer = nonces_list_timer(nonces_lst);

    if (MNTF_I_BIT(hdr)==1){
        OOR_LOG(LDBG_1,"Received Data Map-Notify");
        timer_arg_emn = (timer_encap_map_reg_argument *)oor_timer_cb_argument(timer);
        ms = timer_arg_emn->ms;
        if (MNTF_R_BIT(hdr)==1){
            /* We subtract the RTR authentication field. Is not used in the authentication
             * calculation of the map notify.*/
            // XXX Speculate that this field is removed by RTR so length is 0
            lbuf_set_size(buf, lbuf_size(buf) - sizeof(auth_record_hdr_t));
        }
    }else{
        timer_arg_mn = (timer_map_reg_argument *)oor_timer_cb_argument(timer);
        ms = timer_arg_mn->ms;
    }

    auth_hdr = lisp_msg_pull_auth_field(&b);
    res = lisp_msg_check_auth_field(buf,auth_hdr, ms->key);

    if (res != GOOD){
        OOR_LOG(LDBG_1, "Map-Notify message is invalid");
        return(BAD);
    }

    for (i = 0; i < MNTF_REC_COUNT(hdr); i++) {
        m = mapping_new();
        if (lisp_msg_parse_mapping_record(&b, m, &probed) != GOOD) {
            mapping_del(m);
            return(BAD);
        }

        eid = mapping_eid(m);
        map_loc_e = local_map_db_lookup_eid_exact(xtr->local_mdb, eid);
        if (!map_loc_e) {
            OOR_LOG(LDBG_1, "Map-Notify confirms registration of UNKNOWN EID %s."
                    " Dropping!", lisp_addr_to_char(eid));
            continue;
        }

        OOR_LOG(LDBG_1, "Map-Notify message confirms correct registration of %s",
                lisp_addr_to_char(eid));
        OOR_LOG(LDBG_1, "Scheduling next Map-Register in %d seconds",
                MAP_REGISTER_INTERVAL);

        mapping_del(m);
        htable_nonces_reset_nonces_lst(nonces_ht,nonces_lst);
        oor_timer_start(timer,MAP_REGISTER_INTERVAL);
    }

    return(GOOD);
}


static int
xtr_recv_info_nat(lisp_xtr_t *xtr, lbuf_t *buf, uconn_t *uc)
{
    lisp_addr_t *inf_reply_eid, *inf_req_eid, *nat_lcaf_addr, *rtr_addr;
    void *info_nat_hdr, *info_nat_hdr_2, *auth_hdr;
    lbuf_t  b;
    nonces_list_t *nonces_lst;
    timer_inf_req_argument *timer_arg;
    int len, ttl;
    glist_t *rtr_lst, *final_rtr_lst;
    glist_entry_t *rtr_it;
    map_local_entry_t *mle;
    mapping_t *map;
    uint8_t smr_required = FALSE;

    /* local copy of the buf that can be modified */
    b = *buf;
    info_nat_hdr = lisp_msg_pull_hdr(&b);

    if (INF_REQ_R_bit(info_nat_hdr) == INFO_REQUEST){
        OOR_LOG(LDBG_1, "xTR received an Info-Request. Discarding message");
        return(BAD);
    }

    /* Check NONCE */
    nonces_lst = htable_nonces_lookup(nonces_ht, INF_REQ_NONCE(info_nat_hdr));
    if (!nonces_lst){
        OOR_LOG(LDBG_2, " Nonce %"PRIx64" doesn't match any Info-Request nonce. "
                "Discarding message!", htonll(INF_REQ_NONCE(info_nat_hdr)));
        return(BAD);
    }

    timer_arg = oor_timer_cb_argument(nonces_list_timer(nonces_lst));
    mle = timer_arg->mle;

    auth_hdr = lisp_msg_pull_auth_field(&b);

    info_nat_hdr_2 = lbuf_pull(&b, sizeof(info_nat_hdr_2_t));

    /* Get EID prefix for the info reply and compare with the one of the info request*/
    inf_reply_eid = lisp_addr_new();
    len = lisp_addr_parse(lbuf_data(&b), inf_reply_eid);
    if (len <= 0) {
        lisp_addr_del(inf_reply_eid);
        return(BAD);
    }
    lbuf_pull(&b, len);
    lisp_addr_set_plen(inf_reply_eid, INF_REQ_2_EID_MASK(info_nat_hdr_2));

    inf_req_eid = map_local_entry_eid(mle);

    if (lisp_addr_cmp(inf_reply_eid,inf_req_eid)!=0){
        OOR_LOG(LDBG_2, "EID from Info-Request and Info-Reply are different (%s - %s)",
                lisp_addr_to_char(inf_req_eid),lisp_addr_to_char(inf_reply_eid));
        lisp_addr_del(inf_reply_eid);
        return (BAD);
    }
    lisp_addr_del(inf_reply_eid);

    /* We obtain the key to use in the authentication process from the argument of the timer */
    if (lisp_msg_check_auth_field(buf,auth_hdr,timer_arg->ms->key) != GOOD) {
        OOR_LOG(LDBG_1, "Info-Reply message validation failed for EID %s with key "
                "%s. Stopping processing!", lisp_addr_to_char(inf_req_eid),
                timer_arg->ms->key);
        return (BAD);
    }

    nat_lcaf_addr = lisp_addr_new();
    len = lisp_addr_parse(lbuf_data(&b), nat_lcaf_addr);
    if (len <= 0) {
        lisp_addr_del(nat_lcaf_addr);
        OOR_LOG(LDBG_2, "tr_recv_info_nat: Can not parse NAT LCAF address");
        return(BAD);
    }

    rtr_lst = nat_type_get_rtr_addr_lst(lcaf_addr_get_nat(lisp_addr_get_lcaf(nat_lcaf_addr)));

    /* Select the RTR list to use */
    final_rtr_lst = nat_select_rtrs(rtr_lst);
    if (glist_size(final_rtr_lst) == 0){
        OOR_LOG(LDBG_1, "Info-Reply Message doesn't have any compatible RTR");
        glist_destroy(final_rtr_lst);
        lisp_addr_del(nat_lcaf_addr);
        return (BAD);
    }

    /* Check if selected RTR list has changed */
    map = map_local_entry_mapping(mle);
    glist_for_each_entry(rtr_it,final_rtr_lst){
        rtr_addr = (lisp_addr_t *)glist_entry_data(rtr_it);
        if (!mapping_get_loct_with_addr(map,rtr_addr)){
            smr_required = TRUE;
            break;
        }
    }

    if (xtr_update_nat_info(xtr,mle,timer_arg->loct,final_rtr_lst) == GOOD){
        /* Configure Encap Map Register */
        xtr_program_encap_map_reg_of_loct_for_map(xtr, mle,timer_arg->loct);
        /* Reprogram time for next Info Request interval */
        htable_nonces_reset_nonces_lst(nonces_ht,nonces_lst);
        ttl = ntohl(INF_REQ_2_TTL(info_nat_hdr_2));
        oor_timer_start(nonces_lst->timer, ttl*60);
        OOR_LOG(LDBG_1,"Info-Request of %s to %s from locator %s scheduled in %d minutes.",
                lisp_addr_to_char(map_local_entry_eid(mle)), lisp_addr_to_char(timer_arg->ms->address),
                lisp_addr_to_char(locator_addr(timer_arg->loct)), ttl);
    }

    /* SMR proxy-ITRs list to be updated with new mappings */
    if (smr_required){
        OOR_LOG(LDBG_1,"Selected RTR list has changed. Programming SMR");
        xtr_program_smr(xtr, 1);
    }

    lisp_addr_del(nat_lcaf_addr);
    glist_destroy(final_rtr_lst);
    return (GOOD);
}

/* build and send generic map-register with one record
 * for each map server */
static int
xtr_build_and_send_map_reg(lisp_xtr_t * xtr, mapping_t * m, map_server_elt *ms,
        uint64_t nonce)
{
    lbuf_t * b = NULL;
    void * hdr, *auth_hdr;
    lisp_addr_t * drloc = NULL;
    uconn_t uc;

    b = lisp_msg_mreg_create(m, ms->key_type);

    if (!b) {
        return(BAD);
    }

    hdr = lisp_msg_hdr(b);

    MREG_PROXY_REPLY(hdr) = ms->proxy_reply;
    MREG_NONCE(hdr) = nonce;

    auth_hdr = hdr + sizeof(info_nat_hdr_t);
    if (lisp_msg_fill_auth_data(b,auth_hdr,ms->key_type,
            ms->key) != GOOD) {
        return(BAD);
    }
    drloc =  ms->address;
    OOR_LOG(LDBG_1, "%s, EID: %s, MS: %s", lisp_msg_hdr_to_char(b),
            lisp_addr_to_char(mapping_eid(m)), lisp_addr_to_char(drloc));

    uconn_init(&uc, LISP_CONTROL_PORT, LISP_CONTROL_PORT, NULL, drloc);
    send_msg(&xtr->super, b, &uc);

    lisp_msg_destroy(b);

    return(GOOD);
}

static int
xtr_build_and_send_encap_map_reg(lisp_xtr_t * xtr, mapping_t * m, map_server_elt *ms,
        lisp_addr_t *etr_addr, lisp_addr_t *rtr_addr, uint64_t nonce)
{
    lbuf_t * b;
    void * hdr, *auth_hdr;
    uconn_t uc;

    b = lisp_msg_mreg_create(m, ms->key_type);
    lisp_msg_put_xtr_id_site_id(b, &xtr->xtr_id, &xtr->site_id);
    hdr = lisp_msg_hdr(b);

    MREG_NONCE(hdr) = nonce;
    MREG_PROXY_REPLY(hdr) = 1;
    MREG_IBIT(hdr) = 1;
    MREG_RBIT(hdr) = 1;

    if (lisp_addr_ip_afi(ms->address) != lisp_addr_ip_afi(etr_addr)){
        OOR_LOG(LDBG_1, "build_and_send_ecm_map_reg: Map Server AFI not compatible with selected"
                " local RLOC (%s)", lisp_addr_to_char(etr_addr));
        lisp_msg_destroy(b);
        return (BAD);
    }

    auth_hdr = hdr + sizeof(map_register_hdr_t);
    if (lisp_msg_fill_auth_data(b, auth_hdr, ms->key_type, ms->key) != GOOD) {
        OOR_LOG(LDBG_2, "build_and_send_ecm_map_reg: Error filling the authentication data");
        return(BAD);
    }

    lisp_msg_encap(b, LISP_CONTROL_PORT, LISP_CONTROL_PORT, etr_addr,ms->address);
    hdr = lisp_msg_ecm_hdr(b);

    /* TODO To use when implementing draft version 4 or higher */
    ECM_RTR_PROCESS_BIT(hdr) = 1;

    OOR_LOG(LDBG_1, "%s, Inner IP: %s -> %s, EID: %s, RTR: %s",
             lisp_msg_hdr_to_char(b), lisp_addr_to_char(etr_addr),
             lisp_addr_to_char(ms->address), lisp_addr_to_char(mapping_eid(m)),
             lisp_addr_to_char(rtr_addr));



    uconn_init(&uc, xtr->tr.encap_port, LISP_CONTROL_PORT, etr_addr, rtr_addr);
    send_msg(&xtr->super, b, &uc);

    lisp_msg_destroy(b);
    return(GOOD);
}

static int
xtr_build_and_send_smr_mreq(lisp_xtr_t *xtr, mapping_t *smap,
        lisp_addr_t *deid, lisp_addr_t *drloc)
{
    uconn_t uc;
    lbuf_t * b = NULL;
    lisp_addr_t *seid = NULL;
    lisp_addr_t *srloc = NULL;
    void *hdr = NULL;
    glist_t *itr_rlocs = NULL;
    int res = GOOD;

    seid = mapping_eid(smap);
    itr_rlocs = ctrl_default_rlocs(ctrl_dev_get_ctrl_t(&xtr->super));

    /* build Map-Request */
    b = lisp_msg_mreq_create(seid, itr_rlocs, deid);

    if (b == NULL){
        lisp_msg_destroy(b);
        return (BAD);
    }

    hdr = lisp_msg_hdr(b);
    MREQ_SMR(hdr) = 1;
    OOR_LOG(LDBG_1, "%s, itr-rlocs: %s, src-eid: %s, req-eid: %s ", lisp_msg_hdr_to_char(b),
            laddr_list_to_char(itr_rlocs), lisp_addr_to_char(seid), lisp_addr_to_char(deid));
    glist_destroy(itr_rlocs);

    srloc = ctrl_default_rloc(xtr->super.ctrl, lisp_addr_ip_afi(drloc));
    if (!srloc) {
        OOR_LOG(LDBG_2, "No compatible RLOC was found to send SMR Map-Request "
                "for local EID %s", lisp_addr_to_char(seid));
        lisp_msg_destroy(b);
        return(BAD);
    }

    uconn_init(&uc, LISP_CONTROL_PORT, LISP_CONTROL_PORT, srloc, drloc);
    res = send_msg(&xtr->super, b, &uc);
    lisp_msg_destroy(b);

    return(res);
}

/* build and send generic map-register with one record
 * for each map server */
static int
xtr_build_and_send_info_req(lisp_xtr_t * xtr, mapping_t * m, locator_t *loct,
        map_server_elt *ms, uint64_t nonce)
{
    lbuf_t * b = NULL;
    void *hdr, *auth_hdr;
    lisp_addr_t *srloc, *drloc;
    uconn_t uc;

    b = lisp_msg_inf_req_create(m, ms->key_type);
    if (!b) {
        return(BAD);
    }
    hdr = lisp_msg_hdr(b);
    INF_REQ_NONCE(hdr) = nonce;

    auth_hdr = hdr + sizeof(info_nat_hdr_t);
    if (lisp_msg_fill_auth_data(b, auth_hdr, ms->key_type, ms->key) != GOOD) {
        return(BAD);
    }
    srloc = locator_addr(loct);
    drloc =  ms->address;
    OOR_LOG(LDBG_1, "%s, EID: %s, MS: %s", lisp_msg_hdr_to_char(b),
            lisp_addr_to_char(mapping_eid(m)), lisp_addr_to_char(drloc));

    uconn_init(&uc, LISP_CONTROL_PORT, LISP_CONTROL_PORT, srloc, drloc);
    send_msg(&xtr->super, b, &uc);

    lisp_msg_destroy(b);

    return(GOOD);
}

/**************************** LOGICAL PROCESSES ******************************/
/****************************** Map Register *********************************/

int
xtr_program_map_register(lisp_xtr_t *xtr)
{
    void *map_local_entry_it;
    map_local_entry_t *mle;
    oor_timer_t *timer;
    timer_map_reg_argument *timer_arg;
    map_server_elt *ms;
    glist_entry_t *ms_it;

    if (glist_size(xtr->map_servers) == 0){
        return (BAD);
    }

    local_map_db_foreach_entry(xtr->local_mdb, map_local_entry_it) {
        mle = (map_local_entry_t *)map_local_entry_it;
        /* Cancel timers associated to the map register of the local map entry */
        stop_timers_of_type_from_obj(mle,MAP_REGISTER_TIMER,ptrs_to_timers_ht, nonces_ht);
        /* Configure map register for each map server */
        glist_for_each_entry(ms_it,xtr->map_servers){
            ms = (map_server_elt *)glist_entry_data(ms_it);
            timer_arg = timer_map_reg_argument_new_init(mle,ms);
            timer = oor_timer_with_nonce_new(MAP_REGISTER_TIMER, xtr, xtr_map_register_cb,
                    timer_arg,(oor_timer_del_cb_arg_fn)timer_map_reg_arg_free);
            htable_ptrs_timers_add(ptrs_to_timers_ht, mle, timer);
            xtr_map_register_cb(timer);
        }
    } local_map_db_foreach_end;

    return(GOOD);
}

int
xtr_program_map_register_for_mapping(lisp_xtr_t *xtr, map_local_entry_t *mle)
{
    oor_timer_t *timer;
    timer_map_reg_argument *timer_arg;
    map_server_elt *ms;
    glist_entry_t *ms_it;

    if (glist_size(xtr->map_servers) == 0){
        return (BAD);
    }

    /* Cancel timers associated to the map register of the local map entry */
    stop_timers_of_type_from_obj(mle,MAP_REGISTER_TIMER,ptrs_to_timers_ht, nonces_ht);
    /* Configure map register for each map server */
    glist_for_each_entry(ms_it,xtr->map_servers){
        ms = (map_server_elt *)glist_entry_data(ms_it);
        timer_arg = timer_map_reg_argument_new_init(mle,ms);
        timer = oor_timer_with_nonce_new(MAP_REGISTER_TIMER, xtr, xtr_map_register_cb,
                timer_arg,(oor_timer_del_cb_arg_fn)timer_map_reg_arg_free);
        htable_ptrs_timers_add(ptrs_to_timers_ht, mle, timer);
        xtr_map_register_cb(timer);
    }

    return(GOOD);
}

static int
xtr_map_register_cb(oor_timer_t *timer)
{
    timer_map_reg_argument *timer_arg = oor_timer_cb_argument(timer);
    nonces_list_t *nonces_lst = oor_timer_nonces(timer);
    lisp_xtr_t *xtr = oor_timer_owner(timer);
    mapping_t *map = map_local_entry_mapping(timer_arg->mle);
    map_server_elt *ms = timer_arg->ms;
    uint64_t nonce;

    if ((nonces_list_size(nonces_lst) -1) < 10000){// xtr->probe_retries){
        nonce = nonce_new();
        if (xtr_build_and_send_map_reg(xtr, map, ms, nonce) != GOOD){
            return (BAD);
        }
        if (nonces_list_size(nonces_lst) > 0) {
            OOR_LOG(LDBG_1,"Sent Map-Register retry for mapping %s to %s "
                    "(%d retries)", lisp_addr_to_char(mapping_eid(map)),
                    lisp_addr_to_char(ms->address), nonces_list_size(nonces_lst));
        } else {
            OOR_LOG(LDBG_1,"Sent Map-Register for mapping %s to %s "
                    , lisp_addr_to_char(mapping_eid(map)),
                    lisp_addr_to_char(ms->address));
        }
        htable_nonces_insert(nonces_ht, nonce,nonces_lst);
        oor_timer_start(timer, OOR_INITIAL_MREG_TIMEOUT);
        return (GOOD);
    }else{
        /* If we have reached maximum number of retransmissions, change remote
         *  locator status */

        /* Reprogram time for next Map Register interval */
        htable_nonces_reset_nonces_lst(nonces_ht,nonces_lst);
        oor_timer_start(timer, MAP_REGISTER_INTERVAL);
        OOR_LOG(LWRN,"Map-Register of %s to %s dit not receive reply. Retrying in %d seconds",
                lisp_addr_to_char(mapping_eid(map)), lisp_addr_to_char(ms->address),
                MAP_REGISTER_INTERVAL);

        return (BAD);
    }
}

/****************************** Encap Map Register ***************************/

static int
xtr_encap_map_register_cb(oor_timer_t *timer)
{
    timer_encap_map_reg_argument *timer_arg = oor_timer_cb_argument(timer);
    nonces_list_t *nonces_lst = oor_timer_nonces(timer);
    lisp_xtr_t *xtr = oor_timer_owner(timer);
    mapping_t *map = map_local_entry_mapping(timer_arg->mle);
    map_server_elt *ms = timer_arg->ms;
    lisp_addr_t *etr_addr = locator_addr(timer_arg->src_loct);
    lisp_addr_t *rtr_addr = timer_arg->rtr_rloc;
    uint64_t nonce;

    if ((nonces_list_size(nonces_lst) -1) < xtr->tr.probe_retries){
        nonce = nonce_new();
        if (xtr_build_and_send_encap_map_reg(xtr, map, ms, etr_addr, rtr_addr, nonce) != GOOD){
            return (BAD);
        }
        if (nonces_list_size(nonces_lst) > 0) {
            OOR_LOG(LDBG_1,"Sent Encap Map-Register retry for mapping %s to MS %s from RLOC %s through RTR %s"
                    "(%d retries)", lisp_addr_to_char(mapping_eid(map)), lisp_addr_to_char(ms->address),
                    lisp_addr_to_char(etr_addr),lisp_addr_to_char(rtr_addr),nonces_list_size(nonces_lst));
        } else {
            OOR_LOG(LDBG_1,"Sent Encap Map-Register for mapping %s to MS %s from RLOC %s through RTR %s"
                    , lisp_addr_to_char(mapping_eid(map)),lisp_addr_to_char(ms->address),
                    lisp_addr_to_char(etr_addr),lisp_addr_to_char(rtr_addr));
        }
        htable_nonces_insert(nonces_ht, nonce,nonces_lst);
        oor_timer_start(timer, OOR_INITIAL_MREG_TIMEOUT);
        return (GOOD);
    }else{
        /* If we have reached maximum number of retransmissions, change remote
         *  locator status */

        /* Reprogram time for next Map Register interval */
        htable_nonces_reset_nonces_lst(nonces_ht,nonces_lst);
        oor_timer_start(timer, MAP_REGISTER_INTERVAL);
        OOR_LOG(LDBG_1,"Encap Map-Register for mapping %s to MS %s from RLOC %s through RTR %s did not receive reply."
                " Retry in %d seconds", lisp_addr_to_char(mapping_eid(map)),lisp_addr_to_char(ms->address),
                lisp_addr_to_char(etr_addr),lisp_addr_to_char(rtr_addr), MAP_REGISTER_INTERVAL);

        return (BAD);
    }
}


int
xtr_program_encap_map_reg_of_loct_for_map(lisp_xtr_t *xtr, map_local_entry_t *mle,
        locator_t *src_loct)
{
    oor_timer_t *timer;
    timer_encap_map_reg_argument *timer_arg;
    map_server_elt *ms;
    glist_t *timers_lst, *rtr_addr_lst;
    glist_entry_t *ms_it, *timers_it, *rtr_it;
    lisp_addr_t *rtr_addr;

    if (glist_size(xtr->map_servers) == 0){
        return (BAD);
    }
    /*
     * We configure the timers using the map_local_entry_t pointer instead of locator
     * as we want to isolate locatars form timers
     */

    /* Cancel timers associated to encap map register associated to the locator */
    timers_lst = htable_ptrs_timers_get_timers_of_type_from_obj(ptrs_to_timers_ht, mle, ENCAP_MAP_REGISTER_TIMER);

    glist_for_each_entry(timers_it,timers_lst){
        timer = (oor_timer_t *)glist_entry_data(timers_it);
        timer_arg = oor_timer_cb_argument(timer);
        if(src_loct == timer_arg->src_loct){
            stop_timer_from_obj(mle,timer,ptrs_to_timers_ht, nonces_ht);
            // Continue processing as it could be more than one map server, RTR
        }
    }
    glist_destroy(timers_lst);
    /* Configure encap map register for each RTR associated with the locator and MS*/
    rtr_addr_lst = mle_rtr_addr_list_of_loct(mle, locator_addr(src_loct));
    glist_for_each_entry(rtr_it,rtr_addr_lst){
        rtr_addr = (lisp_addr_t *)glist_entry_data(rtr_it);
        glist_for_each_entry(ms_it,xtr->map_servers){
            ms = (map_server_elt *)glist_entry_data(ms_it);
            timer_arg = timer_encap_map_reg_argument_new_init(mle,ms,src_loct,rtr_addr);
            timer = oor_timer_with_nonce_new(ENCAP_MAP_REGISTER_TIMER, xtr, xtr_encap_map_register_cb,
                    timer_arg,(oor_timer_del_cb_arg_fn)timer_encap_map_reg_arg_free);
            htable_ptrs_timers_add(ptrs_to_timers_ht, mle, timer);
            xtr_encap_map_register_cb(timer);
        }
    }
    return(GOOD);
}

/*********************************** SMR *************************************/

/* Send a solicit map request for each rloc of all eids in the map cache
 * database */
static void
xtr_smr_process_start(lisp_xtr_t *xtr)
{
    map_local_entry_t * map_loc_e = NULL;
    glist_t * map_loc_e_list = NULL; //<map_local_entry_t *>
    glist_entry_t * it = NULL;

    OOR_LOG(LDBG_1,"\n**** Re-Register and send SMRs for mappings with updated "
            "RLOCs ****");

    /* Get a list of mappings that require smrs */
    map_loc_e_list = xtr_get_map_local_entry_to_smr(xtr);

    /* Send map register and SMR request for each mapping */
    //glist_dump(map_loc_e_list,(glist_to_char_fct)map_local_entry_to_char,LDBG_1);

    glist_for_each_entry(it, map_loc_e_list) {
        map_loc_e = (map_local_entry_t *)glist_entry_data(it);
        xtr_smr_start_for_locl_mapping(xtr, map_loc_e);
    }

    glist_destroy(map_loc_e_list);
    OOR_LOG(LDBG_2,"*** Finished sending notifications ***\n");
}

void
xtr_smr_start_for_locl_mapping(lisp_xtr_t *xtr, map_local_entry_t *map_loc_e)
{
    mcache_entry_t * mce;
    mapping_t * mcache_map;
    mapping_t * map;
    glist_entry_t * it_pitr;
    lisp_addr_t * pitr_addr;
    lisp_addr_t * eid;

    assert(map_loc_e);

    map = map_local_entry_mapping(map_loc_e);
    eid = mapping_eid(map);

    if (!xtr->nat_aware){
        xtr_program_map_register_for_mapping(xtr, map_loc_e);
    }

    OOR_LOG(LDBG_1, "Start SMR for local EID %s", lisp_addr_to_char(eid));

    /* TODO: spec says SMRs should be sent only to peer ITRs that sent us
     * traffic in the last minute. Should change this in the future*/
    /* XXX: works ONLY with IP */
    mcache_foreach_active_entry_in_ip_eid_db(xtr->tr.map_cache, eid, mce) {
        mcache_map = mcache_entry_mapping(mce);
        xtr_smr_notify_mcache_entry(xtr, map, mcache_map);
    } mcache_foreach_active_entry_in_ip_eid_db_end;

    /* SMR proxy-itr */
    OOR_LOG(LDBG_1, "Sending SMRs to PITRs");
    glist_for_each_entry(it_pitr, xtr->pitrs){
        pitr_addr = (lisp_addr_t *)glist_entry_data(it_pitr);
        xtr_build_and_send_smr_mreq(xtr, map, eid, pitr_addr);
    }

}

/* solicit SMRs for 'src_map' to all locators of 'dst_map'*/
static int
xtr_smr_notify_mcache_entry(lisp_xtr_t  *xtr, mapping_t *src_map,
        mapping_t *dst_map)
{
    lisp_addr_t *deid = NULL, *drloc = NULL;
    locator_t *loct = NULL;

    deid = mapping_eid(dst_map);

    mapping_foreach_active_locator(dst_map, loct){
        if (loct->state == UP){
            drloc = locator_addr(loct);
            xtr_build_and_send_smr_mreq(xtr, src_map, deid, drloc);
        }
    }mapping_foreach_active_locator_end;

    return(GOOD);
}

static int
xtr_smr_process_start_cb(oor_timer_t *timer)
{
    xtr_smr_process_start((lisp_xtr_t *)oor_timer_cb_argument(timer));
    return(GOOD);
}

static int
xtr_program_smr(lisp_xtr_t *xtr, int time)
{

    OOR_LOG(LDBG_1,"Rescheduling SMR in %d seconds",time);

    if (!xtr->smr_timer) {
        xtr->smr_timer = oor_timer_without_nonce_new(SMR_TIMER, xtr, xtr_smr_process_start_cb, xtr, NULL);
    }

    oor_timer_start(xtr->smr_timer, time);
    return(GOOD);
}


/*
 * Return the list of mappings that has experimented changes in their
 * locators. At the same time iface_locators status is reseted
 * @param xtr
 * @return glist_t with the list of modified mappings (mapping_t *)
 */
static glist_t *
xtr_get_map_local_entry_to_smr(lisp_xtr_t *xtr)
{
    glist_t * map_loc_e_to_smr = glist_new();//<map_local_entry_t>
    glist_t * iface_locators_list = NULL;
    iface_locators * if_loct = NULL;
    glist_entry_t * it = NULL;
    glist_entry_t * it_loc = NULL;
    glist_t * locators[2] = {NULL,NULL};
    map_local_entry_t * map_loc_e = NULL;
    locator_t * locator = NULL;
    int ctr;

    iface_locators_list = shash_values(xtr->tr.iface_locators_table);

    glist_for_each_entry(it,iface_locators_list){
        if_loct = (iface_locators *)glist_entry_data(it);
        /* Select affected locators */
        if (if_loct->status_changed == TRUE){
            locators[0] = if_loct->ipv4_locators;
            locators[1] = if_loct->ipv6_locators;
        }else{
            if(if_loct->ipv4_prev_addr != NULL){
                locators[0] = if_loct->ipv4_locators;
            }
            if(if_loct->ipv6_prev_addr != NULL){
                locators[1] = if_loct->ipv6_locators;
            }
        }
        /* Reset iface_locators status */
        if_loct->status_changed = FALSE;
        lisp_addr_del(if_loct->ipv4_prev_addr);
        lisp_addr_del(if_loct->ipv6_prev_addr);
        if_loct->ipv4_prev_addr = NULL;
        if_loct->ipv6_prev_addr = NULL;
        /* Select not repeated mappings*/
        for (ctr=0 ; ctr<2 ; ctr++){
            if (locators[ctr] != NULL){
                glist_for_each_entry(it_loc,locators[ctr]){
                    locator = (locator_t *)glist_entry_data(it_loc);
                    map_loc_e = get_map_loc_ent_containing_loct_ptr(xtr->local_mdb, locator);
                    if (map_loc_e != NULL && glist_contain(map_loc_e, map_loc_e_to_smr) == FALSE){
                        glist_add(map_loc_e, map_loc_e_to_smr);
                    }
                }
            }
        }
    }
    glist_destroy(iface_locators_list);
    return (map_loc_e_to_smr);
}

/****************************** Info Request *********************************/

static int
xtr_program_initial_info_request_process(lisp_xtr_t *xtr)
{
    void *map_local_entry_it;
    oor_timer_t *timer;
    timer_inf_req_argument *timer_arg;
    map_local_entry_t *mle;
    mapping_t *map;
    locator_t *loct;
    map_server_elt *ms;
    glist_entry_t *ms_it;

    if (glist_size(xtr->map_servers) == 0){
        return (BAD);
    }

    local_map_db_foreach_entry(xtr->local_mdb, map_local_entry_it) {
        mle = (map_local_entry_t *)map_local_entry_it;
        map = map_local_entry_mapping(mle);
        /* Cancel timers associated to the info request process of the local map entry */
        stop_timers_of_type_from_obj(mle,INFO_REQUEST_TIMER,ptrs_to_timers_ht, nonces_ht);
        mapping_foreach_active_locator(map,loct){
            // Only send info request for local locators
            if (!locator_L_bit(loct)){
                continue;
            }
            glist_for_each_entry(ms_it,xtr->map_servers){
                ms = (map_server_elt *)glist_entry_data(ms_it);
                timer_arg = timer_inf_req_argument_new_init(mle,loct,ms);
                map_local_entry_dump(mle,LINF);
                timer = oor_timer_with_nonce_new(INFO_REQUEST_TIMER, xtr, xtr_info_request_cb,
                        timer_arg,(oor_timer_del_cb_arg_fn)timer_inf_req_arg_free);
                htable_ptrs_timers_add(ptrs_to_timers_ht, mle, timer);
                xtr_info_request_cb(timer);
            }
        }mapping_foreach_active_locator_end;
    } local_map_db_foreach_end;

    return(GOOD);
}

static int
xtr_program_info_req_per_loct(lisp_xtr_t *xtr, map_local_entry_t *mle, locator_t *loct)
{
    oor_timer_t *timer;
    timer_inf_req_argument *timer_arg;
    map_server_elt *ms;
    glist_entry_t *ms_it;

    if (glist_size(xtr->map_servers) == 0){
        return (BAD);
    }
    /* Program info request for each Map Server */
    glist_for_each_entry(ms_it,xtr->map_servers){
        ms = (map_server_elt *)glist_entry_data(ms_it);
        timer_arg = timer_inf_req_argument_new_init(mle,loct,ms);
        timer = oor_timer_with_nonce_new(INFO_REQUEST_TIMER, xtr, xtr_info_request_cb,
                timer_arg,(oor_timer_del_cb_arg_fn)timer_inf_req_arg_free);
        htable_ptrs_timers_add(ptrs_to_timers_ht, mle, timer);
        oor_timer_start(timer, OOR_INF_REQ_HANDOVER_TIMEOUT);
    }

    return(GOOD);
}

static int
xtr_info_request_cb(oor_timer_t *timer)
{
    timer_inf_req_argument *timer_arg = oor_timer_cb_argument(timer);
    nonces_list_t *nonces_lst = oor_timer_nonces(timer);
    lisp_xtr_t *xtr = oor_timer_owner(timer);
    mapping_t *map = map_local_entry_mapping(timer_arg->mle);
    locator_t *loct = timer_arg->loct;
    map_server_elt *ms = timer_arg->ms;
    uint64_t nonce;

    if ((nonces_list_size(nonces_lst) -1) < xtr->tr.map_request_retries){
        nonce = nonce_new();
        if (nonces_list_size(nonces_lst) > 0) {
            OOR_LOG(LDBG_1,"Sent Info-Request retry for mapping %s to %s from locator %s"
                    "(%d retries)", lisp_addr_to_char(mapping_eid(map)),
                    lisp_addr_to_char(ms->address), lisp_addr_to_char(locator_addr(loct)),
                    nonces_list_size(nonces_lst));
        } else {
            timer_encap_map_reg_stop_using_locator(timer_arg->mle, loct);
            OOR_LOG(LDBG_1,"Sent Info-Request for mapping %s to %s from locator %s",
                    lisp_addr_to_char(mapping_eid(map)),lisp_addr_to_char(ms->address),
                    lisp_addr_to_char(locator_addr(loct)));
        }

        if (xtr_build_and_send_info_req(xtr, map, loct, ms, nonce) != GOOD){
            return (BAD);
        }

        htable_nonces_insert(nonces_ht, nonce,nonces_lst);
        oor_timer_start(timer, OOR_INITIAL_INF_REQ_TIMEOUT);
        return (GOOD);
    }else{
        /* We reached maximum number of retransmissions */

        /* Reprogram time for next Info Request interval */
        htable_nonces_reset_nonces_lst(nonces_ht,nonces_lst);
        oor_timer_start(timer, OOR_SLEEP_INF_REQ_TIMEOUT);
        OOR_LOG(LWRN,"Info-Request of %s to %s from locator %s did not receive reply. Retrying in %d seconds",
                lisp_addr_to_char(mapping_eid(map)), lisp_addr_to_char(ms->address),
                lisp_addr_to_char(locator_addr(loct)), OOR_SLEEP_INF_REQ_TIMEOUT);

        return (BAD);
    }
}

/**************************** AUXILIAR FUNCTIONS *****************************/

/* Configure SMR or info request depending on NAT traversal */
static int
xtr_iface_event_signaling(lisp_xtr_t * xtr, iface_locators * if_loct)
{
    locator_t *loct;
    lisp_addr_t *loct_addr;
    map_local_entry_t *mle;
    glist_t *timers_lst;
    glist_entry_t *mle_it, *timer_it;
    mapping_t *map;
    oor_timer_t *timer;


    if(xtr->nat_aware == TRUE){
        if (glist_size(if_loct->ipv4_locators) == 0){
            return (GOOD);
        }
        loct = glist_first_data(if_loct->ipv4_locators);
        loct_addr = locator_addr(loct);
        if (lisp_addr_is_no_addr(loct_addr)==TRUE){
            return (GOOD);
        }
        glist_for_each_entry(mle_it,if_loct->map_loc_entries){
            mle = (map_local_entry_t *)glist_entry_data(mle_it);
            map = map_local_entry_mapping(mle);
            loct = mapping_get_loct_with_addr(map,loct_addr);

            /* Stop timers associtated with the locator */
            timer_inf_req_stop_using_locator(mle, loct);
            timer_encap_map_reg_stop_using_locator(mle, loct);

            if (locator_state(loct) == UP){
                OOR_LOG(LDBG_2,"xtr_if_event: Reconfiguring Info-Request process for locator %s of "
                        "the mapping %s.", lisp_addr_to_char(loct_addr),
                        lisp_addr_to_char(mapping_eid(map)));
                xtr_program_info_req_per_loct(xtr, mle, loct);
            }else{
                /* Reprogram all the Encap Map Registers of the other interfaces associated to the mapping
                 * If status is up this process will be done when receiving the Info Reply*/
                timers_lst = htable_ptrs_timers_get_timers_of_type_from_obj(ptrs_to_timers_ht,
                           mle, ENCAP_MAP_REGISTER_TIMER);
                glist_for_each_entry(timer_it, timers_lst){
                    timer = (oor_timer_t *)glist_entry_data(timer_it);
                    oor_timer_start(timer, OOR_INF_REQ_HANDOVER_TIMEOUT);
                }
                glist_destroy(timers_lst);
            }
        }
    }else{
        xtr_program_smr(xtr, OOR_SMR_TIMEOUT);
    }
    return (GOOD);
}


/****************************** NAT traversal ********************************/

static int
xtr_update_nat_info(lisp_xtr_t *xtr, map_local_entry_t *mle, locator_t *loct,
        glist_t *rtr_list)
{
    mle_nat_info_update(mle, loct, rtr_list);
    /* Update forwarding info of the local entry*/
    xtr->tr.fwd_policy->updated_map_loc_inf(xtr->tr.fwd_policy_dev_parm,mle);
    notify_datap_rm_fwd_from_entry(&(xtr->super),map_local_entry_eid(mle),TRUE);
    /* Update forwarding info of rtrs */
    xtr_update_rtrs_caches(xtr);

    return (GOOD);
}

static void
xtr_update_rtrs_caches(lisp_xtr_t *xtr)
{
    xtr_update_rtrs_cache_afi(xtr, AF_INET);
    xtr_update_rtrs_cache_afi(xtr, AF_INET6);
}

static void
xtr_update_rtrs_cache_afi(lisp_xtr_t *xtr, int afi)
{
    lisp_addr_t *rtr_addr;
    map_local_entry_t *mle;
    mcache_entry_t *rtrs_mce;
    mapping_t *map;
    locator_t *rtr_loct;
    glist_t *rtr_addr_list;
    glist_entry_t *addr_it;

    rtrs_mce  = mcache_get_all_space_entry(xtr->tr.map_cache, afi);

    // XXX check before if we have any change in order to avoid modify the data plane
    map = mcache_entry_mapping(rtrs_mce);
    /* Remove the list of rtr locators */
    mapping_remove_locators(map);

    /* Regenerate rtr list using the information of local map entries */
    local_map_db_foreach_entry(xtr->local_mdb,mle){
        rtr_addr_list = mle_rtr_addr_list(mle);
        glist_for_each_entry(addr_it, rtr_addr_list){
            rtr_addr = (lisp_addr_t *)glist_entry_data(addr_it);
            rtr_loct = locator_new_init(rtr_addr,UP,0,1,1,100,255,0);
            mapping_add_locator(map,rtr_loct);
        }
        glist_destroy(rtr_addr_list);
    }local_map_db_foreach_end;


    /* Update forwarding info of rtrs */
    xtr->tr.fwd_policy->updated_map_cache_inf(xtr->tr.fwd_policy_dev_parm,rtrs_mce);
    notify_datap_reset_all_fwd(&(xtr->super));
}

static glist_t *
nat_select_rtrs(glist_t * rtr_list)
{
    glist_t *final_rtr_list = glist_new_managed((glist_del_fct)lisp_addr_del);
    lisp_addr_t *rtr_addr;

    addr_list_rm_not_compatible_addr(rtr_list, IPv4_SUPPORT);

    //TODO Select RTR process
    rtr_addr = (lisp_addr_t *)glist_first_data(rtr_list);
    if (rtr_addr){
        glist_add(lisp_addr_clone(rtr_addr), final_rtr_list);
    }
    return (final_rtr_list);
}

/**********************************  Dump ************************************/

void
proxy_etrs_dump(lisp_xtr_t *xtr, int log_level)
{
    locator_t *locator = NULL;
    mcache_entry_t *    ipv4_petrs_mc,*ipv6_petrs_mc;
    ipv4_petrs_mc = mcache_get_all_space_entry(xtr->tr.map_cache,AF_INET);
    ipv6_petrs_mc = mcache_get_all_space_entry(xtr->tr.map_cache,AF_INET6);

    OOR_LOG(log_level, "****************** Proxy ETR List for IPv4 EIDs **********************");
    OOR_LOG(log_level, "|               Locator (RLOC)            | Status | Priority/Weight |");

    /* Start rloc probing for each locator of the mapping */
    mapping_foreach_active_locator(mcache_entry_mapping(ipv4_petrs_mc),locator){
        OOR_LOG(log_level,"%s",locator_to_char(locator));
    }mapping_foreach_active_locator_end;
    OOR_LOG(log_level, "**********************************************************************\n");

    OOR_LOG(log_level, "****************** Proxy ETR List for IPv6 EIDs **********************");
    OOR_LOG(log_level, "|               Locator (RLOC)            | Status | Priority/Weight |");

    /* Start rloc probing for each locator of the mapping */
    mapping_foreach_active_locator(mcache_entry_mapping(ipv6_petrs_mc),locator){
        OOR_LOG(log_level,"%s",locator_to_char(locator));
    }mapping_foreach_active_locator_end;
    OOR_LOG(log_level, "**********************************************************************\n");
}

void
map_servers_dump(lisp_xtr_t *xtr, int log_level)
{
    map_server_elt *    ms          = NULL;
    glist_entry_t *     it          = NULL;
    char                str[80];
    size_t  str_size = sizeof(str);

    if (glist_size(xtr->map_servers) == 0 || is_loggable(log_level) == FALSE) {
        return;
    }

    OOR_LOG(log_level, "******************* Map-Servers list ********************************");
    OOR_LOG(log_level, "|               Locator (RLOC)            |       Key Type          |");

    glist_for_each_entry(it, xtr->map_servers) {
        ms = (map_server_elt *)glist_entry_data(it);
        snprintf(str,str_size, "| %39s |", lisp_addr_to_char(ms->address));
        if (ms->key_type == NO_KEY) {
            snprintf(str + strlen(str), str_size - strlen(str), "          NONE           |");
        } else if (ms->key_type == HMAC_SHA_1_96) {
            snprintf(str + strlen(str), str_size - strlen(str), "     HMAC-SHA-1-96       |");
        } else {
            snprintf(str + strlen(str), str_size - strlen(str), "    HMAC-SHA-256-128     |");
        }
        OOR_LOG(log_level, "%s", str);
    }
    OOR_LOG(log_level, "*********************************************************************\n");
}

/**************************** Map Server struct ******************************/
map_server_elt *
map_server_elt_new_init(lisp_addr_t *address,uint8_t key_type, char *key,
        uint8_t proxy_reply)
{
    map_server_elt *ms = NULL;
    ms = xzalloc(sizeof(map_server_elt));
    if (ms == NULL){
        OOR_LOG(LWRN,"Couldn't allocate memory for a map_server_elt structure");
        return (NULL);
    }
    ms->address     = lisp_addr_clone(address);
    ms->key_type    = key_type;
    ms->key         = strdup(key);
    ms->proxy_reply = proxy_reply;

    return (ms);
}

void
map_server_elt_del (map_server_elt *map_server)
{
    if (map_server == NULL){
        return;
    }
    lisp_addr_del (map_server->address);
    free(map_server->key);
    free(map_server);
}


static map_local_entry_t *
get_map_loc_ent_containing_loct_ptr(local_map_db_t *local_db, locator_t *locator)
{
    map_local_entry_t *map_loc_e;
    map_local_entry_t *map_loc_e_res = NULL;
    mapping_t *mapping = NULL;
    uint8_t found = FALSE;
    void *it = NULL;
    local_map_db_foreach_entry_with_break(local_db, it, found) {
        map_loc_e = (map_local_entry_t *)it;
        mapping = map_local_entry_mapping(map_loc_e);
        if (mapping_has_locator(mapping, locator) == TRUE){
            found = TRUE;
            map_loc_e_res = map_loc_e;
        }
    } local_map_db_foreach_with_break_end(found);
    if (!map_loc_e_res){
        OOR_LOG(LDBG_2, "get_map_loc_ent_containing_loct_ptr: No mapping has been found with locator %s",
                lisp_addr_to_char(locator_addr(locator)));
    }
    return (map_loc_e_res);

}


/******************************* TIMERS **************************************/
/************************** Map Register timer *******************************/
static timer_map_reg_argument *
timer_map_reg_argument_new_init(map_local_entry_t *mle,
        map_server_elt *ms)
{
    timer_map_reg_argument *timer_arg = xmalloc(sizeof(timer_map_reg_argument));
    timer_arg->mle = mle;
    timer_arg->ms = ms;

    return(timer_arg);
}

static void
timer_map_reg_arg_free(timer_map_reg_argument * timer_arg)
{
    free(timer_arg);
}
/*********************** Encap Map Register timer ****************************/
static timer_encap_map_reg_argument *
timer_encap_map_reg_argument_new_init(map_local_entry_t *mle,
        map_server_elt *ms, locator_t *src_loct, lisp_addr_t *rtr_addr)
{
    timer_encap_map_reg_argument *timer_arg = xmalloc(sizeof(timer_encap_map_reg_argument));
    timer_arg->mle = mle;
    timer_arg->ms = ms;
    timer_arg->src_loct = src_loct;
    timer_arg->rtr_rloc = lisp_addr_clone(rtr_addr);
    return(timer_arg);
}

static void
timer_encap_map_reg_arg_free(timer_encap_map_reg_argument * timer_arg)
{
    lisp_addr_del(timer_arg->rtr_rloc);
    free(timer_arg);
}

/*
 * Stop all the timers of type ENCAP_MAP_REGISTER_TIMER associated with the map local entry
 * introduced as a parameter and using the specified locator.
 */
static void
timer_encap_map_reg_stop_using_locator(map_local_entry_t *mle, locator_t *loct)
{
    glist_t *timers_lst;
    glist_entry_t *timer_it;
    oor_timer_t *timer;
    timer_encap_map_reg_argument * timer_arg;

    timers_lst = htable_ptrs_timers_get_timers_of_type_from_obj(ptrs_to_timers_ht,
            mle, ENCAP_MAP_REGISTER_TIMER);
    glist_for_each_entry(timer_it,timers_lst){
        timer = (oor_timer_t *)glist_entry_data(timer_it);
        timer_arg = (timer_encap_map_reg_argument *)oor_timer_cb_argument(timer);
        if (timer_arg->src_loct == loct){
            stop_timer_from_obj(mle,timer,ptrs_to_timers_ht,nonces_ht);
        }
    }
    glist_destroy(timers_lst);
}
/************************** Info Request timer *******************************/
static timer_inf_req_argument *
timer_inf_req_argument_new_init(map_local_entry_t *mle, locator_t *loct,
        map_server_elt *ms)
{
    timer_inf_req_argument *timer_arg = xmalloc(sizeof(timer_inf_req_argument));
    timer_arg->mle = mle;
    timer_arg->loct = loct;
    timer_arg->ms = ms;
    return (timer_arg);
}

static void
timer_inf_req_arg_free(timer_inf_req_argument * timer_arg)
{
    free(timer_arg);
}

/*
 * Stop all the timers of type INFO_REQUEST_TIMER associated with the map local entry
 * introduced as a parameter and using the specified locator.
 */
static void
timer_inf_req_stop_using_locator(map_local_entry_t *mle, locator_t *loct)
{
    glist_t *timers_lst;
    glist_entry_t *timer_it;
    oor_timer_t *timer;
    timer_inf_req_argument * timer_arg;

    timers_lst = htable_ptrs_timers_get_timers_of_type_from_obj(ptrs_to_timers_ht,
            mle, INFO_REQUEST_TIMER);
    glist_for_each_entry(timer_it,timers_lst){
        timer = (oor_timer_t *)glist_entry_data(timer_it);
        timer_arg = (timer_inf_req_argument *)oor_timer_cb_argument(timer);
        if (timer_arg->loct == loct){
            stop_timer_from_obj(mle,timer,ptrs_to_timers_ht,nonces_ht);
        }
    }
    glist_destroy(timers_lst);
}
