static struct discovery_ipnd_neighbour_list_entry* discovery_ipnd_find_neighbour(const uint32_t eid) { if( discovery_status == 0 ) { // Not initialized yet return NULL; } const linkaddr_t addr = convert_eid_to_rime(eid); for(struct discovery_ipnd_neighbour_list_entry* entry = list_head(neighbour_list); entry != NULL; entry = list_item_next(entry)) { if( linkaddr_cmp(&entry->neighbour, &addr) ) { return entry; } } return NULL; }
/** * \brief Forward a bundle to its destination * \param entry Pointer to the routing entry of the bundle * \return FLOOD_ROUTE_RETURN_OK if queued, FLOOD_ROUTE_RETURN_CONTINUE if not queued and FLOOD_ROUTE_RETURN_FAIL of queue is full */ int routing_chain_forward_directly(struct routing_entry_t * entry) { struct discovery_neighbour_list_entry *nei_l = NULL; linkaddr_t dest_node; int h = 0; /* Who is the destination for this bundle? */ dest_node = convert_eid_to_rime(entry->destination_node); /* First step: check, if the destination is one of our neighbours */ for( nei_l = DISCOVERY.neighbours(); nei_l != NULL; nei_l = list_item_next(nei_l) ) { if( linkaddr_cmp(&nei_l->neighbour, &dest_node) ) { break; } } if( nei_l == NULL ) { return CHAIN_ROUTE_RETURN_CONTINUE; } /* We know the neighbour, send it directly */ LOG(LOGD_DTN, LOG_ROUTE, LOGL_INF, "send bundle %lu to %u.%u directly", entry->bundle_number, nei_l->neighbour.u8[0], nei_l->neighbour.u8[1]); /* Mark bundle as busy */ entry->flags |= ROUTING_FLAG_IN_TRANSIT; /* And queue it for sending */ h = routing_chain_send_bundle(entry->bundle_number, &nei_l->neighbour); if( h < 0 ) { /* Enqueuing bundle failed - unblock it */ entry->flags &= ~ROUTING_FLAG_IN_TRANSIT; /* If sending the bundle fails, all other will likely also fail */ return CHAIN_ROUTE_RETURN_FAIL; } /* We do not want the bundle to be sent to anybody else at the moment, so: */ return CHAIN_ROUTE_RETURN_OK; }
/** * \brief Callback function informing us about the status of a sent bundle * \param ticket CL transmit ticket of the bundle * \param status status code */ void routing_chain_bundle_sent(struct transmit_ticket_t * ticket, uint8_t status) { struct routing_list_entry_t * n = NULL; struct routing_entry_t * entry = NULL; // Tell the agent to call us again to resubmit bundles routing_chain_schedule_resubmission(); // Find the bundle in our internal storage for( n = list_head(routing_list); n != NULL; n = list_item_next(n) ) { entry = (struct routing_entry_t *) MMEM_PTR(&n->entry); if( entry->bundle_number == ticket->bundle_number ) { break; } } if( n == NULL ) { /* Free up the ticket */ convergence_layer_free_transmit_ticket(ticket); LOG(LOGD_DTN, LOG_ROUTE, LOGL_ERR, "Bundle not in storage"); return; } /* Bundle is not busy anymore */ entry->flags &= ~ROUTING_FLAG_IN_TRANSIT; if( status == ROUTING_STATUS_NACK || status == ROUTING_STATUS_FAIL ) { // NACK = Other side rejected the bundle, try again later // FAIL = Transmission failed /* Free up the ticket */ convergence_layer_free_transmit_ticket(ticket); return; } if( status == ROUTING_STATUS_ERROR ) { LOG(LOGD_DTN, LOG_ROUTE, LOGL_ERR, "Bundle %lu has fatal error, deleting", ticket->bundle_number); /* Bundle failed permanently, we can delete it because it will never be delivered anyway */ entry->flags = 0; routing_chain_check_keep_bundle(ticket->bundle_number); /* Free up the ticket */ convergence_layer_free_transmit_ticket(ticket); return; } // Here: status == ROUTING_STATUS_OK statistics_bundle_outgoing(1); #ifndef TEST_DO_NOT_DELETE_ON_DIRECT_DELIVERY linkaddr_t dest_n = convert_eid_to_rime(entry->destination_node); if (linkaddr_cmp(&ticket->neighbour, &dest_n)) { LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "bundle sent to destination node"); uint32_t bundle_number = ticket->bundle_number; /* Free up the ticket */ convergence_layer_free_transmit_ticket(ticket); ticket = NULL; // Unset the forward flag entry->flags &= ~ROUTING_FLAG_FORWARD; routing_chain_check_keep_bundle(bundle_number); return; } LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "bundle for %u.%u delivered to %u.%u", dest_n.u8[0], dest_n.u8[1], ticket->neighbour.u8[0], ticket->neighbour.u8[1]); #endif if (entry->send_to < ROUTING_NEI_MEM) { linkaddr_copy(&entry->neighbours[entry->send_to], &ticket->neighbour); entry->send_to++; LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "bundle %lu sent to %u nodes", ticket->bundle_number, entry->send_to); } else if (entry->send_to >= ROUTING_NEI_MEM) { // Here we can delete the bundle from storage, because it will not be routed anyway LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "bundle %lu sent to max number of nodes, deleting", ticket->bundle_number); // Unset the forward flag entry->flags &= ~ROUTING_FLAG_FORWARD; routing_chain_check_keep_bundle(ticket->bundle_number); } /* Free up the ticket */ convergence_layer_free_transmit_ticket(ticket); }
/** * \brief Forward a bundle to the next hop * \param entry Pointer to the routing entry of the bundle * \return FLOOD_ROUTE_RETURN_OK if queued, FLOOD_ROUTE_RETURN_CONTINUE if not queued and FLOOD_ROUTE_RETURN_FAIL of queue is full */ int routing_chain_forward_normal(struct routing_entry_t * entry) { struct discovery_neighbour_list_entry *nei_l = NULL; linkaddr_t source_node; int h = 0; /* What is the source node of the bundle? */ source_node = convert_eid_to_rime(entry->source_node); for( nei_l = DISCOVERY.neighbours(); nei_l != NULL; nei_l = list_item_next(nei_l) ) { int sent = 0; int i; if( linkaddr_cmp(&nei_l->neighbour, &source_node) ) { LOG(LOGD_DTN, LOG_ROUTE, LOGL_INF, "not sending bundle to originator"); /* Go on with the next neighbour */ continue; } if( linkaddr_cmp(&nei_l->neighbour, &entry->received_from_node) ) { LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "not sending back to sender"); /* Go on with the next neighbour */ continue; } /* Did we forward the bundle to that neighbour already? */ for (i = 0 ; i < ROUTING_NEI_MEM ; i++) { if ( linkaddr_cmp(&entry->neighbours[i], &nei_l->neighbour)){ LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "bundle %lu already sent to node %u.%u!", entry->bundle_number, entry->neighbours[i].u8[0], entry->neighbours[i].u8[1]); sent = 1; // Break the (narrowest) for break; } } uint32_t neighbour_eid = convert_rime_to_eid(&nei_l->neighbour); if( entry->destination_node > entry->source_node ) { // Going "up" the chain if( neighbour_eid <= dtn_node_id ) { LOG(LOGD_DTN, LOG_ROUTE, LOGL_INF, "Not sending bundle %lu to %u.%u, because %lu <= %lu", entry->bundle_number, nei_l->neighbour.u8[0], nei_l->neighbour.u8[1], neighbour_eid, dtn_node_id); sent = 1; } else { LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "Sending bundle %lu to %u.%u, because %lu > %lu", entry->bundle_number, nei_l->neighbour.u8[0], nei_l->neighbour.u8[1], neighbour_eid, dtn_node_id); } } else if( entry->destination_node < entry->source_node ) { // Going "down" the chain if( neighbour_eid >= dtn_node_id ) { LOG(LOGD_DTN, LOG_ROUTE, LOGL_INF, "Not sending bundle %lu to %u.%u, because %lu >= %lu", entry->bundle_number, nei_l->neighbour.u8[0], nei_l->neighbour.u8[1], neighbour_eid, dtn_node_id); sent = 1; } else { LOG(LOGD_DTN, LOG_ROUTE, LOGL_DBG, "Sending bundle %lu to %u.%u, because %lu < %lu", entry->bundle_number, nei_l->neighbour.u8[0], nei_l->neighbour.u8[1], neighbour_eid, dtn_node_id); } } else { LOG(LOGD_DTN, LOG_ROUTE, LOGL_ERR, "Source and destination nodes are the same?"); sent = 1; } if(!sent) { LOG(LOGD_DTN, LOG_ROUTE, LOGL_INF, "send bundle %lu to %u.%u", entry->bundle_number, nei_l->neighbour.u8[0], nei_l->neighbour.u8[1]); /* Mark bundle as busy */ entry->flags |= ROUTING_FLAG_IN_TRANSIT; /* And queue it for sending */ h = routing_chain_send_bundle(entry->bundle_number, &nei_l->neighbour); if( h < 0 ) { /* Enqueuing bundle failed - unblock it */ entry->flags &= ~ROUTING_FLAG_IN_TRANSIT; /* If sending the bundle fails, all other will likely also fail */ return CHAIN_ROUTE_RETURN_FAIL; } /* Only one bundle at a time */ return CHAIN_ROUTE_RETURN_OK; } } return CHAIN_ROUTE_RETURN_CONTINUE; }
/** * \brief Save neighbour to local cache * \param neighbour Address of the neighbour */ static int discovery_ipnd_save_neighbour(const uint32_t eid, const cl_addr_t* const addr) { if( discovery_status == 0 ) { // Not initialized yet return -1; } // If we know that neighbour already, no need to re-add it if(discovery_ipnd_refresh_neighbour(addr) >= 1) { return 0; } /* add informations to an existing LOWPAN entry, * if the eid matches */ if (addr->isIP) { struct discovery_ipnd_neighbour_list_entry* const entry = discovery_ipnd_find_neighbour(eid); if (entry != NULL) { /* entry matching the EID already existing * add the missing parameters */ discovery_ipnd_neighbour_update_ip(addr, entry); return 1; } } struct discovery_ipnd_neighbour_list_entry* const entry = memb_alloc(&neighbour_mem); if( entry == NULL ) { LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_WRN, "no more space for neighbours"); return -2; } LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_INF, "Found new neighbour ipn:%lu", eid); // Clean the entry struct memset(entry, 0, sizeof(struct discovery_ipnd_neighbour_list_entry)); entry->neighbour = convert_eid_to_rime(eid); if (addr->isIP) { discovery_ipnd_neighbour_update_ip(addr, entry); } else { entry->addr_type = CL_TYPE_FLAG_DGRAM_LOWPAN; ip_addr_copy(entry->ip, *IP_ADDR_ANY); entry->port = 0; entry->timestamp_last_lowpan = clock_seconds(); /* rime and eid should be the same * otherwise the wrong destination will be used * in further processing */ // TODO remove const cast if (convert_rime_to_eid((linkaddr_t*)&addr->lowpan) != eid) { LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_ERR, "The EID %u and the PAN addresse %u.%u are different. This is not supported.", eid, addr->lowpan.u8[0], addr->lowpan.u8[1]); } } entry->timestamp_discovered = clock_seconds(); // Notify the statistics module statistics_contacts_up(eid); list_add(neighbour_list, entry); // We have found a new neighbour, now go and notify the agent const event_container_t event = { .event = dtn_beacon_event, .linkaddr = &entry->neighbour }; agent_send_event(&event); return 2; } /** * \brief Returns the list of currently known neighbours * \return Pointer to list with neighbours */ struct discovery_neighbour_list_entry * discovery_ipnd_list_neighbours() { return list_head(neighbour_list); } /** * \brief Stops pending discoveries */ void discovery_ipnd_stop_pending() { } static void discovery_ipnd_start(clock_time_t duration, uint8_t index) { // Send at the beginning of a cycle discovery_ipnd_send(); } static void discovery_ipnd_stop() { // Send at the end of a cycle discovery_ipnd_send(); } void discovery_ipnd_clear() { struct discovery_ipnd_neighbour_list_entry * entry; LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_INF, "Clearing neighbour list"); while(list_head(neighbour_list) != NULL) { entry = list_head(neighbour_list); // Notify the statistics module statistics_contacts_down(&entry->neighbour, entry->timestamp_last_lowpan - entry->timestamp_discovered); // TODO statistics_contacts_down(&entry->neighbour, entry->timestamp_last_ip - entry->timestamp_discovered); /* call convergence_layer_neighbour_down for all discovered address types */ cl_addr_t addr; if (discovery_neighbour_to_addr((struct discovery_neighbour_list_entry*)entry, CL_TYPE_FLAG_DGRAM_LOWPAN, &addr) >= 0) { convergence_layer_dgram_neighbour_down(&addr); } if (discovery_neighbour_to_addr((struct discovery_neighbour_list_entry*)entry, CL_TYPE_FLAG_DGRAM_UDP, &addr) >= 0) { convergence_layer_dgram_neighbour_down(&addr); } list_remove(neighbour_list, entry); memb_free(&neighbour_mem, entry); } } static int discovery_ipnd_check_neighbour_timeout(struct discovery_ipnd_neighbour_list_entry* const entry, const unsigned long timestamp_last, const uint8_t addr_type) { const unsigned long diff = clock_seconds() - timestamp_last; if (diff > DISCOVERY_NEIGHBOUR_TIMEOUT) { LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_WRN, "Neighbour ipn:%u timed out: %lu vs. %lu = %lu", convert_rime_to_eid(&entry->neighbour), clock_seconds(), timestamp_last, diff); cl_addr_t addr; if (discovery_neighbour_to_addr((struct discovery_neighbour_list_entry*)entry, addr_type, &addr) < 0) { // TODO possibly remove the entry LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_ERR, "Could not convert address of discovery entry"); return -1; } else { discovery_ipnd_delete_neighbour(&addr); return 1; } } return 0; } static void discovery_ipnd_remove_stale_neighbours(const TimerHandle_t timer) { bool changed = true; while (changed) { changed = false; for(struct discovery_ipnd_neighbour_list_entry* entry = list_head(neighbour_list); entry != NULL; entry = list_item_next(entry)) { const bool lowpan_exists = (entry->addr_type & CL_TYPE_FLAG_DGRAM_LOWPAN); const bool ip_exists = (entry->addr_type & CL_TYPE_FLAG_DGRAM_UDP); if (!lowpan_exists && !ip_exists) { LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_ERR, "Entry without an vaild address. Deleting"); discovery_ipnd_destroy_neighbour(entry); changed = true; break; } if (lowpan_exists && discovery_ipnd_check_neighbour_timeout(entry, entry->timestamp_last_lowpan, CL_TYPE_FLAG_DGRAM_LOWPAN) > 0) { changed = true; /* not break here, * because possibly the next address type is * timed out too. * So the full entry has to be deleted */ } if (ip_exists && discovery_ipnd_check_neighbour_timeout(entry, entry->timestamp_last_ip, CL_TYPE_FLAG_DGRAM_UDP) > 0) { changed = true; /* not break here, * because possibly the next address type is * timed out too. * So the full entry has to be deleted */ } /* list has changed * so reload list entries */ if (changed) { break; } } } }