rpl-ext-header.c 23.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
 * Copyright (c) 2009, Swedish Institute of Computer Science.
 * 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.
 */
31

32
33
34
35
/**
 * \file
 *         Management of extension headers for ContikiRPL.
 *
36
37
38
39
 * \author Vincent Brillault <vincent.brillault@imag.fr>,
 *         Joakim Eriksson <joakime@sics.se>,
 *         Niclas Finne <nfi@sics.se>,
 *         Nicolas Tsiftes <nvt@sics.se>.
40
41
 */

42
/**
Simon Duquennoy's avatar
Simon Duquennoy committed
43
 * \addtogroup uip
44
45
46
 * @{
 */

47
48
#include "net/ipv6/uip.h"
#include "net/ipv6/tcpip.h"
49
#include "net/ipv6/uip-ds6.h"
50
51
#include "net/routing/rpl-classic/rpl-private.h"
#include "net/routing/rpl-classic/rpl-ns.h"
52
#include "net/packetbuf.h"
53
54

#define DEBUG DEBUG_NONE
55
#include "net/ipv6/uip-debug.h"
56
57
58
59

#include <limits.h>
#include <string.h>

60
/*---------------------------------------------------------------------------*/
nvt's avatar
nvt committed
61
62
63
#define UIP_IP_BUF                ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_EXT_BUF               ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len])
#define UIP_HBHO_BUF              ((struct uip_hbho_hdr *)&uip_buf[uip_l2_l3_hdr_len])
64
#define UIP_HBHO_NEXT_BUF         ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_HOP_BY_HOP_LEN])
65
66
#define UIP_RH_BUF                ((struct uip_routing_hdr *)&uip_buf[uip_l2_l3_hdr_len])
#define UIP_RPL_SRH_BUF           ((struct uip_rpl_srh_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_RH_LEN])
nvt's avatar
nvt committed
67
#define UIP_EXT_HDR_OPT_BUF       ((struct uip_ext_hdr_opt *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
68
#define UIP_EXT_HDR_OPT_PADN_BUF  ((struct uip_ext_hdr_opt_padn *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
nvt's avatar
nvt committed
69
#define UIP_EXT_HDR_OPT_RPL_BUF   ((struct uip_ext_hdr_opt_rpl *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
70
/*---------------------------------------------------------------------------*/
71
int
72
rpl_ext_header_hbh_update(int uip_ext_opt_offset)
73
74
{
  rpl_instance_t *instance;
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
75
  int down;
76
  uint16_t sender_rank;
77
  uint8_t sender_closer;
78
  uip_ds6_route_t *route;
79
  rpl_parent_t *sender = NULL;
80

81
82
83
  if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)
      || UIP_EXT_HDR_OPT_RPL_BUF->opt_type != UIP_EXT_HDR_OPT_RPL
      || UIP_EXT_HDR_OPT_RPL_BUF->opt_len != RPL_HDR_OPT_LEN) {
84

85
86
87
88
89
    PRINTF("RPL: Hop-by-hop extension header has wrong size or type (%u %u %u)\n",
        UIP_HBHO_BUF->len,
        UIP_EXT_HDR_OPT_RPL_BUF->opt_type,
        UIP_EXT_HDR_OPT_RPL_BUF->opt_len);
    return 0; /* Drop */
90
91
92
93
  }

  instance = rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance);
  if(instance == NULL) {
94
    PRINTF("RPL: Unknown instance: %u\n",
95
           UIP_EXT_HDR_OPT_RPL_BUF->instance);
96
    return 0;
97
98
  }

99
  if(UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_FWD_ERR) {
100
    PRINTF("RPL: Forward error!\n");
101
    /* We should try to repair it by removing the neighbor that caused
102
103
104
105
106
107
108
109
         the packet to be forwareded in the first place. We drop any
         routes that go through the neighbor that sent the packet to
         us. */
    if(RPL_IS_STORING(instance)) {
      route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
      if(route != NULL) {
        uip_ds6_route_rm(route);
      }
110
    }
111
112
113
114
    RPL_STAT(rpl_stats.forward_errors++);
    /* Trigger DAO retransmission */
    rpl_reset_dio_timer(instance);
    /* drop the packet as it is not routable */
115
    return 0;
116
117
  }

