/****************************************************************************** Copyright 2009, Freie Universitaet Berlin (FUB). All rights reserved. These sources were developed at the Freie Universitaet Berlin, Computer Systems and Telematics group (http://cst.mi.fu-berlin.de). ------------------------------------------------------------------------------- This file is part of RIOT. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RIOT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ . -------------------------------------------------------------------------------- For further information and questions please use the web site http://scatterweb.mi.fu-berlin.de and the mailinglist (subscription via web site) scatterweb@lists.spline.inf.fu-berlin.de *******************************************************************************/ /** * @file * @internal * @brief Micro Mesh Routing * * @author Freie Universität Berlin, Computer Systems & Telematics * @author Thomas Hillebrandt * @version $Revision: 3854 $ * * @note $Id: mmr.c 3854 2011-12-06 15:27:01Z hwill $ */ #include "configure.h" #include "mmr.h" #include #include #include #include "net.h" #include "clock.h" #include "utimer.h" #include "kernel.h" #include "thread.h" #include "msg.h" #define LEVEL_INFO 2 ///< All messages are printed #define LEVEL_WARN 1 ///< Only warnings and error messages are printed #define LEVEL_ERROR 0 ///< Only error messages are printed #define MMR_INFO_LEVEL LEVEL_WARN ///< Current information level #define DEBUG(...) //#define DEBUG(...) printf(__VA_ARGS__) #define CONSTANT_SECOND (1) #define RREQ_ID_SEQUENCE_NUMBER_START (1) #define RREQ_THRESHOLD (3) #define RREQ_NONE (0xFF) /* Send no RREQs for these messages, value */ /* must be greater than RREQ_THRESHOLD */ #define TTL_START (1) #define TTL_THRESHOLD (10) #define RREQ_TIMEOUT_BASE (2*CONSTANT_SECOND) #define RREQ_TIMEOUT_PER_TTL (1*CONSTANT_SECOND) /*---------------------------------------------------------------------------*/ // Message queue data structures /*---------------------------------------------------------------------------*/ #define MESSAGE_QUEUE_SIZE (20) typedef struct { net_message_t message; volatile uint32_t timestamp; uint8_t retry_count; } message_queue_entry_t; static message_queue_entry_t message_queue[MESSAGE_QUEUE_SIZE]; /*---------------------------------------------------------------------------*/ // RREQ-Timeout data structures /*---------------------------------------------------------------------------*/ static struct utimer ut; static volatile bool rreq_to_active = false; /* RREQ timeout active bit */ static const char *rreq_timeout_process_name = "mmrd"; static uint16_t rreq_timeout_process_pid; static void rreq_timeout_process(void); static void post_next_rreq_timeout(void); /*---------------------------------------------------------------------------*/ // Statistic data structures /*---------------------------------------------------------------------------*/ typedef struct mmr_stat { uint32_t rreq_originated; uint32_t rrep_originated; uint32_t rerr_originated; uint32_t rreq_received; uint32_t rrep_received; uint32_t rerr_received; uint32_t messages_no_route_found; /* RREQ found no route */ uint32_t messages_no_route_avail_on_forward; /* Forwarding: no route in route table */ uint32_t messages_broken_link_on_forward; /* Forwarding: broken link detected */ uint32_t rreq_duplicated; } mmr_stat_t; static mmr_stat_t mmr_stats; /*---------------------------------------------------------------------------*/ /** * Returns time of RTC in seconds. * * @return Time of RTC in seconds */ static uint32_t rtc_now(void) { return (uint32_t)(clock_get_systemtime() / 1000); } /*---------------------------------------------------------------------------*/ // Routing table management /*---------------------------------------------------------------------------*/ /** * @brief Extract route information and store them in route table. * * @param local_addr Local network address of this node * @param length Length of address list * @param list Address list with route information */ static void rt_extract_routes(uint16_t local_addr, uint8_t length, uint16_t *list) { DEBUG("call [%u]: rt_extract_routes\n", fk_thread->pid); uint16_t net_id = NETWORK_ADDR_BC(list[0]); /* BC address of source of RREQ */ route_table_entry_t *rte = rt_lookup_route(net_id); /* Should exist (preconfigured) */ if (rte == NULL) { DEBUG("exit [%u]: rt_extract_routes\n", fk_thread->pid); return; /* else exit here */ } int i = 0; while (i < length && list[i] != local_addr) { i++; } if (i == length) { DEBUG("exit [%u]: rt_extract_routes\n", fk_thread->pid); return; } int pos = i; int leftNeighbour = -1; int rightNeighbour = -1; if (pos > 0) { leftNeighbour = list[pos - 1]; } if (pos + 1 != length) { rightNeighbour = list[pos + 1]; } i = 0; while (i < length) { uint16_t next = list[i]; if (local_addr != next) { int distance = pos - i; int router = leftNeighbour; if (distance < 0) { router = rightNeighbour; distance *= -1; } rt_add_route(next, (uint16_t)router, (uint8_t)distance, rte->interface_id); } i++; } DEBUG("exit [%u]: rt_extract_routes\n", fk_thread->pid); } /*---------------------------------------------------------------------------*/ // Message queue management /*---------------------------------------------------------------------------*/ /** * @brief Add a message to the message queue. * * @param msg The packet to add to the queue * * @return A pointer to a message queue entry or NULL if * message queue is full. */ static message_queue_entry_t *mq_add(net_message_t *msg) { DEBUG("call [%u]: mq_add\n", fk_thread->pid); /* Holds eventually first active RREQ to same destination */ message_queue_entry_t *pFirstFoundDup = NULL; /* Find the first active RREQ to this destination */ int i; for (i = 0; i < MESSAGE_QUEUE_SIZE; i++) { if (message_queue[i].timestamp != 0 && message_queue[i].message.destination == msg->destination && message_queue[i].retry_count != RREQ_NONE) { DEBUG("%s FOUND Duplicated Request to %u.%u in route req queue\n", __FUNCTION__, (0xFF00 & msg->destination) >> 8, (0xFF & msg->destination)); /* Save the first found entry to modify later if insertion was successful */ pFirstFoundDup = &message_queue[i]; break; } } /* If RREQ for same destination found then reset values * even if the new message will get dropped later on because of * limited queue space. Route to this destination gets queried * again for sure so make new RREQ as soon as possible... */ if (pFirstFoundDup != NULL) { pFirstFoundDup->retry_count = 0; pFirstFoundDup->timestamp = 1; mmr_stats.rreq_duplicated++; } /* Find free position to insert new message */ for (i = 0; i < MESSAGE_QUEUE_SIZE; i++) { if (message_queue[i].timestamp == 0) { /* Free position found, add entry */ message_queue[i].message = *msg; if (pFirstFoundDup != NULL) { /* There is already a RREQ for this destination, so don't * generate a new one */ message_queue[i].retry_count = RREQ_NONE; } else { /* Set initial RREQ retry counter to zero */ message_queue[i].retry_count = 0; } message_queue[i].timestamp = 1; DEBUG("exit [%u]: mq_add\n", fk_thread->pid); return &message_queue[i]; } } DEBUG("exit [%u]: mq_add\n", fk_thread->pid); return NULL; } /** * @brief Count messages for given destination. * * @param dst Destination address * * @return The number of messages for the destination. */ static int mq_msgs_for_destination(uint16_t dst) { DEBUG("call [%u]: mq_msgs_for_destination\n", fk_thread->pid); int i, dst_count = 0; for (i = 0; i < MESSAGE_QUEUE_SIZE; i++) { if (message_queue[i].timestamp != 0 && message_queue[i].message.destination == dst) { dst_count++; } } DEBUG("exit [%u]: mq_msgs_for_destination\n", fk_thread->pid); return dst_count; } /** * @brief Remove all messages for given destination out of message queue. * * @param dst Destination address */ static void mq_remove_msgs_for_destination(uint16_t dst) { DEBUG("call [%u]: mq_remove_msgs_for_destination\n", fk_thread->pid); int i; for (i = 0; i < MESSAGE_QUEUE_SIZE; i++) { if (message_queue[i].timestamp != 0 && message_queue[i].message.destination == dst) { message_queue[i].timestamp = 0; } } DEBUG("exit [%u]: mq_remove_msgs_for_destination\n", fk_thread->pid); } /** * @brief Send all queued messages for given destination. * * @param dst Destination address */ static void mq_dequeue_and_send(uint16_t dst) { int i; DEBUG("call [%u]: mq_dequeue_and_send\n", fk_thread->pid); /* Stop any pending RREQ-Timeout, it's possibly handled now */ rreq_to_active = false; utimer_remove(&ut); /* Prioritize packets for given destination, route entry should exist */ route_table_entry_t *rte = rt_lookup_route(dst); if (rte != NULL) { for (i = 0; i < MESSAGE_QUEUE_SIZE; i++) { if (message_queue[i].timestamp != 0 && message_queue[i].message.destination == dst) { bool res = net_enqueue_for_transmission(&message_queue[i].message, rte->interface_id, rte->gateway, true); if (res) { message_queue[i].timestamp = 0; } } } } /* Now all other packets */ for (i = 0; i < MESSAGE_QUEUE_SIZE; i++) { if (message_queue[i].timestamp != 0 && message_queue[i].message.destination != dst) { route_table_entry_t *rte = rt_lookup_route(message_queue[i].message.destination); if (rte != NULL) { bool res = net_enqueue_for_transmission(&message_queue[i].message, rte->interface_id, rte->gateway, true); if (res) { message_queue[i].timestamp = 0; } } } } /* This function was triggered either by RREP packet or RREQ-Timeout. */ /* So update or set new RREQ-Timeout for other packets in queue. */ post_next_rreq_timeout(); DEBUG("exit [%u]: mq_dequeue_and_send\n", fk_thread->pid); } /*---------------------------------------------------------------------------*/ // Initialization of MMR layer /*---------------------------------------------------------------------------*/ void mmr_init(void) { rt_init(); memset(message_queue, 0, sizeof(message_queue_entry_t) * MESSAGE_QUEUE_SIZE); rreq_timeout_process_pid = thread_create(2500, PRIORITY_MMREQ, CREATE_STACKTEST, rreq_timeout_process, rreq_timeout_process_name); } /*---------------------------------------------------------------------------*/ // Send & receive functions /*---------------------------------------------------------------------------*/ /** * @brief Tests if the net message contains a RERR. * * @param msg The net message to test * * @return true if the net message contains a RERR; false otherwise. */ static bool is_route_error(net_message_t *msg) { if (msg->protocol == LAYER_2_PROTOCOL_MMR) { /* First byte in {RREQ, RREP, RERR} is always type */ if (msg->payload[0] == MMR_TYPE_RERR) { return true; } } return false; } /** * @brief Generates a route reply message. * * @param rreq_msg Corresponding route request message */ static void generate_route_reply_message(mmr_rreq_message_t *rreq_msg) { DEBUG("call [%u]: generate_route_reply_message\n", fk_thread->pid); /* Create RREP message */ mmr_rrep_message_t rrep_msg; rrep_msg.type = MMR_TYPE_RREP; rrep_msg.length = rreq_msg->length; rrep_msg.destination = rreq_msg->source; rrep_msg.source = rreq_msg->destination; memcpy(rrep_msg.address, rreq_msg->address, rreq_msg->length * sizeof(uint16_t)); /* Create MMR message containing the RREP message */ net_message_t net_msg; net_msg.protocol = LAYER_2_PROTOCOL_MMR; net_msg.flags_tos = PRIORITY_ALARM; net_msg.seq_clr_id = 0; net_msg.ttl = TTL_THRESHOLD; net_msg.source = rrep_msg.source; net_msg.destination = rrep_msg.destination; memcpy(net_msg.payload, (void *)&rrep_msg, sizeof(mmr_rrep_message_t)); /* Source address must exist in route table to find correct */ /* interface id and next hop (should be created by RREQ) */ route_table_entry_t *rte = rt_lookup_route(net_msg.destination); if (rte != NULL) { /* Send message to next hop */ mmr_stats.rrep_originated++; net_enqueue_for_transmission(&net_msg, rte->interface_id, rte->gateway, true); } DEBUG("exit [%u]: generate_route_reply_message\n", fk_thread->pid); } /** * @brief Generates a route error message. * * @param dst Destination address of RERR packet * @param gateway Next hop network address of RERR packet * @param intf Interface id of RERR packet * @param type Error type of RERR packet * @param type_data Type specific data of RERR packet */ static void generate_route_error_message(uint16_t dst, uint16_t gateway, int intf, uint8_t type, uint16_t type_data) { DEBUG("call [%u]: generate_route_error_message\n", fk_thread->pid); /* Define RERR message */ mmr_rerr_message_t rerr_msg; rerr_msg.type = MMR_TYPE_RERR; rerr_msg.error_type = type; rerr_msg.type_specific_info = type_data; /* Wrap RERR message in net message */ net_message_t net_msg; net_msg.protocol = LAYER_2_PROTOCOL_MMR; net_msg.flags_tos = PRIORITY_DATA; net_msg.seq_clr_id = 0; net_msg.ttl = TTL_THRESHOLD; net_msg.source = net_get_address_in_subnet(dst); net_msg.destination = dst; memcpy(net_msg.payload, (void *)&rerr_msg, sizeof(mmr_rerr_message_t)); /* Send message to next hop */ mmr_stats.rerr_originated++; net_enqueue_for_transmission(&net_msg, intf, gateway, true); DEBUG("exit [%u]: generate_route_error_message\n", fk_thread->pid); } /** * @brief Receive a route request message. * * @param msg The route request packet * @param packet_info Additional packet information */ static void receive_route_request_message(mmr_rreq_message_t *msg, packet_info_t *packet_info) { DEBUG("call [%u]: receive_route_request_message\n", fk_thread->pid); uint16_t my_addr = net_get_address_in_subnet(msg->source); #if (MMR_INFO_LEVEL >= LEVEL_WARN) if (my_addr == 0) { puts("MMR [WARN]: received RREQ with unknown network part of source address"); puts("MMR [WARN]: => can't find own net address in sub net!"); } #endif /* If address list of RREQ message has enough space */ if (msg->length < ADDRESS_LIST_SIZE) { /* append our node id to list */ msg->address[msg->length++] = my_addr; /* add routes with overhearing */ rt_extract_routes(my_addr, msg->length, msg->address); } /* Distance between sender and receiver is too long, discard packet */ else { /* Drop RREQ packet => set TTL to zero */ *packet_info->ttl_ptr = 0; DEBUG("exit [%u]: receive_route_request_message\n", fk_thread->pid); return; } /* If RREQ message was send to us, then send RREP message */ if (msg->destination == my_addr) { /* Don't forward RREQ packet any further => set TTL to zero */ *packet_info->ttl_ptr = 0; generate_route_reply_message(msg); } DEBUG("exit [%u]: receive_route_request_message\n", fk_thread->pid); } /** * @brief Receive a route reply message. * * @param msg The route reply packet * @param packet_info Additional packet information */ static void receive_route_reply_message(mmr_rrep_message_t *msg, packet_info_t *packet_info) { DEBUG("call [%u]: receive_route_reply_message\n", fk_thread->pid); /* RREP received: Send out queued packets for which routes are now known */ mq_dequeue_and_send(msg->source); DEBUG("exit [%u]: receive_route_reply_message\n", fk_thread->pid); } /** * @brief Receive a route error message. * * @param msg The route error packet * @param packet_info Additional packet information */ static void receive_route_error_message(mmr_rerr_message_t *msg, packet_info_t *packet_info) { DEBUG("call [%u]: receive_route_error_message\n", fk_thread->pid); switch(msg->error_type) { case RERR_NODE_UNREACHABLE: rt_remove_route(msg->type_specific_info); break; default: #if (MMR_INFO_LEVEL >= LEVEL_INFO) puts("MMR [INFO]: RERR error type is unknown"); #endif break; } DEBUG("exit [%u]: receive_route_error_message\n", fk_thread->pid); } /** * @brief Computes the RREQ timeout period, given a * TTL and destination address value. * * @param ttl Time to live * @param dst Network destination address * * @return RREQ timeout period in seconds */ static int compute_rreq_timeout(int ttl, uint16_t dst) { int t_hop = net_get_interface_transmission_duration(dst); if (t_hop == -1) { t_hop = RREQ_TIMEOUT_PER_TTL * ttl; } else { t_hop = (t_hop * ttl + 999) / 1000; } return RREQ_TIMEOUT_BASE + 2 * t_hop; } /** * @brief Broadcast a RREQ message. * * A single route request can repeatedly broadcast RREQ messages, * with increasing TTL value, until a route has been found. * * @param mq_entry Pointer to a message queue entry (the packet * for which to find the route) */ static void rreq_broadcast(message_queue_entry_t *mq_entry) { DEBUG("call [%u]: rreq_broadcast\n", fk_thread->pid); if (mq_entry->retry_count == RREQ_NONE) { DEBUG("call [%u]: rreq duplicated do not send\n", fk_thread->pid); return; } /* Create RREQ message */ mmr_rreq_message_t rreq_msg; rreq_msg.type = MMR_TYPE_RREQ; rreq_msg.length = 1; rreq_msg.destination = mq_entry->message.destination; rreq_msg.source = mq_entry->message.source; rreq_msg.address[0] = mq_entry->message.source; /* Wrap RREQ message in net message */ net_message_t net_msg; net_msg.protocol = LAYER_2_PROTOCOL_MMR; net_msg.flags_tos = PRIORITY_DATA; net_msg.seq_clr_id = 0; net_msg.ttl = mq_entry->retry_count == 0 ? TTL_START : TTL_THRESHOLD; net_msg.source = rreq_msg.source; net_msg.destination = NETWORK_ADDR_BC(rreq_msg.destination); memcpy(net_msg.payload, (void *)&rreq_msg, sizeof(mmr_rreq_message_t)); /* Broadcast the net message */ mq_entry->retry_count++; mq_entry->timestamp = rtc_now(); /* Find the broadcast route table entry */ route_table_entry_t *rte = rt_lookup_route(net_msg.destination); if (rte != NULL) { /* Next hop address is broadcast address of lower layer */ net_enqueue_for_transmission(&net_msg, rte->interface_id, rte->gateway, true); } DEBUG("exit [%u]: rreq_broadcast\n", fk_thread->pid); } /** * @brief Find next RREQ to time out. Post event immediately or * with utimer. */ static void post_next_rreq_timeout(void) { DEBUG("call [%u]: post_next_rreq_timeout\n", fk_thread->pid); int i, j = -1; uint32_t now, next = 0xffffffff; for (i = 0; i < MESSAGE_QUEUE_SIZE; i++) { if ((message_queue[i].timestamp != 0) && (message_queue[i].retry_count != RREQ_NONE)) { int ttl = message_queue[i].retry_count == 1 ? TTL_START : TTL_THRESHOLD; int to = compute_rreq_timeout(ttl, message_queue[i].message.destination); if (message_queue[i].timestamp + to < next) { next = message_queue[i].timestamp + to; j = i; } } } if (j == -1) { DEBUG("exit [%u]: post_next_rreq_timeout\n", fk_thread->pid); return; } /* Stop any utimer */ rreq_to_active = false; utimer_remove(&ut); /* If current time greater than RREQ timeout value */ now = rtc_now(); if (now >= next) { /* Schedule RREQ-Timeout immediately */ msg m; m.type = MSG_TIMER; m.content.ptr = (char *)&message_queue[j]; rreq_to_active = true; if (msg_send(&m, rreq_timeout_process_pid, false) != 1) { /* Message could not be send (receiver not waiting), schedule * timer with minimum delay */ #if (MMR_INFO_LEVEL >= LEVEL_WARN) puts("MMR [WARN]: Immediate schedule of RREQ-Timeout failed, process not waiting!"); #endif utimer_set_wpid(&ut, 1, rreq_timeout_process_pid, &message_queue[j]); } } else { /* Set new utimer with time difference */ rreq_to_active = true; utimer_set_wpid(&ut, next - now, rreq_timeout_process_pid, &message_queue[j]); } DEBUG("exit [%u]: post_next_rreq_timeout\n", fk_thread->pid); } /** * This event is called periodically after a route request is originated, * until a route has been found. * * Each time it is called, it rebroadcasts the route request message with a * new rreq id and incremented TTL. */ static void rreq_timeout(message_queue_entry_t *mqe) { DEBUG("call [%u]: rreq_timeout\n", fk_thread->pid); /* Test if valid entry passed */ if (mqe->timestamp == 0) { #if (MMR_INFO_LEVEL >= LEVEL_WARN) puts("MMR [WARN]: invalid message queue entry for RREQ-Timeout"); #endif goto post_next_to; } /* See if route to destination was found */ route_table_entry_t *rte = rt_lookup_route(mqe->message.destination); /* If found and no messages in queue for destination: return (queued * packets are send on reception of RREP); If found but messages in queue: * trigger send immediately here!*/ if (rte != NULL) { int msg_count = mq_msgs_for_destination(mqe->message.destination); if (msg_count > 0) { mq_dequeue_and_send(mqe->message.destination); DEBUG("exit [%u]: rreq_timeout\n", fk_thread->pid); return; } else { /* Added just for security but this case should never occur */ #if (MMR_INFO_LEVEL >= LEVEL_WARN) puts("MMR [WARN]: RREQ-Timeout occurred, route is available but no messages for destination"); #endif /* Anyway: jump to update next RREQ-Timeout */ goto post_next_to; } } /* Otherwise send new RREQ if below threshold (means also retry count != * RREQ_NONE) */ if (mqe->retry_count < RREQ_THRESHOLD) { /* Broadcast new RREQ message (with incremented TTL) */ rreq_broadcast(mqe); } else { /* Remove all messages for this destination */ mmr_stats.messages_no_route_found++; mq_remove_msgs_for_destination(mqe->message.destination); } /* Anyway: update or set next RREQ-Timeout */ post_next_to: post_next_rreq_timeout(); DEBUG("exit [%u]: rreq_timeout\n", fk_thread->pid); } static void rreq_timeout_process(void) { msg m; do { msg_receive(&m); if (m.type == MSG_TIMER && rreq_to_active) { rreq_to_active = false; rreq_timeout((message_queue_entry_t *)m.content.ptr); } } while (m.type != MSG_EXIT); } void mmr_peek(net_message_t *message, packet_info_t *packet_info) { DEBUG("call [%u]: mmr_peek\n", fk_thread->pid); /* Only look at micro mesh routing messages */ if (message->protocol == LAYER_2_PROTOCOL_MMR) { uint8_t type = message->payload[0]; uint16_t my_addr = net_get_address_in_subnet(message->source); if (type == MMR_TYPE_RREP) { /* Add routes to route table */ mmr_rrep_message_t *rrep_msg = (mmr_rrep_message_t *)message->payload; #if (MMR_INFO_LEVEL >= LEVEL_WARN) if (my_addr == 0) { puts("MMR [WARN]: received RREP with unknown network part of source address"); puts("MMR [WARN]: => can't find own net address in sub net!"); } #endif rt_extract_routes(my_addr, rrep_msg->length, rrep_msg->address); } else if (type == MMR_TYPE_RERR) { #if (MMR_INFO_LEVEL >= LEVEL_WARN) if (my_addr == 0) { puts("MMR [WARN]: received RERR with unknown network part of source address"); puts("MMR [WARN]: => can't find own net address in sub net!"); } #endif /* If not destination of RERR, then remove route to unavailable * node in RERR packet */ if (message->destination != my_addr) { mmr_rerr_message_t *rerr_msg = (mmr_rerr_message_t *)message->payload; if (rerr_msg->error_type == RERR_NODE_UNREACHABLE) { rt_remove_route(rerr_msg->type_specific_info); } } } } DEBUG("exit [%u]: mmr_peek\n", fk_thread->pid); } bool mmr_send(net_message_t *message) { DEBUG("call [%u]: mmr_send\n", fk_thread->pid); bool enqueue = true; if (message->destination == net_get_address_in_subnet(message->destination)) { #if (MMR_INFO_LEVEL >= LEVEL_WARN) puts("MMR [WARN]: message is already at destination, why is routing called?"); #endif DEBUG("exit [%u]: mmr_send\n", fk_thread->pid); return false; } if (NETWORK_ADDR_NET(message->destination) == 0) { #if (MMR_INFO_LEVEL >= LEVEL_WARN) puts("MMR [WARN]: NET part of address cannot be 0!"); #endif DEBUG("exit [%u]: mmr_send\n", fk_thread->pid); return false; } if (NETWORK_ADDR_HOST(message->destination) == 0) { #if (MMR_INFO_LEVEL >= LEVEL_INFO) puts("MMR [INFO]: broadcast destination, why is routing called? A route entry should exist!"); #endif enqueue = false; } /* Look up next hop address for this destination in routing table */ route_table_entry_t *rte = rt_lookup_route(message->destination); /* If next hop address found in routing table, forward message */ if (rte != NULL) { DEBUG("exit [%u]: mmr_send\n", fk_thread->pid); return net_enqueue_for_transmission(message, rte->interface_id, rte->gateway, true); } /* Otherwise, save message in queue; broadcast RREQ message */ else { if (!enqueue) { /* Don't enqueue broadcast destinations */ DEBUG("exit [%u]: mmr_send\n", fk_thread->pid); return false; } message_queue_entry_t *mqe = mq_add(message); if (mqe != NULL) { rreq_broadcast(mqe); post_next_rreq_timeout(); mmr_stats.rreq_originated++; DEBUG("exit [%u]: mmr_send\n", fk_thread->pid); return true; } } DEBUG("exit [%u]: mmr_send\n", fk_thread->pid); return false; } void mmr_packet_dropped(net_message_t *message, uint16_t next_hop, int error) { DEBUG("call [%u]: mmr_packet_dropped\n", fk_thread->pid); if (error == ROUTE_ERROR_BROKEN_ROUTE) { /* Local failure detected - remove all routes through broken link */ rt_remove_gateway_routes(next_hop); mmr_stats.messages_broken_link_on_forward++; } else if (error == ROUTE_ERROR_MISSING_ROUTE) { mmr_stats.messages_no_route_avail_on_forward++; } /* If source != net_addr, send RERR to source of message */ if (message->source != net_get_address_in_subnet(message->source)) { /* Do not generate RERR if it is already a RERR message */ if (is_route_error(message)) { DEBUG("exit [%u]: mmr_packet_dropped\n", fk_thread->pid); return; } /* Find next hop to source */ route_table_entry_t *rte = rt_lookup_route(message->source); if (rte != NULL) { generate_route_error_message(message->source, rte->gateway, rte->interface_id, RERR_NODE_UNREACHABLE, message->destination); } #if (MMR_INFO_LEVEL >= LEVEL_WARN) else { printf("MMR [WARN]: cannot send RERR to source #%u, no route found!\n", message->source); } #endif } DEBUG("exit [%u]: mmr_packet_dropped\n", fk_thread->pid); } void mmr_receive(void *msg, int msg_size, packet_info_t *packet_info) { DEBUG("call [%u]: mmr_receive\n", fk_thread->pid); uint8_t *p = (uint8_t *) msg; uint8_t type = p[0]; if (type == MMR_TYPE_RREQ) { receive_route_request_message((mmr_rreq_message_t *)msg, packet_info); mmr_stats.rreq_received++; } else if (type == MMR_TYPE_RREP) { receive_route_reply_message((mmr_rrep_message_t *)msg, packet_info); mmr_stats.rrep_received++; } else if (type == MMR_TYPE_RERR) { receive_route_error_message((mmr_rerr_message_t *)msg, packet_info); mmr_stats.rerr_received++; } #if (MMR_INFO_LEVEL >= LEVEL_INFO) else { printf("MMR [INFO]: can't handle message of type %u\n", type); } #endif DEBUG("exit [%u]: mmr_receive\n", fk_thread->pid); } void mmr_print_stats(void) { printf("ROUTING LAYER STATS\r\n"); printf("-------------------\r\n"); printf("Route requests originated: %lu\r\n", mmr_stats.rreq_originated); printf("Route requests duplicated: %lu\r\n", mmr_stats.rreq_duplicated); printf("Route replies originated: %lu\r\n", mmr_stats.rrep_originated); printf("Route errors originated: %lu\r\n", mmr_stats.rerr_originated); printf("Route requests received: %lu\r\n", mmr_stats.rreq_received); printf("Route replies received: %lu\r\n", mmr_stats.rrep_received); printf("Route errors received: %lu\r\n", mmr_stats.rerr_received); printf("\r\n"); printf("#Messages with no route found: %lu\r\n", mmr_stats.messages_no_route_found); printf("#Messages with broken link on forward: %lu\r\n", mmr_stats.messages_broken_link_on_forward); printf("#Messages with no route available on forward: %lu\r\n", mmr_stats.messages_no_route_avail_on_forward); printf("\r\n"); }