Contiki-Inga 3.x
uip-ds6-route.c
1 /*
2  * Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * 3. Neither the name of the copyright holder nor the names of its
15  * contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 #include "net/ipv6/uip-ds6.h"
33 #include "net/ip/uip.h"
34 
35 #include "lib/list.h"
36 #include "lib/memb.h"
37 #include "net/nbr-table.h"
38 
39 #if UIP_CONF_IPV6
40 
41 #include <string.h>
42 
43 /* The nbr_routes holds a neighbor table to be able to maintain
44  information about what routes go through what neighbor. This
45  neighbor table is registered with the central nbr-table repository
46  so that it will be maintained along with the rest of the neighbor
47  tables in the system. */
48 NBR_TABLE(struct uip_ds6_route_neighbor_routes, nbr_routes);
49 MEMB(neighborroutememb, struct uip_ds6_route_neighbor_route, UIP_DS6_ROUTE_NB);
50 
51 /* Each route is repressented by a uip_ds6_route_t structure and
52  memory for each route is allocated from the routememb memory
53  block. These routes are maintained on the routelist. */
54 LIST(routelist);
55 MEMB(routememb, uip_ds6_route_t, UIP_DS6_ROUTE_NB);
56 
57 /* Default routes are held on the defaultrouterlist and their
58  structures are allocated from the defaultroutermemb memory block.*/
59 LIST(defaultrouterlist);
60 MEMB(defaultroutermemb, uip_ds6_defrt_t, UIP_DS6_DEFRT_NB);
61 
62 #if UIP_DS6_NOTIFICATIONS
63 LIST(notificationlist);
64 #endif
65 
66 static int num_routes = 0;
67 
68 #undef DEBUG
69 #define DEBUG DEBUG_PRINT
70 #include "net/ip/uip-debug.h"
71 
72 static void rm_routelist_callback(nbr_table_item_t *ptr);
73 /*---------------------------------------------------------------------------*/
74 #if DEBUG != DEBUG_NONE
75 static void
76 assert_nbr_routes_list_sane(void)
77 {
78  uip_ds6_route_t *r;
79  int count;
80 
81  /* Check if the route list has an infinite loop. */
82  for(r = uip_ds6_route_head(),
83  count = 0;
84  r != NULL &&
85  count < UIP_DS6_ROUTE_NB * 2;
86  r = uip_ds6_route_next(r),
87  count++);
88 
89  if(count > UIP_DS6_ROUTE_NB) {
90  printf("uip-ds6-route.c: assert_nbr_routes_list_sane route list is in infinite loop\n");
91  }
92 
93  /* Make sure that the route list has as many entries as the
94  num_routes vairable. */
95  if(count < num_routes) {
96  printf("uip-ds6-route.c: assert_nbr_routes_list_sane too few entries on route list: should be %d, is %d, max %d\n",
97  num_routes, count, UIP_CONF_MAX_ROUTES);
98  }
99 }
100 #endif /* DEBUG != DEBUG_NONE */
101 /*---------------------------------------------------------------------------*/
102 #if UIP_DS6_NOTIFICATIONS
103 static void
104 call_route_callback(int event, uip_ipaddr_t *route,
105  uip_ipaddr_t *nexthop)
106 {
107  int num;
108  struct uip_ds6_notification *n;
109  for(n = list_head(notificationlist);
110  n != NULL;
111  n = list_item_next(n)) {
112  if(event == UIP_DS6_NOTIFICATION_DEFRT_ADD ||
113  event == UIP_DS6_NOTIFICATION_DEFRT_RM) {
114  num = list_length(defaultrouterlist);
115  } else {
116  num = num_routes;
117  }
118  n->callback(event, route, nexthop, num);
119  }
120 }
121 /*---------------------------------------------------------------------------*/
122 void
123 uip_ds6_notification_add(struct uip_ds6_notification *n,
124  uip_ds6_notification_callback c)
125 {
126  if(n != NULL && c != NULL) {
127  n->callback = c;
128  list_add(notificationlist, n);
129  }
130 }
131 /*---------------------------------------------------------------------------*/
132 void
133 uip_ds6_notification_rm(struct uip_ds6_notification *n)
134 {
135  list_remove(notificationlist, n);
136 }
137 #endif
138 /*---------------------------------------------------------------------------*/
139 void
141 {
142  memb_init(&routememb);
143  list_init(routelist);
144  nbr_table_register(nbr_routes,
145  (nbr_table_callback *)rm_routelist_callback);
146 
147  memb_init(&defaultroutermemb);
148  list_init(defaultrouterlist);
149 
150 #if UIP_DS6_NOTIFICATIONS
151  list_init(notificationlist);
152 #endif
153 }
154 /*---------------------------------------------------------------------------*/
155 static uip_lladdr_t *
156 uip_ds6_route_nexthop_lladdr(uip_ds6_route_t *route)
157 {
158  if(route != NULL) {
159  return (uip_lladdr_t *)nbr_table_get_lladdr(nbr_routes,
160  route->neighbor_routes);
161  } else {
162  return NULL;
163  }
164 }
165 /*---------------------------------------------------------------------------*/
166 uip_ipaddr_t *
167 uip_ds6_route_nexthop(uip_ds6_route_t *route)
168 {
169  if(route != NULL) {
170  return uip_ds6_nbr_ipaddr_from_lladdr(uip_ds6_route_nexthop_lladdr(route));
171  } else {
172  return NULL;
173  }
174 }
175 /*---------------------------------------------------------------------------*/
178 {
179  return list_head(routelist);
180 }
181 /*---------------------------------------------------------------------------*/
184 {
185  if(r != NULL) {
187  return n;
188  }
189  return NULL;
190 }
191 /*---------------------------------------------------------------------------*/
192 int
194 {
195  return num_routes;
196 }
197 /*---------------------------------------------------------------------------*/
199 uip_ds6_route_lookup(uip_ipaddr_t *addr)
200 {
201  uip_ds6_route_t *r;
202  uip_ds6_route_t *found_route;
203  uint8_t longestmatch;
204 
205  PRINTF("uip-ds6-route: Looking up route for ");
206  PRINT6ADDR(addr);
207  PRINTF("\n");
208 
209 
210  found_route = NULL;
211  longestmatch = 0;
212  for(r = uip_ds6_route_head();
213  r != NULL;
214  r = uip_ds6_route_next(r)) {
215  if(r->length >= longestmatch &&
216  uip_ipaddr_prefixcmp(addr, &r->ipaddr, r->length)) {
217  longestmatch = r->length;
218  found_route = r;
219  }
220  }
221 
222  if(found_route != NULL) {
223  PRINTF("uip-ds6-route: Found route: ");
224  PRINT6ADDR(addr);
225  PRINTF(" via ");
226  PRINT6ADDR(uip_ds6_route_nexthop(found_route));
227  PRINTF("\n");
228  } else {
229  PRINTF("uip-ds6-route: No route found\n");
230  }
231 
232  if(found_route != NULL) {
233  /* If we found a route, we put it at the end of the routeslist
234  list. The list is ordered by how recently we looked them up:
235  the least recently used route will be at the start of the
236  list. */
237  list_remove(routelist, found_route);
238  list_add(routelist, found_route);
239  }
240 
241  return found_route;
242 }
243 /*---------------------------------------------------------------------------*/
245 uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
246  uip_ipaddr_t *nexthop)
247 {
248  uip_ds6_route_t *r;
249  struct uip_ds6_route_neighbor_route *nbrr;
250 
251 #if DEBUG != DEBUG_NONE
252  assert_nbr_routes_list_sane();
253 #endif /* DEBUG != DEBUG_NONE */
254 
255  /* Get link-layer address of next hop, make sure it is in neighbor table */
256  const uip_lladdr_t *nexthop_lladdr = uip_ds6_nbr_lladdr_from_ipaddr(nexthop);
257  if(nexthop_lladdr == NULL) {
258  PRINTF("uip_ds6_route_add: neighbor link-local address unknown for ");
259  PRINT6ADDR(nexthop);
260  PRINTF("\n");
261  return NULL;
262  }
263 
264  /* First make sure that we don't add a route twice. If we find an
265  existing route for our destination, we'll just update the old
266  one. */
267  r = uip_ds6_route_lookup(ipaddr);
268  if(r != NULL) {
269  PRINTF("uip_ds6_route_add: old route already found, updating this one instead: ");
270  PRINT6ADDR(ipaddr);
271  PRINTF("\n");
272  } else {
273  struct uip_ds6_route_neighbor_routes *routes;
274  /* If there is no routing entry, create one. We first need to
275  check if we have room for this route. If not, we remove the
276  least recently used one we have. */
277 
278  if(uip_ds6_route_num_routes() == UIP_DS6_ROUTE_NB) {
279  /* Removing the oldest route entry from the route table. The
280  least recently used route is the first route on the list. */
281  uip_ds6_route_t *oldest;
282 
283  oldest = uip_ds6_route_head();
284  PRINTF("uip_ds6_route_add: dropping route to ");
285  PRINT6ADDR(&oldest->ipaddr);
286  PRINTF("\n");
287  uip_ds6_route_rm(oldest);
288  }
289 
290 
291  /* Every neighbor on our neighbor table holds a struct
292  uip_ds6_route_neighbor_routes which holds a list of routes that
293  go through the neighbor. We add our route entry to this list.
294 
295  We first check to see if we already have this neighbor in our
296  nbr_route table. If so, the neighbor already has a route entry
297  list.
298  */
299  routes = nbr_table_get_from_lladdr(nbr_routes,
300  (linkaddr_t *)nexthop_lladdr);
301 
302  if(routes == NULL) {
303  /* If the neighbor did not have an entry in our neighbor table,
304  we create one. The nbr_table_add_lladdr() function returns a
305  pointer to a pointer that we may use for our own purposes. We
306  initialize this pointer with the list of routing entries that
307  are attached to this neighbor. */
308  routes = nbr_table_add_lladdr(nbr_routes,
309  (linkaddr_t *)nexthop_lladdr);
310  if(routes == NULL) {
311  /* This should not happen, as we explicitly deallocated one
312  route table entry above. */
313  PRINTF("uip_ds6_route_add: could not allocate neighbor table entry\n");
314  return NULL;
315  }
316  LIST_STRUCT_INIT(routes, route_list);
317  }
318 
319  /* Allocate a routing entry and populate it. */
320  r = memb_alloc(&routememb);
321 
322  if(r == NULL) {
323  /* This should not happen, as we explicitly deallocated one
324  route table entry above. */
325  PRINTF("uip_ds6_route_add: could not allocate route\n");
326  return NULL;
327  }
328 
329  list_add(routelist, r);
330 
331  nbrr = memb_alloc(&neighborroutememb);
332  if(nbrr == NULL) {
333  /* This should not happen, as we explicitly deallocated one
334  route table entry above. */
335  PRINTF("uip_ds6_route_add: could not allocate neighbor route list entry\n");
336  memb_free(&routememb, r);
337  return NULL;
338  }
339 
340  nbrr->route = r;
341  /* Add the route to this neighbor */
342  list_add(routes->route_list, nbrr);
343  r->neighbor_routes = routes;
344  num_routes++;
345 
346  PRINTF("uip_ds6_route_add num %d\n", num_routes);
347  }
348 
349  uip_ipaddr_copy(&(r->ipaddr), ipaddr);
350  r->length = length;
351 
352 #ifdef UIP_DS6_ROUTE_STATE_TYPE
353  memset(&r->state, 0, sizeof(UIP_DS6_ROUTE_STATE_TYPE));
354 #endif
355 
356  PRINTF("uip_ds6_route_add: adding route: ");
357  PRINT6ADDR(ipaddr);
358  PRINTF(" via ");
359  PRINT6ADDR(nexthop);
360  PRINTF("\n");
361  ANNOTATE("#L %u 1;blue\n", nexthop->u8[sizeof(uip_ipaddr_t) - 1]);
362 
363 #if UIP_DS6_NOTIFICATIONS
364  call_route_callback(UIP_DS6_NOTIFICATION_ROUTE_ADD, ipaddr, nexthop);
365 #endif
366 
367 #if DEBUG != DEBUG_NONE
368  assert_nbr_routes_list_sane();
369 #endif /* DEBUG != DEBUG_NONE */
370  return r;
371 }
372 
373 /*---------------------------------------------------------------------------*/
374 void
376 {
377  struct uip_ds6_route_neighbor_route *neighbor_route;
378 #if DEBUG != DEBUG_NONE
379  assert_nbr_routes_list_sane();
380 #endif /* DEBUG != DEBUG_NONE */
381  if(route != NULL && route->neighbor_routes != NULL) {
382 
383  PRINTF("uip_ds6_route_rm: removing route: ");
384  PRINT6ADDR(&route->ipaddr);
385  PRINTF("\n");
386 
387  /* Remove the neighbor from the route list */
388  list_remove(routelist, route);
389 
390  /* Find the corresponding neighbor_route and remove it. */
391  for(neighbor_route = list_head(route->neighbor_routes->route_list);
392  neighbor_route != NULL && neighbor_route->route != route;
393  neighbor_route = list_item_next(neighbor_route));
394 
395  if(neighbor_route == NULL) {
396  PRINTF("uip_ds6_route_rm: neighbor_route was NULL for ");
397  uip_debug_ipaddr_print(&route->ipaddr);
398  PRINTF("\n");
399  }
400  list_remove(route->neighbor_routes->route_list, neighbor_route);
401  if(list_head(route->neighbor_routes->route_list) == NULL) {
402  /* If this was the only route using this neighbor, remove the
403  neibhor from the table */
404  PRINTF("uip_ds6_route_rm: removing neighbor too\n");
405  nbr_table_remove(nbr_routes, route->neighbor_routes->route_list);
406  }
407  memb_free(&routememb, route);
408  memb_free(&neighborroutememb, neighbor_route);
409 
410  num_routes--;
411 
412  PRINTF("uip_ds6_route_rm num %d\n", num_routes);
413 
414 #if UIP_DS6_NOTIFICATIONS
415  call_route_callback(UIP_DS6_NOTIFICATION_ROUTE_RM,
416  &route->ipaddr, uip_ds6_route_nexthop(route));
417 #endif
418 #if 0 //(DEBUG & DEBUG_ANNOTATE) == DEBUG_ANNOTATE
419  /* we need to check if this was the last route towards "nexthop" */
420  /* if so - remove that link (annotation) */
421  uip_ds6_route_t *r;
422  for(r = uip_ds6_route_head();
423  r != NULL;
424  r = uip_ds6_route_next(r)) {
425  uip_ipaddr_t *nextr, *nextroute;
426  nextr = uip_ds6_route_nexthop(r);
427  nextroute = uip_ds6_route_nexthop(route);
428  if(nextr != NULL &&
429  nextroute != NULL &&
430  uip_ipaddr_cmp(nextr, nextroute)) {
431  /* we found another link using the specific nexthop, so keep the #L */
432  return;
433  }
434  }
435  ANNOTATE("#L %u 0\n", uip_ds6_route_nexthop(route)->u8[sizeof(uip_ipaddr_t) - 1]);
436 #endif
437  }
438 
439 #if DEBUG != DEBUG_NONE
440  assert_nbr_routes_list_sane();
441 #endif /* DEBUG != DEBUG_NONE */
442  return;
443 }
444 /*---------------------------------------------------------------------------*/
445 static void
446 rm_routelist(struct uip_ds6_route_neighbor_routes *routes)
447 {
448 #if DEBUG != DEBUG_NONE
449  assert_nbr_routes_list_sane();
450 #endif /* DEBUG != DEBUG_NONE */
451  PRINTF("uip_ds6_route_rm_routelist\n");
452  if(routes != NULL && routes->route_list != NULL) {
454  r = list_head(routes->route_list);
455  while(r != NULL) {
456  uip_ds6_route_rm(r->route);
457  r = list_head(routes->route_list);
458  }
459  nbr_table_remove(nbr_routes, routes);
460  }
461 #if DEBUG != DEBUG_NONE
462  assert_nbr_routes_list_sane();
463 #endif /* DEBUG != DEBUG_NONE */
464 }
465 /*---------------------------------------------------------------------------*/
466 static void
467 rm_routelist_callback(nbr_table_item_t *ptr)
468 {
469  rm_routelist((struct uip_ds6_route_neighbor_routes *)ptr);
470 }
471 /*---------------------------------------------------------------------------*/
472 void
473 uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop)
474 {
475  /* Get routing entry list of this neighbor */
476  const uip_lladdr_t *nexthop_lladdr;
477  struct uip_ds6_route_neighbor_routes *routes;
478 
479  nexthop_lladdr = uip_ds6_nbr_lladdr_from_ipaddr(nexthop);
480  routes = nbr_table_get_from_lladdr(nbr_routes,
481  (linkaddr_t *)nexthop_lladdr);
482  rm_routelist(routes);
483 }
484 /*---------------------------------------------------------------------------*/
486 uip_ds6_defrt_add(uip_ipaddr_t *ipaddr, unsigned long interval)
487 {
488  uip_ds6_defrt_t *d;
489 
490 #if DEBUG != DEBUG_NONE
491  assert_nbr_routes_list_sane();
492 #endif /* DEBUG != DEBUG_NONE */
493 
494  PRINTF("uip_ds6_defrt_add\n");
495  d = uip_ds6_defrt_lookup(ipaddr);
496  if(d == NULL) {
497  d = memb_alloc(&defaultroutermemb);
498  if(d == NULL) {
499  PRINTF("uip_ds6_defrt_add: could not add default route to ");
500  PRINT6ADDR(ipaddr);
501  PRINTF(", out of memory\n");
502  return NULL;
503  } else {
504  PRINTF("uip_ds6_defrt_add: adding default route to ");
505  PRINT6ADDR(ipaddr);
506  PRINTF("\n");
507  }
508 
509  list_push(defaultrouterlist, d);
510  }
511 
512  uip_ipaddr_copy(&d->ipaddr, ipaddr);
513  if(interval != 0) {
514  stimer_set(&d->lifetime, interval);
515  d->isinfinite = 0;
516  } else {
517  d->isinfinite = 1;
518  }
519 
520  ANNOTATE("#L %u 1\n", ipaddr->u8[sizeof(uip_ipaddr_t) - 1]);
521 
522 #if UIP_DS6_NOTIFICATIONS
523  call_route_callback(UIP_DS6_NOTIFICATION_DEFRT_ADD, ipaddr, ipaddr);
524 #endif
525 
526 #if DEBUG != DEBUG_NONE
527  assert_nbr_routes_list_sane();
528 #endif /* DEBUG != DEBUG_NONE */
529 
530  return d;
531 }
532 /*---------------------------------------------------------------------------*/
533 void
535 {
536  uip_ds6_defrt_t *d;
537 
538 #if DEBUG != DEBUG_NONE
539  assert_nbr_routes_list_sane();
540 #endif /* DEBUG != DEBUG_NONE */
541 
542  /* Make sure that the defrt is in the list before we remove it. */
543  for(d = list_head(defaultrouterlist);
544  d != NULL;
545  d = list_item_next(d)) {
546  if(d == defrt) {
547  PRINTF("Removing default route\n");
548  list_remove(defaultrouterlist, defrt);
549  memb_free(&defaultroutermemb, defrt);
550  ANNOTATE("#L %u 0\n", defrt->ipaddr.u8[sizeof(uip_ipaddr_t) - 1]);
551 #if UIP_DS6_NOTIFICATIONS
552  call_route_callback(UIP_DS6_NOTIFICATION_DEFRT_RM,
553  &defrt->ipaddr, &defrt->ipaddr);
554 #endif
555  return;
556  }
557  }
558 #if DEBUG != DEBUG_NONE
559  assert_nbr_routes_list_sane();
560 #endif /* DEBUG != DEBUG_NONE */
561 
562 }
563 /*---------------------------------------------------------------------------*/
565 uip_ds6_defrt_lookup(uip_ipaddr_t *ipaddr)
566 {
567  uip_ds6_defrt_t *d;
568  for(d = list_head(defaultrouterlist);
569  d != NULL;
570  d = list_item_next(d)) {
571  if(uip_ipaddr_cmp(&d->ipaddr, ipaddr)) {
572  return d;
573  }
574  }
575  return NULL;
576 }
577 /*---------------------------------------------------------------------------*/
578 uip_ipaddr_t *
580 {
581  uip_ds6_defrt_t *d;
582  uip_ds6_nbr_t *bestnbr;
583  uip_ipaddr_t *addr;
584 
585  addr = NULL;
586  for(d = list_head(defaultrouterlist);
587  d != NULL;
588  d = list_item_next(d)) {
589  PRINTF("Defrt, IP address ");
590  PRINT6ADDR(&d->ipaddr);
591  PRINTF("\n");
592  bestnbr = uip_ds6_nbr_lookup(&d->ipaddr);
593  if(bestnbr != NULL && bestnbr->state != NBR_INCOMPLETE) {
594  PRINTF("Defrt found, IP address ");
595  PRINT6ADDR(&d->ipaddr);
596  PRINTF("\n");
597  return &d->ipaddr;
598  } else {
599  addr = &d->ipaddr;
600  PRINTF("Defrt INCOMPLETE found, IP address ");
601  PRINT6ADDR(&d->ipaddr);
602  PRINTF("\n");
603  }
604  }
605  return addr;
606 }
607 /*---------------------------------------------------------------------------*/
608 void
610 {
611  uip_ds6_defrt_t *d;
612  d = list_head(defaultrouterlist);
613  while(d != NULL) {
614  if(!d->isinfinite &&
615  stimer_expired(&d->lifetime)) {
616  PRINTF("uip_ds6_defrt_periodic: defrt lifetime expired\n");
617  uip_ds6_defrt_rm(d);
618  d = list_head(defaultrouterlist);
619  } else {
620  d = list_item_next(d);
621  }
622  }
623 }
624 /*---------------------------------------------------------------------------*/
625 
626 #endif /* UIP_CONF_IPV6 */