118
119
  if(!instance->current_dag->joined) {
    PRINTF("RPL: No DAG in the instance\n");
120
    return 0;
121
  }
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
122
  down = 0;
123
  if(UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_DOWN) {
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
124
125
126
    down = 1;
  }

127
  sender_rank = UIP_HTONS(UIP_EXT_HDR_OPT_RPL_BUF->senderrank);
128
129
  sender = nbr_table_get_from_lladdr(rpl_parents, packetbuf_addr(PACKETBUF_ADDR_SENDER));

130
  if(sender != NULL && (UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_RANK_ERR)) {
131
132
133
    /* A rank error was signalled, attempt to repair it by updating
     * the sender's rank from ext header */
    sender->rank = sender_rank;
134
135
136
137
138
139
    if(RPL_IS_NON_STORING(instance)) {
      /* Select DAG and preferred parent only in non-storing mode. In storing mode,
       * a parent switch would result in an immediate No-path DAO transmission, dropping
       * current incoming packet. */
      rpl_select_dag(instance, sender);
    }
140
141
  }

142
143
144
145
146
147
148
149
  sender_closer = sender_rank < instance->current_dag->rank;

  PRINTF("RPL: Packet going %s, sender closer %d (%d < %d)\n", down == 1 ? "down" : "up",
   sender_closer,
   sender_rank,
   instance->current_dag->rank
   );

150
151
  if((down && !sender_closer) || (!down && sender_closer)) {
    PRINTF("RPL: Loop detected - senderrank: %d my-rank: %d sender_closer: %d\n",
152
153
           sender_rank, instance->current_dag->rank,
           sender_closer);
154
155
156
157
158
159
    /* Attempt to repair the loop by sending a unicast DIO back to the sender
     * so that it gets a fresh update of our rank. */
    if(sender != NULL) {
      instance->unicast_dio_target = sender;
      rpl_schedule_unicast_dio_immediately(instance);
    }
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
160
    if(UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_RANK_ERR) {
Laurent Deru's avatar
Laurent Deru committed
161
      RPL_STAT(rpl_stats.loop_errors++);
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
162
      PRINTF("RPL: Rank error signalled in RPL option!\n");
163
      /* Packet must be dropped and dio trickle timer reset, see RFC6550 - 11.2.2.2 */
164
      rpl_reset_dio_timer(instance);
165
      return 0;
166
    }
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
167
    PRINTF("RPL: Single error tolerated\n");
Laurent Deru's avatar
Laurent Deru committed
168
    RPL_STAT(rpl_stats.loop_warnings++);
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
169
    UIP_EXT_HDR_OPT_RPL_BUF->flags |= RPL_HDR_OPT_RANK_ERR;
170
    return 1;
171
172
173
  }

  PRINTF("RPL: Rank OK\n");
174
  return 1;
