contiki/core/net/uip-ds6-route.c
2013-08-19 17:48:30 +02:00

467 lines
13 KiB
C

/*
* 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/nbr-table.h"
#if UIP_CONF_IPV6
#include <string.h>
void uip_ds6_route_rm_routelist(list_t nbr_table_get_from_lladdr);
NBR_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, (nbr_table_callback *)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 */