/* * Copyright (c) 2016, Inria. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file is part of the Contiki operating system. */ /** * \addtogroup uip * @{ * * \file * Source routing support * * \author Simon Duquennoy */ #include "contiki.h" #include "net/ipv6/uip-sr.h" #include "net/ipv6/uiplib.h" #include "net/routing/routing.h" #include "lib/list.h" #include "lib/memb.h" /* Log configuration */ #include "sys/log.h" #define LOG_MODULE "IPv6 SR" #define LOG_LEVEL LOG_LEVEL_IPV6 /* Total number of nodes */ static int num_nodes; /* Every known node in the network */ LIST(nodelist); MEMB(nodememb, uip_sr_node_t, UIP_SR_LINK_NUM); /*---------------------------------------------------------------------------*/ int uip_sr_num_nodes(void) { return num_nodes; } /*---------------------------------------------------------------------------*/ static int node_matches_address(void *graph, const uip_sr_node_t *node, const uip_ipaddr_t *addr) { if(node == NULL || addr == NULL || graph != node->graph) { return 0; } else { uip_ipaddr_t node_ipaddr; NETSTACK_ROUTING.get_sr_node_ipaddr(&node_ipaddr, node); return uip_ipaddr_cmp(&node_ipaddr, addr); } } /*---------------------------------------------------------------------------*/ uip_sr_node_t * uip_sr_get_node(void *graph, const uip_ipaddr_t *addr) { uip_sr_node_t *l; for(l = list_head(nodelist); l != NULL; l = list_item_next(l)) { /* Compare prefix and node identifier */ if(node_matches_address(graph, l, addr)) { return l; } } return NULL; } /*---------------------------------------------------------------------------*/ int uip_sr_is_addr_reachable(void *graph, const uip_ipaddr_t *addr) { int max_depth = UIP_SR_LINK_NUM; uip_ipaddr_t root_ipaddr; uip_sr_node_t *node; uip_sr_node_t *root_node; NETSTACK_ROUTING.get_root_ipaddr(&root_ipaddr); node = uip_sr_get_node(graph, addr); root_node = uip_sr_get_node(graph, &root_ipaddr); while(node != NULL && node != root_node && max_depth > 0) { node = node->parent; max_depth--; } return node != NULL && node == root_node; } /*---------------------------------------------------------------------------*/ void uip_sr_expire_parent(void *graph, const uip_ipaddr_t *child, const uip_ipaddr_t *parent) { uip_sr_node_t *l = uip_sr_get_node(graph, child); /* Check if parent matches */ if(l != NULL && node_matches_address(graph, l->parent, parent)) { l->lifetime = UIP_SR_REMOVAL_DELAY; } } /*---------------------------------------------------------------------------*/ uip_sr_node_t * uip_sr_update_node(void *graph, const uip_ipaddr_t *child, const uip_ipaddr_t *parent, uint32_t lifetime) { uip_sr_node_t *child_node = uip_sr_get_node(graph, child); uip_sr_node_t *parent_node = uip_sr_get_node(graph, parent); uip_sr_node_t *old_parent_node; if(parent != NULL) { /* No node for the parent, add one with infinite lifetime */ if(parent_node == NULL) { parent_node = uip_sr_update_node(graph, parent, NULL, UIP_SR_INFINITE_LIFETIME); if(parent_node == NULL) { LOG_ERR("NS: no space left for root node!\n"); return NULL; } } } /* No node for this child, add one */ if(child_node == NULL) { child_node = memb_alloc(&nodememb); /* No space left, abort */ if(child_node == NULL) { LOG_ERR("NS: no space left for child "); LOG_ERR_6ADDR(child); LOG_ERR_("\n"); return NULL; } child_node->parent = NULL; list_add(nodelist, child_node); num_nodes++; } /* Initialize node */ child_node->graph = graph; child_node->lifetime = lifetime; memcpy(child_node->link_identifier, ((const unsigned char *)child) + 8, 8); /* Is the node reachable before the update? */ if(uip_sr_is_addr_reachable(graph, child)) { old_parent_node = child_node->parent; /* Update node */ child_node->parent = parent_node; /* Has the node become unreachable? May happen if we create a loop. */ if(!uip_sr_is_addr_reachable(graph, child)) { /* The new parent makes the node unreachable, restore old parent. * We will take the update next time, with chances we know more of * the topology and the loop is gone. */ child_node->parent = old_parent_node; } } else { child_node->parent = parent_node; } LOG_INFO("NS: updating link, child "); LOG_INFO_6ADDR(child); LOG_INFO_(", parent "); LOG_INFO_6ADDR(parent); LOG_INFO_(", lifetime %u, num_nodes %u\n", (unsigned)lifetime, num_nodes); return child_node; } /*---------------------------------------------------------------------------*/ void uip_sr_init(void) { num_nodes = 0; memb_init(&nodememb); list_init(nodelist); } /*---------------------------------------------------------------------------*/ uip_sr_node_t * uip_sr_node_head(void) { return list_head(nodelist); } /*---------------------------------------------------------------------------*/ uip_sr_node_t * uip_sr_node_next(uip_sr_node_t *item) { return list_item_next(item); } /*---------------------------------------------------------------------------*/ void uip_sr_periodic(unsigned seconds) { uip_sr_node_t *l; uip_sr_node_t *next; /* First pass, for all expired nodes, deallocate them iff no child points to them */ for(l = list_head(nodelist); l != NULL; l = next) { next = list_item_next(l); if(l->lifetime == 0) { uip_sr_node_t *l2; for(l2 = list_head(nodelist); l2 != NULL; l2 = list_item_next(l2)) { if(l2->parent == l) { break; } } if(LOG_INFO_ENABLED) { uip_ipaddr_t node_addr; NETSTACK_ROUTING.get_sr_node_ipaddr(&node_addr, l); LOG_INFO("NS: removing expired node "); LOG_INFO_6ADDR(&node_addr); LOG_INFO_("\n"); } /* No child found, deallocate node */ list_remove(nodelist, l); memb_free(&nodememb, l); num_nodes--; } else if(l->lifetime != UIP_SR_INFINITE_LIFETIME) { l->lifetime = l->lifetime > seconds ? l->lifetime - seconds : 0; } } } /*---------------------------------------------------------------------------*/ void uip_sr_free_all(void) { uip_sr_node_t *l; uip_sr_node_t *next; for(l = list_head(nodelist); l != NULL; l = next) { next = list_item_next(l); list_remove(nodelist, l); memb_free(&nodememb, l); num_nodes--; } } /*---------------------------------------------------------------------------*/ int uip_sr_link_snprint(char *buf, int buflen, uip_sr_node_t *link) { int index = 0; uip_ipaddr_t child_ipaddr; uip_ipaddr_t parent_ipaddr; NETSTACK_ROUTING.get_sr_node_ipaddr(&child_ipaddr, link); NETSTACK_ROUTING.get_sr_node_ipaddr(&parent_ipaddr, link->parent); if(LOG_WITH_COMPACT_ADDR) { index += log_6addr_compact_snprint(buf+index, buflen-index, &child_ipaddr); } else { index += uiplib_ipaddr_snprint(buf+index, buflen-index, &child_ipaddr); } if(index >= buflen) { return index; } if(link->parent == NULL) { index += snprintf(buf+index, buflen-index, " (DODAG root)"); if(index >= buflen) { return index; } } else { index += snprintf(buf+index, buflen-index, " to "); if(index >= buflen) { return index; } if(LOG_WITH_COMPACT_ADDR) { index += log_6addr_compact_snprint(buf+index, buflen-index, &parent_ipaddr); } else { index += uiplib_ipaddr_snprint(buf+index, buflen-index, &parent_ipaddr); } if(index >= buflen) { return index; } } if(link->lifetime != UIP_SR_INFINITE_LIFETIME) { index += snprintf(buf+index, buflen-index, " (lifetime: %lu seconds)", (unsigned long)link->lifetime); if(index >= buflen) { return index; } } else { index += snprintf(buf+index, buflen-index, " (lifetime: infinite)"); if(index >= buflen) { return index; } } return index; } /** @} */