175
176
177
}
/*---------------------------------------------------------------------------*/
int
178
rpl_ext_header_srh_get_next_hop(uip_ipaddr_t *ipaddr)
179
{
180
#if RPL_WITH_NON_STORING
181
182
  uint8_t *uip_next_hdr;
  int last_uip_ext_len = uip_ext_len;
183
  rpl_dag_t *dag;
184
185
  rpl_ns_node_t *dest_node;
  rpl_ns_node_t *root_node;
186

187
188
189
190
191
192
193
194
  uip_ext_len = 0;
  uip_next_hdr = &UIP_IP_BUF->proto;

  /* Look for routing header */
  while(uip_next_hdr != NULL && *uip_next_hdr != UIP_PROTO_ROUTING) {
    switch(*uip_next_hdr) {
      case UIP_PROTO_HBHO:
      case UIP_PROTO_DESTO:
195
196
197
198
199
        /*
         * As per RFC 2460, only the Hop-by-Hop Options header and
         * Destination Options header can appear before the Routing
         * header.
         */
200
201
        /* Move to next header */
        uip_next_hdr = &UIP_EXT_BUF->next;
202
        uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
203
204
        break;
      default:
205
        uip_next_hdr = NULL;
206
207
208
209
        break;
    }
  }

210
  dag = rpl_get_dag(&UIP_IP_BUF->destipaddr);
211
212
213
  root_node = rpl_ns_get_node(dag, &dag->dag_id);
  dest_node = rpl_ns_get_node(dag, &UIP_IP_BUF->destipaddr);

214
215
  if((uip_next_hdr != NULL && *uip_next_hdr == UIP_PROTO_ROUTING
      && UIP_RH_BUF->routing_type == RPL_RH_TYPE_SRH) ||
216
217
     (dest_node != NULL && root_node != NULL &&
      dest_node->parent == root_node)) {
218
219
    /* Routing header found or the packet destined for a direct child of the root.
     * The next hop should be already copied as the IPv6 destination
220
     * address, via rpl_ext_header_srh_update. We turn this address into a link-local to enable
221
222
223
224
225
226
227
228
     * forwarding to next hop */
    uip_ipaddr_copy(ipaddr, &UIP_IP_BUF->destipaddr);
    uip_create_linklocal_prefix(ipaddr);
    uip_ext_len = last_uip_ext_len;
    return 1;
  }

  uip_ext_len = last_uip_ext_len;
229
  return 0;
230
231
232
#else /* RPL_WITH_NON_STORING */
  return 0; /* SRH not found */
#endif /* RPL_WITH_NON_STORING */
233
}
234
/*---------------------------------------------------------------------------*/
235
int
236
rpl_ext_header_srh_update(void)
237
{
238
#if RPL_WITH_NON_STORING
239
240
241
242
243
244
245
246
247
248
249
  uint8_t *uip_next_hdr;
  int last_uip_ext_len = uip_ext_len;

  uip_ext_len = 0;
  uip_next_hdr = &UIP_IP_BUF->proto;

  /* Look for routing header */
  while(uip_next_hdr != NULL && *uip_next_hdr != UIP_PROTO_ROUTING) {
    switch(*uip_next_hdr) {
      case UIP_PROTO_HBHO:
      case UIP_PROTO_DESTO:
250
251
252
253
254
        /*
         * As per RFC 2460, only the Hop-by-Hop Options header and
         * Destination Options header can appear before the Routing
         * header.
         */
255
256
        /* Move to next header */
        uip_next_hdr = &UIP_EXT_BUF->next;
257
        uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
258
259
        break;
      default:
260
        uip_next_hdr = NULL;
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
        break;
    }
  }

  if(uip_next_hdr != NULL && *uip_next_hdr == UIP_PROTO_ROUTING
      && UIP_RH_BUF->routing_type == RPL_RH_TYPE_SRH) {
    /* SRH found, now look for next hop */
    uint8_t cmpri, cmpre;
    uint8_t ext_len;
    uint8_t padding;
    uint8_t path_len;
    uint8_t segments_left;
    uip_ipaddr_t current_dest_addr;

    segments_left = UIP_RH_BUF->seg_left;
    ext_len = (UIP_RH_BUF->len * 8) + 8;
    cmpri = UIP_RPL_SRH_BUF->cmpr >> 4;
    cmpre = UIP_RPL_SRH_BUF->cmpr & 0x0f;
    padding = UIP_RPL_SRH_BUF->pad >> 4;
    path_len = ((ext_len - padding - RPL_RH_LEN - RPL_SRH_LEN - (16 - cmpre)) / (16 - cmpri)) + 1;
    (void)path_len;

    PRINTF("RPL: read SRH, path len %u, segments left %u, Cmpri %u, Cmpre %u, ext len %u (padding %u)\n",
        path_len, segments_left, cmpri, cmpre, ext_len, padding);

    if(segments_left == 0) {
      /* We are the final destination, do nothing */
    } else {
      uint8_t i = path_len - segments_left; /* The index of the next address to be visited */
      uint8_t *addr_ptr = ((uint8_t *)UIP_RH_BUF) + RPL_RH_LEN + RPL_SRH_LEN + (i * (16 - cmpri));
      uint8_t cmpr = segments_left == 1 ? cmpre : cmpri;

      /* As per RFC6554: swap the IPv6 destination address and address[i] */

      /* First, copy the current IPv6 destination address */
      uip_ipaddr_copy(&current_dest_addr, &UIP_IP_BUF->destipaddr);
      /* Second, update the IPv6 destination address with addresses[i] */
      memcpy(((uint8_t *)&UIP_IP_BUF->destipaddr) + cmpr, addr_ptr, 16 - cmpr);
      /* Third, write current_dest_addr to addresses[i] */
      memcpy(addr_ptr, ((uint8_t *)&current_dest_addr) + cmpr, 16 - cmpr);

      /* Update segments left field */
      UIP_RH_BUF->seg_left--;

      PRINTF("RPL: SRH next hop ");
      PRINT6ADDR(&UIP_IP_BUF->destipaddr);
      PRINTF("\n");
    }
    uip_ext_len = last_uip_ext_len;
    return 1;
  }

  uip_ext_len = last_uip_ext_len;
  return 0;
315
316
317
#else /* RPL_WITH_NON_STORING */
  return 0; /* SRH not found */
#endif /* RPL_WITH_NON_STORING */
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
}
/*---------------------------------------------------------------------------*/
static int
count_matching_bytes(const void *p1, const void *p2, size_t n)
{
  int i = 0;
  for(i = 0; i < n; i++) {
    if(((uint8_t *)p1)[i] != ((uint8_t *)p2)[i]) {
      return i;
    }
  }
  return n;
}
/*---------------------------------------------------------------------------*/
static int
insert_srh_header(void)
{
  /* Implementation of RFC6554 */
336
  uint8_t temp_len;
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
  uint8_t path_len;
  uint8_t ext_len;
  uint8_t cmpri, cmpre; /* ComprI and ComprE fields of the RPL Source Routing Header */
  uint8_t *hop_ptr;
  uint8_t padding;
  rpl_ns_node_t *dest_node;
  rpl_ns_node_t *root_node;
  rpl_ns_node_t *node;
  rpl_dag_t *dag;
  uip_ipaddr_t node_addr;

  PRINTF("RPL: SRH creating source routing header with destination ");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF(" \n");

  /* Construct source route. We do not do this recursively to keep the runtime stack usage constant. */

  /* Get link of the destination and root */
  dag = rpl_get_dag(&UIP_IP_BUF->destipaddr);

  if(dag == NULL) {
    PRINTF("RPL: SRH DAG not found\n");
    return 0;
  }
361

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
  dest_node = rpl_ns_get_node(dag, &UIP_IP_BUF->destipaddr);
  if(dest_node == NULL) {
    /* The destination is not found, skip SRH insertion */
    return 1;
  }

  root_node = rpl_ns_get_node(dag, &dag->dag_id);
  if(root_node == NULL) {
    PRINTF("RPL: SRH root node not found\n");
    return 0;
  }

  if(!rpl_ns_is_node_reachable(dag, &UIP_IP_BUF->destipaddr)) {
    PRINTF("RPL: SRH no path found to destination\n");
    return 0;
  }

  /* Compute path length and compression factors (we use cmpri == cmpre) */
  path_len = 0;
  node = dest_node->parent;
  /* For simplicity, we use cmpri = cmpre */
  cmpri = 15;
  cmpre = 15;
385

386
  if(node == root_node) {
387
    PRINTF("RPL: SRH no need to insert SRH\n");
388
    return 1;
389
390
  }

391
392
393
394
395
396
397
398
  while(node != NULL && node != root_node) {

    rpl_ns_get_node_global_addr(&node_addr, node);

    /* How many bytes in common between all nodes in the path? */
    cmpri = MIN(cmpri, count_matching_bytes(&node_addr, &UIP_IP_BUF->destipaddr, 16));
    cmpre = cmpri;

399
400
401
    PRINTF("RPL: SRH Hop ");
    PRINT6ADDR(&node_addr);
    PRINTF("\n");
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
    node = node->parent;
    path_len++;
  }

  /* Extension header length: fixed headers + (n-1) * (16-ComprI) + (16-ComprE)*/
  ext_len = RPL_RH_LEN + RPL_SRH_LEN
      + (path_len - 1) * (16 - cmpre)
      + (16 - cmpri);

  padding = ext_len % 8 == 0 ? 0 : (8 - (ext_len % 8));
  ext_len += padding;

  PRINTF("RPL: SRH Path len: %u, ComprI %u, ComprE %u, ext len %u (padding %u)\n",
      path_len, cmpri, cmpre, ext_len, padding);

  /* Check if there is enough space to store the extension header */
Laurent Deru's avatar
Laurent Deru committed
418
  if(uip_len + ext_len > UIP_BUFSIZE - UIP_LLH_LEN) {
419
    PRINTF("RPL: Packet too long: impossible to add source routing header (%u bytes)\n", ext_len);
420
    return 0;
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
  }

  /* Move existing ext headers and payload uip_ext_len further */
  memmove(uip_buf + uip_l2_l3_hdr_len + ext_len,
      uip_buf + uip_l2_l3_hdr_len, uip_len - UIP_IPH_LEN);
  memset(uip_buf + uip_l2_l3_hdr_len, 0, ext_len);

  /* Insert source routing header */
  UIP_RH_BUF->next = UIP_IP_BUF->proto;
  UIP_IP_BUF->proto = UIP_PROTO_ROUTING;

  /* Initialize IPv6 Routing Header */
  UIP_RH_BUF->len = (ext_len - 8) / 8;
  UIP_RH_BUF->routing_type = RPL_RH_TYPE_SRH;
  UIP_RH_BUF->seg_left = path_len;

  /* Initialize RPL Source Routing Header */
  UIP_RPL_SRH_BUF->cmpr = (cmpri << 4) + cmpre;
  UIP_RPL_SRH_BUF->pad = padding << 4;

  /* Initialize addresses field (the actual source route).
   * From last to first. */
  node = dest_node;
  hop_ptr = ((uint8_t *)UIP_RH_BUF) + ext_len - padding; /* Pointer where to write the next hop compressed address */

  while(node != NULL && node->parent != root_node) {
    rpl_ns_get_node_global_addr(&node_addr, node);

    hop_ptr -= (16 - cmpri);
    memcpy(hop_ptr, ((uint8_t*)&node_addr) + cmpri, 16 - cmpri);

    node = node->parent;
  }

  /* The next hop (i.e. node whose parent is the root) is placed as the current IPv6 destination */
  rpl_ns_get_node_global_addr(&node_addr, node);
  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &node_addr);

  /* In-place update of IPv6 length field */
