/* * Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/. * 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 copyright holder 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 COPYRIGHT HOLDERS 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 * COPYRIGHT HOLDER 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. * */ #include "net/uip-ds6.h" #include "net/uip.h" #include "lib/list.h" #include "lib/memb.h" #include "net/neighbor-table.h" #if UIP_CONF_IPV6 #include void uip_ds6_route_rm_routelist(list_t nbr_table_get_from_lladdr); NEIGHBOR_TABLE(uip_ds6_route_t *, nbr_routes); MEMB(routememb, uip_ds6_route_t, UIP_DS6_ROUTE_NB); LIST(defaultrouterlist); MEMB(defaultroutermemb, uip_ds6_defrt_t, UIP_DS6_DEFRT_NB); #if UIP_DS6_NOTIFICATIONS LIST(notificationlist); #endif static int num_routes = 0; #undef DEBUG #define DEBUG DEBUG_NONE #include "net/uip-debug.h" /*---------------------------------------------------------------------------*/ #if UIP_DS6_NOTIFICATIONS static void call_route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *nexthop) { int num; struct uip_ds6_notification *n; for(n = list_head(notificationlist); n != NULL; n = list_item_next(n)) { if(event == UIP_DS6_NOTIFICATION_DEFRT_ADD || event == UIP_DS6_NOTIFICATION_DEFRT_RM) { num = list_length(defaultrouterlist); } else { num = num_routes; } n->callback(event, route, nexthop, num); } } /*---------------------------------------------------------------------------*/ void uip_ds6_notification_add(struct uip_ds6_notification *n, uip_ds6_notification_callback c) { if(n != NULL && c != NULL) { n->callback = c; list_add(notificationlist, n); } } /*---------------------------------------------------------------------------*/ void uip_ds6_notification_rm(struct uip_ds6_notification *n) { list_remove(notificationlist, n); } #endif /*---------------------------------------------------------------------------*/ void uip_ds6_route_init(void) { memb_init(&routememb); nbr_table_register(nbr_routes, (remove_callback_func *)uip_ds6_route_rm_routelist); memb_init(&defaultroutermemb); list_init(defaultrouterlist); #if UIP_DS6_NOTIFICATIONS list_init(notificationlist); #endif } /*---------------------------------------------------------------------------*/ static uip_lladdr_t * uip_ds6_route_nexthop_lladdr(uip_ds6_route_t *route) { if(route != NULL) { return (uip_lladdr_t *)nbr_table_get_lladdr(nbr_routes, route->route_list); } else { return NULL; } } /*---------------------------------------------------------------------------*/ uip_ipaddr_t * uip_ds6_route_nexthop(uip_ds6_route_t *route) { if(route != NULL) { return uip_ds6_nbr_ipaddr_from_lladdr(uip_ds6_route_nexthop_lladdr(route)); } else { return NULL; } } /*---------------------------------------------------------------------------*/ uip_ds6_route_t * uip_ds6_route_head(void) { list_t nbr_route_list = nbr_table_head(nbr_routes); if(nbr_route_list != NULL) { return list_head((list_t)nbr_route_list); } else { return NULL; } } /*---------------------------------------------------------------------------*/ uip_ds6_route_t * uip_ds6_route_next(uip_ds6_route_t *r) { if(r != NULL) { uip_ds6_route_t *n = list_item_next(r); if(n != NULL) { return n; } else { list_t nbr_route_list = nbr_table_next(nbr_routes, r->route_list); if(nbr_route_list != NULL) { return list_head((list_t)nbr_route_list); } } } return NULL; } /*---------------------------------------------------------------------------*/ int uip_ds6_route_num_routes(void) { return num_routes; } /*---------------------------------------------------------------------------*/ uip_ds6_route_t * uip_ds6_route_lookup(uip_ipaddr_t *addr) { uip_ds6_route_t *r; uip_ds6_route_t *found_route; uint8_t longestmatch; PRINTF("uip-ds6-route: Looking up route for "); PRINT6ADDR(addr); PRINTF("\n"); found_route = NULL; longestmatch = 0; for(r = uip_ds6_route_head(); r != NULL; r = uip_ds6_route_next(r)) { if(r->length >= longestmatch && uip_ipaddr_prefixcmp(addr, &r->ipaddr, r->length)) { longestmatch = r->length; found_route = r; } } if(found_route != NULL) { PRINTF("uip-ds6-route: Found route: "); PRINT6ADDR(addr); PRINTF(" via "); PRINT6ADDR(uip_ds6_route_nexthop(found_route)); PRINTF("\n"); } else { PRINTF("uip-ds6-route: No route found\n"); } return found_route; } /*---------------------------------------------------------------------------*/ uip_ds6_route_t * uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length, uip_ipaddr_t *nexthop) { uip_ds6_route_t *r; list_t nbr_route_list; /* Get link-layer address of next hop, make sure it is in neighbor table */ uip_lladdr_t *nexthop_lladdr = uip_ds6_nbr_lladdr_from_ipaddr(nexthop); if(nexthop_lladdr == NULL) { PRINTF("uip_ds6_route_add: neighbor link-local address unknown "); PRINT6ADDR(ipaddr); PRINTF("\n"); return NULL; } /* Get routing entry list of this neighbor */ nbr_route_list = nbr_table_get_from_lladdr(nbr_routes, (rimeaddr_t *)nexthop_lladdr); /* First make sure that we don't add a route twice. If we find an existing route for our destination, we'll just update the old one. */ r = uip_ds6_route_lookup(ipaddr); if(r != NULL) { PRINTF("uip_ds6_route_add: old route already found, updating this one instead: "); PRINT6ADDR(ipaddr); PRINTF("\n"); } else { /* If there is no routing entry, create one */ if(nbr_route_list == NULL) { nbr_route_list = nbr_table_add_lladdr(nbr_routes, (rimeaddr_t *)nexthop_lladdr); if(nbr_route_list == NULL) { PRINTF("uip_ds6_route_add: could not allocate memory (route list) for new route to "); PRINT6ADDR(ipaddr); PRINTF(", dropping it\n"); return NULL; } list_init((list_t)nbr_route_list); } /* Allocate a routing entry and add the route to the list */ r = memb_alloc(&routememb); if(r == NULL) { PRINTF("uip_ds6_route_add: could not allocate memory for new route to "); PRINT6ADDR(ipaddr); PRINTF(", dropping it\n"); return NULL; } /* Add the route to this neighbor */ list_add((list_t)nbr_route_list, r); num_routes++; PRINTF("uip_ds6_route_add num %d\n", num_routes); } r->route_list = nbr_route_list; uip_ipaddr_copy(&(r->ipaddr), ipaddr); r->length = length; #ifdef UIP_DS6_ROUTE_STATE_TYPE memset(&r->state, 0, sizeof(UIP_DS6_ROUTE_STATE_TYPE)); #endif PRINTF("uip_ds6_route_add: adding route: "); PRINT6ADDR(ipaddr); PRINTF(" via "); PRINT6ADDR(nexthop); PRINTF("\n"); ANNOTATE("#L %u 1;blue\n", nexthop->u8[sizeof(uip_ipaddr_t) - 1]); #if UIP_DS6_NOTIFICATIONS call_route_callback(UIP_DS6_NOTIFICATION_ROUTE_ADD, ipaddr, nexthop); #endif return r; } /*---------------------------------------------------------------------------*/ void uip_ds6_route_rm(uip_ds6_route_t *route) { if(route != NULL && route->route_list != NULL) { if(list_head((list_t)route->route_list) == NULL) { /* If this was the only route using this neighbor, remove the neibhor from the table */ nbr_table_remove(nbr_routes, route->route_list); } list_remove((list_t)route->route_list, route); memb_free(&routememb, route); num_routes--; PRINTF("uip_ds6_route_rm num %d\n", num_routes); #if UIP_DS6_NOTIFICATIONS call_route_callback(UIP_DS6_NOTIFICATION_ROUTE_RM, &route->ipaddr, uip_ds6_route_nexthop(route)); #endif #if (DEBUG & DEBUG_ANNOTATE) == DEBUG_ANNOTATE /* we need to check if this was the last route towards "nexthop" */ /* if so - remove that link (annotation) */ for(r = uip_ds6_route_head(); r != NULL; r = uip_ds6_route_next(r)) { if(uip_ipaddr_cmp(uip_ds6_route_nexthop(r), uip_ds6_route_nexthop(route))) { /* we found another link using the specific nexthop, so keep the #L */ return; } } ANNOTATE("#L %u 0\n", uip_ds6_route_nexthop(route)->u8[sizeof(uip_ipaddr_t) - 1]); #endif } return; } /*---------------------------------------------------------------------------*/ void uip_ds6_route_rm_routelist(list_t nbr_route_list) { if(nbr_route_list != NULL) { uip_ds6_route_t *r; r = list_head((list_t)nbr_route_list); while(r != NULL) { uip_ds6_route_rm(r); r = list_head((list_t)nbr_route_list); } nbr_table_remove(nbr_routes, nbr_route_list); } } /*---------------------------------------------------------------------------*/ void uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop) { /* Get routing entry list of this neighbor */ uip_lladdr_t *nexthop_lladdr = uip_ds6_nbr_lladdr_from_ipaddr(nexthop); list_t nbr_route_list = nbr_table_get_from_lladdr(nbr_routes, (rimeaddr_t *)nexthop_lladdr); uip_ds6_route_rm_routelist(nbr_route_list); } /*---------------------------------------------------------------------------*/ uip_ds6_defrt_t * uip_ds6_defrt_add(uip_ipaddr_t *ipaddr, unsigned long interval) { uip_ds6_defrt_t *d; d = uip_ds6_defrt_lookup(ipaddr); if(d == NULL) { d = memb_alloc(&defaultroutermemb); if(d == NULL) { PRINTF("uip_ds6_defrt_add: could not add default route to "); PRINT6ADDR(ipaddr); PRINTF(", out of memory\n"); return NULL; } else { PRINTF("uip_ds6_defrt_add: adding default route to "); PRINT6ADDR(ipaddr); PRINTF("\n"); } list_push(defaultrouterlist, d); } uip_ipaddr_copy(&d->ipaddr, ipaddr); if(interval != 0) { stimer_set(&d->lifetime, interval); d->isinfinite = 0; } else { d->isinfinite = 1; } ANNOTATE("#L %u 1\n", ipaddr->u8[sizeof(uip_ipaddr_t) - 1]); #if UIP_DS6_NOTIFICATIONS call_route_callback(UIP_DS6_NOTIFICATION_DEFRT_ADD, ipaddr, ipaddr); #endif return d; } /*---------------------------------------------------------------------------*/ void uip_ds6_defrt_rm(uip_ds6_defrt_t *defrt) { uip_ds6_defrt_t *d; /* Make sure that the defrt is in the list before we remove it. */ for(d = list_head(defaultrouterlist); d != NULL; d = list_item_next(d)) { if(d == defrt) { PRINTF("Removing default route\n"); list_remove(defaultrouterlist, defrt); memb_free(&defaultroutermemb, defrt); ANNOTATE("#L %u 0\n", defrt->ipaddr.u8[sizeof(uip_ipaddr_t) - 1]); #if UIP_DS6_NOTIFICATIONS call_route_callback(UIP_DS6_NOTIFICATION_DEFRT_RM, &defrt->ipaddr, &defrt->ipaddr); #endif return; } } } /*---------------------------------------------------------------------------*/ uip_ds6_defrt_t * uip_ds6_defrt_lookup(uip_ipaddr_t *ipaddr) { uip_ds6_defrt_t *d; for(d = list_head(defaultrouterlist); d != NULL; d = list_item_next(d)) { if(uip_ipaddr_cmp(&d->ipaddr, ipaddr)) { return d; } } return NULL; } /*---------------------------------------------------------------------------*/ uip_ipaddr_t * uip_ds6_defrt_choose(void) { uip_ds6_defrt_t *d; uip_ds6_nbr_t *bestnbr; uip_ipaddr_t *addr; addr = NULL; for(d = list_head(defaultrouterlist); d != NULL; d = list_item_next(d)) { PRINTF("Defrt, IP address "); PRINT6ADDR(&d->ipaddr); PRINTF("\n"); bestnbr = uip_ds6_nbr_lookup(&d->ipaddr); if(bestnbr != NULL && bestnbr->state != NBR_INCOMPLETE) { PRINTF("Defrt found, IP address "); PRINT6ADDR(&d->ipaddr); PRINTF("\n"); return &d->ipaddr; } else { addr = &d->ipaddr; PRINTF("Defrt INCOMPLETE found, IP address "); PRINT6ADDR(&d->ipaddr); PRINTF("\n"); } } return addr; } /*---------------------------------------------------------------------------*/ void uip_ds6_defrt_periodic(void) { uip_ds6_defrt_t *d; d = list_head(defaultrouterlist); while(d != NULL) { if(!d->isinfinite && stimer_expired(&d->lifetime)) { PRINTF("uip_ds6_defrt_periodic: defrt lifetime expired\n"); uip_ds6_defrt_rm(d); d = list_head(defaultrouterlist); } else { d = list_item_next(d); } } } /*---------------------------------------------------------------------------*/ #endif /* UIP_CONF_IPV6 */