diff options
Diffstat (limited to 'hicn-light/src/hicn/core/mapme.c')
-rw-r--r-- | hicn-light/src/hicn/core/mapme.c | 1573 |
1 files changed, 757 insertions, 816 deletions
diff --git a/hicn-light/src/hicn/core/mapme.c b/hicn-light/src/hicn/core/mapme.c index a22d01ae7..4a254c701 100644 --- a/hicn-light/src/hicn/core/mapme.c +++ b/hicn-light/src/hicn/core/mapme.c @@ -16,6 +16,98 @@ /** * @file mapme.c * @brief MAP-Me : AnchorLess Producer Mobility Management. + * + * TODO: + * - review notification code with to integration of VPP implementation + * - reflect changes back in VPP + * - implement heuristic for update/notification selection + * + * MAP-Me hooks in forwarder + * + * A) Face table changes + * + * - face added + * + * * new local/producer face : this is a new prefix that we need to advertise + * on existing connections. + * + * We go over non-local connections an advertise the prefix through an IU + * provided that the connection satisfies the policy associated to the FIB + * entry. MAP-Me assumes the prefix already exists in the network, and the + * IU shall be discarded if the entry does not exist at the next hop. Three + * possibilities: + * . a bootstrap mechanism + * . we allow subprefixes of a prefix that is not empty by duplicating the + * FIB entry + * . we allow prefix creation in all circumstances : this is problematic + * since we might be creating spurious entries in routers for which we + * don't expect entries to be created. + * + * NOTE: because in general we will not allow for FIB entry creation, we + * cannot let the forwarder remove FIB entries with no nexthop (for instance + * after the producer leaves a point-of-attachment). This might creates + * permanent state in router's tables, but we assume it is the role of the + * routing plane to take care of routing entries. + * + * * new non-local face : a new face is available (eg. thanks to the face + * manager, after the node has connection to a new WiFi/LTE access point), + * and we thus need to advertise all local/producer prefixes onto this + * interface. + * + * For this, we currently scan the FIB for entries that have at least one + * local/producer face in nexthops, advertise the prefix on this new + * connection provided that it satisfies the associated policy. + * + * - face removed + * + * Currently, we take no action when a face is removed. It might however be a + * signal that a producer application is no more running at a given node, and + * that we can temporarily disable the forwarding towards that path. + * + * - face up / down + * + * - face nexthop added + * + * - face changed priority/tags + * + * B) Interest and Data forwarder path + * + * mapme_on_interest + * + * mapme_on_data + * + * + * EVENTS + * NH_SET + * NH_ADD + * PH_ADD + * PH_DEL + * + * C) Retransmission management + * + * Data structure + * + * mapme_on_timeout + * + * + * This allows us to define a convenient API for implementing MAP-Me: + * + * mapme_on_face_event XXX rename + * + * mapme_send_to_nexthops(entry, nexthops) + * + * mapme_send_to_nexthop(entry, nexthop) + * A special case of the previous function when we only need to send to a + * single nexthop. This is because we might have some processing to do before + * iterating on nexthops (eg clear FIB) XXX TO BE CONFIRMED. + * + * mapme_maybe_send_to_nexthops(entry, nexthops) + * XXX Prev nexthops stored in FIB entry + * XXX this is valid for which prefixes ? + * + * mapme_send_to_all_nexthops + * + * */ #ifdef WITH_MAPME @@ -25,90 +117,123 @@ #include <stdio.h> // printf #include <hicn/core/connection.h> -#include <hicn/core/connectionList.h> #include <hicn/core/forwarder.h> -#include <hicn/core/logger.h> -#include <hicn/core/message.h> +#include <hicn/core/msgbuf.h> #include <hicn/core/messagePacketType.h> // packet types #include <hicn/core/ticks.h> -#include <hicn/processor/fibEntry.h> -#include <hicn/processor/pitEntry.h> - -#include <parc/algol/parc_HashMap.h> -#include <parc/algol/parc_Iterator.h> -#include <parc/algol/parc_Unsigned.h> -#include <parc/assert/parc_Assert.h> +#include <hicn/core/fib_entry.h> +#include <hicn/core/pit.h> +#include <hicn/base/loop.h> +#include <hicn/util/log.h> #define MS2NS(x) x * 1000000 #define T2NS(x) forwarder_TicksToNanos(x) +//#define MAPME_ALLOW_NONEXISTING_FIB_ENTRY #define MAPME_DEFAULT_TU 5000 /* ms */ #define MAPME_DEFAULT_RETX 500 /* ms */ -#define MAX_RETX 3 +#define MAPME_DEFAULT_DISCOVERY false +#define MAPME_DEFAULT_PROTOCOL IPPROTO_IPV6 +#define MAPME_MAX_RETX 3 +#define MTU 1500 // XXX TODO Mutualize this define -#define NOT_A_NOTIFICATION false -#define NO_INGRESS 0 +#define DONT_QUEUE false #define TIMER_NO_REPEAT false -#define DO_DISCOVERY 1 #define MAPME_INVALID_DICOVERY_SEQ -1 +#define INIT_SEQ 0 -#define LOG_FACILITY LoggerFacility_Core +#define foreach_mapme_event \ + _(UNDEFINED) \ + _(FACE_ADD) \ + _(FACE_DEL) \ + _(NH_SET) \ + _(NH_ADD) \ + _(PH_ADD) \ + _(PH_DEL) \ + _(N) + +typedef enum { +#define _(x) MAPME_EVENT_ ## x, + foreach_mapme_event +#undef _ +} mapme_event_t; -#define LOG(mapme, log_level, fmt, ...) \ - do { \ - Logger *logger = forwarder_GetLogger(mapme->forwarder); \ - if (logger_IsLoggable(logger, LOG_FACILITY, log_level)) { \ - logger_Log(logger, LOG_FACILITY, log_level, __func__, fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) +/* + * We need a retransmission pool holding all necessary information for crafting + * special interests, thus including both the DPO and the prefix associated to + * it. + */ +#define NUM_RETX_ENTRIES 100 +#define NUM_RETX_SLOT 2 -#define WARN(mapme, fmt, ...) \ - LOG(mapme, PARCLogLevel_Warning, fmt, ##__VA_ARGS__) -#define ERR(mapme, fmt, ...) LOG(mapme, PARCLogLevel_Error, fmt, ##__VA_ARGS__) -#define INFO(mapme, fmt, ...) LOG(mapme, PARCLogLevel_Info, fmt, ##__VA_ARGS__) -#define DEBUG(mapme, fmt, ...) \ - LOG(mapme, PARCLogLevel_Debug, fmt, ##__VA_ARGS__) +typedef struct { + hicn_prefix_t prefix; + fib_entry_t * entry; + uint8_t retx_count; // Number of retransmissions since last tfib addition +} mapme_retx_t; /** * MAP-Me state data structure */ -struct mapme { - uint32_t retx; /* ms */ - uint32_t Tu; /* ms */ - bool removeFibEntries; +struct mapme_s { + /* Options XXX mapme_conf_t ! */ + uint32_t retx; /* retx timeout (in ms) */ + uint32_t timescale; /* timescale (in ms) */ + bool discovery; /* discovery flag */ + int protocol; - Forwarder *forwarder; + /* + * Retransmissions + * Lite calendar queue with NUM_RETX_SLOT slots + */ + int timer_fd; + mapme_retx_t retx_array[NUM_RETX_SLOT][NUM_RETX_ENTRIES]; + uint8_t retx_len[NUM_RETX_SLOT]; + uint8_t cur; + uint8_t idle; + + forwarder_t * forwarder; }; -static MapMe MapMeDefault = {.retx = MAPME_DEFAULT_RETX, - .Tu = MAPME_DEFAULT_TU, - .removeFibEntries = false}; +#define NEXT_SLOT(CUR) (1-CUR) +#define CUR mapme->retx_array[mapme->cur] +#define NXT mapme->retx_array[NEXT_SLOT(mapme->cur)] +#define CURLEN mapme->retx_len[mapme->cur] +#define NXTLEN mapme->retx_len[NEXT_SLOT(mapme->cur)] + +static mapme_t mapme_default = { + .retx = MAPME_DEFAULT_RETX, + .timescale = MAPME_DEFAULT_TU, + .discovery = MAPME_DEFAULT_DISCOVERY, + .protocol = MAPME_DEFAULT_PROTOCOL, + + .timer_fd = -1, +// .retx_array = {{ 0 }}, // memset + .retx_len = { 0 }, + .cur = 0, /* current slot */ + .idle = 0, +}; /******************************************************************************/ -bool mapme_create(MapMe **mapme, Forwarder *forwarder) { - *mapme = malloc(sizeof(MapMe)); - if (!mapme) goto ERR_MALLOC; - - /* Internal state : set default values */ - memcpy(*mapme, &MapMeDefault, sizeof(MapMe)); - - (*mapme)->forwarder = forwarder; +mapme_t * +mapme_create(void * forwarder) +{ + mapme_t * mapme = malloc(sizeof(mapme_t)); + if (!mapme) + return NULL; - /* As there is no face table and no related events, we need to install hooks - * in various places in the forwarder, where both control commands and - * signalization are processed. - */ + /* Internal state : set default values */ + memcpy(mapme, &mapme_default, sizeof(mapme_t)); + memset(mapme->retx_array, 0, NUM_RETX_SLOT * NUM_RETX_ENTRIES); - return true; + mapme->forwarder = forwarder; -ERR_MALLOC: - return false; + return mapme; } -void mapme_free(MapMe *mapme) +void mapme_free(mapme_t * mapme) { free(mapme); } @@ -117,191 +242,37 @@ void mapme_free(MapMe *mapme) * TFIB ******************************************************************************/ -#define INVALID_SEQ 0 -#define INIT_SEQ 0 typedef struct { - uint32_t seq; - PARCHashMap *nexthops; - /* Update/Notification heuristic */ - Ticks lastAckedUpdate; -} MapMeTFIB; - -static MapMeTFIB *mapmeTFIB_Create() { - MapMeTFIB *tfib; - tfib = malloc(sizeof(MapMeTFIB)); - if (!tfib) goto ERR_MALLOC; - tfib->seq = INIT_SEQ; - tfib->lastAckedUpdate = 0; - tfib->nexthops = parcHashMap_Create(); - if (!tfib->nexthops) goto ERR_HASHMAP; - - return tfib; - -ERR_HASHMAP: - free(tfib); -ERR_MALLOC: - return NULL; -} - -void mapmeTFIB_Release(MapMeTFIB **tfibPtr) { - MapMeTFIB *tfib = *tfibPtr; - /* TODO; Release all timers */ - parcHashMap_Release(&tfib->nexthops); - free(tfib); - *tfibPtr = NULL; -} - -/** - * @function mapme_CreateTFIB - * @abstract Associate a new TFIB entry to a FIB entry. - * @param [in] - Pointer to the FIB entry. - * @return Boolean indicating the success of the operation. - */ -static void mapme_CreateTFIB(FibEntry *fibEntry) { - MapMeTFIB *tfib; - - /* Make sure we don't already have an associated TFIB entry */ - tfib = fibEntry_getUserData(fibEntry); - // assertNull(tfib); - - tfib = mapmeTFIB_Create(); - fibEntry_setUserData(fibEntry, tfib, (void (*)(void **))mapmeTFIB_Release); -} - -#define TFIB(fibEntry) ((MapMeTFIB *)fibEntry_getUserData(fibEntry)) - -static const PARCEventTimer *mapmeTFIB_Get(const MapMeTFIB *tfib, - unsigned conn_id) { - const PARCEventTimer *timer; - const PARCBuffer *buffer; - PARCUnsigned *cid = parcUnsigned_Create(conn_id); - buffer = parcHashMap_Get(tfib->nexthops, cid); - if (!buffer) return NULL; - PARCByteArray *array = parcBuffer_Array(buffer); - timer = *((PARCEventTimer **)parcByteArray_Array(array)); - parcUnsigned_Release(&cid); - return timer; -} + // XXX We need magic number to know whether the TFIB was initialized or not + // ... or merge it inside the real data structure. + // NOTE: in VPP we reuse the nexthops in opposite order to gain room + // XXX need enough space in user_data !! + uint32_t seq; + nexthops_t nexthops; // XXX useless shadow structure + /* Update/Notification heuristic */ + Ticks last_acked_update; +} mapme_tfib_t; + +#define TFIB(FIB_ENTRY) ((mapme_tfib_t * )fib_entry_get_user_data(FIB_ENTRY)) -static void mapmeTFIB_Put(MapMeTFIB *tfib, unsigned conn_id, - const PARCEventTimer *timer) { - /* NOTE: Timers are not objects (the only class not being an object in - * fact), and as such, we cannot use them as values for the HashMap. - * Just like for unsigned we needed the PARC wrapper. - * There is no wrapper for pointers, so we use Arrays, which has an ubly - * syntax... - */ - PARCUnsigned *cid = parcUnsigned_Create(conn_id); - PARCBuffer *buffer = - parcBuffer_CreateFromArray(&timer, sizeof(PARCEventTimer *)); - parcHashMap_Put(tfib->nexthops, cid, buffer); - parcUnsigned_Release(&cid); - parcBuffer_Release(&buffer); -} - -static void mapmeTFIB_Remove(MapMeTFIB *tfib, unsigned conn_id) { - // Who releases the timer ? - PARCUnsigned *cid = parcUnsigned_Create(conn_id); - parcHashMap_Remove(tfib->nexthops, cid); - parcUnsigned_Release(&cid); -} - -static PARCIterator *mapmeTFIB_CreateKeyIterator(const MapMeTFIB *tfib) { - return parcHashMap_CreateKeyIterator(tfib->nexthops); -} - -int hicn_prefix_from_name(const Name *name, hicn_prefix_t *prefix) { - NameBitvector *bv = name_GetContentName(name); - ip_prefix_t ip_prefix; - nameBitvector_ToIPAddress(bv, &ip_prefix); - - /* The name length will be equal to ip address' prefix length */ - return hicn_prefix_create_from_ip_prefix(&ip_prefix, prefix); -} - -static Message *mapme_createMessage(const MapMe *mapme, const Name *name, - mapme_params_t *params) { - Ticks now = forwarder_GetTicks(mapme->forwarder); - Logger *logger = logger_Acquire(forwarder_GetLogger(mapme->forwarder)); - - INFO(mapme, "[MAP-Me] CreateMessage type=%d seq=%d", params->type, - params->seq); - - size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN - : HICN_MAPME_V4_HDRLEN; - uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); - - hicn_prefix_t prefix; - int rc = hicn_prefix_from_name(name, &prefix); - if (rc < 0) { - ERR(mapme, "[MAP-Me] Failed to create lib's name"); - goto ERR_NAME; - } - - INFO(mapme, "[MAP-Me] Creating MAP-Me packet"); - size_t len = hicn_mapme_create_packet(icmp_pkt, &prefix, params); - if (len == 0) { - ERR(mapme, "[MAP-Me] Failed to create mapme packet through lib"); - goto ERR_CREATE; - } - - // hicn_packet_dump(icmp_pkt, MAPME_HDRLEN); - - return message_CreateFromByteArray(NO_INGRESS, icmp_pkt, - MessagePacketType_Interest, now, logger); - -ERR_CREATE: -ERR_NAME: - return NULL; -} - -static Message *mapme_createAckMessage(const MapMe *mapme, - const uint8_t *msgBuffer, - const mapme_params_t *params) { - Ticks now = forwarder_GetTicks(mapme->forwarder); - Logger *logger = logger_Acquire(forwarder_GetLogger(mapme->forwarder)); - - size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN - : HICN_MAPME_V4_HDRLEN; - uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); - memcpy(icmp_pkt, msgBuffer, size); - - size_t len = hicn_mapme_create_ack(icmp_pkt, params); - if (len != size) { - ERR(mapme, "[MAP-Me] Failed to create mapme ack packet through lib"); - return NULL; - } - - return message_CreateFromByteArray( - NO_INGRESS, icmp_pkt, MessagePacketType_ContentObject, now, logger); +void +mapme_tfib_initialize(mapme_tfib_t * tfib) +{ + tfib->seq = INIT_SEQ; + tfib->last_acked_update = 0; + nexthops_set_len(&tfib->nexthops, 0); } -struct setFacePendingArgs { - const MapMe *mapme; - const Name *name; - FibEntry *fibEntry; - unsigned conn_id; - bool send; - bool is_producer; - uint32_t num_retx; -}; - -static bool mapme_setFacePending(const MapMe *mapme, const Name *name, - FibEntry *fibEntry, unsigned conn_id, - bool send, bool is_producer, bool clear_tfib, uint32_t num_retx); - -static void mapme_setFacePendingCallback(int fd, PARCEventType which_event, - void *data) { - struct setFacePendingArgs *args = (struct setFacePendingArgs *)data; - - parcAssertTrue(which_event & PARCEventType_Timeout, - "Event incorrect, expecting %X set, got %X", - PARCEventType_Timeout, which_event); +int +hicn_prefix_from_name(const Name *name, hicn_prefix_t *prefix) +{ + NameBitvector *bv = name_GetContentName(name); + ip_prefix_t ip_prefix; + nameBitvector_ToIPAddress(bv, &ip_prefix); - INFO(args->mapme, "Timeout during retransmission. Re-sending"); - mapme_setFacePending(args->mapme, args->name, args->fibEntry, args->conn_id, - args->send, args->is_producer, false, args->num_retx); + /* The name length will be equal to ip address' prefix length */ + return hicn_prefix_create_from_ip_prefix(&ip_prefix, prefix); } /** @@ -310,369 +281,204 @@ static void mapme_setFacePendingCallback(int fd, PARCEventType which_event, * NOTE: IN are currently disabled until the proper placeholder is agreed in the * interest header. */ -static hicn_mapme_type_t mapme_getTypeFromHeuristic(const MapMe *mapme, - FibEntry *fibEntry) { +static +hicn_mapme_type_t +mapme_get_type_from_heuristic(const mapme_t * mapme, fib_entry_t * entry) +{ + if (fib_entry_has_local_nexthop(entry)) + /* We are a producer for this entry, send update */ + return UPDATE; + #if 0 /* interplay of IU/IN */ - if (TFIB(fibEntry)->lastAckedUpdate == 0) { + if (TFIB(fib_entry)->lastAckedUpdate == 0) { return UPDATE; } else { - Ticks interval = now - TFIB(fibEntry)->lastAckedUpdate; - return (T2NS(interval) > MS2NS(mapme->Tu)) ? UPDATE : NOTIFICATION; + Ticks interval = now - TFIB(fib_entry)->lastAckedUpdate; + return (T2NS(interval) > MS2NS(mapme->timescale)) ? UPDATE : NOTIFICATION; } #else /* Always send IU */ - return UPDATE; + return UPDATE; #endif } -static bool mapme_setFacePending(const MapMe *mapme, const Name *name, - FibEntry *fibEntry, unsigned conn_id, - bool send, bool is_producer, bool clear_tfib, uint32_t num_retx) { - int rc; - - INFO(mapme, "[MAP-Me] SetFacePending connection=%d prefix=XX retx=%d", - conn_id, num_retx); - - /* NOTE: if the face is pending an we receive an IN, maybe we should not - * cancel the timer - */ - Dispatcher *dispatcher = forwarder_GetDispatcher(mapme->forwarder); - PARCEventTimer *timer; - - /* Safeguard during retransmissions */ - if (!TFIB(fibEntry)) - return true; - - /* - * On the producer side, we have to clear the TFIB everytime we change the list - * of adjacencies, otherwise retransmissions will occur to preserve them. - */ - if (clear_tfib) { - /* - * It is likely we cannot iterator and remove elements from the hashmap at - * the same time, so we proceed in two steps - */ - if (parcHashMap_Size(TFIB(fibEntry)->nexthops) > 0) { - - NumberSet * conns = numberSet_Create(); - - PARCIterator *it = parcHashMap_CreateKeyIterator(TFIB(fibEntry)->nexthops); - while (parcIterator_HasNext(it)) { - PARCUnsigned *cid = parcIterator_Next(it); - unsigned conn_id = parcUnsigned_GetUnsigned(cid); - numberSet_Add(conns, conn_id); - } - parcIterator_Release(&it); - - for (size_t i = 0; i < numberSet_Length(conns); i++) { - unsigned conn_id = numberSet_GetItem(conns, i); - PARCEventTimer *oldTimer = (PARCEventTimer *)mapmeTFIB_Get(TFIB(fibEntry), conn_id); - if (oldTimer) - parcEventTimer_Stop(oldTimer); - mapmeTFIB_Remove(TFIB(fibEntry), conn_id); - } - - numberSet_Release(&conns); - } - } - - // NOTE - // - at producer, send always true, we always send something reliably so we - // set the timer. - // - in the network, we always forward an IU, and never an IN - //if (is_producer || send) { - if (send) { + +/** + * + * Here nexthops is not necessarily FIB nexthops as we might advertise given FIB + * entries on various other connections. + */ +/* NOTE: if the face is pending an we receive an IN, maybe we should not cancel + * the timer + */ +// XXX Make sure this function is never called for Notifications +// XXX overall review notification code and integrate it in VPP +int +mapme_send_to_nexthops(const mapme_t * mapme, fib_entry_t * entry, + const nexthops_t * nexthops) +{ + mapme_tfib_t * tfib = TFIB(entry); + + tfib->seq++; + + const Name *name = fib_entry_get_prefix(entry); + + char *name_str = name_ToString(name); + DEBUG("sending IU/IN for name %s on all nexthops", name_str); + free(name_str); + mapme_params_t params = { - .protocol = IPPROTO_IPV6, - .type = is_producer ? mapme_getTypeFromHeuristic(mapme, fibEntry) : UPDATE, - .seq = TFIB(fibEntry)->seq}; - Message *special_interest = mapme_createMessage(mapme, name, ¶ms); - if (!special_interest) { - INFO(mapme, "[MAP-Me] Could not create special interest"); - return false; + .protocol = mapme->protocol, + .type = mapme_get_type_from_heuristic(mapme, entry), + .seq = tfib->seq, + }; + + hicn_prefix_t prefix; + if (hicn_prefix_from_name(name, &prefix) < 0) { + ERROR("Failed to create lib's name"); + return -1; } - const ConnectionTable *table = - forwarder_GetConnectionTable(mapme->forwarder); - const Connection *conn = - connectionTable_FindById((ConnectionTable *)table, conn_id); - if (conn) { - const Name * name = message_GetName(special_interest); - char * name_str = name_ToString(name); - INFO(mapme, "[MAP-Me] Sending MAP-Me packet name=%s seq=%d conn=%d", - name_str, params.seq, conn_id); - free(name_str); - connection_ReSend(conn, special_interest, NOT_A_NOTIFICATION); - } else { - INFO(mapme, "[MAP-Me] Stopped retransmissions as face went down"); + uint8_t packet[MTU]; + size_t size = hicn_mapme_create_packet(packet, &prefix, ¶ms); + if (size <= 0) { + ERROR("Could not create MAP-Me packet"); + return -1; } - if (num_retx < MAX_RETX) { - INFO(mapme, "[MAP-Me] - Scheduling retransmission\n"); - /* Schedule retransmission */ - struct setFacePendingArgs *args = - malloc(sizeof(struct setFacePendingArgs)); - if (!args) goto ERR_MALLOC; - args->mapme = mapme; - args->name = name; - args->fibEntry = fibEntry; - args->conn_id = conn_id; - args->send = send; - args->is_producer = is_producer; - args->num_retx = num_retx + 1; - - timer = dispatcher_CreateTimer(dispatcher, TIMER_NO_REPEAT, - mapme_setFacePendingCallback, args); - struct timeval timeout = {mapme->retx / 1000, - (mapme->retx % 1000) * 1000}; - rc = parcEventTimer_Start(timer, &timeout); - if (rc < 0) goto ERR_TIMER; - } else { - INFO(mapme, "[MAP-Me] Last retransmission."); - timer = NULL; - } - } else { - INFO(mapme, "[MAP-Me] - not forwarding as send is False"); - timer = NULL; - } - - PARCEventTimer *oldTimer = - (PARCEventTimer *)mapmeTFIB_Get(TFIB(fibEntry), conn_id); - if (oldTimer) { - INFO(mapme, "[MAP-Me] - Found old timer, would need to cancel !"); - // parcEventTimer_Stop(oldTimer); - } - INFO(mapme, "[MAP-Me] - Putting new timer in TFIB"); - if (timer) mapmeTFIB_Put(TFIB(fibEntry), conn_id, timer); - - return true; - -ERR_MALLOC: -ERR_TIMER: - return false; -} + /* + * We used to clear TFIB everytime which is wrong. + * XXX When to clear TFIB ?? + * + * On the producer side, we have to clear the TFIB everytime we change the list + * of adjacencies, otherwise retransmissions will occur to preserve them. + */ -/*------------------------------------------------------------------------------ - * Event handling - *----------------------------------------------------------------------------*/ + nexthops_clear(&tfib->nexthops); -/* - * Return true if we have at least one local connection as next hop - */ -static bool mapme_hasLocalNextHops(const MapMe *mapme, - const FibEntry *fibEntry) { - const NumberSet *nexthops = fibEntry_GetNexthops(fibEntry); - const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); - - for (size_t j = 0; j < fibEntry_NexthopCount(fibEntry); j++) { - /* Retrieve Nexthop #j */ - unsigned conn_id = numberSet_GetItem(nexthops, j); - const Connection *conn = - connectionTable_FindById((ConnectionTable *)table, conn_id); - - /* Ignore non-local connections */ - if (!connection_IsLocal(conn)) continue; - /* We don't need to test against conn_added since we don't - * expect it to have any entry in the FIB */ - - return true; - } - return false; + connection_table_t * table = forwarder_get_connection_table(mapme->forwarder); + + unsigned nexthop; + nexthops_foreach(nexthops, nexthop, { + DEBUG("sending packet on connection %d", nexthop); + const connection_t * conn = connection_table_get_by_id(table, nexthop); + connection_send_packet(conn, packet, size); + }); + + return 0; } -void -mapme_send_updates(const MapMe * mapme, FibEntry * fibEntry, const NumberSet * nexthops) +int +mapme_send_to_nexthop(const mapme_t * mapme, fib_entry_t * entry, unsigned nexthop) { - if (!TFIB(fibEntry)) /* Create TFIB associated to FIB entry */ - mapme_CreateTFIB(fibEntry); - TFIB(fibEntry)->seq++; - - const Name *name = fibEntry_GetPrefix(fibEntry); - char *name_str = name_ToString(name); - bool clear_tfib = true; - for (size_t j = 0; j < numberSet_Length(nexthops); j++) { - unsigned nexthop_id = numberSet_GetItem(nexthops, j); - INFO(mapme, "[MAP-Me] sending IU/IN for name %s on connection %d", name_str, - nexthop_id); - mapme_setFacePending(mapme, name, fibEntry, nexthop_id, true, true, clear_tfib, 0); - clear_tfib = false; - } - free(name_str); + nexthops_t nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, nexthop); + + return mapme_send_to_nexthops(mapme, entry, &nexthops); } +/** + * + * Here nexthops is not necessarily FIB nexthops as we might advertise given FIB + * entries on various other connections. + */ void -mapme_maybe_send_updates(const MapMe * mapme, FibEntry * fibEntry, const NumberSet * nexthops) +mapme_maybe_send_to_nexthops(const mapme_t * mapme, fib_entry_t * fib_entry, + const nexthops_t * nexthops) { - /* Detect change */ - NumberSet * previous_nexthops = fibEntry_GetPreviousNextHops(fibEntry); - if (numberSet_Equals(nexthops, previous_nexthops)) { - INFO(mapme, "[MAP-Me] No change in nexthops"); - return; - } - fibEntry_SetPreviousNextHops(fibEntry, nexthops); - - mapme_send_updates(mapme, fibEntry, nexthops); + /* Detect change */ + if (!fib_entry_nexthops_changed(fib_entry)) { + INFO("No change in nexthops"); + return; + } + fib_entry_set_prev_nexthops(fib_entry); + + mapme_send_to_nexthops(mapme, fib_entry, nexthops); } void -mapme_reconsiderFibEntry(const MapMe *mapme, FibEntry * fibEntry) +mapme_send_to_all_nexthops(const mapme_t *mapme, fib_entry_t * entry) { - /* - * Skip entries that do not correspond to a producer ( / have a locally - * served prefix / have no local connection as next hop) - */ - if (!mapme_hasLocalNextHops(mapme, fibEntry)) - return; + /* Apply the policy of the fib_entry over all neighbours */ + nexthops_t new_nexthops; + nexthops_t * nexthops = fib_entry_get_available_nexthops(entry, ~0, &new_nexthops); - /* Apply the policy of the fibEntry over all neighbours */ - NumberSet * available_nexthops = fibEntry_GetAvailableNextHops(fibEntry, ~0); - - /* Advertise prefix on all available next hops (if needed) */ - mapme_send_updates(mapme, fibEntry, available_nexthops); - - numberSet_Release(&available_nexthops); + /* Advertise prefix on all available next hops (if needed) */ + mapme_maybe_send_to_nexthops(mapme, entry, nexthops); } /* * Callback called everytime a new connection is created by the control protocol */ void -mapme_onConnectionEvent(const MapMe *mapme, const Connection *conn_added, connection_event_t event) { - /* Does the priority change impacts the default route selection; if so, - * advertise the prefix on this default route. If there are many default - * routes, either v4 v6, or many connections as next hops on this default - * route, then send to all. - */ - if (conn_added) { - if (connection_IsLocal(conn_added)) - return; - - unsigned conn_added_id = connection_GetConnectionId(conn_added); - switch(event) { - case CONNECTION_EVENT_CREATE: - INFO(mapme, "[MAP-Me] Connection %d got created", conn_added_id); - break; - case CONNECTION_EVENT_DELETE: - INFO(mapme, "[MAP-Me] Connection %d got deleted", conn_added_id); - break; - case CONNECTION_EVENT_UPDATE: - INFO(mapme, "[MAP-Me] Connection %d got updated", conn_added_id); - break; - case CONNECTION_EVENT_SET_UP: - INFO(mapme, "[MAP-Me] Connection %d went up", conn_added_id); - break; - case CONNECTION_EVENT_SET_DOWN: - INFO(mapme, "[MAP-Me] Connection %d went down", conn_added_id); - break; - case CONNECTION_EVENT_TAGS_CHANGED: - INFO(mapme, "[MAP-Me] Connection %d changed tags", conn_added_id); - break; - case CONNECTION_EVENT_PRIORITY_CHANGED: - INFO(mapme, "[MAP-Me] Connection %d changed priority to %d", - conn_added_id, connection_GetPriority(conn_added)); - break; +mapme_on_connection_event(const mapme_t *mapme, const connection_t * conn_added, connection_event_t event) { + /* Does the priority change impacts the default route selection; if so, + * advertise the prefix on this default route. If there are many default + * routes, either v4 v6, or many connections as next hops on this default + * route, then send to all. + */ + if (conn_added) { + if (connection_is_local(conn_added)) + return; + + unsigned conn_added_id = connection_get_id(conn_added); + switch(event) { + case CONNECTION_EVENT_CREATE: + INFO("Connection %d got created", conn_added_id); + break; + case CONNECTION_EVENT_DELETE: + INFO("Connection %d got deleted", conn_added_id); + break; + case CONNECTION_EVENT_UPDATE: + INFO("Connection %d got updated", conn_added_id); + break; + case CONNECTION_EVENT_SET_UP: + INFO("Connection %d went up", conn_added_id); + break; + case CONNECTION_EVENT_SET_DOWN: + INFO("Connection %d went down", conn_added_id); + break; + case CONNECTION_EVENT_TAGS_CHANGED: + INFO("Connection %d changed tags", conn_added_id); + break; + case CONNECTION_EVENT_PRIORITY_CHANGED: + INFO("Connection %d changed priority to %d", + conn_added_id, connection_get_priority(conn_added)); + break; + } } - } - - /* We need to send a MapMe update on the newly selected connections for - * each concerned fibEntry : connection is involved, or no more involved */ - FibEntryList *fiblist = forwarder_GetFibEntries(mapme->forwarder); - - /* Iterate a first time on the FIB to get the locally served prefixes */ - for (size_t i = 0; i < fibEntryList_Length(fiblist); i++) { - FibEntry *fibEntry = (FibEntry *)fibEntryList_Get(fiblist, i); - mapme_reconsiderFibEntry(mapme, fibEntry); - } - - fibEntryList_Destroy(&fiblist); - - INFO(mapme, "[MAP-Me] Done"); -} -#if 0 -#ifdef WITH_POLICY -void mapme_onPolicyUpdate(const MapMe *mapme, const Connection *conn_selected, FibEntry * fibEntry) -{ - /* Ignore local connections corresponding to applications for now */ - if (connection_IsLocal(conn_selected)) - return; - - unsigned conn_selected_id = connection_GetConnectionId(conn_selected); - INFO(mapme, "[MAP-Me] New connection %d", conn_selected_id); - - const Name *name = fibEntry_GetPrefix(fibEntry); - - /* Skip entries that have no local connection as next hop */ - if (!mapme_hasLocalNextHops(mapme, fibEntry)) - return; - - /* This entry corresponds to a locally served prefix, set - * Special Interest */ - if (!TFIB(fibEntry)) /* Create TFIB associated to FIB entry */ - mapme_CreateTFIB(fibEntry); - TFIB(fibEntry)->seq++; + /* We need to send a MapMe update on the newly selected connections for + * each concerned fib_entry : connection is involved, or no more involved */ + const fib_t * fib = forwarder_get_fib(mapme->forwarder); + fib_entry_t * entry; + fib_foreach_entry(fib, entry, { + if (!fib_entry_has_local_nexthop(entry)) + continue; + /* + * On the producer side, we have to clear the TFIB everytime we change + * the list of adjacencies, otherwise retransmissions will occur to + * preserve them. + */ + mapme_tfib_t * tfib = TFIB(entry); + nexthops_clear(&tfib->nexthops); - char *name_str = name_ToString(name); - INFO(mapme, "[MAP-Me] sending IU/IN for name %s on connection %d", name_str, - conn_selected_id); - free(name_str); + mapme_send_to_all_nexthops(mapme, entry); + }); - mapme_setFacePending(mapme, name, fibEntry, conn_selected_id, true, true, true, 0); + INFO("Done"); } -#endif /* WITH_POLICY */ -#endif /*------------------------------------------------------------------------------ * Special Interest handling *----------------------------------------------------------------------------*/ -/** - * @discussion This function is way too long and should be cut out - */ -static bool mapme_onSpecialInterest(const MapMe *mapme, - const uint8_t *msgBuffer, - unsigned conn_in_id, hicn_prefix_t *prefix, - mapme_params_t *params) { - const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); - /* The cast is needed since connectionTable_FindById miss the - * const qualifier for the first parameter */ - const Connection *conn_in = - connectionTable_FindById((ConnectionTable *)table, conn_in_id); - seq_t fibSeq, seq = params->seq; - bool send = (params->type == UPDATE); - bool rv; - - Name *name = name_CreateFromPacket(msgBuffer, MessagePacketType_Interest); - name_setLen(name, prefix->len); - char *name_str = name_ToString(name); - INFO(mapme, - "[MAP-Me] Ack'ed Special Interest on connection %d - prefix=%s type=XX " - "seq=%d", - conn_in_id, name_str, seq); - free(name_str); - - /* - * Immediately send an acknowledgement back on the ingress connection - * We always ack, even duplicates. - */ - Message *ack = mapme_createAckMessage(mapme, msgBuffer, params); - if (!ack) goto ERR_ACK_CREATE; - rv = connection_ReSend(conn_in, ack, NOT_A_NOTIFICATION); - if (!rv) goto ERR_ACK_SEND; - message_Release(&ack); - - /* EPM on FIB */ - /* only the processor has access to the FIB */ - FIB *fib = forwarder_getFib(mapme->forwarder); - - FibEntry *fibEntry = fib_Contains(fib, name); - if (!fibEntry) { - INFO(mapme, "Ignored update with no FIB entry"); - return 0; -#if 0 - INFO(mapme, - "[MAP-Me] - Re-creating FIB entry with next hop on connection %d", - conn_in_id); +#ifdef MAPME_ALLOW_NONEXISTING_FIB_ENTRY +int +mapme_create_fib_entry(const mapme_t * mapme, const Name * name, unsigned ingress_id) +{ + INFO(" - Re-creating FIB entry with next hop on connection %d", + ingress_id); /* * This might happen for a node hosting a producer which has moved. * Destroying the face has led to removing all corresponding FIB @@ -686,17 +492,16 @@ static bool mapme_onSpecialInterest(const MapMe *mapme, * the message should be propagated. */ #ifdef WITH_POLICY - fibEntry = fibEntry_Create(name, fwdStrategy, mapme->forwarder); + entry = fib_entry_Create(name, fwdStrategy, mapme->forwarder); #else - fibEntry = fibEntry_Create(name, fwdStrategy); + entry = fib_entry_Create(name, fwdStrategy); #endif /* WITH_POLICY */ - FibEntry *lpm = fib_MatchName(fib, name); - mapme_CreateTFIB(fibEntry); - fib_Add(fib, fibEntry); + fib_entry_t *lpm = fib_MatchName(fib, name); + fib_Add(fib, entry); if (!lpm) { - TFIB(fibEntry)->seq = seq; - fibEntry_AddNexthop(fibEntry, conn_in_id); - return true; + TFIB(entry)->seq = seq; + fib_entry_AddNexthop(entry, ingress_id); + return true; } /* @@ -704,279 +509,387 @@ static bool mapme_onSpecialInterest(const MapMe *mapme, * the more specific name, and proceed as usual. Worst case we clone the * default route... */ - const NumberSet *lpm_nexthops = fibEntry_GetNexthops(lpm); + const NumberSet *lpm_nexthops = fib_entry_nexthops_get(lpm); for (size_t i = 0; i < numberSet_Length(lpm_nexthops); i++) { - fibEntry_AddNexthop(fibEntry, numberSet_GetItem(lpm_nexthops, i)); + fib_entry_AddNexthop(entry, numberSet_GetItem(lpm_nexthops, i)); } + return 0; +} #endif - } else if (!TFIB(fibEntry)) { - /* Create TFIB associated to FIB entry */ - INFO(mapme, - "[MAP-Me] - Creating TFIB entry with default sequence number"); - mapme_CreateTFIB(fibEntry); - } - - /* - * In case of multihoming, we might receive a message about our own prefix, we - * should never take it into account, nor send the IU backwards as a sign of - * outdated propagation. - * - * Detection: we receive a message initially sent by ourselves, ie a message - * for which the prefix has a local next hop in the FIB. - */ - if (mapme_hasLocalNextHops(mapme, fibEntry)) { - INFO(mapme, "[MAP-Me] - Received original interest... Update complete"); - return true; - } - - fibSeq = TFIB(fibEntry)->seq; - if (seq > fibSeq) { - INFO(mapme, - "[MAP-Me] - Higher sequence number than FIB %d, updating seq and " - "next hops", - fibSeq); - /* This has to be done first to allow processing SpecialInterestAck's */ - TFIB(fibEntry)->seq = seq; - - /* Reliably forward the IU on all prevHops */ - INFO(mapme, "[MAP-Me] - (1/3) processing prev hops"); - if (params->type == UPDATE) { - PARCIterator *iterator = mapmeTFIB_CreateKeyIterator(TFIB(fibEntry)); - while (parcIterator_HasNext(iterator)) { - PARCUnsigned *cid = parcIterator_Next(iterator); - unsigned conn_id = parcUnsigned_GetUnsigned(cid); - INFO(mapme, "[MAP-Me] - Re-sending IU to pending connection %d", - conn_id); - mapme_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, - conn_id, false, false, false, 0); - } - parcIterator_Release(&iterator); - } - /* nextHops -> prevHops +void +mapme_on_timeout(mapme_t * mapme, int fd, void * data) +{ + assert(mapme); + assert(!data); + /* Timeout occurred, we have to retransmit IUs for all pending + * prefixes having entries in TFIB * - * We add to the list of pendingUpdates the current next hops, and - * eventually forward them an IU too. + * timeouts are slotted + * | | | | * - * Exception: nextHops -> nextHops - * Because of retransmission issues, it is possible that a second interest - * (with same of higher sequence number) is receive from a next-hop - * interface. In that case, the face remains a next hop. + * ^ + * +- event occurred + * new face, wait for the second next + * (having two arrays and swapping cur and next) + * retx : put in next */ - const NumberSet *nexthops_old = fibEntry_GetNexthops(fibEntry); + mapme->idle += 1; - /* We make a copy to be able to send IU _after_ updating next hops */ - NumberSet *nexthops = numberSet_Create(); - numberSet_AddSet(nexthops, nexthops_old); + for (uint8_t pos = 0; pos < CURLEN; pos++) { + mapme_retx_t * retx = &CUR[pos]; - /* We are considering : * -> nextHops - * - * If inFace was a previous hop, we need to cancel the timer and remove - * the entry. Also, the face should be added to next hops. - * - * Optimization : nextHops -> nextHops - * - no next hop to add - * - we know that inFace was not a previous hop since it was a next hop and - * this forms a partition. No need for a search - */ + if (!retx->entry) /* deleted entry */ + continue; + + mapme_tfib_t * tfib = TFIB(retx->entry); + assert(tfib); + + /* Re-send interest for all entries */ + mapme_send_to_all_nexthops(mapme, retx->entry); - INFO(mapme, "[MAP-Me] - (3/3) next hops ~~> prev hops"); - PARCEventTimer *oldTimer = - (PARCEventTimer *)mapmeTFIB_Get(TFIB(fibEntry), conn_in_id); - if (oldTimer) { - /* This happens if we receive an IU while we are still sending - * one in the other direction - */ - INFO(mapme, "[MAP-Me] - Canceled pending timer"); - parcEventTimer_Stop(oldTimer); + retx->retx_count++; + /* If we exceed the numver of retransmittion it means that all tfib + * entries have seens at least HICN_PARAM_RETX_MAX of retransmission + */ + if (retx->retx_count < MAPME_MAX_RETX) { + /* + * We did some retransmissions, so let's reschedule a check in the + * next slot + */ + NXT[NXTLEN++] = CUR[pos]; + mapme->idle = 0; + } else { + WARN("Maximum retransmissions exceeded"); + /* If we exceed the numver of retransmission it means that all TFIB + * entries have seens at least HICN_PARAM_RTX_MAX retransmissions. + * (Deletion might be slightly late). + * + * XXX document: when adding an entry in TFIB, we might exceed max + * retransmissions for previous entries that started retransmitting + * beforehand. + */ + nexthops_clear(&tfib->nexthops); + } } - mapmeTFIB_Remove(TFIB(fibEntry), conn_in_id); - /* Remove all next hops */ - for (size_t k = 0; k < numberSet_Length(nexthops); k++) { - unsigned conn_id = numberSet_GetItem(nexthops, k); - INFO(mapme, "[MAP-Me] - Replaced next hops by connection %d", conn_id); - fibEntry_RemoveNexthopByConnectionId(fibEntry, conn_id); + /* Reset events in this slot and prepare for next one */ + CURLEN = 0; + mapme->cur = NEXT_SLOT(mapme->cur); + + /* After two empty slots, we disable the timer */ + if (mapme->idle > 1) { + loop_unregister_timer(MAIN_LOOP, mapme->timer_fd); + mapme->timer_fd = -1; } - fibEntry_AddNexthop(fibEntry, conn_in_id); - - INFO(mapme, "[MAP-Me] - (2/3) processing next hops"); - bool complete = true; - for (size_t k = 0; k < numberSet_Length(nexthops); k++) { - unsigned conn_id = numberSet_GetItem(nexthops, k); - INFO(mapme, " - Next hop connection %d", conn_id); - if (conn_id == conn_in_id) { - INFO(mapme, " . Ignored this next hop since equal to ingress face"); - continue; - } - - INFO(mapme, "[MAP-Me] - Sending IU on current next hop connection %d", - conn_id); - mapme_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, - conn_id, send, false, false, 0); - complete = false; +} + +static +void +mapme_on_event(mapme_t * mapme, mapme_event_t event, fib_entry_t * entry, + unsigned ingress_id) +{ + switch (event) { +#if 0 + case HICN_MAPME_EVENT_FACE_ADD: + { + /* + * A face has been added: + * - In case of a local app face, we need to advertise a new prefix + * - For another local face type, we need to advertise local + * prefixes and schedule retransmissions + */ + mapme_retx_t *retx_events = event_data; + for (uint8_t i = 0; i < vec_len (retx_events); i++) { + hicn_mapme_on_face_added(mapme, retx_events[i].dpo); + } + + if (mapme->timer_fd == -1) + mapme->timer_fd = loop_register_timer(MAIN_LOOP, + mapme->retx, mapme, mapme_on_timeout, NULL); + mapme->idle = 0; + break; + } + case HICN_MAPME_EVENT_FACE_DEL: + if (mapme->timer_fd == -1) + mapme->timer_fd = loop_register_timer(MAIN_LOOP, + DEFAULT_TIMEOUT, mapme, mapme_on_timeout, NULL); + mapme->idle = 0; + break; +#endif + + case MAPME_EVENT_NH_SET: + /* + * An hICN FIB entry has been modified. All operations so far + * have been procedded in the nodes. Here we need to track + * retransmissions upon timeout: we mark the FIB entry as pending in + * the second-to-next slot + */ + + /* + * XXX Move this in doc + * + * The FIB entry has a new next hop, and its TFIB section has: + * - eventually previous prev hops for which a IU with a + * lower seqno has been sent + * - the prev hops that have just been added. + * + * We don't distinguish any and just send an updated IU to all + * of them. The retransmission of the latest IU to all + * facilitates the matching of ACKs to a single seqno which is + * the one stored in the FIB. + * + * Since we retransmit to all prev hops, we can remove this + * (T)FIB entry for the check at the end of the current slot. + */ + + /* Mark FIB entry as pending for second-to-next slot */ + /* + * Transmit IU for all TFIB entries with latest seqno (we have + * at least one for sure!) + */ + mapme_send_to_all_nexthops(mapme, entry); + + /* Delete entry_id from retransmissions in the current slot (if present) ... */ + /* ... and schedule it for next slot (if not already) */ + uint8_t j; + for (j = 0; j < CURLEN; j++) { + if (CUR[j].entry == entry) + CUR[j].entry = NULL; /* sufficient */ + } + for (j = 0; j < NXTLEN; j++) { + if (NXT[j].entry == entry) + break; + } + if (j == NXTLEN) /* not found */ + NXT[NXTLEN++] = (mapme_retx_t) { + .entry = entry, + .retx_count = 0, + }; + + if (mapme->timer_fd == -1) + mapme->timer_fd = loop_register_timer(MAIN_LOOP, + mapme->retx, mapme, mapme_on_timeout, NULL); + mapme->idle = 0; + break; + + case MAPME_EVENT_NH_ADD: + /* + * XXX move this in doc + * + * As per the description of states, this event should add the face + * to the list of next hops, and eventually remove it from TFIB. + * This corresponds to the multipath case. + * + * In all cases, we assume the propagation was already done when the first + * interest with the same sequence number was received, so we stop here + * No change in TFIB = no IU to send + * + * No change in timers. + */ + + // XXX useless +#if 0 + /* Add ingress face as next hop */ + idle = 0; +#endif + break; + + case MAPME_EVENT_PH_ADD: + /* Back-propagation, interesting even for IN (desync) */ + mapme_send_to_nexthop(mapme, entry, ingress_id); + + mapme->idle = 0; + if (mapme->timer_fd == -1) + mapme->timer_fd = loop_register_timer(MAIN_LOOP, + mapme->retx, mapme, mapme_on_timeout, NULL); + break; + + case MAPME_EVENT_PH_DEL: + /* Ack : remove an element from TFIB */ + break; + + case MAPME_EVENT_FACE_ADD: + case MAPME_EVENT_FACE_DEL: + + case MAPME_EVENT_UNDEFINED: + case MAPME_EVENT_N: + ERROR("Unexpected event"); + break; + } +} - /* - * The update is completed when the IU could not be sent to any - * other next hop. - */ - if (complete) INFO(mapme, "[MAP-Me] - Update completed !"); +static +void +mapme_on_interest(mapme_t * mapme, uint8_t * packet, + unsigned ingress_id, hicn_prefix_t * prefix, mapme_params_t * params) +{ + connection_table_t * table = forwarder_get_connection_table(mapme->forwarder); - numberSet_Release(&nexthops); + /* The cast is needed since connectionTable_FindById miss the + * const qualifier for the first parameter */ + const connection_t * conn_in = connection_table_get_by_id(table, ingress_id); - } else if (seq == fibSeq) { /* - * Multipath, multihoming, multiple producers or duplicate interest - * - * In all cases, we assume the propagation was already done when the first - * interest with the same sequence number was received, so we stop here - * - * It might happen that the previous AP has still a connection to the - * producer and that we received back our own IU. In that case, we just - * need to Ack and ignore it. + * Immediately send an acknowledgement back on the ingress connection + * We always ack, even duplicates. */ -#if 0 - if (mapme_hasLocalNextHops(mapme, fibEntry)) { - INFO(mapme, "[MAP-Me] - Received original interest... Update complete"); - return true; + size_t size = hicn_mapme_create_ack(packet, params); + if (connection_send_packet(conn_in, packet, size) < 0) { + /* We accept the packet knowing we will get a retransmit */ + ERROR("Failed to send ACK packet"); } + + Name *name = name_CreateFromPacket(packet, MESSAGE_TYPE_INTEREST); + name_setLen(name, prefix->len); + + char *name_str = name_ToString(name); + DEBUG("Ack'ed interest : connection=%d prefix=%s seq=%d", ingress_id, + name_str, params->seq); + free(name_str); + + /* EPM on FIB */ + const fib_t * fib = forwarder_get_fib(mapme->forwarder); + fib_entry_t * entry = fib_contains(fib, name); + if (!entry) { +#ifdef HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY + if (mapme_create_fib_entry(mapme, name, ingress_id) < 0) { + ERROR("Failed to create FIB entry"); + return; + } +#else + INFO("Ignored update with no FIB entry"); + return; #endif + } - INFO(mapme, "[MAP-Me] - Adding multipath next hop on connection %d", - conn_in_id); - fibEntry_AddNexthop(fibEntry, conn_in_id); + mapme_tfib_t * tfib = TFIB(entry); + assert(tfib); - } else { // seq < fibSeq /* - * Face is propagating outdated information, we can just - * consider it as a prevHops. Send the special interest backwards with - * the new sequence number to reconciliate this outdated part of the - * arborescence. + * In case of multihoming, we might receive a message about our own prefix, we + * should never take it into account, nor send the IU backwards as a sign of + * outdated propagation. + * + * Detection: we receive a message initially sent by ourselves, ie a message + * for which the prefix has a local next hop in the FIB. */ - INFO( - mapme, - "[MAP-Me] - Update interest %d -> %d sent backwards on connection %d", - seq, fibSeq, conn_in_id); - mapme_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, - conn_in_id, send, false, false, 0); - } - - return true; - -ERR_ACK_SEND: - message_Release(&ack); -ERR_ACK_CREATE: - return false; -} + // XXX NOT IN VPP ? + if (fib_entry_has_local_nexthop(entry)) { + INFO("Received original interest... Update complete"); + return; + } -void mapme_onSpecialInterestAck(const MapMe *mapme, const uint8_t *msgBuffer, - unsigned conn_in_id, hicn_prefix_t *prefix, - mapme_params_t *params) { - INFO(mapme, "[MAP-Me] Receive IU/IN Ack on connection %d", conn_in_id); - - const Name * name = - name_CreateFromPacket(msgBuffer, MessagePacketType_ContentObject); - name_setLen((Name*) name, prefix->len); - char * name_str = name_ToString(name); - INFO(mapme, "[MAP-Me] Received ack for name prefix=%s seq=%d on conn id=%d", - name_str, params->seq, conn_in_id); - free(name_str); - - FIB *fib = forwarder_getFib(mapme->forwarder); - FibEntry *fibEntry = fib_Contains(fib, name); - if (!fibEntry) { - return; - } - parcAssertNotNull(fibEntry, - "No corresponding FIB entry for name contained in IU Ack"); - - /* Test if the latest pending update has been ack'ed, otherwise just ignore */ - seq_t seq = params->seq; - if (seq != INVALID_SEQ) { - seq_t fibSeq = TFIB(fibEntry)->seq; - - if (seq < fibSeq) { - - /* If we receive an old ack: - * - either the connection is still a next hop and we have to ignore - * the ack until we receive a further update with higher seqno - * - or the connection is no more to be informed and the ack is - * sufficient and we can remove future retransmissions + mapme_event_t event = MAPME_EVENT_UNDEFINED; + if (params->seq > tfib->seq) { + DEBUG("seq %d > fib_seq %d, updating seq and next hops", params->seq, + tfib->seq); + /* This has to be done first to allow processing ack */ + // XXX this should even be done before sending ack, as in VPP. + tfib->seq = params->seq; + + /* + * Move nexthops to TFIB... but ingress_id that lands in nexthops + * + * This could might optimized for situations where nothing changes, but + * this is very unlikely if not impossible... + * */ + unsigned prevhop; + nexthops_foreach(&entry->nexthops, prevhop, { + nexthops_add(&tfib->nexthops, prevhop); + }); + nexthops_remove(&tfib->nexthops, ingress_id); + nexthops_add(&tfib->nexthops, ingress_id); + + event = MAPME_EVENT_NH_SET; + + // XXX tell things are complete if we have no IU to send + + } else if (params->seq == tfib->seq) { + /* + * Multipath, multihoming, multiple producers or duplicate interest + * + * In all cases, we assume the propagation was already done when the first + * interest with the same sequence number was received, so we stop here + * + * It might happen that the previous AP has still a connection to the + * producer and that we received back our own IU. In that case, we just + * need to Ack and ignore it. */ + DEBUG("params.seq %d == fib_seq %d, adding nethop %d", params->seq, + tfib->seq, ingress_id); + + /* Move ingress to nexthops (and eventually remove it from TFIB) */ + nexthops_add(&entry->nexthops, ingress_id); + nexthops_remove(&tfib->nexthops, ingress_id); + + event = MAPME_EVENT_NH_ADD; - INFO(mapme, - "[MAP-Me] - Ignored special interest Ack with seq=%u, expected %u", - seq, fibSeq); - return; + } else { // params->seq < tfib->seq + /* + * Face is propagating outdated information, we can just + * consider it as a prevHops. Send the special interest backwards with + * the new sequence number to reconciliate this outdated part of the + * arborescence. + */ + DEBUG("params.seq %d < fib_seq %d, sending backwards on face %d", params->seq, tfib->seq, ingress_id); + nexthops_remove(&entry->nexthops, ingress_id); + nexthops_add(&tfib->nexthops, ingress_id); + + event = MAPME_EVENT_PH_ADD; } - } - - /* - * Ignore the Ack if no TFIB is present, or it has no corresponding entry - * with the ingress face. - * Note: previously, we were creating the TFIB entry - */ - if (!TFIB(fibEntry)) { - INFO(mapme, "[MAP-Me] - Ignored ACK for prefix with no TFIB entry"); - return; - } - - PARCEventTimer *timer = - (PARCEventTimer *)mapmeTFIB_Get(TFIB(fibEntry), conn_in_id); - if (!timer) { - INFO(mapme, - "[MAP-Me] - Ignored ACK for prefix not having the Connection in " - "TFIB entry. Possible duplicate ?"); - return; - } - - /* Stop timer and remove entry from TFIB */ - parcEventTimer_Stop(timer); - mapmeTFIB_Remove(TFIB(fibEntry), conn_in_id); - - INFO(mapme, "[MAP-Me] - Removing TFIB entry for ack on connection %d", - conn_in_id); - - /* We need to update the timestamp only for IU Acks, not for IN Acks */ - if (params->type == UPDATE_ACK) { - INFO(mapme, "[MAP-Me] - Updating LastAckedUpdate"); - TFIB(fibEntry)->lastAckedUpdate = forwarder_GetTicks(mapme->forwarder); - } + + /* Don't trigger events for notification unless we need to send interests backwards */ + if ((params->type != UPDATE) && (event != MAPME_EVENT_PH_ADD)) + return; + + mapme_on_event(mapme, event, entry, ingress_id); } -/*----------------------------------------------------------------------------- - * Overloaded functions - *----------------------------------------------------------------------------*/ +static +void +mapme_on_data(mapme_t *mapme, const uint8_t * packet, + unsigned ingress_id, hicn_prefix_t *prefix, + mapme_params_t * params) +{ + INFO("Receive IU/IN Ack on connection %d", ingress_id); -/* - * @abstract returns where to forward a normal interests(nexthops) defined by - * mapme, it also set the sequnence number properly if needed - */ + const Name * name = + name_CreateFromPacket(packet, MESSAGE_TYPE_DATA); + name_setLen((Name*) name, prefix->len); -/****************************************************************************** - * Public functions (exposed in the .h) - ******************************************************************************/ + char * name_str = name_ToString(name); + DEBUG("Received ack for name prefix=%s seq=%d on conn id=%d", + name_str, params->seq, ingress_id); + free(name_str); -/* - * Returns true iif the message corresponds to a MAP-Me packet - */ -bool mapme_isMapMe(const uint8_t *packet) { - hicn_mapme_header_t * mapme = (hicn_mapme_header_t*)packet; - - switch(HICN_IP_VERSION(packet)) { - case 4: - if (mapme->v4.ip.protocol != IPPROTO_ICMP) - return false; - return HICN_IS_MAPME(mapme->v4.icmp_rd.type, mapme->v4.icmp_rd.code); - case 6: - if (mapme->v6.ip.nxt != IPPROTO_ICMPV6) - return false; - return HICN_IS_MAPME(mapme->v6.icmp_rd.type, mapme->v6.icmp_rd.code); - default: - return false; - } + const fib_t * fib = forwarder_get_fib(mapme->forwarder); + fib_entry_t * entry = fib_contains(fib, name); + if (!entry) { + INFO("Ignored ACK with no corresponding FIB entry"); + return; + } + mapme_tfib_t * tfib = TFIB(entry); + + /* + * As we always retransmit IU with the latest seq, we are not interested in + * ACKs with inferior seq + */ + if (params->seq < tfib->seq) { + INFO("Ignored ACK with seq %d < %d", params->seq, tfib->seq); + return; + } + + nexthops_remove(&tfib->nexthops, ingress_id); + mapme_on_event(mapme, MAPME_EVENT_PH_DEL, entry, ingress_id); + + /* We need to update the timestamp only for IU Acks, not for IN Acks */ + if (params->type == UPDATE_ACK) { + INFO(" - Updating LastAckedUpdate"); + tfib->last_acked_update = ticks_now(); + } } /** @@ -990,25 +903,53 @@ bool mapme_isMapMe(const uint8_t *packet) { * MAP-Me (eg. ICMP packets) and return higher level messages that can be * processed by MAP-Me core. */ -void mapme_Process(const MapMe *mapme, const uint8_t *msgBuffer, - unsigned conn_id) { - hicn_prefix_t prefix; - mapme_params_t params; - hicn_mapme_parse_packet(msgBuffer, &prefix, ¶ms); - - switch (params.type) { - case UPDATE: - case NOTIFICATION: - mapme_onSpecialInterest(mapme, msgBuffer, conn_id, &prefix, ¶ms); - break; - case UPDATE_ACK: - case NOTIFICATION_ACK: - mapme_onSpecialInterestAck(mapme, msgBuffer, conn_id, &prefix, ¶ms); - break; - default: - ERR(mapme, "[MAP-Me] Unknown message"); - break; - } +void +mapme_process(mapme_t *mapme, uint8_t *packet, unsigned conn_id) +{ + hicn_prefix_t prefix; + mapme_params_t params; + int rc = hicn_mapme_parse_packet(packet, &prefix, ¶ms); + if (rc < 0) + return; + + // XXX TYPE STR + DEBUG("Received interest type:%d seq:%d len:%d", params.type, params.seq, prefix.len); + + // XXX RENAME TYPES + switch (params.type) { + case UPDATE: + case NOTIFICATION: + mapme_on_interest(mapme, packet, conn_id, &prefix, ¶ms); + break; + case UPDATE_ACK: + case NOTIFICATION_ACK: + mapme_on_data(mapme, packet, conn_id, &prefix, ¶ms); + break; + default: + ERROR("Unknown message"); + break; + } } +/* + * Returns true iif the message corresponds to a MAP-Me packet + */ +bool mapme_match_packet(const uint8_t *packet) { + hicn_mapme_header_t * mapme = (hicn_mapme_header_t*)packet; + + switch(HICN_IP_VERSION(packet)) { + case 4: + if (mapme->v4.ip.protocol != IPPROTO_ICMP) + return false; + return HICN_IS_MAPME(mapme->v4.icmp_rd.type, mapme->v4.icmp_rd.code); + case 6: + if (mapme->v6.ip.nxt != IPPROTO_ICMPV6) + return false; + return HICN_IS_MAPME(mapme->v6.icmp_rd.type, mapme->v6.icmp_rd.code); + default: + return false; + } +} + + #endif /* WITH_MAPME */ |