460
  temp_len = UIP_IP_BUF->len[1];
461
  UIP_IP_BUF->len[1] += ext_len;
462
463
464
  if(UIP_IP_BUF->len[1] < temp_len) {
    UIP_IP_BUF->len[0]++;
  }
465
466
467
468
469

  uip_ext_len += ext_len;
  uip_len += ext_len;

  return 1;
470
}
471
/*---------------------------------------------------------------------------*/
472
473
static int
update_hbh_header(void)
474
475
476
477
{
  rpl_instance_t *instance;
  int uip_ext_opt_offset;
  int last_uip_ext_len;
478
  rpl_parent_t *parent;
479
480
481
482
483

  last_uip_ext_len = uip_ext_len;
  uip_ext_len = 0;
  uip_ext_opt_offset = 2;

484
485
486
  if(UIP_IP_BUF->proto == UIP_PROTO_HBHO && UIP_EXT_HDR_OPT_RPL_BUF->opt_type == UIP_EXT_HDR_OPT_RPL) {
    if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)
        || UIP_EXT_HDR_OPT_RPL_BUF->opt_len != RPL_HDR_OPT_LEN) {
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
487

488
489
490
491
      PRINTF("RPL: Hop-by-hop extension header has wrong size (%u %u)\n",
          UIP_EXT_HDR_OPT_RPL_BUF->opt_len,
          uip_ext_len);
      return 0; /* Drop */
492
    }
493

494
495
    instance = rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance);
    if(instance == NULL || !instance->used || !instance->current_dag->joined) {
496
497
498
      PRINTF("RPL: Unable to add/update hop-by-hop extension header: incorrect instance\n");
      uip_ext_len = last_uip_ext_len;
      return 0; /* Drop */
499
500
501
    }

    PRINTF("RPL: Updating RPL option\n");
502
    /* Update sender rank and instance, will update flags next */
503
    UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(instance->current_dag->rank);
504
    UIP_EXT_HDR_OPT_RPL_BUF->instance = instance->instance_id;
505

506
507
    if(RPL_IS_STORING(instance)) { /* In non-storing mode, downwards traffic does not have the HBH option */
      /* Check the direction of the down flag, as per Section 11.2.2.3,
508
509
510
            which states that if a packet is going down it should in
            general not go back up again. If this happens, a
            RPL_HDR_OPT_FWD_ERR should be flagged. */
511
512
513
514
515
      if((UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_DOWN)) {
        if(uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr) == NULL) {
          UIP_EXT_HDR_OPT_RPL_BUF->flags |= RPL_HDR_OPT_FWD_ERR;
          PRINTF("RPL forwarding error\n");
          /* We should send back the packet to the originating parent,
516
                but it is not feasible yet, so we send a No-Path DAO instead */
517
518
519
520
521
522
523
          PRINTF("RPL generate No-Path DAO\n");
          parent = rpl_get_parent((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
          if(parent != NULL) {
            dao_output_target(parent, &UIP_IP_BUF->destipaddr, RPL_ZERO_LIFETIME);
          }
          /* Drop packet */
          return 0;
524
        }
525
      } else {
526
        /* Set the down extension flag correctly as described in Section
527
528
              11.2 of RFC6550. If the packet progresses along a DAO route,
              the down flag should be set. */
529
530
        if(uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr) == NULL) {
          /* No route was found, so this packet will go towards the RPL
531
                root. If so, we should not set the down flag. */
532
533
534
535
536
537
538
          UIP_EXT_HDR_OPT_RPL_BUF->flags &= ~RPL_HDR_OPT_DOWN;
          PRINTF("RPL option going up\n");
        } else {
          /* A DAO route was found so we set the down flag. */
          UIP_EXT_HDR_OPT_RPL_BUF->flags |= RPL_HDR_OPT_DOWN;
          PRINTF("RPL option going down\n");
        }
539
      }
540
    }
541
  }
542
543
544

  uip_ext_len = last_uip_ext_len;
  return 1;
545
546
547
}
/*---------------------------------------------------------------------------*/
static int
548
insert_hbh_header(const rpl_instance_t *instance)
549
550
551
552
553
554
555
556
557
558
559
{
  int uip_ext_opt_offset;
  int last_uip_ext_len;
  uint8_t temp_len;

  last_uip_ext_len = uip_ext_len;
  uip_ext_len = 0;
  uip_ext_opt_offset = 2;

  /* Insert hop-by-hop header */
  PRINTF("RPL: Creating hop-by-hop option\n");
Laurent Deru's avatar
Laurent Deru committed
560
  if(uip_len + RPL_HOP_BY_HOP_LEN > UIP_BUFSIZE - UIP_LLH_LEN) {
561
562
    PRINTF("RPL: Packet too long: impossible to add hop-by-hop option\n");
    uip_ext_len = last_uip_ext_len;
563
    return 0;
564
  }
565
566
567
568
569
570
571
572
573
574
575
576
577
578

  /* Move existing ext headers and payload UIP_EXT_BUF further */
  memmove(UIP_HBHO_NEXT_BUF, UIP_EXT_BUF, uip_len - UIP_IPH_LEN);
  memset(UIP_HBHO_BUF, 0, RPL_HOP_BY_HOP_LEN);

  /* Update IP and HBH protocol and fields */
  UIP_HBHO_BUF->next = UIP_IP_BUF->proto;
  UIP_IP_BUF->proto = UIP_PROTO_HBHO;

  /* Initialize HBH option */
  UIP_HBHO_BUF->len = (RPL_HOP_BY_HOP_LEN - 8) / 8;
  UIP_EXT_HDR_OPT_RPL_BUF->opt_type = UIP_EXT_HDR_OPT_RPL;
  UIP_EXT_HDR_OPT_RPL_BUF->opt_len = RPL_HDR_OPT_LEN;
  UIP_EXT_HDR_OPT_RPL_BUF->flags = 0;
579
580
  UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(instance->current_dag->rank);
  UIP_EXT_HDR_OPT_RPL_BUF->instance = instance->instance_id;
581
582
  uip_len += RPL_HOP_BY_HOP_LEN;
  temp_len = UIP_IP_BUF->len[1];
583
  UIP_IP_BUF->len[1] += RPL_HOP_BY_HOP_LEN;
584
585
586
587
588
  if(UIP_IP_BUF->len[1] < temp_len) {
    UIP_IP_BUF->len[0]++;
  }

  uip_ext_len = last_uip_ext_len + RPL_HOP_BY_HOP_LEN;
589

590
591
  /* Update header before returning */
  return update_hbh_header();
592
}
593
/*---------------------------------------------------------------------------*/
594
void
595
rpl_ext_header_remove(void)
596
{
597
  uint8_t temp_len;
598
  uint8_t rpl_ext_hdr_len;
599
  int uip_ext_opt_offset;
600
  uint8_t *uip_next_hdr;
601
602

  uip_ext_len = 0;
603
  uip_ext_opt_offset = 2;
604
605
606
607
608
609
610
  uip_next_hdr = &UIP_IP_BUF->proto;

  /* Look for hop-by-hop and routing headers */
  while(uip_next_hdr != NULL) {
    switch(*uip_next_hdr) {
      case UIP_PROTO_HBHO:
      case UIP_PROTO_ROUTING:
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
        if((*uip_next_hdr != UIP_PROTO_HBHO || UIP_EXT_HDR_OPT_RPL_BUF->opt_type == UIP_EXT_HDR_OPT_RPL)) {
          /* Remove hop-by-hop and routing headers */
          *uip_next_hdr = UIP_EXT_BUF->next;
          rpl_ext_hdr_len = (UIP_EXT_BUF->len * 8) + 8;
          temp_len = UIP_IP_BUF->len[1];
          uip_len -= rpl_ext_hdr_len;
          UIP_IP_BUF->len[1] -= rpl_ext_hdr_len;
          if(UIP_IP_BUF->len[1] > temp_len) {
            UIP_IP_BUF->len[0]--;
          }
          PRINTF("RPL: Removing RPL extension header (type %u, len %u)\n", *uip_next_hdr, rpl_ext_hdr_len);
          memmove(UIP_EXT_BUF, ((uint8_t *)UIP_EXT_BUF) + rpl_ext_hdr_len, uip_len - UIP_IPH_LEN);
        } else {
          uip_next_hdr = &UIP_EXT_BUF->next;
          uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
626
627
        }
        break;
628
629
630
631
632
633
634
635
636
      case UIP_PROTO_DESTO:
        /*
         * As per RFC 2460, any header other than the Destination
         * Options header does not appear between the Hop-by-Hop
         * Options header and the Routing header.
         *
         * We're moving to the next header only if uip_next_hdr has
         * UIP_PROTO_DESTO. Otherwise, we'll return.
         */
637
638
        /* Move to next header */
        uip_next_hdr = &UIP_EXT_BUF->next;
639
        uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
640
        break;
641
642
    default:
      return;
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
643
    }
644
645
  }
}
646
/*---------------------------------------------------------------------------*/
647
int
648
rpl_ext_header_update(void)
649
{
650
651
  if(default_instance == NULL || default_instance->current_dag == NULL
      || uip_is_addr_linklocal(&UIP_IP_BUF->destipaddr) || uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
652
    return 1;
653
  }
Nicolas Tsiftes's avatar
Nicolas Tsiftes committed
654

655
656
  if(default_instance->current_dag->rank == ROOT_RANK(default_instance)) {
    /* At the root, remove headers if any, and insert SRH or HBH
657
    * (SRH is inserted only if the destination is in the DODAG) */
658
    rpl_ext_header_remove();
659
660
661
662
663
664
665
    if(rpl_get_dag(&UIP_IP_BUF->destipaddr) != NULL) {
      /* dest is in a DODAG; the packet is going down. */
      if(RPL_IS_NON_STORING(default_instance)) {
        return insert_srh_header();
      } else {
        return insert_hbh_header(default_instance);
      }
666
    } else {
667
668
      /* dest is outside of DODAGs; no ext header is needed. */
      return 1;
669
    }
670
671
672
673
674
675
  } else {
    if(uip_ds6_is_my_addr(&UIP_IP_BUF->srcipaddr)
        && UIP_IP_BUF->ttl == uip_ds6_if.cur_hop_limit) {
      /* Insert HBH option at source. Checking the address is not sufficient because
       * in non-storing mode, a packet may go up and then down the same path again */
      return insert_hbh_header(default_instance);
676
    } else {
677
      /* Update HBH option at forwarders */
678
679
      return update_hbh_header();
    }
680
681
  }
}
Rémy Léone's avatar
Rémy Léone committed
682
683

/** @}*/