cleanup confilicrts

ico
Harald Pichler 2016-06-28 16:37:16 +02:00
commit 3c8e91d74e
701 changed files with 56477 additions and 7743 deletions

6
.gitignore vendored
View File

@ -80,7 +80,7 @@ contiki-cc2530dk.lib
*.dsc
#cc65 build artifacts
*.S
*.s
*.eth
*.dsk
*.po
@ -125,3 +125,7 @@ platform/galileo/bsp/grub/bin/
*.galileo.dll
*.galileo.efi
LOG_OPENOCD
# nRF52 build artifacts
*.jlink
*.nrf52dk

View File

@ -24,7 +24,11 @@ before_script:
msp430-gcc --version
## Install avr toolchain
- sudo apt-get -qq install gcc-avr avr-libc
- $WGET http://atiselsts.github.io/resources/avr-gcc-4.9.2-compiled.tar.bz2 &&
tar xjf avr-gcc*.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/avr-gcc/* /usr/local/ &&
rm -rf /tmp/avr-gcc avr-gcc*.tar.bz2 &&
avr-gcc --version
## Install 32-bit compatibility libraries
- sudo apt-get -qq install libc6:i386 libgcc1:i386 gcc-4.6-base:i386
@ -40,13 +44,13 @@ before_script:
arm-none-eabi-gcc --version ;
fi
## Install mainline ARM toolchain. gcc-arm-none-eabi is available
## in Ubuntu >= 14.04, but this external PPA is needed for 12.04.
## Install srecord
## Install mainline ARM toolchain and srecord.
- if [ ${BUILD_ARCH:-0} = arm-aapcs ] ; then
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa &&
sudo apt-get -qq update &&
sudo apt-get -qq install gcc-arm-embedded=5-2015q4-1~precise1 srecord &&
sudo apt-get -qq install srecord &&
$WGET https://launchpad.net/gcc-arm-embedded/5.0/5-2015-q4-major/+download/gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 &&
tar xjf gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 -C /tmp/ &&
sudo cp -f -r /tmp/gcc-arm-none-eabi-5_2-2015q4/* /usr/local/ &&
rm -rf /tmp/gcc-arm-none-eabi-* gcc-arm-none-eabi-*-linux.tar.bz2 &&
arm-none-eabi-gcc --version ;
fi
@ -70,7 +74,6 @@ before_script:
git clone https://github.com/cc65/cc65 /tmp/cc65 &&
make -C /tmp/cc65 bin apple2enh atarixl c64 c128 &&
sudo make -C /tmp/cc65 avail &&
export CC65_HOME=/tmp/cc65/ &&
cc65 --version ;
fi
@ -89,6 +92,18 @@ before_script:
rm -rf /tmp/ba-elf-gcc* /tmp/jn516x-sdk* &&
ba-elf-gcc --version ;
fi
## Install mainline ARM toolchain and download nRF52 SDK
- if [ ${BUILD_ARCH:-0} = nrf52dk ] ; then
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa &&
sudo apt-get -qq update &&
sudo apt-get -qq install gcc-arm-embedded srecord &&
arm-none-eabi-gcc --version &&
$WGET https://developer.nordicsemi.com/nRF5_IoT_SDK/nRF5_IoT_SDK_v0.9.x/nrf5_iot_sdk_3288530.zip &&
mkdir /tmp/nrf52-sdk &&
unzip nrf5_iot_sdk_3288530.zip -d /tmp/nrf52-sdk &&
export NRF52_SDK_ROOT=/tmp/nrf52-sdk ;
fi
## Compile cooja.jar only when it's going to be needed
- if [ ${BUILD_CATEGORY:-sim} = sim ] ; then
@ -120,6 +135,7 @@ env:
- BUILD_TYPE='collect'
- BUILD_TYPE='collect-lossy'
- BUILD_TYPE='rpl'
- BUILD_TYPE='rpl-non-storing'
- BUILD_TYPE='large-rpl'
- BUILD_TYPE='rime'
- BUILD_TYPE='ipv6'
@ -138,5 +154,7 @@ env:
- BUILD_TYPE='compile-6502-ports' BUILD_CATEGORY='compile' BUILD_ARCH='6502'
- BUILD_TYPE='compile-arm-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-aapcs'
- BUILD_TYPE='compile-nxp-ports' BUILD_CATEGORY='compile' BUILD_ARCH='jn516x'
- BUILD_TYPE='compile-nrf52-ports' BUILD_CATEGORY='compile' BUILD_ARCH='nrf52dk'
- BUILD_TYPE='slip-radio' MAKE_TARGETS='cooja'
- BUILD_TYPE='llsec' MAKE_TARGETS='cooja'
- BUILD_TYPE='compile-avr' BUILD_CATEGORY='compile' BUILD_ARCH='avr-rss2'

View File

@ -184,7 +184,7 @@ CONTIKI_CPU_DIRS_CONCAT = ${addprefix $(CONTIKI_CPU)/, \
$(CONTIKI_CPU_DIRS)}
SOURCEDIRS = . $(PROJECTDIRS) $(CONTIKI_TARGET_DIRS_CONCAT) \
$(CONTIKI_CPU_DIRS_CONCAT) $(CONTIKIDIRS) $(APPDS) ${dir $(target_makefile)}
$(CONTIKI_CPU_DIRS_CONCAT) $(CONTIKIDIRS) $(APPDS) $(EXTERNALDIRS) ${dir $(target_makefile)}
vpath %.c $(SOURCEDIRS)
vpath %.cpp $(SOURCEDIRS)

View File

@ -0,0 +1 @@
at-master_src = at-master.c

148
apps/at-master/at-master.c Normal file
View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2015, Zolertia - http://www.zolertia.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 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.
*
*/
#include "contiki.h"
#include "contiki-lib.h"
#include "at-master.h"
#include "cpu.h"
#include "dev/uart.h"
#include "dev/serial-line.h"
#include "dev/sys-ctrl.h"
#include "lib/list.h"
#include "sys/cc.h"
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
/*---------------------------------------------------------------------------*/
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
LIST(at_cmd_list);
process_event_t at_cmd_received_event;
/*---------------------------------------------------------------------------*/
static uint8_t at_uart = 0;
/*---------------------------------------------------------------------------*/
PROCESS(at_process, "AT process");
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(at_process, ev, data)
{
uint8_t plen;
char *pch, *buf;
struct at_cmd *a;
PROCESS_BEGIN();
while(1) {
PROCESS_WAIT_EVENT_UNTIL(ev == serial_line_event_message && data != NULL);
buf = (char *)data;
plen = strlen(buf);
for(a = list_head(at_cmd_list); a != NULL; a = list_item_next(a)) {
pch = strstr(buf, a->cmd_header);
if((plen <= a->cmd_max_len) && (pch != NULL)) {
if(strncmp(a->cmd_header, pch, a->cmd_hdr_len) == 0) {
if((a->cmd_hdr_len == plen) || (a->cmd_max_len > a->cmd_hdr_len)) {
a->event_callback(a, plen, (char *)pch);
process_post(a->app_process, at_cmd_received_event, NULL);
break;
}
}
}
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
struct at_cmd *
at_list(void)
{
return list_head(at_cmd_list);
}
/*---------------------------------------------------------------------------*/
uint8_t
at_send(char *s, uint8_t len)
{
uint8_t i = 0;
while(s && *s != 0) {
if(i >= len) {
break;
}
uart_write_byte(at_uart, *s++);
i++;
}
return i;
}
/*---------------------------------------------------------------------------*/
void
at_init(uint8_t uart_sel)
{
static uint8_t inited = 0;
if(!inited) {
list_init(at_cmd_list);
at_cmd_received_event = process_alloc_event();
inited = 1;
at_uart = uart_sel;
uart_init(at_uart);
uart_set_input(at_uart, serial_line_input_byte);
serial_line_init();
process_start(&at_process, NULL);
PRINTF("AT: Started (%u)\n", at_uart);
}
}
/*---------------------------------------------------------------------------*/
at_status_t
at_register(struct at_cmd *cmd, struct process *app_process,
const char *cmd_hdr, const uint8_t hdr_len,
const uint8_t cmd_max_len, at_event_callback_t event_callback)
{
if((hdr_len < 1) || (cmd_max_len < 1) || (!strncmp(cmd_hdr, "AT", 2) == 0) ||
(event_callback == NULL)) {
PRINTF("AT: Invalid argument\n");
return AT_STATUS_INVALID_ARGS_ERROR;
}
memset(cmd, 0, sizeof(struct at_cmd));
cmd->event_callback = event_callback;
cmd->cmd_header = cmd_hdr;
cmd->cmd_hdr_len = hdr_len;
cmd->cmd_max_len = cmd_max_len;
cmd->app_process = app_process;
list_add(at_cmd_list, cmd);
PRINTF("AT: registered HDR %s LEN %u MAX %u\n", cmd->cmd_header,
cmd->cmd_hdr_len,
cmd->cmd_max_len);
return AT_STATUS_OK;
}
/*---------------------------------------------------------------------------*/

123
apps/at-master/at-master.h Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2015, Zolertia - http://www.zolertia.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 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.
*
*/
#ifndef AT_MASTER_H_
#define AT_MASTER_H_
#include "contiki.h"
/*---------------------------------------------------------------------------*/
#define AT_DEFAULT_RESPONSE_OK "\r\nOK\r\n"
#define AT_DEFAULT_RESPONSE_ERROR "\r\nERROR\r\n"
/*---------------------------------------------------------------------------*/
#define AT_RESPONSE(x) at_send((x), (strlen(x)))
/*---------------------------------------------------------------------------*/
extern process_event_t at_cmd_received_event;
struct at_cmd;
/*---------------------------------------------------------------------------*/
typedef enum {
AT_STATUS_OK,
AT_STATUS_ERROR,
AT_STATUS_INVALID_ARGS_ERROR,
} at_status_t;
/*---------------------------------------------------------------------------*/
/**
* \brief AT initialization
* \param uart selects which UART to use
*
* The AT driver invokes this function upon registering a command, this will
* wait for the serial_line_event_message event
*/
void at_init(uint8_t uart);
/*---------------------------------------------------------------------------*/
/**
* \brief AT initialization
* \param uart selects which UART to use
*
* The AT driver invokes this function upon registering a command, this will
* wait for the serial_line_event_message event
*/
uint8_t at_send(char *s, uint8_t len);
/*---------------------------------------------------------------------------*/
/**
* \brief AT event callback
* \param cmd A pointer to the AT command placeholder
* \param len Lenght of the received data (including the AT command header)
* \param data A user-defined pointer
*
* The AT event callback function gets called whenever there is an
* event on an incoming AT command
*/
typedef void (*at_event_callback_t)(struct at_cmd *cmd,
uint8_t len,
char *data);
/*---------------------------------------------------------------------------*/
struct at_cmd {
struct at_cmd *next;
const char *cmd_header;
uint8_t cmd_hdr_len;
uint8_t cmd_max_len;
at_event_callback_t event_callback;
struct process *app_process;
};
/*---------------------------------------------------------------------------*/
/**
* \brief Registers the callback to return an AT command
* \param cmd A pointer to the CMD placeholder
* \param cmd_hdr String to compare when an AT command is received
* \param cmd_len Lenght of cmd_hdr
* \param event_callback Callback function to handle the AT command
* \return AT_STATUS_OK or AT_STATUS_INVALID_ARGS_ERROR
*
* Register the commands to search for when a valid AT frame has been received
*/
at_status_t at_register(struct at_cmd *cmd,
struct process *app_process,
const char *cmd_hdr,
const uint8_t cmd_hdr_len,
const uint8_t cmd_max_len,
at_event_callback_t event_callback);
/*---------------------------------------------------------------------------*/
struct at_cmd *at_list(void);
/*---------------------------------------------------------------------------*/
/**
* \brief Registers the callback to return an AT command
* \param cmd A pointer to the CMD placeholder
* \param cmd_hdr String to compare when an AT command is received
* \param cmd_len Lenght of cmd_hdr
* \param event_callback Callback function to handle the AT command
* \return AT_STATUS_OK or AT_STATUS_INVALID_ARGS_ERROR
*
* Register the commands to search for when a valid AT frame has been received
*/
at_status_t at_register(struct at_cmd *cmd,
struct process *app_process,
const char *cmd_hdr,
const uint8_t cmd_hdr_len,
const uint8_t cmd_max_len,
at_event_callback_t event_callback);
#endif /* AT_MASTER_H_ */

View File

@ -1 +0,0 @@
deluge_src = deluge.c

View File

@ -1,698 +0,0 @@
/*
* Copyright (c) 2007, 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.
*
*/
/**
* \file
* An implementation of the Deluge protocol.
* (Hui and Culler: The dynamic behavior of a data
* dissemination protocol for network programming at scale,
* ACM SenSys 2004)
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#include "contiki.h"
#include "net/rime/rime.h"
#include "cfs/cfs.h"
#include "loader/elfloader.h"
#include "lib/crc16.h"
#include "lib/random.h"
#include "sys/node-id.h"
#include "deluge.h"
#if NETSIM
#include "ether.h"
#include <stdio.h>
#endif
#include "dev/leds.h"
#include <stdlib.h>
#include <string.h>
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/* Implementation-specific variables. */
static struct broadcast_conn deluge_broadcast;
static struct unicast_conn deluge_uc;
static struct deluge_object current_object;
static process_event_t deluge_event;
/* Deluge variables. */
static int deluge_state;
static int old_summary;
static int neighbor_inconsistency;
static unsigned r_interval;
static unsigned recv_adv;
static int broadcast_profile;
/* Deluge timers. */
static struct ctimer rx_timer;
static struct ctimer tx_timer;
static struct ctimer summary_timer;
static struct ctimer profile_timer;
/* Deluge objects will get an ID that defaults to the current value of
the next_object_id parameter. */
static deluge_object_id_t next_object_id;
/* Rime callbacks. */
static void broadcast_recv(struct broadcast_conn *, const linkaddr_t *);
static void unicast_recv(struct unicast_conn *, const linkaddr_t *);
static const struct broadcast_callbacks broadcast_call = {broadcast_recv, NULL};
static const struct unicast_callbacks unicast_call = {unicast_recv, NULL};
/* The Deluge process manages the main Deluge timer. */
PROCESS(deluge_process, "Deluge");
static void
transition(int state)
{
if(state != deluge_state) {
switch(deluge_state) {
case DELUGE_STATE_MAINTAIN:
ctimer_stop(&summary_timer);
ctimer_stop(&profile_timer);
break;
case DELUGE_STATE_RX:
ctimer_stop(&rx_timer);
break;
case DELUGE_STATE_TX:
ctimer_stop(&tx_timer);
break;
}
deluge_state = state;
}
}
static int
write_page(struct deluge_object *obj, unsigned pagenum, unsigned char *data)
{
cfs_offset_t offset;
offset = pagenum * S_PAGE;
if(cfs_seek(obj->cfs_fd, offset, CFS_SEEK_SET) != offset) {
return -1;
}
return cfs_write(obj->cfs_fd, (char *)data, S_PAGE);
}
static int
read_page(struct deluge_object *obj, unsigned pagenum, unsigned char *buf)
{
cfs_offset_t offset;
offset = pagenum * S_PAGE;
if(cfs_seek(obj->cfs_fd, offset, CFS_SEEK_SET) != offset) {
return -1;
}
return cfs_read(obj->cfs_fd, (char *)buf, S_PAGE);
}
static void
init_page(struct deluge_object *obj, int pagenum, int have)
{
struct deluge_page *page;
unsigned char buf[S_PAGE];
page = &obj->pages[pagenum];
page->flags = 0;
page->last_request = 0;
page->last_data = 0;
if(have) {
page->version = obj->version;
page->packet_set = ALL_PACKETS;
page->flags |= PAGE_COMPLETE;
read_page(obj, pagenum, buf);
page->crc = crc16_data(buf, S_PAGE, 0);
} else {
page->version = 0;
page->packet_set = 0;
}
}
static cfs_offset_t
file_size(const char *file)
{
int fd;
cfs_offset_t size;
fd = cfs_open(file, CFS_READ);
if(fd < 0) {
return (cfs_offset_t)-1;
}
size = cfs_seek(fd, 0, CFS_SEEK_END);
cfs_close(fd);
return size;
}
static int
init_object(struct deluge_object *obj, char *filename, unsigned version)
{
static struct deluge_page *page;
int i;
obj->cfs_fd = cfs_open(filename, CFS_READ | CFS_WRITE);
if(obj->cfs_fd < 0) {
return -1;
}
obj->filename = filename;
obj->object_id = next_object_id++;
obj->size = file_size(filename);
obj->version = obj->update_version = version;
obj->current_rx_page = 0;
obj->nrequests = 0;
obj->tx_set = 0;
obj->pages = malloc(OBJECT_PAGE_COUNT(*obj) * sizeof(*obj->pages));
if(obj->pages == NULL) {
cfs_close(obj->cfs_fd);
return -1;
}
for(i = 0; i < OBJECT_PAGE_COUNT(current_object); i++) {
page = &current_object.pages[i];
init_page(&current_object, i, 1);
}
memset(obj->current_page, 0, sizeof(obj->current_page));
return 0;
}
static int
highest_available_page(struct deluge_object *obj)
{
int i;
for(i = 0; i < OBJECT_PAGE_COUNT(*obj); i++) {
if(!(obj->pages[i].flags & PAGE_COMPLETE)) {
break;
}
}
return i;
}
static void
send_request(void *arg)
{
struct deluge_object *obj;
struct deluge_msg_request request;
obj = (struct deluge_object *)arg;
request.cmd = DELUGE_CMD_REQUEST;
request.pagenum = obj->current_rx_page;
request.version = obj->pages[request.pagenum].version;
request.request_set = ~obj->pages[obj->current_rx_page].packet_set;
request.object_id = obj->object_id;
PRINTF("Sending request for page %d, version %u, request_set %u\n",
request.pagenum, request.version, request.request_set);
packetbuf_copyfrom(&request, sizeof(request));
unicast_send(&deluge_uc, &obj->summary_from);
/* Deluge R.2 */
if(++obj->nrequests == CONST_LAMBDA) {
/* XXX check rate here too. */
obj->nrequests = 0;
transition(DELUGE_STATE_MAINTAIN);
} else {
ctimer_reset(&rx_timer);
}
}
static void
advertise_summary(struct deluge_object *obj)
{
struct deluge_msg_summary summary;
if(recv_adv >= CONST_K) {
ctimer_stop(&summary_timer);
return;
}
summary.cmd = DELUGE_CMD_SUMMARY;
summary.version = obj->update_version;
summary.highest_available = highest_available_page(obj);
summary.object_id = obj->object_id;
PRINTF("Advertising summary for object id %u: version=%u, available=%u\n",
(unsigned)obj->object_id, summary.version, summary.highest_available);
packetbuf_copyfrom(&summary, sizeof(summary));
broadcast_send(&deluge_broadcast);
}
static void
handle_summary(struct deluge_msg_summary *msg, const linkaddr_t *sender)
{
int highest_available, i;
clock_time_t oldest_request, oldest_data, now;
struct deluge_page *page;
highest_available = highest_available_page(&current_object);
if(msg->version != current_object.version ||
msg->highest_available != highest_available) {
neighbor_inconsistency = 1;
} else {
recv_adv++;
}
if(msg->version < current_object.version) {
old_summary = 1;
broadcast_profile = 1;
}
/* Deluge M.5 */
if(msg->version == current_object.update_version &&
msg->highest_available > highest_available) {
if(msg->highest_available > OBJECT_PAGE_COUNT(current_object)) {
PRINTF("Error: highest available is above object page count!\n");
return;
}
oldest_request = oldest_data = now = clock_time();
for(i = 0; i < msg->highest_available; i++) {
page = &current_object.pages[i];
if(page->last_request < oldest_request) {
oldest_request = page->last_request;
}
if(page->last_request < oldest_data) {
oldest_data = page->last_data;
}
}
if(((now - oldest_request) / CLOCK_SECOND) <= 2 * r_interval ||
((now - oldest_data) / CLOCK_SECOND) <= r_interval) {
return;
}
linkaddr_copy(&current_object.summary_from, sender);
transition(DELUGE_STATE_RX);
if(ctimer_expired(&rx_timer)) {
ctimer_set(&rx_timer,
CONST_OMEGA * ESTIMATED_TX_TIME + ((unsigned)random_rand() % T_R),
send_request, &current_object);
}
}
}
static void
send_page(struct deluge_object *obj, unsigned pagenum)
{
unsigned char buf[S_PAGE];
struct deluge_msg_packet pkt;
unsigned char *cp;
pkt.cmd = DELUGE_CMD_PACKET;
pkt.pagenum = pagenum;
pkt.version = obj->pages[pagenum].version;
pkt.packetnum = 0;
pkt.object_id = obj->object_id;
pkt.crc = 0;
read_page(obj, pagenum, buf);
/* Divide the page into packets and send them one at a time. */
for(cp = buf; cp + S_PKT <= (unsigned char *)&buf[S_PAGE]; cp += S_PKT) {
if(obj->tx_set & (1 << pkt.packetnum)) {
pkt.crc = crc16_data(cp, S_PKT, 0);
memcpy(pkt.payload, cp, S_PKT);
packetbuf_copyfrom(&pkt, sizeof(pkt));
broadcast_send(&deluge_broadcast);
}
pkt.packetnum++;
}
obj->tx_set = 0;
}
static void
tx_callback(void *arg)
{
struct deluge_object *obj;
obj = (struct deluge_object *)arg;
if(obj->current_tx_page >= 0 && obj->tx_set) {
send_page(obj, obj->current_tx_page);
/* Deluge T.2. */
if(obj->tx_set) {
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM);
ctimer_reset(&tx_timer);
} else {
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM_END);
obj->current_tx_page = -1;
transition(DELUGE_STATE_MAINTAIN);
}
}
}
static void
handle_request(struct deluge_msg_request *msg)
{
int highest_available;
if(msg->pagenum >= OBJECT_PAGE_COUNT(current_object)) {
return;
}
if(msg->version != current_object.version) {
neighbor_inconsistency = 1;
}
highest_available = highest_available_page(&current_object);
/* Deluge M.6 */
if(msg->version == current_object.version &&
msg->pagenum <= highest_available) {
current_object.pages[msg->pagenum].last_request = clock_time();
/* Deluge T.1 */
if(msg->pagenum == current_object.current_tx_page) {
current_object.tx_set |= msg->request_set;
} else {
current_object.current_tx_page = msg->pagenum;
current_object.tx_set = msg->request_set;
}
transition(DELUGE_STATE_TX);
ctimer_set(&tx_timer, CLOCK_SECOND, tx_callback, &current_object);
}
}
static void
handle_packet(struct deluge_msg_packet *msg)
{
struct deluge_page *page;
uint16_t crc;
struct deluge_msg_packet packet;
memcpy(&packet, msg, sizeof(packet));
PRINTF("Incoming packet for object id %u, version %u, page %u, packet num %u!\n",
(unsigned)packet.object_id, (unsigned)packet.version,
(unsigned)packet.pagenum, (unsigned)packet.packetnum);
if(packet.pagenum != current_object.current_rx_page) {
return;
}
if(packet.version != current_object.version) {
neighbor_inconsistency = 1;
}
page = &current_object.pages[packet.pagenum];
if(packet.version == page->version && !(page->flags & PAGE_COMPLETE)) {
memcpy(&current_object.current_page[S_PKT * packet.packetnum],
packet.payload, S_PKT);
crc = crc16_data(packet.payload, S_PKT, 0);
if(packet.crc != crc) {
PRINTF("packet crc: %hu, calculated crc: %hu\n", packet.crc, crc);
return;
}
page->last_data = clock_time();
page->packet_set |= (1 << packet.packetnum);
if(page->packet_set == ALL_PACKETS) {
/* This is the last packet of the requested page; stop streaming. */
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM_END);
write_page(&current_object, packet.pagenum, current_object.current_page);
page->version = packet.version;
page->flags = PAGE_COMPLETE;
PRINTF("Page %u completed\n", packet.pagenum);
current_object.current_rx_page++;
if(packet.pagenum == OBJECT_PAGE_COUNT(current_object) - 1) {
current_object.version = current_object.update_version;
leds_on(LEDS_RED);
PRINTF("Update completed for object %u, version %u\n",
(unsigned)current_object.object_id, packet.version);
} else if(current_object.current_rx_page < OBJECT_PAGE_COUNT(current_object)) {
if(ctimer_expired(&rx_timer)) {
ctimer_set(&rx_timer,
CONST_OMEGA * ESTIMATED_TX_TIME + (random_rand() % T_R),
send_request, &current_object);
}
}
/* Deluge R.3 */
transition(DELUGE_STATE_MAINTAIN);
} else {
/* More packets to come. Put lower layers in streaming mode. */
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_PACKET_TYPE_STREAM);
}
}
}
static void
send_profile(struct deluge_object *obj)
{
struct deluge_msg_profile *msg;
unsigned char buf[sizeof(*msg) + OBJECT_PAGE_COUNT(*obj)];
int i;
if(broadcast_profile && recv_adv < CONST_K) {
broadcast_profile = 0;
msg = (struct deluge_msg_profile *)buf;
msg->cmd = DELUGE_CMD_PROFILE;
msg->version = obj->version;
msg->npages = OBJECT_PAGE_COUNT(*obj);
msg->object_id = obj->object_id;
for(i = 0; i < msg->npages; i++) {
msg->version_vector[i] = obj->pages[i].version;
}
packetbuf_copyfrom(buf, sizeof(buf));
broadcast_send(&deluge_broadcast);
}
}
static void
handle_profile(struct deluge_msg_profile *msg)
{
int i;
int npages;
struct deluge_object *obj;
char *p;
obj = &current_object;
if(msg->version <= current_object.update_version) {
return;
}
PRINTF("Received profile of version %u with a vector of %u pages.\n",
msg->version, msg->npages);
leds_off(LEDS_RED);
current_object.tx_set = 0;
npages = OBJECT_PAGE_COUNT(*obj);
obj->size = msg->npages * S_PAGE;
p = malloc(OBJECT_PAGE_COUNT(*obj) * sizeof(*obj->pages));
if(p == NULL) {
PRINTF("Failed to reallocate memory for pages!\n");
return;
}
memcpy(p, obj->pages, npages * sizeof(*obj->pages));
free(obj->pages);
obj->pages = (struct deluge_page *)p;
if(msg->npages < npages) {
npages = msg->npages;
}
for(i = 0; i < npages; i++) {
if(msg->version_vector[i] > obj->pages[i].version) {
obj->pages[i].packet_set = 0;
obj->pages[i].flags &= ~PAGE_COMPLETE;
obj->pages[i].version = msg->version_vector[i];
}
}
for(; i < msg->npages; i++) {
init_page(obj, i, 0);
}
obj->current_rx_page = highest_available_page(obj);
obj->update_version = msg->version;
transition(DELUGE_STATE_RX);
ctimer_set(&rx_timer,
CONST_OMEGA * ESTIMATED_TX_TIME + ((unsigned)random_rand() % T_R),
send_request, obj);
}
static void
command_dispatcher(const linkaddr_t *sender)
{
char *msg;
int len;
struct deluge_msg_profile *profile;
msg = packetbuf_dataptr();
len = packetbuf_datalen();
if(len < 1)
return;
switch(msg[0]) {
case DELUGE_CMD_SUMMARY:
if(len >= sizeof(struct deluge_msg_summary))
handle_summary((struct deluge_msg_summary *)msg, sender);
break;
case DELUGE_CMD_REQUEST:
if(len >= sizeof(struct deluge_msg_request))
handle_request((struct deluge_msg_request *)msg);
break;
case DELUGE_CMD_PACKET:
if(len >= sizeof(struct deluge_msg_packet))
handle_packet((struct deluge_msg_packet *)msg);
break;
case DELUGE_CMD_PROFILE:
profile = (struct deluge_msg_profile *)msg;
if(len >= sizeof(*profile) &&
len >= sizeof(*profile) + profile->npages * profile->version_vector[0])
handle_profile((struct deluge_msg_profile *)msg);
break;
default:
PRINTF("Incoming packet with unknown command: %d\n", msg[0]);
}
}
static void
unicast_recv(struct unicast_conn *c, const linkaddr_t *sender)
{
command_dispatcher(sender);
}
static void
broadcast_recv(struct broadcast_conn *c, const linkaddr_t *sender)
{
command_dispatcher(sender);
}
int
deluge_disseminate(char *file, unsigned version)
{
/* This implementation disseminates at most one object. */
if(next_object_id > 0 || init_object(&current_object, file, version) < 0) {
return -1;
}
process_start(&deluge_process, (void *)file);
return 0;
}
PROCESS_THREAD(deluge_process, ev, data)
{
static struct etimer et;
static unsigned time_counter;
static unsigned r_rand;
PROCESS_EXITHANDLER(goto exit);
PROCESS_BEGIN();
deluge_event = process_alloc_event();
broadcast_open(&deluge_broadcast, DELUGE_BROADCAST_CHANNEL, &broadcast_call);
unicast_open(&deluge_uc, DELUGE_UNICAST_CHANNEL, &unicast_call);
r_interval = T_LOW;
PRINTF("Maintaining state for object %s of %d pages\n",
current_object.filename, OBJECT_PAGE_COUNT(current_object));
deluge_state = DELUGE_STATE_MAINTAIN;
for(r_interval = T_LOW;;) {
if(neighbor_inconsistency) {
/* Deluge M.2 */
r_interval = T_LOW;
neighbor_inconsistency = 0;
} else {
/* Deluge M.3 */
r_interval = (2 * r_interval >= T_HIGH) ? T_HIGH : 2 * r_interval;
}
r_rand = r_interval / 2 + ((unsigned)random_rand() % (r_interval / 2));
recv_adv = 0;
old_summary = 0;
/* Deluge M.1 */
ctimer_set(&summary_timer, r_rand * CLOCK_SECOND,
(void *)(void *)advertise_summary, &current_object);
/* Deluge M.4 */
ctimer_set(&profile_timer, r_rand * CLOCK_SECOND,
(void *)(void *)send_profile, &current_object);
LONG_TIMER(et, time_counter, r_interval);
}
exit:
unicast_close(&deluge_uc);
broadcast_close(&deluge_broadcast);
if(current_object.cfs_fd >= 0) {
cfs_close(current_object.cfs_fd);
}
if(current_object.pages != NULL) {
free(current_object.pages);
}
PROCESS_END();
}

View File

@ -1,159 +0,0 @@
/*
* Copyright (c) 2007, 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.
*
*/
/**
* \file
* Header for Deluge.
* \author
* Nicolas Tsiftes <nvt@sics.se>
*/
#ifndef DELUGE_H
#define DELUGE_H
#include "net/rime/rime.h"
PROCESS_NAME(deluge_process);
#define LONG_TIMER(et, counter, time) \
do { \
for (counter = 0; counter < time; counter++) { \
etimer_set(&et, CLOCK_SECOND); \
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); \
} \
} while (0)
#define DELUGE_UNICAST_CHANNEL 55
#define DELUGE_BROADCAST_CHANNEL 56
/* All the packets in a page have been received. */
#define PAGE_COMPLETE 1
/* All pages up to, and including, this page are complete. */
#define PAGE_AVAILABLE 1
#define S_PKT 64 /* Deluge packet size. */
#define N_PKT 4 /* Packets per page. */
#define S_PAGE (S_PKT * N_PKT) /* Fixed page size. */
/* Bounds for the round time in seconds. */
#define T_LOW 2
#define T_HIGH 64
/* Random interval for request transmissions in jiffies. */
#define T_R (CLOCK_SECOND * 2)
/* Bound for the number of advertisements. */
#define CONST_K 1
/* The number of pages in this object. */
#define OBJECT_PAGE_COUNT(obj) (((obj).size + (S_PAGE - 1)) / S_PAGE)
#define ALL_PACKETS ((1 << N_PKT) - 1)
#define DELUGE_CMD_SUMMARY 1
#define DELUGE_CMD_REQUEST 2
#define DELUGE_CMD_PACKET 3
#define DELUGE_CMD_PROFILE 4
#define DELUGE_STATE_MAINTAIN 1
#define DELUGE_STATE_RX 2
#define DELUGE_STATE_TX 3
#define CONST_LAMBDA 2
#define CONST_ALPHA 0.5
#define CONST_OMEGA 8
#define ESTIMATED_TX_TIME (CLOCK_SECOND)
typedef uint8_t deluge_object_id_t;
struct deluge_msg_summary {
uint8_t cmd;
uint8_t version;
uint8_t highest_available;
deluge_object_id_t object_id;
};
struct deluge_msg_request {
uint8_t cmd;
uint8_t version;
uint8_t pagenum;
uint8_t request_set;
deluge_object_id_t object_id;
};
struct deluge_msg_packet {
uint8_t cmd;
uint8_t version;
uint8_t pagenum;
uint8_t packetnum;
uint16_t crc;
deluge_object_id_t object_id;
unsigned char payload[S_PKT];
};
struct deluge_msg_profile {
uint8_t cmd;
uint8_t version;
uint8_t npages;
deluge_object_id_t object_id;
uint8_t version_vector[];
};
struct deluge_object {
char *filename;
uint16_t object_id;
uint16_t size;
uint8_t version;
uint8_t update_version;
struct deluge_page *pages;
uint8_t current_rx_page;
int8_t current_tx_page;
uint8_t nrequests;
uint8_t current_page[S_PAGE];
uint8_t tx_set;
int cfs_fd;
linkaddr_t summary_from;
};
struct deluge_page {
uint32_t packet_set;
uint16_t crc;
clock_time_t last_request;
clock_time_t last_data;
uint8_t flags;
uint8_t version;
};
int deluge_disseminate(char *file, unsigned version);
#endif

View File

@ -61,7 +61,7 @@ typedef struct coap_separate {
int coap_separate_handler(resource_t *resource, void *request,
void *response);
void coap_separate_reject();
void coap_separate_reject(void);
void coap_separate_accept(void *request, coap_separate_t *separate_store);
void coap_separate_resume(void *response, coap_separate_t *separate_store,
uint8_t code);

View File

@ -67,7 +67,7 @@ typedef struct coap_transaction {
* Use snprintf(buf, len+1, "", ...) to completely fill payload */
} coap_transaction_t;
void coap_register_as_transaction_handler();
void coap_register_as_transaction_handler(void);
coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr,
uint16_t port);
@ -75,6 +75,6 @@ void coap_send_transaction(coap_transaction_t *t);
void coap_clear_transaction(coap_transaction_t *t);
coap_transaction_t *coap_get_transaction_by_mid(uint16_t mid);
void coap_check_transactions();
void coap_check_transactions(void);
#endif /* COAP_TRANSACTIONS_H_ */

View File

@ -79,7 +79,7 @@ LWM2M_RESOURCES(temperature_resources,
/* Temperature (Current) */
LWM2M_RESOURCE_CALLBACK(5700, { temp, NULL, NULL }),
/* Units */
LWM2M_RESOURCE_STRING(5701, "Celcius"),
LWM2M_RESOURCE_STRING(5701, "Cel"),
/* Min Range Value */
LWM2M_RESOURCE_FLOATFIX(5603, IPSO_TEMPERATURE_MIN),
/* Max Range Value */

View File

@ -71,6 +71,7 @@ enum {
JSON_ERROR_UNEXPECTED_ARRAY,
JSON_ERROR_UNEXPECTED_END_OF_ARRAY,
JSON_ERROR_UNEXPECTED_OBJECT,
JSON_ERROR_UNEXPECTED_END_OF_OBJECT,
JSON_ERROR_UNEXPECTED_STRING
};

View File

@ -43,6 +43,14 @@ push(struct jsonparse_state *state, char c)
return state->depth < JSONPARSE_MAX_DEPTH;
}
/*--------------------------------------------------------------------*/
static void
modify(struct jsonparse_state *state, char c)
{
if(state->depth > 0) {
state->stack[state->depth - 1] = c;
}
}
/*--------------------------------------------------------------------*/
static char
pop(struct jsonparse_state *state)
{
@ -50,25 +58,31 @@ pop(struct jsonparse_state *state)
return JSON_TYPE_ERROR;
}
state->depth--;
state->vtype = state->stack[state->depth];
return state->stack[state->depth];
}
/*--------------------------------------------------------------------*/
/* will pass by the value and store the start and length of the value for
atomic types */
/*--------------------------------------------------------------------*/
static void
static char
atomic(struct jsonparse_state *state, char type)
{
char c;
const char *str;
int len;
state->vstart = state->pos;
state->vtype = type;
if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) {
while((c = state->json[state->pos++]) && c != '"') {
if(c == '\\') {
state->pos++; /* skip current char */
}
}
if (c != '"') {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
state->vlen = state->pos - state->vstart - 1;
} else if(type == JSON_TYPE_NUMBER) {
do {
@ -82,8 +96,31 @@ atomic(struct jsonparse_state *state, char type)
/* need to back one step since first char is already gone */
state->vstart--;
state->vlen = state->pos - state->vstart;
} else if(type == JSON_TYPE_NULL || type == JSON_TYPE_TRUE || type == JSON_TYPE_FALSE) {
state->vstart--;
switch (type) {
case JSON_TYPE_NULL: str = "null"; break;
case JSON_TYPE_TRUE: str = "true"; break;
case JSON_TYPE_FALSE: str = "false"; break;
default: str = ""; break;
}
while ((c = state->json[state->pos]) && c != ' ' && c != ',' && c != ']' && c != '}') {
state->pos++;
}
state->vlen = state->pos - state->vstart;
len = strlen(str);
len = state->vlen > len ? state->vlen : len;
if (strncmp(str, &state->json[state->vstart], len) != 0) {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
}
/* no other types for now... */
state->vtype = type;
return state->vtype;
}
/*--------------------------------------------------------------------*/
static void
@ -97,6 +134,17 @@ skip_ws(struct jsonparse_state *state)
}
}
/*--------------------------------------------------------------------*/
static int
is_atomic(struct jsonparse_state *state)
{
char v = state->vtype;
if(v == 'N' || v == '"' || v == '0' || v == 'n' || v == 't' || v == 'f') {
return 1;
} else {
return 0;
}
}
/*--------------------------------------------------------------------*/
void
jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
{
@ -105,6 +153,7 @@ jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
state->pos = 0;
state->depth = 0;
state->error = 0;
state->vtype = 0;
state->stack[0] = 0;
}
/*--------------------------------------------------------------------*/
@ -113,31 +162,33 @@ jsonparse_next(struct jsonparse_state *state)
{
char c;
char s;
char v;
skip_ws(state);
c = state->json[state->pos];
s = jsonparse_get_type(state);
v = state->vtype;
state->pos++;
switch(c) {
case '{':
push(state, c);
if((s == 0 && v == 0) || s == '[' || s == ':') {
push(state, c);
} else {
state->error = JSON_ERROR_UNEXPECTED_OBJECT;
return JSON_TYPE_ERROR;
}
return c;
case '}':
if(s == ':' && state->vtype != 0) {
/* printf("Popping vtype: '%c'\n", state->vtype); */
pop(state);
s = jsonparse_get_type(state);
}
if(s == '{') {
if((s == ':' && v != ',' && v != 0 ) || (s == '{' && v == 0)) {
pop(state);
} else {
state->error = JSON_ERROR_SYNTAX;
state->error = JSON_ERROR_UNEXPECTED_END_OF_OBJECT;
return JSON_TYPE_ERROR;
}
return c;
case ']':
if(s == '[') {
if(s == '[' && v != ',') {
pop(state);
} else {
state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY;
@ -145,41 +196,67 @@ jsonparse_next(struct jsonparse_state *state)
}
return c;
case ':':
push(state, c);
return c;
if(s == '{' && v == 'N') {
modify(state, ':');
state->vtype = 0;
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
return jsonparse_next(state);
case ',':
/* if x:y ... , */
if(s == ':' && state->vtype != 0) {
pop(state);
if(s == ':' && v != 0) {
modify(state, '{');
state->vtype = c;
} else if(s == '[') {
/* ok! */
state->vtype = c;
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
return c;
case '"':
if(s == '{' || s == '[' || s == ':') {
atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
if((s == 0 && v == 0) || s == '{' || s == '[' || s == ':') {
return atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
} else {
state->error = JSON_ERROR_UNEXPECTED_STRING;
return JSON_TYPE_ERROR;
}
return c;
case '[':
if(s == '{' || s == '[' || s == ':') {
if((s == 0 && v == 0) || s == '[' || s == ':') {
push(state, c);
} else {
state->error = JSON_ERROR_UNEXPECTED_ARRAY;
return JSON_TYPE_ERROR;
}
return c;
case 0:
if(v == 0 || state->depth > 0) {
state->error = JSON_ERROR_SYNTAX;
}
return JSON_TYPE_ERROR;
default:
if(s == ':' || s == '[') {
if(c <= '9' && c >= '0') {
atomic(state, JSON_TYPE_NUMBER);
return JSON_TYPE_NUMBER;
if(s == 0 || s == ':' || s == '[') {
if (v != 0 && v != ',') {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
if(c == '-' || (c <= '9' && c >= '0')) {
return atomic(state, JSON_TYPE_NUMBER);
} else if(c == 'n') {
return atomic(state, JSON_TYPE_NULL);
} else if(c == 't') {
return atomic(state, JSON_TYPE_TRUE);
} else if(c == 'f') {
return atomic(state, JSON_TYPE_FALSE);
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
} else if(s == '{') {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
}
return 0;
@ -192,16 +269,31 @@ jsonparse_next(struct jsonparse_state *state)
int
jsonparse_copy_value(struct jsonparse_state *state, char *str, int size)
{
int i;
int i, o;
char c;
if(state->vtype == 0) {
if(!is_atomic(state)) {
return 0;
}
size = size <= state->vlen ? (size - 1) : state->vlen;
for(i = 0; i < size; i++) {
str[i] = state->json[state->vstart + i];
for(i = 0, o = 0; i < state->vlen && o < size - 1; i++) {
c = state->json[state->vstart + i];
if(c == '\\') {
i++;
switch(state->json[state->vstart + i]) {
case '"': str[o++] = '"'; break;
case '\\': str[o++] = '\\'; break;
case '/': str[o++] = '/'; break;
case 'b': str[o++] = '\b'; break;
case 'f': str[o++] = '\f'; break;
case 'n': str[o++] = '\n'; break;
case 'r': str[o++] = '\r'; break;
case 't': str[o++] = '\t'; break;
}
continue;
}
str[o++] = c;
}
str[i] = 0;
str[o] = 0;
return state->vtype;
}
/*--------------------------------------------------------------------*/
@ -228,7 +320,7 @@ jsonparse_get_value_as_long(struct jsonparse_state *state)
int
jsonparse_strcmp_value(struct jsonparse_state *state, const char *str)
{
if(state->vtype == 0) {
if(!is_atomic(state)) {
return -1;
}
return strncmp(str, &state->json[state->vstart], state->vlen);

View File

@ -1,5 +1,13 @@
oma-lwm2m_src = lwm2m-object.c lwm2m-engine.c \
lwm2m-device.c lwm2m-server.c lwm2m-security.c \
oma-tlv.c oma-tlv-reader.c oma-tlv-writer.c \
lwm2m-plain-text.c
oma-lwm2m_src = \
lwm2m-object.c \
lwm2m-engine.c \
lwm2m-device.c \
lwm2m-server.c \
lwm2m-security.c \
oma-tlv.c \
oma-tlv-reader.c \
oma-tlv-writer.c \
lwm2m-plain-text.c \
lwm2m-json.c \
#
CFLAGS += -DHAVE_OMA_LWM2M=1

View File

@ -46,10 +46,12 @@
#include "lwm2m-object.h"
#include "lwm2m-device.h"
#include "lwm2m-plain-text.h"
#include "lwm2m-json.h"
#include "rest-engine.h"
#include "er-coap-constants.h"
#include "er-coap-engine.h"
#include "oma-tlv.h"
#include "oma-tlv-reader.h"
#include "oma-tlv-writer.h"
#include "net/ipv6/uip-ds6.h"
#include <stdio.h>
@ -639,10 +641,10 @@ write_rd_json_data(const lwm2m_context_t *context,
value = lwm2m_object_get_resource_string(resource, context);
slen = lwm2m_object_get_resource_strlen(resource, context);
if(value != NULL) {
PRINTF("%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s,
PRINTF("%s{\"n\":\"%u\",\"sv\":\"%.*s\"}", s,
resource->id, slen, value);
len = snprintf(&buffer[rdlen], size - rdlen,
"%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s,
"%s{\"n\":\"%u\",\"sv\":\"%.*s\"}", s,
resource->id, slen, value);
}
} else if(lwm2m_object_is_resource_int(resource)) {
@ -682,10 +684,10 @@ write_rd_json_data(const lwm2m_context_t *context,
} else if(lwm2m_object_is_resource_boolean(resource)) {
int value;
if(lwm2m_object_get_resource_boolean(resource, context, &value)) {
PRINTF("%s{\"n\":\"%u\",\"v\":%s}", s, resource->id,
PRINTF("%s{\"n\":\"%u\",\"bv\":%s}", s, resource->id,
value ? "true" : "false");
len = snprintf(&buffer[rdlen], size - rdlen,
"%s{\"n\":\"%u\",\"v\":%s}", s, resource->id,
"%s{\"n\":\"%u\",\"bv\":%s}", s, resource->id,
value ? "true" : "false");
}
}
@ -707,6 +709,63 @@ write_rd_json_data(const lwm2m_context_t *context,
return rdlen;
}
/*---------------------------------------------------------------------------*/
/**
* @brief Set the writer pointer to the proper writer based on the Accept: header
*
* @param[in] context LWM2M context to operate on
* @param[in] accept Accept type number from CoAP headers
*
* @return The content type of the response if the selected writer is used
*/
static unsigned int
lwm2m_engine_select_writer(lwm2m_context_t *context, unsigned int accept)
{
switch(accept) {
case LWM2M_TLV:
context->writer = &oma_tlv_writer;
break;
case LWM2M_TEXT_PLAIN:
case TEXT_PLAIN:
context->writer = &lwm2m_plain_text_writer;
break;
case LWM2M_JSON:
case APPLICATION_JSON:
context->writer = &lwm2m_json_writer;
break;
default:
PRINTF("Unknown Accept type %u, using LWM2M plain text\n", accept);
context->writer = &lwm2m_plain_text_writer;
/* Set the response type to plain text */
accept = LWM2M_TEXT_PLAIN;
break;
}
return accept;
}
/*---------------------------------------------------------------------------*/
/**
* @brief Set the reader pointer to the proper reader based on the Content-format: header
*
* @param[in] context LWM2M context to operate on
* @param[in] content_format Content-type type number from CoAP headers
*/
static void
lwm2m_engine_select_reader(lwm2m_context_t *context, unsigned int content_format)
{
switch(content_format) {
case LWM2M_TLV:
context->reader = &oma_tlv_reader;
break;
case LWM2M_TEXT_PLAIN:
case TEXT_PLAIN:
context->reader = &lwm2m_plain_text_reader;
break;
default:
PRINTF("Unknown content type %u, using LWM2M plain text\n", accept);
context->reader = &lwm2m_plain_text_reader;
break;
}
}
/*---------------------------------------------------------------------------*/
void
lwm2m_engine_handler(const lwm2m_object_t *object,
void *request, void *response,
@ -716,6 +775,8 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
int len;
const char *url;
unsigned int format;
unsigned int accept;
unsigned int content_type;
int depth;
lwm2m_context_t context;
rest_resource_flags_t method;
@ -734,11 +795,19 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
/* CoAP content format text plain - assume LWM2M text plain */
format = LWM2M_TEXT_PLAIN;
}
if(!REST.get_header_accept(request, &accept)) {
PRINTF("No Accept header, using same as Content-format...\n");
accept = format;
}
depth = lwm2m_engine_parse_context(object, url, len, &context);
PRINTF("Context: %u/%u/%u found: %d\n", context.object_id,
context.object_instance_id, context.resource_id, depth);
/* Select reader and writer based on provided Content type and Accept headers */
lwm2m_engine_select_reader(&context, format);
content_type = lwm2m_engine_select_writer(&context, accept);
#if (DEBUG) & DEBUG_PRINT
/* for debugging */
if(method == METHOD_GET) {
@ -861,7 +930,7 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
if(depth == 3) {
const lwm2m_resource_t *resource = get_resource(instance, &context);
size_t tlvlen = 0;
size_t content_len = 0;
if(resource == NULL) {
PRINTF("Error - do not have resource %d\n", context.resource_id);
REST.set_response_status(response, NOT_FOUND_4_04);
@ -879,9 +948,9 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
context.reader = &lwm2m_plain_text_reader;
PRINTF("PUT Callback with data: '%.*s'\n", plen, data);
/* no specific reader for plain text */
tlvlen = resource->value.callback.write(&context, data, plen,
content_len = resource->value.callback.write(&context, data, plen,
buffer, preferred_size);
PRINTF("tlvlen:%u\n", (unsigned int)tlvlen);
PRINTF("content_len:%u\n", (unsigned int)content_len);
REST.set_response_status(response, CHANGED_2_04);
} else {
PRINTF("PUT callback with format %d\n", format);
@ -899,48 +968,39 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
} else if(method == METHOD_GET) {
if(lwm2m_object_is_resource_string(resource)) {
const uint8_t *value;
uint16_t len;
value = lwm2m_object_get_resource_string(resource, &context);
len = lwm2m_object_get_resource_strlen(resource, &context);
if(value != NULL) {
uint16_t len = lwm2m_object_get_resource_strlen(resource, &context);
PRINTF("Get string value: %.*s\n", (int)len, (char *)value);
/* TODO check format */
REST.set_response_payload(response, value, len);
REST.set_header_content_type(response, LWM2M_TEXT_PLAIN);
/* Done */
return;
content_len = context.writer->write_string(&context, buffer,
preferred_size, (const char *)value, len);
}
} else if(lwm2m_object_is_resource_int(resource)) {
int32_t value;
if(lwm2m_object_get_resource_int(resource, &context, &value)) {
/* export INT as TLV */
tlvlen = oma_tlv_write_int32(resource->id, value, buffer, preferred_size);
PRINTF("Exporting int as TLV: %" PRId32 ", len: %u\n",
value, (unsigned int)tlvlen);
content_len = context.writer->write_int(&context, buffer, preferred_size, value);
}
} else if(lwm2m_object_is_resource_floatfix(resource)) {
int32_t value;
if(lwm2m_object_get_resource_floatfix(resource, &context, &value)) {
/* export FLOATFIX as TLV */
/* export FLOATFIX */
PRINTF("Exporting %d-bit fix as float: %" PRId32 "\n",
LWM2M_FLOAT32_BITS, value);
tlvlen = oma_tlv_write_float32(resource->id,
value, LWM2M_FLOAT32_BITS,
buffer, preferred_size);
PRINTF("Exporting as TLV: len:%u\n", (unsigned int)tlvlen);
content_len = context.writer->write_float32fix(&context, buffer,
preferred_size, value, LWM2M_FLOAT32_BITS);
}
} else if(lwm2m_object_is_resource_callback(resource)) {
if(resource->value.callback.read != NULL) {
tlvlen = resource->value.callback.read(&context,
content_len = resource->value.callback.read(&context,
buffer, preferred_size);
} else {
REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05);
return;
}
}
if(tlvlen > 0) {
REST.set_response_payload(response, buffer, tlvlen);
REST.set_header_content_type(response, LWM2M_TLV);
if(content_len > 0) {
REST.set_response_payload(response, buffer, content_len);
REST.set_header_content_type(response, content_type);
} else {
/* failed to produce output - it is an internal error */
REST.set_response_status(response, INTERNAL_SERVER_ERROR_5_00);
@ -952,7 +1012,7 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
const uint8_t *data;
int plen = REST.get_request_payload(request, &data);
PRINTF("Execute Callback with data: '%.*s'\n", plen, data);
tlvlen = resource->value.callback.exec(&context,
content_len = resource->value.callback.exec(&context,
data, plen,
buffer, preferred_size);
REST.set_response_status(response, CHANGED_2_04);
@ -973,7 +1033,7 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
REST.set_response_status(response, NOT_FOUND_4_04);
} else {
int rdlen;
if(format == APPLICATION_LINK_FORMAT) {
if(accept == APPLICATION_LINK_FORMAT) {
rdlen = write_rd_link_data(object, instance,
(char *)buffer, preferred_size);
} else {
@ -986,10 +1046,10 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
return;
}
REST.set_response_payload(response, buffer, rdlen);
if(format == APPLICATION_LINK_FORMAT) {
if(accept == APPLICATION_LINK_FORMAT) {
REST.set_header_content_type(response, REST.type.APPLICATION_LINK_FORMAT);
} else {
REST.set_header_content_type(response, REST.type.APPLICATION_JSON);
REST.set_header_content_type(response, LWM2M_JSON);
}
}
}

162
apps/oma-lwm2m/lwm2m-json.c Normal file
View File

@ -0,0 +1,162 @@
/*
* Copyright (c) 2016, Eistec AB.
* 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Implementation of the Contiki OMA LWM2M JSON writer
* \author
* Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#include "lwm2m-object.h"
#include "lwm2m-json.h"
#include "lwm2m-plain-text.h"
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
static size_t
write_boolean(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
int value)
{
int len = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"bv\":%s}]}\n", ctx->resource_id, value ? "true" : "false");
if((len < 0) || (len >= outlen)) {
return 0;
}
return len;
}
/*---------------------------------------------------------------------------*/
static size_t
write_int(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
int32_t value)
{
int len = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"v\":%" PRId32 "}]}\n", ctx->resource_id, value);
if((len < 0) || (len >= outlen)) {
return 0;
}
return len;
}
/*---------------------------------------------------------------------------*/
static size_t
write_float32fix(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
int32_t value, int bits)
{
size_t len = 0;
int res;
res = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"v\":", ctx->resource_id);
if(res <= 0 || res >= outlen) {
return 0;
}
len += res;
outlen -= res;
res = lwm2m_plain_text_write_float32fix(&outbuf[len], outlen, value, bits);
if((res <= 0) || (res >= outlen)) {
return 0;
}
len += res;
outlen -= res;
res = snprintf((char *)&outbuf[len], outlen, "}]}\n");
if((res <= 0) || (res >= outlen)) {
return 0;
}
len += res;
return len;
}
/*---------------------------------------------------------------------------*/
static size_t
write_string(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen,
const char *value, size_t stringlen)
{
size_t i;
size_t len = 0;
int res;
PRINTF("{\"e\":[{\"n\":\"%u\",\"sv\":\"", ctx->resource_id);
res = snprintf((char *)outbuf, outlen, "{\"e\":[{\"n\":\"%u\",\"sv\":\"", ctx->resource_id);
if(res < 0 || res >= outlen) {
return 0;
}
len += res;
for (i = 0; i < stringlen && len < outlen; ++i) {
/* Escape special characters */
/* TODO: Handle UTF-8 strings */
if(value[i] < '\x20') {
PRINTF("\\x%x", value[i]);
res = snprintf((char *)&outbuf[len], outlen - len, "\\x%x", value[i]);
if((res < 0) || (res >= (outlen - len))) {
return 0;
}
len += res;
continue;
} else if(value[i] == '"' || value[i] == '\\') {
PRINTF("\\");
outbuf[len] = '\\';
++len;
if(len >= outlen) {
return 0;
}
}
PRINTF("%c", value[i]);
outbuf[len] = value[i];
++len;
if(len >= outlen) {
return 0;
}
}
PRINTF("\"}]}\n");
res = snprintf((char *)&outbuf[len], outlen - len, "\"}]}\n");
if((res < 0) || (res >= (outlen - len))) {
return 0;
}
len += res;
return len;
}
/*---------------------------------------------------------------------------*/
const lwm2m_writer_t lwm2m_json_writer = {
write_int,
write_string,
write_float32fix,
write_boolean
};
/*---------------------------------------------------------------------------*/
/** @} */

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2016, Eistec AB.
* 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 HOLDER 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.
*/
/**
* \addtogroup oma-lwm2m
* @{
*/
/**
* \file
* Header file for the Contiki OMA LWM2M JSON writer
* \author
* Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef LWM2M_JSON_H_
#define LWM2M_JSON_H_
#include "lwm2m-object.h"
extern const lwm2m_writer_t lwm2m_json_writer;
#endif /* LWM2M_JSON_H_ */
/** @} */

View File

@ -100,6 +100,11 @@ lwm2m_plain_text_read_float32fix(const uint8_t *inbuf, size_t len,
break;
}
}
if(dot == 0) {
integerpart = counter;
counter = 0;
frac = 1;
}
*value = integerpart << bits;
if(frac > 1) {
*value += ((counter << bits) / frac);

View File

@ -1 +1 @@
orchestra_src = orchestra.c orchestra-rule-default-common.c orchestra-rule-eb-per-time-source.c orchestra-rule-unicast-per-neighbor.c
orchestra_src = orchestra.c orchestra-rule-default-common.c orchestra-rule-eb-per-time-source.c orchestra-rule-unicast-per-neighbor-rpl-storing.c orchestra-rule-unicast-per-neighbor-rpl-ns.c

View File

@ -46,10 +46,10 @@
* - a sender-based or receiver-based slotframe for unicast to RPL parents and children
* - a common shared slotframe for any other traffic (mostly broadcast)
* */
#define ORCHESTRA_RULES { &eb_per_time_source, \
&unicast_per_neighbor, \
&default_common, \
}
#define ORCHESTRA_RULES { &eb_per_time_source, &unicast_per_neighbor_rpl_storing, &default_common }
/* Example configuration for RPL non-storing mode: */
/* #define ORCHESTRA_RULES { &eb_per_time_source, &unicast_per_neighbor_rpl_ns, &default_common } */
#endif /* ORCHESTRA_CONF_RULES */
/* Length of the various slotframes. Tune to balance network capacity,

View File

@ -74,8 +74,8 @@ select_packet(uint16_t *slotframe, uint16_t *timeslot)
static void
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{
uint16_t old_ts = get_node_timeslot(&old->addr);
uint16_t new_ts = get_node_timeslot(&new->addr);
uint16_t old_ts = old != NULL ? get_node_timeslot(&old->addr) : 0xffff;
uint16_t new_ts = new != NULL ? get_node_timeslot(&new->addr) : 0xffff;
if(new_ts == old_ts) {
return;
@ -83,14 +83,24 @@ new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new
if(old_ts != 0xffff) {
/* Stop listening to the old time source's EBs */
tsch_schedule_remove_link_by_timeslot(sf_eb, old_ts);
if(old_ts == get_node_timeslot(&linkaddr_node_addr)) {
/* This was the same timeslot as slot. Reset original link options */
tsch_schedule_add_link(sf_eb, LINK_OPTION_TX, LINK_TYPE_ADVERTISING_ONLY,
&tsch_broadcast_address, old_ts, 0);
} else {
/* Remove slot */
tsch_schedule_remove_link_by_timeslot(sf_eb, old_ts);
}
}
if(new_ts != 0xffff) {
uint8_t link_options = LINK_OPTION_RX;
if(new_ts == get_node_timeslot(&linkaddr_node_addr)) {
/* This is also our timeslot, add necessary flags */
link_options |= LINK_OPTION_TX;
}
/* Listen to the time source's EBs */
tsch_schedule_add_link(sf_eb,
LINK_OPTION_RX,
LINK_TYPE_ADVERTISING_ONLY, NULL,
new_ts, 0);
tsch_schedule_add_link(sf_eb, link_options, LINK_TYPE_ADVERTISING_ONLY,
&tsch_broadcast_address, new_ts, 0);
}
}
/*---------------------------------------------------------------------------*/

View File

@ -0,0 +1,119 @@
/*
* 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.
*
*/
/**
* \file
* Orchestra: a slotframe dedicated to unicast data transmission. Designed primarily
* for RPL non-storing mode but would work with any mode-of-operation. Does not require
* any knowledge of the children. Works only as received-base, and as follows:
* Nodes listen at a timeslot defined as hash(MAC) % ORCHESTRA_SB_UNICAST_PERIOD
* Nodes transmit at: for any neighbor, hash(nbr.MAC) % ORCHESTRA_SB_UNICAST_PERIOD
*
* \author Simon Duquennoy <simon.duquennoy@inria.fr>
*/
#include "contiki.h"
#include "orchestra.h"
#include "net/ipv6/uip-ds6-route.h"
#include "net/packetbuf.h"
static uint16_t slotframe_handle = 0;
static uint16_t channel_offset = 0;
static struct tsch_slotframe *sf_unicast;
/*---------------------------------------------------------------------------*/
static uint16_t
get_node_timeslot(const linkaddr_t *addr)
{
if(addr != NULL && ORCHESTRA_UNICAST_PERIOD > 0) {
return ORCHESTRA_LINKADDR_HASH(addr) % ORCHESTRA_UNICAST_PERIOD;
} else {
return 0xffff;
}
}
/*---------------------------------------------------------------------------*/
static void
child_added(const linkaddr_t *linkaddr)
{
}
/*---------------------------------------------------------------------------*/
static void
child_removed(const linkaddr_t *linkaddr)
{
}
/*---------------------------------------------------------------------------*/
static int
select_packet(uint16_t *slotframe, uint16_t *timeslot)
{
/* Select data packets we have a unicast link to */
const linkaddr_t *dest = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
if(packetbuf_attr(PACKETBUF_ATTR_FRAME_TYPE) == FRAME802154_DATAFRAME
&& !linkaddr_cmp(dest, &linkaddr_null)) {
if(slotframe != NULL) {
*slotframe = slotframe_handle;
}
if(timeslot != NULL) {
*timeslot = get_node_timeslot(dest);
}
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
static void
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{
}
/*---------------------------------------------------------------------------*/
static void
init(uint16_t sf_handle)
{
int i;
uint16_t rx_timeslot;
slotframe_handle = sf_handle;
channel_offset = sf_handle;
/* Slotframe for unicast transmissions */
sf_unicast = tsch_schedule_add_slotframe(slotframe_handle, ORCHESTRA_UNICAST_PERIOD);
rx_timeslot = get_node_timeslot(&linkaddr_node_addr);
/* Add a Tx link at each available timeslot. Make the link Rx at our own timeslot. */
for(i = 0; i < ORCHESTRA_UNICAST_PERIOD; i++) {
tsch_schedule_add_link(sf_unicast,
LINK_OPTION_SHARED | LINK_OPTION_TX | ( i == rx_timeslot ? LINK_OPTION_RX : 0 ),
LINK_TYPE_NORMAL, &tsch_broadcast_address,
i, channel_offset);
}
}
/*---------------------------------------------------------------------------*/
struct orchestra_rule unicast_per_neighbor_rpl_ns = {
init,
new_time_source,
select_packet,
child_added,
child_removed,
};

View File

@ -29,12 +29,13 @@
*/
/**
* \file
* Orchestra: a slotframe dedicated to unicast data transmission.
* If sender-based:
* Orchestra: a slotframe dedicated to unicast data transmission. Designed for
* RPL storing mode only, as this is based on the knowledge of the children (and parent).
* If receiver-based:
* Nodes listen at a timeslot defined as hash(MAC) % ORCHESTRA_SB_UNICAST_PERIOD
* Nodes transmit at: for each nbr in RPL children and RPL preferred parent,
* hash(nbr.MAC) % ORCHESTRA_SB_UNICAST_PERIOD
* If receiver-based: the opposite
* If sender-based: the opposite
*
* \author Simon Duquennoy <simonduq@sics.se>
*/
@ -43,6 +44,7 @@
#include "orchestra.h"
#include "net/ipv6/uip-ds6-route.h"
#include "net/packetbuf.h"
#include "net/rpl/rpl-conf.h"
#if ORCHESTRA_UNICAST_SENDER_BASED && ORCHESTRA_COLLISION_FREE_HASH
#define UNICAST_SLOT_SHARED_FLAG ((ORCHESTRA_UNICAST_PERIOD < (ORCHESTRA_MAX_HASH + 1)) ? LINK_OPTION_SHARED : 0)
@ -85,10 +87,16 @@ add_uc_link(const linkaddr_t *linkaddr)
{
if(linkaddr != NULL) {
uint16_t timeslot = get_node_timeslot(linkaddr);
tsch_schedule_add_link(sf_unicast,
ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG,
LINK_TYPE_NORMAL, &tsch_broadcast_address,
timeslot, channel_offset);
uint8_t link_options = ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG;
if(timeslot == get_node_timeslot(&linkaddr_node_addr)) {
/* This is also our timeslot, add necessary flags */
link_options |= ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG: LINK_OPTION_RX;
}
/* Add/update link */
tsch_schedule_add_link(sf_unicast, link_options, LINK_TYPE_NORMAL, &tsch_broadcast_address,
timeslot, channel_offset);
}
}
/*---------------------------------------------------------------------------*/
@ -123,7 +131,17 @@ remove_uc_link(const linkaddr_t *linkaddr)
}
item = nbr_table_next(nbr_routes, item);
}
tsch_schedule_remove_link(sf_unicast, l);
/* Do we need this timeslot? */
if(timeslot == get_node_timeslot(&linkaddr_node_addr)) {
/* This is our link, keep it but update the link options */
uint8_t link_options = ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG: LINK_OPTION_RX;
tsch_schedule_add_link(sf_unicast, link_options, LINK_TYPE_NORMAL, &tsch_broadcast_address,
timeslot, channel_offset);
} else {
/* Remove link */
tsch_schedule_remove_link(sf_unicast, l);
}
}
/*---------------------------------------------------------------------------*/
static void
@ -160,13 +178,14 @@ static void
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{
if(new != old) {
const linkaddr_t *old_addr = old != NULL ? &old->addr : NULL;
const linkaddr_t *new_addr = new != NULL ? &new->addr : NULL;
if(new_addr != NULL) {
linkaddr_copy(&orchestra_parent_linkaddr, new_addr);
} else {
linkaddr_copy(&orchestra_parent_linkaddr, &linkaddr_null);
}
remove_uc_link(new_addr);
remove_uc_link(old_addr);
add_uc_link(new_addr);
}
}
@ -185,7 +204,7 @@ init(uint16_t sf_handle)
timeslot, channel_offset);
}
/*---------------------------------------------------------------------------*/
struct orchestra_rule unicast_per_neighbor = {
struct orchestra_rule unicast_per_neighbor_rpl_storing = {
init,
new_time_source,
select_packet,

View File

@ -53,7 +53,8 @@ struct orchestra_rule {
};
struct orchestra_rule eb_per_time_source;
struct orchestra_rule unicast_per_neighbor;
struct orchestra_rule unicast_per_neighbor_rpl_storing;
struct orchestra_rule unicast_per_neighbor_rpl_ns;
struct orchestra_rule default_common;
extern linkaddr_t orchestra_parent_linkaddr;

View File

@ -116,11 +116,13 @@ PROCESS_THREAD(shell_exec_process, ev, data)
shell_output_str(&exec_command, print, symbol);
if(ret == ELFLOADER_OK) {
#if !PROCESS_CONF_NO_PROCESS_NAMES
int i;
for(i = 0; elfloader_autostart_processes[i] != NULL; ++i) {
shell_output_str(&exec_command, "exec: starting process ",
elfloader_autostart_processes[i]->name);
}
#endif
autostart_start(elfloader_autostart_processes);
}

View File

@ -75,12 +75,6 @@
#error "Cannot have COFFEE_APPEND_ONLY set when COFFEE_MICRO_LOGS is set."
#endif
/* I/O semantics can be set on file descriptors in order to optimize
file access on certain storage types. */
#ifndef COFFEE_IO_SEMANTICS
#define COFFEE_IO_SEMANTICS 0
#endif
/*
* Prevent sectors from being erased directly after file removal.
* This will level the wear across sectors better, but may lead
@ -94,61 +88,66 @@
#error COFFEE_START must point to the first byte in a sector.
#endif
/* File descriptor flags. */
#define COFFEE_FD_FREE 0x0
#define COFFEE_FD_READ 0x1
#define COFFEE_FD_WRITE 0x2
#define COFFEE_FD_APPEND 0x4
/* File object flags. */
#define COFFEE_FILE_MODIFIED 0x1
#define INVALID_PAGE ((coffee_page_t)-1)
/* Internal Coffee markers. */
#define INVALID_PAGE ((coffee_page_t)-1)
#define UNKNOWN_OFFSET ((cfs_offset_t)-1)
#define REMOVE_LOG 1
#define CLOSE_FDS 1
#define ALLOW_GC 1
/* File removal actions. They can have the same values because
they are passed as separate parameters. */
#define REMOVE_LOG 1
#define CLOSE_FDS 1
#define ALLOW_GC 1
/* "Greedy" garbage collection erases as many sectors as possible. */
#define GC_GREEDY 0
#define GC_GREEDY 0
/* "Reluctant" garbage collection stops after erasing one sector. */
#define GC_RELUCTANT 1
#define GC_RELUCTANT 1
/* File descriptor macros. */
#define FD_VALID(fd) \
((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
#define FD_VALID(fd) ((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
#define FD_READABLE(fd) (coffee_fd_set[(fd)].flags & CFS_READ)
#define FD_WRITABLE(fd) (coffee_fd_set[(fd)].flags & CFS_WRITE)
#define FD_APPENDABLE(fd) (coffee_fd_set[(fd)].flags & CFS_APPEND)
/* File object macros. */
#define FILE_MODIFIED(file) ((file)->flags & COFFEE_FILE_MODIFIED)
#define FILE_FREE(file) ((file)->max_pages == 0)
#define FILE_MODIFIED(file) ((file)->flags & COFFEE_FILE_MODIFIED)
#define FILE_FREE(file) ((file)->max_pages == 0)
#define FILE_UNREFERENCED(file) ((file)->references == 0)
/* File header flags. */
#define HDR_FLAG_VALID 0x1 /* Completely written header. */
#define HDR_FLAG_ALLOCATED 0x2 /* Allocated file. */
#define HDR_FLAG_OBSOLETE 0x4 /* File marked for GC. */
#define HDR_FLAG_MODIFIED 0x8 /* Modified file, log exists. */
#define HDR_FLAG_LOG 0x10 /* Log file. */
#define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */
#define HDR_FLAG_VALID 0x01 /* Completely written header. */
#define HDR_FLAG_ALLOCATED 0x02 /* Allocated file. */
#define HDR_FLAG_OBSOLETE 0x04 /* File marked for GC. */
#define HDR_FLAG_MODIFIED 0x08 /* Modified file, log exists. */
#define HDR_FLAG_LOG 0x10 /* Log file. */
#define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */
/* File header macros. */
#define CHECK_FLAG(hdr, flag) ((hdr).flags & (flag))
#define HDR_VALID(hdr) CHECK_FLAG(hdr, HDR_FLAG_VALID)
#define HDR_ALLOCATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED)
#define HDR_FREE(hdr) !HDR_ALLOCATED(hdr)
#define HDR_LOG(hdr) CHECK_FLAG(hdr, HDR_FLAG_LOG)
#define HDR_MODIFIED(hdr) CHECK_FLAG(hdr, HDR_FLAG_MODIFIED)
#define HDR_ISOLATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ISOLATED)
#define HDR_OBSOLETE(hdr) CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE)
#define HDR_ACTIVE(hdr) (HDR_ALLOCATED(hdr) && \
!HDR_OBSOLETE(hdr) && \
!HDR_ISOLATED(hdr))
#define HDR_VALID(hdr) CHECK_FLAG(hdr, HDR_FLAG_VALID)
#define HDR_ALLOCATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED)
#define HDR_FREE(hdr) !HDR_ALLOCATED(hdr)
#define HDR_LOG(hdr) CHECK_FLAG(hdr, HDR_FLAG_LOG)
#define HDR_MODIFIED(hdr) CHECK_FLAG(hdr, HDR_FLAG_MODIFIED)
#define HDR_ISOLATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ISOLATED)
#define HDR_OBSOLETE(hdr) CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE)
#define HDR_ACTIVE(hdr) (HDR_ALLOCATED(hdr) && \
!HDR_OBSOLETE(hdr) && \
!HDR_ISOLATED(hdr))
/* Shortcuts derived from the hardware-dependent configuration of Coffee. */
#define COFFEE_SECTOR_COUNT (unsigned)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
#define COFFEE_SECTOR_COUNT \
(coffee_page_t)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
#define COFFEE_PAGE_COUNT \
((coffee_page_t)(COFFEE_SIZE / COFFEE_PAGE_SIZE))
#define COFFEE_PAGES_PER_SECTOR \
@ -176,9 +175,7 @@ struct file_desc {
cfs_offset_t offset;
struct file *file;
uint8_t flags;
#if COFFEE_IO_SEMANTICS
uint8_t io_flags;
#endif
};
/* The file header structure mimics the representation of file headers
@ -196,26 +193,18 @@ struct file_header {
/* This is needed because of a buggy compiler. */
struct log_param {
cfs_offset_t offset;
const char *buf;
char *buf;
uint16_t size;
};
/*
* The protected memory consists of structures that should not be
* overwritten during system checkpointing because they may be used by
* the checkpointing implementation. These structures need not be
* protected if checkpointing is not used.
* Variables that keep track of opened files and internal
* optimization information for Coffee.
*/
static struct protected_mem_t {
struct file coffee_files[COFFEE_MAX_OPEN_FILES];
struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
coffee_page_t next_free;
char gc_wait;
} protected_mem;
static struct file *const coffee_files = protected_mem.coffee_files;
static struct file_desc *const coffee_fd_set = protected_mem.coffee_fd_set;
static coffee_page_t *const next_free = &protected_mem.next_free;
static char *const gc_wait = &protected_mem.gc_wait;
static struct file coffee_files[COFFEE_MAX_OPEN_FILES];
static struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
static coffee_page_t next_free;
static char gc_wait;
/*---------------------------------------------------------------------------*/
static void
@ -229,11 +218,9 @@ static void
read_header(struct file_header *hdr, coffee_page_t page)
{
COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
#if DEBUG
if(HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
PRINTF("Invalid header at page %u!\n", (unsigned)page);
if(DEBUG && HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
PRINTF("Coffee: Invalid header at page %u!\n", (unsigned)page);
}
#endif
}
/*---------------------------------------------------------------------------*/
static cfs_offset_t
@ -243,7 +230,7 @@ absolute_offset(coffee_page_t page, cfs_offset_t offset)
}
/*---------------------------------------------------------------------------*/
static coffee_page_t
get_sector_status(uint16_t sector, struct sector_status *stats)
get_sector_status(coffee_page_t sector, struct sector_status *stats)
{
static coffee_page_t skip_pages;
static char last_pages_are_active;
@ -313,7 +300,7 @@ get_sector_status(uint16_t sector, struct sector_status *stats)
/*
* Determine the amount of pages in the following sectors that
* should be remembered for the next iteration. This is necessary
* because no page except the first of a file contains information
* because no file page except the first contains information
* about what type of page it is. A side effect of remembering this
* amount is that there is no need to read in the headers of each
* of these pages from the storage.
@ -364,11 +351,11 @@ isolate_pages(coffee_page_t start, coffee_page_t skip_pages)
static void
collect_garbage(int mode)
{
uint16_t sector;
coffee_page_t sector;
struct sector_status stats;
coffee_page_t first_page, isolation_count;
PRINTF("Coffee: Running the file system garbage collector in %s mode\n",
PRINTF("Coffee: Running the garbage collector in %s mode\n",
mode == GC_RELUCTANT ? "reluctant" : "greedy");
/*
* The garbage collector erases as many sectors as possible. A sector is
@ -377,7 +364,7 @@ collect_garbage(int mode)
for(sector = 0; sector < COFFEE_SECTOR_COUNT; sector++) {
isolation_count = get_sector_status(sector, &stats);
PRINTF("Coffee: Sector %u has %u active, %u obsolete, and %u free pages.\n",
sector, (unsigned)stats.active,
(unsigned)sector, (unsigned)stats.active,
(unsigned)stats.obsolete, (unsigned)stats.free);
if(stats.active > 0) {
@ -387,8 +374,8 @@ collect_garbage(int mode)
if((mode == GC_RELUCTANT && stats.free == 0) ||
(mode == GC_GREEDY && stats.obsolete > 0)) {
first_page = sector * COFFEE_PAGES_PER_SECTOR;
if(first_page < *next_free) {
*next_free = first_page;
if(first_page < next_free) {
next_free = first_page;
}
if(isolation_count > 0) {
@ -410,7 +397,7 @@ next_file(coffee_page_t page, struct file_header *hdr)
{
/*
* The quick-skip algorithm for finding file extents is the most
* essential part of Coffee. The file allocation rules enables this
* essential part of Coffee. The file allocation rules enable this
* algorithm to quickly jump over free areas and allocated extents
* after reading single headers and determining their status.
*
@ -458,10 +445,7 @@ load_file(coffee_page_t start, struct file_header *hdr)
file->page = start;
file->end = UNKNOWN_OFFSET;
file->max_pages = hdr->max_pages;
file->flags = 0;
if(HDR_MODIFIED(*hdr)) {
file->flags |= COFFEE_FILE_MODIFIED;
}
file->flags = HDR_MODIFIED(*hdr) ? COFFEE_FILE_MODIFIED : 0;
/* We don't know the amount of records yet. */
file->record_count = -1;
@ -539,7 +523,7 @@ find_contiguous_pages(coffee_page_t amount)
struct file_header hdr;
start = INVALID_PAGE;
for(page = *next_free; page < COFFEE_PAGE_COUNT;) {
for(page = next_free; page < COFFEE_PAGE_COUNT;) {
read_header(&hdr, page);
if(HDR_FREE(hdr)) {
if(start == INVALID_PAGE) {
@ -555,8 +539,8 @@ find_contiguous_pages(coffee_page_t amount)
page = next_file(page, &hdr);
if(start + amount <= page) {
if(start == *next_free) {
*next_free = start + amount;
if(start == next_free) {
next_free = start + amount;
}
return start;
}
@ -569,8 +553,8 @@ find_contiguous_pages(coffee_page_t amount)
}
/*---------------------------------------------------------------------------*/
static int
remove_by_page(coffee_page_t page, int remove_log, int close_fds,
int gc_allowed)
remove_by_page(coffee_page_t page, int remove_log,
int close_fds, int gc_allowed)
{
struct file_header hdr;
int i;
@ -589,7 +573,7 @@ remove_by_page(coffee_page_t page, int remove_log, int close_fds,
hdr.flags |= HDR_FLAG_OBSOLETE;
write_header(&hdr, page);
*gc_wait = 0;
gc_wait = 0;
/* Close all file descriptors that reference the removed file. */
if(close_fds) {
@ -608,11 +592,9 @@ remove_by_page(coffee_page_t page, int remove_log, int close_fds,
}
}
#if !COFFEE_EXTENDED_WEAR_LEVELLING
if(gc_allowed) {
if(!COFFEE_EXTENDED_WEAR_LEVELLING && gc_allowed) {
collect_garbage(GC_RELUCTANT);
}
#endif
return 0;
}
@ -638,13 +620,13 @@ reserve(const char *name, coffee_page_t pages,
page = find_contiguous_pages(pages);
if(page == INVALID_PAGE) {
if(*gc_wait) {
if(gc_wait) {
return NULL;
}
collect_garbage(GC_GREEDY);
page = find_contiguous_pages(pages);
if(page == INVALID_PAGE) {
*gc_wait = 1;
gc_wait = 1;
return NULL;
}
}
@ -656,7 +638,7 @@ reserve(const char *name, coffee_page_t pages,
write_header(&hdr, page);
PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n",
pages, page, name);
(unsigned)pages, (unsigned)page, name);
file = load_file(page, &hdr);
if(file != NULL) {
@ -851,7 +833,7 @@ merge_log(coffee_page_t file_page, int extend)
return -1;
}
/* Copy the log configuration and the EOF hint. */
/* Copy the log configuration. */
read_header(&hdr2, new_file->page);
hdr2.log_record_size = hdr.log_record_size;
hdr2.log_records = hdr.log_records;
@ -1005,6 +987,7 @@ cfs_open(const char *name, int flags)
fdp = &coffee_fd_set[fd];
fdp->flags = 0;
fdp->io_flags = 0;
fdp->file = find_file(name);
if(fdp->file == NULL) {
@ -1063,7 +1046,12 @@ cfs_seek(int fd, cfs_offset_t offset, int whence)
}
if(fdp->file->end < new_offset) {
fdp->file->end = new_offset;
if(FD_WRITABLE(fd)) {
fdp->file->end = new_offset;
} else {
/* Disallow seeking past the end of the file for read only FDs */
return (cfs_offset_t)-1;
}
}
return fdp->offset = new_offset;
@ -1106,11 +1094,16 @@ cfs_read(int fd, void *buf, unsigned size)
fdp = &coffee_fd_set[fd];
file = fdp->file;
if(fdp->offset + size > file->end) {
if(fdp->io_flags & CFS_COFFEE_IO_ENSURE_READ_LENGTH) {
while(fdp->offset + size > file->end) {
((char *)buf)[--size] = '\0';
}
} else if(fdp->offset + size > file->end) {
size = file->end - fdp->offset;
}
/* If the file is allocated, read directly in the file. */
/* If the file is not modified, read directly from the file extent. */
if(!FILE_MODIFIED(file)) {
COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset));
fdp->offset += size;
@ -1121,8 +1114,9 @@ cfs_read(int fd, void *buf, unsigned size)
read_header(&hdr, file->page);
/*
* Fill the buffer by copying from the log in first hand, or the
* ordinary file if the page has no log record.
* Copy the contents of the most recent log record. If there is
* no log record for the file area to read from, we simply read
* from the original file extent.
*/
for(bytes_left = size; bytes_left > 0; bytes_left -= r) {
lp.offset = fdp->offset;
@ -1164,32 +1158,24 @@ cfs_write(int fd, const void *buf, unsigned size)
file = fdp->file;
/* Attempt to extend the file if we try to write past the end. */
#if COFFEE_IO_SEMANTICS
if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) {
#endif
while(size + fdp->offset + sizeof(struct file_header) >
(file->max_pages * COFFEE_PAGE_SIZE)) {
if(merge_log(file->page, 1) < 0) {
return -1;
while(size + fdp->offset + sizeof(struct file_header) >
(file->max_pages * COFFEE_PAGE_SIZE)) {
if(merge_log(file->page, 1) < 0) {
return -1;
}
file = fdp->file;
PRINTF("Extended the file at page %u\n", (unsigned)file->page);
}
file = fdp->file;
PRINTF("Extended the file at page %u\n", (unsigned)file->page);
}
#if COFFEE_IO_SEMANTICS
}
#endif
#if COFFEE_MICRO_LOGS
#if COFFEE_IO_SEMANTICS
if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) &&
(FILE_MODIFIED(file) || fdp->offset < file->end)) {
#else
if(FILE_MODIFIED(file) || fdp->offset < file->end) {
#endif
need_dummy_write = 0;
for(bytes_left = size; bytes_left > 0;) {
lp.offset = fdp->offset;
lp.buf = buf;
lp.buf = (void *)buf;
lp.size = bytes_left;
i = write_log_page(file, &lp);
if(i < 0) {
@ -1227,16 +1213,14 @@ cfs_write(int fd, const void *buf, unsigned size)
}
} else {
#endif /* COFFEE_MICRO_LOGS */
#if COFFEE_APPEND_ONLY
if(fdp->offset < file->end) {
return -1;
}
#endif /* COFFEE_APPEND_ONLY */
if(COFFEE_APPEND_ONLY && fdp->offset < file->end) {
return -1;
}
COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
fdp->offset += size;
COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
fdp->offset += size;
#if COFFEE_MICRO_LOGS
}
}
#endif /* COFFEE_MICRO_LOGS */
if(fdp->offset > file->end) {
@ -1250,10 +1234,10 @@ int
cfs_opendir(struct cfs_dir *dir, const char *name)
{
/*
* Coffee is only guaranteed to support "/" and ".", but it does not
* currently enforce this.
* Coffee is only guaranteed to support the directory names "/" and ".",
* but it does not enforce this currently.
*/
memset(dir->dummy_space, 0, sizeof(coffee_page_t));
memset(dir->state, 0, sizeof(coffee_page_t));
return 0;
}
/*---------------------------------------------------------------------------*/
@ -1262,19 +1246,19 @@ cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
{
struct file_header hdr;
coffee_page_t page;
coffee_page_t next_page;
memcpy(&page, dir->dummy_space, sizeof(coffee_page_t));
memcpy(&page, dir->state, sizeof(coffee_page_t));
while(page < COFFEE_PAGE_COUNT) {
read_header(&hdr, page);
if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) {
coffee_page_t next_page;
memcpy(record->name, hdr.name, sizeof(record->name));
record->name[sizeof(record->name) - 1] = '\0';
record->size = file_end(page);
next_page = next_file(page, &hdr);
memcpy(dir->dummy_space, &next_page, sizeof(coffee_page_t));
memcpy(dir->state, &next_page, sizeof(coffee_page_t));
return 0;
}
page = next_file(page, &hdr);
@ -1342,11 +1326,9 @@ cfs_coffee_set_io_semantics(int fd, unsigned flags)
int
cfs_coffee_format(void)
{
unsigned i;
coffee_page_t i;
PRINTF("Coffee: Formatting %u sectors", COFFEE_SECTOR_COUNT);
*next_free = 0;
PRINTF("Coffee: Formatting %u sectors", (unsigned)COFFEE_SECTOR_COUNT);
for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
COFFEE_ERASE(i);
@ -1354,16 +1336,13 @@ cfs_coffee_format(void)
}
/* Formatting invalidates the file information. */
memset(&protected_mem, 0, sizeof(protected_mem));
memset(&coffee_files, 0, sizeof(coffee_files));
memset(&coffee_fd_set, 0, sizeof(coffee_fd_set));
next_free = 0;
gc_wait = 1;
PRINTF(" done!\n");
return 0;
}
/*---------------------------------------------------------------------------*/
void *
cfs_coffee_get_protected_mem(unsigned *size)
{
*size = sizeof(protected_mem);
return &protected_mem;
}

View File

@ -46,15 +46,15 @@
* invoke its own micro logs when file modifications occur.
*
* This semantical I/O setting is useful when implementing flash storage
* algorithms on top of Coffee.
* algorithms such as database indices on top of Coffee.
*
* \sa cfs_coffee_set_io_semantics()
*/
#define CFS_COFFEE_IO_FLASH_AWARE 0x1
/**
* Instruct Coffee not to attempt to extend the file when there is
* an attempt to write past the reserved file size.
* Instruct Coffee not to attempt to extend the file upon a request
* to write past the reserved file size.
*
* A case when this is necessary is when the file has a firm size limit,
* and a safeguard is needed to protect against writes beyond this limit.
@ -63,6 +63,15 @@
*/
#define CFS_COFFEE_IO_FIRM_SIZE 0x2
/**
* Instruct Coffee to set unused bytes in the destination buffer to zero.
* Trailing zeros may cause a wrong file size, this option ensures that
* the corresponding bytes get set, so Coffee does not read unexpected data.
*
* \sa cfs_coffee_set_io_semantics()
*/
#define CFS_COFFEE_IO_ENSURE_READ_LENGTH 0x4
/**
* \file
* Header for the Coffee file system.
@ -75,8 +84,8 @@
/**
* \brief Reserve space for a file.
* \param name The filename.
* \param size The size of the file.
* \param name The file name.
* \param size The initial size to be reserved for the file.
* \return 0 on success, -1 on failure.
*
* Coffee uses sequential page structures for files. The sequential
@ -88,15 +97,15 @@ int cfs_coffee_reserve(const char *name, cfs_offset_t size);
/**
* \brief Configure the on-demand log file.
* \param file The filename.
* \param log_size The total log size.
* \param file The file name.
* \param log_size The total log file size.
* \param log_entry_size The log entry size.
* \return 0 on success, -1 on failure.
*
* When file data is first modified, Coffee creates a micro log for the
* file. The micro log stores a table of modifications whose
* parameters--the log size and the log entry size--can be modified
* through the cfs_coffee_configure_log function.
* file. The micro log stores a table of modifications whose parameters --
* the log size and the log entry size -- can be modified through the
* cfs_coffee_configure_log function.
*/
int cfs_coffee_configure_log(const char *file, unsigned log_size,
unsigned log_entry_size);
@ -109,8 +118,8 @@ int cfs_coffee_configure_log(const char *file, unsigned log_size,
*
* Coffee is used on a wide range of storage types, and the default
* I/O file semantics may not be optimal for the access pattern
* of a certain file. Hence, this functions allows programmers to
* switch the /O semantics on a file that is accessed through a
* of a certain file. Hence, this function allows programmers to
* switch the I/O semantics on a file that is accessed through a
* particular file descriptor.
*
*/
@ -123,21 +132,14 @@ int cfs_coffee_set_io_semantics(int fd, unsigned flags);
* Coffee formats the underlying storage by setting all bits to zero.
* Formatting must be done before using Coffee for the first time in
* a mote.
*
* Notice that the erased bits may be set to 1 on the physical storage
* when using flash memory. In this case, Coffee requires that the
* COFFEE_READ and COFFEE_WRITE functions used to access the flash memory
* invert all bits.
*/
int cfs_coffee_format(void);
/**
* \brief Points out a memory region that may not be altered during
* checkpointing operations that use the file system.
* \param size
* \return A pointer to the protected memory.
*
* This function returns the protected memory pointer and writes its size
* to the given parameter. Mainly used by sensornet checkpointing to protect
* the coffee state during CFS-based checkpointing operations.
*/
void *cfs_coffee_get_protected_mem(unsigned *size);
/** @} */
/** @} */

View File

@ -68,7 +68,9 @@ typedef CFS_CONF_OFFSET_TYPE cfs_offset_t;
#endif
struct cfs_dir {
char dummy_space[32];
/* Iteration state, which is implementation-defined and should not be
accessed externally. */
char state[32];
};
struct cfs_dirent {

View File

@ -78,13 +78,6 @@
#define NETSTACK_CONF_LLSEC nullsec_driver
#endif /* NETSTACK_CONF_LLSEC */
/* To avoid unnecessary complexity, we assume the common case of
a constant LoWPAN-wide IEEE 802.15.4 security level, which
can be specified by defining LLSEC802154_CONF_SECURITY_LEVEL. */
#ifndef LLSEC802154_CONF_SECURITY_LEVEL
#define LLSEC802154_CONF_SECURITY_LEVEL 0
#endif /* LLSEC802154_CONF_SECURITY_LEVEL */
/* NETSTACK_CONF_NETWORK specifies the network layer and can be either
sicslowpan_driver, for IPv6 networking, or rime_driver, for the
custom Rime network stack. */
@ -148,12 +141,32 @@
#define UIP_CONF_IPV6_RPL 1
#endif /* UIP_CONF_IPV6_RPL */
/* If RPL is enabled also enable the RPL NBR Policy */
#if UIP_CONF_IPV6_RPL
#ifndef NBR_TABLE_FIND_REMOVABLE
#define NBR_TABLE_FIND_REMOVABLE rpl_nbr_policy_find_removable
#endif /* NBR_TABLE_FIND_REMOVABLE */
#endif /* UIP_CONF_IPV6_RPL */
/* RPL_CONF_MOP specifies the RPL mode of operation that will be
* advertised by the RPL root. Possible values: RPL_MOP_NO_DOWNWARD_ROUTES,
* RPL_MOP_NON_STORING, RPL_MOP_STORING_NO_MULTICAST, RPL_MOP_STORING_MULTICAST */
#ifndef RPL_CONF_MOP
#define RPL_CONF_MOP RPL_MOP_STORING_NO_MULTICAST
#endif /* RPL_CONF_MOP */
/* UIP_CONF_MAX_ROUTES specifies the maximum number of routes that each
node will be able to handle. */
#ifndef UIP_CONF_MAX_ROUTES
#define UIP_CONF_MAX_ROUTES 20
#endif /* UIP_CONF_MAX_ROUTES */
/* RPL_NS_CONF_LINK_NUM specifies the maximum number of links a RPL root
* will maintain in non-storing mode. */
#ifndef RPL_NS_CONF_LINK_NUM
#define RPL_NS_CONF_LINK_NUM 20
#endif /* RPL_NS_CONF_LINK_NUM */
/* UIP_CONF_UDP specifies if UDP support should be included or
not. Disabling UDP saves memory but breaks a lot of stuff. */
#ifndef UIP_CONF_UDP
@ -220,14 +233,6 @@
* on the target platform, and are therefore platform-specific.
*/
/* SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS specifies how many times the
MAC layer should resend packets if no link-layer ACK was
received. This only makes sense with the csma_driver
NETSTACK_CONF_MAC. */
#ifndef SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS
#define SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS 4
#endif /* SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS */
/* SICSLOWPAN_CONF_FRAG specifies if 6lowpan fragmentation should be
used or not. Fragmentation is on by default. */
#ifndef SICSLOWPAN_CONF_FRAG

View File

@ -64,13 +64,13 @@ static unsigned char gcr_bits = 0;
static unsigned short gcr_val = 0;
/* Call before starting encoding or decoding */
void gcr_init() {
void gcr_init(void) {
gcr_val = 0;
gcr_bits = 0;
}
/* Use this to check if encoding / decoding is complete for now */
unsigned char gcr_finished() {
unsigned char gcr_finished(void) {
return gcr_bits == 0;
}
@ -100,7 +100,7 @@ void gcr_decode(unsigned char gcr_data) {
}
/* check if the current decoded stream is correct */
unsigned char gcr_valid() {
unsigned char gcr_valid(void) {
if (gcr_bits >= 10) {
unsigned short val = gcr_val & 0x3ff;
if ((GCR_decode[val >> 5u] << 4u) == 0xff ||

View File

@ -186,7 +186,7 @@ extern settings_status_t settings_delete(settings_key_t key, uint8_t index);
typedef eeprom_addr_t settings_iter_t;
/** Will return \ref SETTINGS_INVALID_ITER if the settings store is empty. */
extern settings_iter_t settings_iter_begin();
extern settings_iter_t settings_iter_begin(void);
/** Will return \ref SETTINGS_INVALID_ITER if at the end of settings list. */
extern settings_iter_t settings_iter_next(settings_iter_t iter);

View File

@ -79,7 +79,7 @@ static void double_interval(void *ptr);
#if TRICKLE_TIMER_WIDE_RAND
/* Returns a 4-byte wide, unsigned random number */
static uint32_t
wide_rand()
wide_rand(void)
{
return ((uint32_t)random_rand() << 16 | random_rand());
}

View File

@ -324,6 +324,7 @@ parse_url(const char *url, char *host, uint16_t *portptr, char *path)
if(host != NULL) {
host[i] = 0;
}
urlptr++;
break;
}
if(host != NULL) {
@ -349,6 +350,11 @@ parse_url(const char *url, char *host, uint16_t *portptr, char *path)
}
}
/* check if host is null terminated */
if(!memchr(host, 0, MAX_HOSTLEN)) {
return 0;
}
/* Find the port. Default is 80. */
port = 80;
if(*urlptr == ':') {
@ -412,7 +418,15 @@ event(struct tcp_socket *tcps, void *ptr,
tcp_socket_send_str(tcps, " HTTP/1.1\r\n");
tcp_socket_send_str(tcps, "Connection: close\r\n");
tcp_socket_send_str(tcps, "Host: ");
/* If we have IPv6 host, add the '[' and the ']' characters
to the host. As in rfc2732. */
if(memchr(host, ':', MAX_HOSTLEN)) {
tcp_socket_send_str(tcps, "[");
}
tcp_socket_send_str(tcps, host);
if(memchr(host, ':', MAX_HOSTLEN)) {
tcp_socket_send_str(tcps, "]");
}
tcp_socket_send_str(tcps, "\r\n");
if(s->postdata != NULL) {
if(s->content_type) {

View File

@ -111,6 +111,9 @@ strcasecmp(const char *s1, const char *s2)
/* TODO: Add case support! */
return strcmp(s1, s2);
}
#else
int strcasecmp(const char *s1, const char *s2);
int strncasecmp(const char *s1, const char *s2, size_t n);
#endif /* __SDCC */
#define UIP_UDP_BUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])
@ -783,9 +786,9 @@ check_entries(void)
static void
newdata(void)
{
static uint8_t nquestions, nanswers;
uint8_t nquestions, nanswers;
static int8_t i;
int8_t i;
register struct namemap *namemapptr = NULL;
@ -872,7 +875,7 @@ newdata(void)
}
return;
} else {
static uint8_t nauthrr;
uint8_t nauthrr;
PRINTF("resolver: But we are still probing. Waiting...\n");
/* We are still probing. We need to do the mDNS
* probe race condition check here and make sure
@ -960,7 +963,7 @@ newdata(void)
#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */
#if VERBOSE_DEBUG
static char debug_name[40];
char debug_name[40];
decode_name(queryptr, debug_name, uip_appdata);
DEBUG_PRINTF("resolver: Answer %d: \"%s\", type %d, class %d, ttl %d, length %d\n",
++i, debug_name, uip_ntohs(ans->type),
@ -1266,9 +1269,9 @@ remove_trailing_dots(const char *name) {
void
resolv_query(const char *name)
{
static uint8_t i;
uint8_t i;
static uint8_t lseq, lseqi;
uint8_t lseq, lseqi;
register struct namemap *nameptr = 0;
@ -1315,7 +1318,7 @@ resolv_query(const char *name)
{
size_t name_len = strlen(name);
static const char local_suffix[] = "local";
const char local_suffix[] = "local";
if((name_len > (sizeof(local_suffix) - 1)) &&
(0 == strcasecmp(name + name_len - (sizeof(local_suffix) - 1), local_suffix))) {
@ -1347,7 +1350,7 @@ resolv_lookup(const char *name, uip_ipaddr_t ** ipaddr)
{
resolv_status_t ret = RESOLV_STATUS_UNCACHED;
static uint8_t i;
uint8_t i;
struct namemap *nameptr;

View File

@ -119,7 +119,7 @@ simple_udp_register(struct simple_udp_connection *c,
PROCESS_CONTEXT_BEGIN(&simple_udp_process);
c->udp_conn = udp_new(remote_addr, UIP_HTONS(remote_port), c);
if(c->udp_conn != NULL) {
if(c->udp_conn != NULL && local_port) {
udp_bind(c->udp_conn, UIP_HTONS(local_port));
}
PROCESS_CONTEXT_END();

View File

@ -47,6 +47,11 @@
#include "net/ipv6/uip-ds6.h"
#endif
#if UIP_CONF_IPV6_RPL
#include "net/rpl/rpl.h"
#include "net/rpl/rpl-private.h"
#endif
#include <string.h>
#define DEBUG DEBUG_NONE
@ -219,7 +224,7 @@ struct uip_conn *
tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
{
struct uip_conn *c;
c = uip_connect(ripaddr, port);
if(c == NULL) {
return NULL;
@ -227,9 +232,9 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
c->appstate.p = PROCESS_CURRENT();
c->appstate.state = appstate;
tcpip_poll_tcp(c);
return c;
}
#endif /* UIP_ACTIVE_OPEN */
@ -237,7 +242,7 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
void
tcp_unlisten(uint16_t port)
{
static unsigned char i;
unsigned char i;
struct listenport *l;
l = s.listenports;
@ -255,7 +260,7 @@ tcp_unlisten(uint16_t port)
void
tcp_listen(uint16_t port)
{
static unsigned char i;
unsigned char i;
struct listenport *l;
l = s.listenports;
@ -300,7 +305,7 @@ udp_new(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
{
struct uip_udp_conn *c;
uip_udp_appstate_t *s;
c = uip_udp_new(ripaddr, port);
if(c == NULL) {
return NULL;
@ -359,157 +364,157 @@ static void
eventhandler(process_event_t ev, process_data_t data)
{
#if UIP_TCP
static unsigned char i;
unsigned char i;
register struct listenport *l;
#endif /*UIP_TCP*/
struct process *p;
switch(ev) {
case PROCESS_EVENT_EXITED:
/* This is the event we get if a process has exited. We go through
case PROCESS_EVENT_EXITED:
/* This is the event we get if a process has exited. We go through
the TCP/IP tables to see if this process had any open
connections or listening TCP ports. If so, we'll close those
connections. */
p = (struct process *)data;
p = (struct process *)data;
#if UIP_TCP
l = s.listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->p == p) {
uip_unlisten(l->port);
l->port = 0;
l->p = PROCESS_NONE;
}
++l;
l = s.listenports;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->p == p) {
uip_unlisten(l->port);
l->port = 0;
l->p = PROCESS_NONE;
}
{
struct uip_conn *cptr;
for(cptr = &uip_conns[0]; cptr < &uip_conns[UIP_CONNS]; ++cptr) {
if(cptr->appstate.p == p) {
cptr->appstate.p = PROCESS_NONE;
cptr->tcpstateflags = UIP_CLOSED;
}
++l;
}
{
struct uip_conn *cptr;
for(cptr = &uip_conns[0]; cptr < &uip_conns[UIP_CONNS]; ++cptr) {
if(cptr->appstate.p == p) {
cptr->appstate.p = PROCESS_NONE;
cptr->tcpstateflags = UIP_CLOSED;
}
}
}
#endif /* UIP_TCP */
#if UIP_UDP
{
struct uip_udp_conn *cptr;
{
struct uip_udp_conn *cptr;
for(cptr = &uip_udp_conns[0];
cptr < &uip_udp_conns[UIP_UDP_CONNS]; ++cptr) {
if(cptr->appstate.p == p) {
cptr->lport = 0;
}
for(cptr = &uip_udp_conns[0];
cptr < &uip_udp_conns[UIP_UDP_CONNS]; ++cptr) {
if(cptr->appstate.p == p) {
cptr->lport = 0;
}
}
}
#endif /* UIP_UDP */
break;
break;
case PROCESS_EVENT_TIMER:
/* We get this event if one of our timers have expired. */
{
/* Check the clock so see if we should call the periodic uIP
case PROCESS_EVENT_TIMER:
/* We get this event if one of our timers have expired. */
{
/* Check the clock so see if we should call the periodic uIP
processing. */
if(data == &periodic &&
etimer_expired(&periodic)) {
if(data == &periodic &&
etimer_expired(&periodic)) {
#if UIP_TCP
for(i = 0; i < UIP_CONNS; ++i) {
if(uip_conn_active(i)) {
/* Only restart the timer if there are active
for(i = 0; i < UIP_CONNS; ++i) {
if(uip_conn_active(i)) {
/* Only restart the timer if there are active
connections. */
etimer_restart(&periodic);
uip_periodic(i);
etimer_restart(&periodic);
uip_periodic(i);
#if NETSTACK_CONF_WITH_IPV6
tcpip_ipv6_output();
tcpip_ipv6_output();
#else
if(uip_len > 0) {
PRINTF("tcpip_output from periodic len %d\n", uip_len);
tcpip_output();
PRINTF("tcpip_output after periodic len %d\n", uip_len);
}
#endif /* NETSTACK_CONF_WITH_IPV6 */
}
if(uip_len > 0) {
PRINTF("tcpip_output from periodic len %d\n", uip_len);
tcpip_output();
PRINTF("tcpip_output after periodic len %d\n", uip_len);
}
#endif /* NETSTACK_CONF_WITH_IPV6 */
}
}
#endif /* UIP_TCP */
#if UIP_CONF_IP_FORWARD
uip_fw_periodic();
uip_fw_periodic();
#endif /* UIP_CONF_IP_FORWARD */
}
}
#if NETSTACK_CONF_WITH_IPV6
#if UIP_CONF_IPV6_REASSEMBLY
/*
* check the timer for reassembly
*/
if(data == &uip_reass_timer &&
etimer_expired(&uip_reass_timer)) {
uip_reass_over();
tcpip_ipv6_output();
}
/*
* check the timer for reassembly
*/
if(data == &uip_reass_timer &&
etimer_expired(&uip_reass_timer)) {
uip_reass_over();
tcpip_ipv6_output();
}
#endif /* UIP_CONF_IPV6_REASSEMBLY */
/*
* check the different timers for neighbor discovery and
* stateless autoconfiguration
*/
/*if(data == &uip_ds6_timer_periodic &&
/*
* check the different timers for neighbor discovery and
* stateless autoconfiguration
*/
/*if(data == &uip_ds6_timer_periodic &&
etimer_expired(&uip_ds6_timer_periodic)) {
uip_ds6_periodic();
tcpip_ipv6_output();
}*/
#if !UIP_CONF_ROUTER
if(data == &uip_ds6_timer_rs &&
etimer_expired(&uip_ds6_timer_rs)) {
uip_ds6_send_rs();
tcpip_ipv6_output();
}
if(data == &uip_ds6_timer_rs &&
etimer_expired(&uip_ds6_timer_rs)) {
uip_ds6_send_rs();
tcpip_ipv6_output();
}
#endif /* !UIP_CONF_ROUTER */
if(data == &uip_ds6_timer_periodic &&
etimer_expired(&uip_ds6_timer_periodic)) {
uip_ds6_periodic();
tcpip_ipv6_output();
}
if(data == &uip_ds6_timer_periodic &&
etimer_expired(&uip_ds6_timer_periodic)) {
uip_ds6_periodic();
tcpip_ipv6_output();
}
#endif /* NETSTACK_CONF_WITH_IPV6 */
}
break;
}
break;
#if UIP_TCP
case TCP_POLL:
if(data != NULL) {
uip_poll_conn(data);
case TCP_POLL:
if(data != NULL) {
uip_poll_conn(data);
#if NETSTACK_CONF_WITH_IPV6
tcpip_ipv6_output();
tcpip_ipv6_output();
#else /* NETSTACK_CONF_WITH_IPV6 */
if(uip_len > 0) {
PRINTF("tcpip_output from tcp poll len %d\n", uip_len);
tcpip_output();
}
#endif /* NETSTACK_CONF_WITH_IPV6 */
/* Start the periodic polling, if it isn't already active. */
start_periodic_tcp_timer();
if(uip_len > 0) {
PRINTF("tcpip_output from tcp poll len %d\n", uip_len);
tcpip_output();
}
break;
#endif /* NETSTACK_CONF_WITH_IPV6 */
/* Start the periodic polling, if it isn't already active. */
start_periodic_tcp_timer();
}
break;
#endif /* UIP_TCP */
#if UIP_UDP
case UDP_POLL:
if(data != NULL) {
uip_udp_periodic_conn(data);
case UDP_POLL:
if(data != NULL) {
uip_udp_periodic_conn(data);
#if NETSTACK_CONF_WITH_IPV6
tcpip_ipv6_output();
tcpip_ipv6_output();
#else
if(uip_len > 0) {
tcpip_output();
}
#endif /* UIP_UDP */
if(uip_len > 0) {
tcpip_output();
}
break;
#endif /* UIP_UDP */
}
break;
#endif /* UIP_UDP */
case PACKET_INPUT:
packet_input();
break;
case PACKET_INPUT:
packet_input();
break;
};
}
/*---------------------------------------------------------------------------*/
@ -525,7 +530,7 @@ void
tcpip_ipv6_output(void)
{
uip_ds6_nbr_t *nbr = NULL;
uip_ipaddr_t *nexthop;
uip_ipaddr_t *nexthop = NULL;
if(uip_len == 0) {
return;
@ -545,14 +550,25 @@ tcpip_ipv6_output(void)
if(!uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
/* Next hop determination */
#if UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING
uip_ipaddr_t ipaddr;
/* Look for a RPL Source Route */
if(rpl_srh_get_next_hop(&ipaddr)) {
nexthop = &ipaddr;
}
#endif /* UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING */
nbr = NULL;
/* We first check if the destination address is on our immediate
link. If so, we simply use the destination address as our
nexthop address. */
if(uip_ds6_is_addr_onlink(&UIP_IP_BUF->destipaddr)){
if(nexthop == NULL && uip_ds6_is_addr_onlink(&UIP_IP_BUF->destipaddr)){
nexthop = &UIP_IP_BUF->destipaddr;
} else {
}
if(nexthop == NULL) {
uip_ds6_route_t *route;
/* Check if we have a route to the destination address. */
route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
@ -563,25 +579,25 @@ tcpip_ipv6_output(void)
nexthop = uip_ds6_defrt_choose();
if(nexthop == NULL) {
#ifdef UIP_FALLBACK_INTERFACE
PRINTF("FALLBACK: removing ext hdrs & setting proto %d %d\n",
uip_ext_len, *((uint8_t *)UIP_IP_BUF + 40));
if(uip_ext_len > 0) {
extern void remove_ext_hdr(void);
uint8_t proto = *((uint8_t *)UIP_IP_BUF + 40);
remove_ext_hdr();
/* This should be copied from the ext header... */
UIP_IP_BUF->proto = proto;
}
/* Inform the other end that the destination is not reachable. If it's
* not informed routes might get lost unexpectedly until there's a need
* to send a new packet to the peer */
if(UIP_FALLBACK_INTERFACE.output() < 0) {
PRINTF("FALLBACK: output error. Reporting DST UNREACH\n");
uip_icmp6_error_output(ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0);
uip_flags = 0;
tcpip_ipv6_output();
return;
}
PRINTF("FALLBACK: removing ext hdrs & setting proto %d %d\n",
uip_ext_len, *((uint8_t *)UIP_IP_BUF + 40));
if(uip_ext_len > 0) {
extern void remove_ext_hdr(void);
uint8_t proto = *((uint8_t *)UIP_IP_BUF + 40);
remove_ext_hdr();
/* This should be copied from the ext header... */
UIP_IP_BUF->proto = proto;
}
/* Inform the other end that the destination is not reachable. If it's
* not informed routes might get lost unexpectedly until there's a need
* to send a new packet to the peer */
if(UIP_FALLBACK_INTERFACE.output() < 0) {
PRINTF("FALLBACK: output error. Reporting DST UNREACH\n");
uip_icmp6_error_output(ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0);
uip_flags = 0;
tcpip_ipv6_output();
return;
}
#else
PRINTF("tcpip_ipv6_output: Destination off-link but no route\n");
#endif /* !UIP_FALLBACK_INTERFACE */
@ -636,7 +652,7 @@ tcpip_ipv6_output(void)
/* End of next hop determination */
#if UIP_CONF_IPV6_RPL
if(rpl_update_header_final(nexthop)) {
if(!rpl_finalize_header(nexthop)) {
uip_clear_buf();
return;
}
@ -644,8 +660,9 @@ tcpip_ipv6_output(void)
nbr = uip_ds6_nbr_lookup(nexthop);
if(nbr == NULL) {
#if UIP_ND6_SEND_NA
if((nbr = uip_ds6_nbr_add(nexthop, NULL, 0, NBR_INCOMPLETE)) == NULL) {
if((nbr = uip_ds6_nbr_add(nexthop, NULL, 0, NBR_INCOMPLETE, NBR_TABLE_REASON_IPV6_ND, NULL)) == NULL) {
uip_clear_buf();
PRINTF("tcpip_ipv6_output: failed to add neighbor to cache\n");
return;
} else {
#if UIP_CONF_IPV6_QUEUE_PKT
@ -655,13 +672,13 @@ tcpip_ipv6_output(void)
uip_packetqueue_set_buflen(&nbr->packethandle, uip_len);
}
#endif
/* RFC4861, 7.2.2:
* "If the source address of the packet prompting the solicitation is the
* same as one of the addresses assigned to the outgoing interface, that
* address SHOULD be placed in the IP Source Address of the outgoing
* solicitation. Otherwise, any one of the addresses assigned to the
* interface should be used."*/
if(uip_ds6_is_my_addr(&UIP_IP_BUF->srcipaddr)){
/* RFC4861, 7.2.2:
* "If the source address of the packet prompting the solicitation is the
* same as one of the addresses assigned to the outgoing interface, that
* address SHOULD be placed in the IP Source Address of the outgoing
* solicitation. Otherwise, any one of the addresses assigned to the
* interface should be used."*/
if(uip_ds6_is_my_addr(&UIP_IP_BUF->srcipaddr)){
uip_nd6_ns_output(&UIP_IP_BUF->srcipaddr, NULL, &nbr->ipaddr);
} else {
uip_nd6_ns_output(NULL, NULL, &nbr->ipaddr);
@ -672,6 +689,7 @@ tcpip_ipv6_output(void)
/* Send the first NS try from here (multicast destination IP address). */
}
#else /* UIP_ND6_SEND_NA */
PRINTF("tcpip_ipv6_output: neighbor not in cache\n");
uip_len = 0;
return;
#endif /* UIP_ND6_SEND_NA */
@ -747,7 +765,7 @@ void
tcpip_uipcall(void)
{
uip_udp_appstate_t *ts;
#if UIP_UDP
if(uip_conn != NULL) {
ts = &uip_conn->appstate;
@ -759,30 +777,30 @@ tcpip_uipcall(void)
#endif /* UIP_UDP */
#if UIP_TCP
{
static unsigned char i;
struct listenport *l;
/* If this is a connection request for a listening port, we must
{
unsigned char i;
struct listenport *l;
/* If this is a connection request for a listening port, we must
mark the connection with the right process ID. */
if(uip_connected()) {
l = &s.listenports[0];
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == uip_conn->lport &&
l->p != PROCESS_NONE) {
ts->p = l->p;
ts->state = NULL;
break;
}
++l;
}
/* Start the periodic polling, if it isn't already active. */
start_periodic_tcp_timer();
}
}
if(uip_connected()) {
l = &s.listenports[0];
for(i = 0; i < UIP_LISTENPORTS; ++i) {
if(l->port == uip_conn->lport &&
l->p != PROCESS_NONE) {
ts->p = l->p;
ts->state = NULL;
break;
}
++l;
}
/* Start the periodic polling, if it isn't already active. */
start_periodic_tcp_timer();
}
}
#endif /* UIP_TCP */
if(ts->p != NULL) {
process_post_synch(ts->p, tcpip_event, ts->state);
}
@ -791,16 +809,16 @@ tcpip_uipcall(void)
PROCESS_THREAD(tcpip_process, ev, data)
{
PROCESS_BEGIN();
#if UIP_TCP
{
static unsigned char i;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
s.listenports[i].port = 0;
}
s.p = PROCESS_CURRENT();
}
{
unsigned char i;
for(i = 0; i < UIP_LISTENPORTS; ++i) {
s.listenports[i].port = 0;
}
s.p = PROCESS_CURRENT();
}
#endif
tcpip_event = process_alloc_event();
@ -813,7 +831,7 @@ PROCESS_THREAD(tcpip_process, ev, data)
#ifdef UIP_FALLBACK_INTERFACE
UIP_FALLBACK_INTERFACE.init();
#endif
/* initialize RPL if configured for using RPL */
/* initialize RPL if configured for using RPL */
#if NETSTACK_CONF_WITH_IPV6 && UIP_CONF_IPV6_RPL
rpl_init();
#endif /* UIP_CONF_IPV6_RPL */
@ -822,7 +840,7 @@ PROCESS_THREAD(tcpip_process, ev, data)
PROCESS_YIELD();
eventhandler(ev, data);
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/

View File

@ -30,7 +30,7 @@
/**
* \file
* A set of debugging tools
* A set of debugging tools for the IP stack
* \author
* Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@sics.se>
@ -92,19 +92,3 @@ uip_debug_ipaddr_print(const uip_ipaddr_t *addr)
#endif /* NETSTACK_CONF_WITH_IPV6 */
}
/*---------------------------------------------------------------------------*/
void
uip_debug_lladdr_print(const uip_lladdr_t *addr)
{
unsigned int i;
if(addr == NULL) {
PRINTA("(NULL LL addr)");
return;
}
for(i = 0; i < sizeof(uip_lladdr_t); i++) {
if(i > 0) {
PRINTA(":");
}
PRINTA("%02x", addr->addr[i]);
}
}
/*---------------------------------------------------------------------------*/

View File

@ -31,57 +31,27 @@
*/
/**
* \file
* A set of debugging macros.
* A set of debugging macros for the IP stack
*
* \author Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@sics.se>
* Joakim Eriksson <joakime@sics.se>
* Simon Duquennoy <simon.duquennoy@inria.fr>
*/
#ifndef UIP_DEBUG_H
#define UIP_DEBUG_H
#include "net/net-debug.h"
#include "net/ip/uip.h"
#include <stdio.h>
void uip_debug_ipaddr_print(const uip_ipaddr_t *addr);
void uip_debug_lladdr_print(const uip_lladdr_t *addr);
#define DEBUG_NONE 0
#define DEBUG_PRINT 1
#define DEBUG_ANNOTATE 2
#define DEBUG_FULL DEBUG_ANNOTATE | DEBUG_PRINT
/* PRINTA will always print if the debug routines are called directly */
#ifdef __AVR__
#include <avr/pgmspace.h>
#define PRINTA(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define PRINTA(...) printf(__VA_ARGS__)
#endif
#if (DEBUG) & DEBUG_ANNOTATE
#ifdef __AVR__
#define ANNOTATE(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define ANNOTATE(...) printf(__VA_ARGS__)
#endif
#else
#define ANNOTATE(...)
#endif /* (DEBUG) & DEBUG_ANNOTATE */
#if (DEBUG) & DEBUG_PRINT
#ifdef __AVR__
#define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define PRINTF(...) printf(__VA_ARGS__)
#endif
#define PRINT6ADDR(addr) uip_debug_ipaddr_print(addr)
#define PRINTLLADDR(lladdr) uip_debug_lladdr_print(lladdr)
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(lladdr)
#endif /* (DEBUG) & DEBUG_PRINT */
#endif
#endif /* UIP_DEBUG_H */

View File

@ -51,12 +51,10 @@ void
uip_udp_packet_send(struct uip_udp_conn *c, const void *data, int len)
{
#if UIP_UDP
if(data != NULL) {
if(data != NULL && len <= (UIP_BUFSIZE - (UIP_LLH_LEN + UIP_IPUDPH_LEN))) {
uip_udp_conn = c;
uip_slen = len;
memmove(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data,
len > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?
UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: len);
memmove(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data, len);
uip_process(UIP_UDP_SEND_CONN);
#if UIP_CONF_IPV6_MULTICAST

View File

@ -1801,6 +1801,13 @@ typedef struct uip_routing_hdr {
uint8_t seg_left;
} uip_routing_hdr;
/* RPL Source Routing Header */
typedef struct uip_rpl_srh_hdr {
uint8_t cmpr; /* CmprI and CmprE */
uint8_t pad;
uint8_t reserved[2];
} uip_rpl_srh_hdr;
/* fragmentation header */
typedef struct uip_frag_hdr {
uint8_t next;

View File

@ -62,6 +62,7 @@
#include "contiki.h"
#include "dev/watchdog.h"
#include "net/link-stats.h"
#include "net/ip/tcpip.h"
#include "net/ip/uip.h"
#include "net/ipv6/uip-ds6.h"
@ -92,12 +93,6 @@ void uip_log(char *msg);
#define UIP_LOG(m)
#endif /* UIP_LOGGING == 1 */
#ifdef SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS
#define SICSLOWPAN_MAX_MAC_TRANSMISSIONS SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS
#else
#define SICSLOWPAN_MAX_MAC_TRANSMISSIONS 4
#endif
#ifndef SICSLOWPAN_COMPRESSION
#ifdef SICSLOWPAN_CONF_COMPRESSION
#define SICSLOWPAN_COMPRESSION SICSLOWPAN_CONF_COMPRESSION
@ -169,6 +164,14 @@ void uip_log(char *msg);
#define COMPRESSION_THRESHOLD 0
#endif
/** \brief Fixed size of a frame header. This value is
* used in case framer returns an error or if SICSLOWPAN_USE_FIXED_HDRLEN
* is defined.
*/
#ifndef SICSLOWPAN_FIXED_HDRLEN
#define SICSLOWPAN_FIXED_HDRLEN 21
#endif
/** \name General variables
* @{
*/
@ -452,7 +455,7 @@ rime_sniffer_remove(struct rime_sniffer *s)
}
static void
set_packet_attrs()
set_packet_attrs(void)
{
int c = 0;
/* set protocol in NETWORK_ID */
@ -1277,11 +1280,6 @@ output(const uip_lladdr_t *localdest)
/* The MAC address of the destination of the packet */
linkaddr_t dest;
#if SICSLOWPAN_CONF_FRAG
/* Number of bytes processed. */
uint16_t processed_ip_out_len;
#endif /* SICSLOWPAN_CONF_FRAG */
/* init */
uncomp_hdr_len = 0;
packetbuf_hdr_len = 0;
@ -1290,9 +1288,6 @@ output(const uip_lladdr_t *localdest)
packetbuf_clear();
packetbuf_ptr = packetbuf_dataptr();
packetbuf_set_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS,
SICSLOWPAN_MAX_MAC_TRANSMISSIONS);
if(callback) {
/* call the attribution when the callback comes, but set attributes
here ! */
@ -1345,21 +1340,23 @@ output(const uip_lladdr_t *localdest)
/* Calculate NETSTACK_FRAMER's header length, that will be added in the NETSTACK_RDC.
* We calculate it here only to make a better decision of whether the outgoing packet
* needs to be fragmented or not. */
#define USE_FRAMER_HDRLEN 1
#if USE_FRAMER_HDRLEN
#ifndef SICSLOWPAN_USE_FIXED_HDRLEN
packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &dest);
framer_hdrlen = NETSTACK_FRAMER.length();
if(framer_hdrlen < 0) {
/* Framing failed, we assume the maximum header length */
framer_hdrlen = 21;
framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN;
}
#else /* USE_FRAMER_HDRLEN */
framer_hdrlen = 21;
framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN;
#endif /* USE_FRAMER_HDRLEN */
max_payload = MAC_MAX_PAYLOAD - framer_hdrlen;
if((int)uip_len - (int)uncomp_hdr_len > max_payload - (int)packetbuf_hdr_len) {
#if SICSLOWPAN_CONF_FRAG
/* Number of bytes processed. */
uint16_t processed_ip_out_len;
struct queuebuf *q;
uint16_t frag_tag;
@ -1520,6 +1517,9 @@ input(void)
uint8_t first_fragment = 0, last_fragment = 0;
#endif /*SICSLOWPAN_CONF_FRAG*/
/* Update link statistics */
link_stats_input_callback(packetbuf_addr(PACKETBUF_ADDR_SENDER));
/* init */
uncomp_hdr_len = 0;
packetbuf_hdr_len = 0;
@ -1757,8 +1757,8 @@ sicslowpan_init(void)
#ifdef SICSLOWPAN_CONF_ADDR_CONTEXT_0
SICSLOWPAN_CONF_ADDR_CONTEXT_0;
#else
addr_contexts[0].prefix[0] = 0xaa;
addr_contexts[0].prefix[1] = 0xaa;
addr_contexts[0].prefix[0] = UIP_DS6_DEFAULT_PREFIX_0;
addr_contexts[0].prefix[1] = UIP_DS6_DEFAULT_PREFIX_1;
#endif
#endif /* SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS > 0 */

View File

@ -47,6 +47,7 @@
#include <stdlib.h>
#include <stddef.h>
#include "lib/list.h"
#include "net/link-stats.h"
#include "net/linkaddr.h"
#include "net/packetbuf.h"
#include "net/ipv6/uip-ds6-nbr.h"
@ -74,25 +75,32 @@ NBR_TABLE_GLOBAL(uip_ds6_nbr_t, ds6_neighbors);
void
uip_ds6_neighbors_init(void)
{
link_stats_init();
nbr_table_register(ds6_neighbors, (nbr_table_callback *)uip_ds6_nbr_rm);
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
uint8_t isrouter, uint8_t state)
uint8_t isrouter, uint8_t state, nbr_table_reason_t reason,
void *data)
{
uip_ds6_nbr_t *nbr = nbr_table_add_lladdr(ds6_neighbors, (linkaddr_t*)lladdr);
uip_ds6_nbr_t *nbr = nbr_table_add_lladdr(ds6_neighbors, (linkaddr_t*)lladdr
, reason, data);
if(nbr) {
uip_ipaddr_copy(&nbr->ipaddr, ipaddr);
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
nbr->isrouter = isrouter;
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
nbr->state = state;
#if UIP_CONF_IPV6_QUEUE_PKT
#if UIP_CONF_IPV6_QUEUE_PKT
uip_packetqueue_new(&nbr->packethandle);
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
#if UIP_ND6_SEND_NA
/* timers are set separately, for now we put them in expired state */
stimer_set(&nbr->reachable, 0);
stimer_set(&nbr->sendns, 0);
nbr->nscount = 0;
#endif /* UIP_ND6_SEND_NA */
PRINTF("Adding neighbor with ip addr ");
PRINT6ADDR(ipaddr);
PRINTF(" link addr ");
@ -111,7 +119,7 @@ uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
}
/*---------------------------------------------------------------------------*/
void
int
uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
{
if(nbr != NULL) {
@ -119,9 +127,9 @@ uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
uip_packetqueue_free(&nbr->packethandle);
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
NEIGHBOR_STATE_CHANGED(nbr);
nbr_table_remove(ds6_neighbors, nbr);
return nbr_table_remove(ds6_neighbors, nbr);
}
return;
return 0;
}
/*---------------------------------------------------------------------------*/
@ -198,6 +206,9 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
return;
}
/* Update neighbor link statistics */
link_stats_packet_sent(dest, status, numtx);
/* Call upper-layer callback (e.g. RPL) */
LINK_NEIGHBOR_CALLBACK(dest, status, numtx);
#if UIP_DS6_LL_NUD
@ -230,6 +241,7 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
#endif /* UIP_DS6_LL_NUD */
}
#if UIP_ND6_SEND_NA
/*---------------------------------------------------------------------------*/
/** Periodic processing on neighbors */
void
@ -241,7 +253,7 @@ uip_ds6_neighbor_periodic(void)
case NBR_REACHABLE:
if(stimer_expired(&nbr->reachable)) {
#if UIP_CONF_IPV6_RPL
/* when a neighbor leave it's REACHABLE state and is a default router,
/* when a neighbor leave its REACHABLE state and is a default router,
instead of going to STALE state it enters DELAY state in order to
force a NUD on it. Otherwise, if there is no upward traffic, the
node never knows if the default router is still reachable. This
@ -268,7 +280,6 @@ uip_ds6_neighbor_periodic(void)
#endif /* UIP_CONF_IPV6_RPL */
}
break;
#if UIP_ND6_SEND_NA
case NBR_INCOMPLETE:
if(nbr->nscount >= UIP_ND6_MAX_MULTICAST_SOLICIT) {
uip_ds6_nbr_rm(nbr);
@ -304,7 +315,6 @@ uip_ds6_neighbor_periodic(void)
stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000);
}
break;
#endif /* UIP_ND6_SEND_NA */
default:
break;
}
@ -330,5 +340,6 @@ uip_ds6_get_least_lifetime_neighbor(void)
}
return nbr_expiring;
}
#endif /* UIP_ND6_SEND_NA */
/*---------------------------------------------------------------------------*/
/** @} */

View File

@ -69,12 +69,13 @@ NBR_TABLE_DECLARE(ds6_neighbors);
/** \brief An entry in the nbr cache */
typedef struct uip_ds6_nbr {
uip_ipaddr_t ipaddr;
uint8_t isrouter;
uint8_t state;
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
struct stimer reachable;
struct stimer sendns;
uint8_t nscount;
uint8_t isrouter;
uint8_t state;
uint16_t link_metric;
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
#if UIP_CONF_IPV6_QUEUE_PKT
struct uip_packetqueue_handle packethandle;
#define UIP_DS6_NBR_PACKET_LIFETIME CLOCK_SECOND * 4
@ -84,9 +85,11 @@ typedef struct uip_ds6_nbr {
void uip_ds6_neighbors_init(void);
/** \brief Neighbor Cache basic routines */
uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
uint8_t isrouter, uint8_t state);
void uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr);
uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr,
const uip_lladdr_t *lladdr,
uint8_t isrouter, uint8_t state,
nbr_table_reason_t reason, void *data);
int uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr);
const uip_lladdr_t *uip_ds6_nbr_get_ll(const uip_ds6_nbr_t *nbr);
const uip_ipaddr_t *uip_ds6_nbr_get_ipaddr(const uip_ds6_nbr_t *nbr);
uip_ds6_nbr_t *uip_ds6_nbr_lookup(const uip_ipaddr_t *ipaddr);

View File

@ -57,6 +57,7 @@ void NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK(const linkaddr_t *addr);
void NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK(const linkaddr_t *addr);
#endif /* NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK */
#if (UIP_CONF_MAX_ROUTES != 0)
/* The nbr_routes holds a neighbor table to be able to maintain
information about what routes go through what neighbor. This
neighbor table is registered with the central nbr-table repository
@ -71,6 +72,11 @@ MEMB(neighborroutememb, struct uip_ds6_route_neighbor_route, UIP_DS6_ROUTE_NB);
LIST(routelist);
MEMB(routememb, uip_ds6_route_t, UIP_DS6_ROUTE_NB);
static int num_routes = 0;
static void rm_routelist_callback(nbr_table_item_t *ptr);
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
/* Default routes are held on the defaultrouterlist and their
structures are allocated from the defaultroutermemb memory block.*/
LIST(defaultrouterlist);
@ -80,13 +86,10 @@ MEMB(defaultroutermemb, uip_ds6_defrt_t, UIP_DS6_DEFRT_NB);
LIST(notificationlist);
#endif
static int num_routes = 0;
#undef DEBUG
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
static void rm_routelist_callback(nbr_table_item_t *ptr);
/*---------------------------------------------------------------------------*/
#if DEBUG != DEBUG_NONE
static void
@ -156,10 +159,12 @@ uip_ds6_notification_rm(struct uip_ds6_notification *n)
void
uip_ds6_route_init(void)
{
#if (UIP_CONF_MAX_ROUTES != 0)
memb_init(&routememb);
list_init(routelist);
nbr_table_register(nbr_routes,
(nbr_table_callback *)rm_routelist_callback);
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
memb_init(&defaultroutermemb);
list_init(defaultrouterlist);
@ -168,6 +173,7 @@ uip_ds6_route_init(void)
list_init(notificationlist);
#endif
}
#if (UIP_CONF_MAX_ROUTES != 0)
/*---------------------------------------------------------------------------*/
static uip_lladdr_t *
uip_ds6_route_nexthop_lladdr(uip_ds6_route_t *route)
@ -179,42 +185,75 @@ uip_ds6_route_nexthop_lladdr(uip_ds6_route_t *route)
return NULL;
}
}
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
/*---------------------------------------------------------------------------*/
uip_ipaddr_t *
uip_ds6_route_nexthop(uip_ds6_route_t *route)
{
#if (UIP_CONF_MAX_ROUTES != 0)
if(route != NULL) {
return uip_ds6_nbr_ipaddr_from_lladdr(uip_ds6_route_nexthop_lladdr(route));
} else {
return NULL;
}
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
}
/*---------------------------------------------------------------------------*/
uip_ds6_route_t *
uip_ds6_route_head(void)
{
#if (UIP_CONF_MAX_ROUTES != 0)
return list_head(routelist);
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
}
/*---------------------------------------------------------------------------*/
uip_ds6_route_t *
uip_ds6_route_next(uip_ds6_route_t *r)
{
#if (UIP_CONF_MAX_ROUTES != 0)
if(r != NULL) {
uip_ds6_route_t *n = list_item_next(r);
return n;
}
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
}
/*---------------------------------------------------------------------------*/
int
uip_ds6_route_is_nexthop(const uip_ipaddr_t *ipaddr)
{
#if (UIP_CONF_MAX_ROUTES != 0)
const uip_lladdr_t *lladdr;
lladdr = uip_ds6_nbr_lladdr_from_ipaddr(ipaddr);
if(lladdr == NULL) {
return 0;
}
return nbr_table_get_from_lladdr(nbr_routes, (linkaddr_t *)lladdr) != NULL;
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return 0;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
}
/*---------------------------------------------------------------------------*/
int
uip_ds6_route_num_routes(void)
{
#if (UIP_CONF_MAX_ROUTES != 0)
return num_routes;
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return 0;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
}
/*---------------------------------------------------------------------------*/
uip_ds6_route_t *
uip_ds6_route_lookup(uip_ipaddr_t *addr)
{
#if (UIP_CONF_MAX_ROUTES != 0)
uip_ds6_route_t *r;
uip_ds6_route_t *found_route;
uint8_t longestmatch;
@ -261,12 +300,16 @@ uip_ds6_route_lookup(uip_ipaddr_t *addr)
}
return found_route;
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
}
/*---------------------------------------------------------------------------*/
uip_ds6_route_t *
uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
uip_ipaddr_t *nexthop)
{
#if (UIP_CONF_MAX_ROUTES != 0)
uip_ds6_route_t *r;
struct uip_ds6_route_neighbor_route *nbrr;
@ -307,11 +350,16 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
least recently used one we have. */
if(uip_ds6_route_num_routes() == UIP_DS6_ROUTE_NB) {
uip_ds6_route_t *oldest;
oldest = NULL;
#if UIP_DS6_ROUTE_REMOVE_LEAST_RECENTLY_USED
/* Removing the oldest route entry from the route table. The
least recently used route is the first route on the list. */
uip_ds6_route_t *oldest;
oldest = list_tail(routelist); /* uip_ds6_route_head(); */
oldest = list_tail(routelist);
#endif
if(oldest == NULL) {
return NULL;
}
PRINTF("uip_ds6_route_add: dropping route to ");
PRINT6ADDR(&oldest->ipaddr);
PRINTF("\n");
@ -337,7 +385,8 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
initialize this pointer with the list of routing entries that
are attached to this neighbor. */
routes = nbr_table_add_lladdr(nbr_routes,
(linkaddr_t *)nexthop_lladdr);
(linkaddr_t *)nexthop_lladdr,
NBR_TABLE_REASON_ROUTE, NULL);
if(routes == NULL) {
/* This should not happen, as we explicitly deallocated one
route table entry above. */
@ -407,12 +456,17 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
assert_nbr_routes_list_sane();
#endif /* DEBUG != DEBUG_NONE */
return r;
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
}
/*---------------------------------------------------------------------------*/
void
uip_ds6_route_rm(uip_ds6_route_t *route)
{
#if (UIP_CONF_MAX_ROUTES != 0)
struct uip_ds6_route_neighbor_route *neighbor_route;
#if DEBUG != DEBUG_NONE
assert_nbr_routes_list_sane();
@ -469,8 +523,11 @@ uip_ds6_route_rm(uip_ds6_route_t *route)
#if DEBUG != DEBUG_NONE
assert_nbr_routes_list_sane();
#endif /* DEBUG != DEBUG_NONE */
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
return;
}
#if (UIP_CONF_MAX_ROUTES != 0)
/*---------------------------------------------------------------------------*/
static void
rm_routelist(struct uip_ds6_route_neighbor_routes *routes)
@ -498,10 +555,12 @@ rm_routelist_callback(nbr_table_item_t *ptr)
{
rm_routelist((struct uip_ds6_route_neighbor_routes *)ptr);
}
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
/*---------------------------------------------------------------------------*/
void
uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop)
{
#if (UIP_CONF_MAX_ROUTES != 0)
/* Get routing entry list of this neighbor */
const uip_lladdr_t *nexthop_lladdr;
struct uip_ds6_route_neighbor_routes *routes;
@ -510,6 +569,7 @@ uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop)
routes = nbr_table_get_from_lladdr(nbr_routes,
(linkaddr_t *)nexthop_lladdr);
rm_routelist(routes);
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
}
/*---------------------------------------------------------------------------*/
uip_ds6_defrt_t *

View File

@ -50,7 +50,7 @@ NBR_TABLE_DECLARE(nbr_routes);
void uip_ds6_route_init(void);
#ifndef UIP_CONF_UIP_DS6_NOTIFICATIONS
#define UIP_DS6_NOTIFICATIONS 1
#define UIP_DS6_NOTIFICATIONS (UIP_CONF_MAX_ROUTES != 0)
#else
#define UIP_DS6_NOTIFICATIONS UIP_CONF_UIP_DS6_NOTIFICATIONS
#endif
@ -97,11 +97,48 @@ void uip_ds6_notification_rm(struct uip_ds6_notification *n);
#ifndef UIP_DS6_ROUTE_STATE_TYPE
#define UIP_DS6_ROUTE_STATE_TYPE rpl_route_entry_t
/* Needed for the extended route entry state when using ContikiRPL */
#define RPL_ROUTE_ENTRY_NOPATH_RECEIVED 0x01
#define RPL_ROUTE_ENTRY_DAO_PENDING 0x02
#define RPL_ROUTE_ENTRY_DAO_NACK 0x04
#define RPL_ROUTE_IS_NOPATH_RECEIVED(route) \
(((route)->state.state_flags & RPL_ROUTE_ENTRY_NOPATH_RECEIVED) != 0)
#define RPL_ROUTE_SET_NOPATH_RECEIVED(route) do { \
(route)->state.state_flags |= RPL_ROUTE_ENTRY_NOPATH_RECEIVED; \
} while(0)
#define RPL_ROUTE_CLEAR_NOPATH_RECEIVED(route) do { \
(route)->state.state_flags &= ~RPL_ROUTE_ENTRY_NOPATH_RECEIVED; \
} while(0)
#define RPL_ROUTE_IS_DAO_PENDING(route) \
((route->state.state_flags & RPL_ROUTE_ENTRY_DAO_PENDING) != 0)
#define RPL_ROUTE_SET_DAO_PENDING(route) do { \
(route)->state.state_flags |= RPL_ROUTE_ENTRY_DAO_PENDING; \
} while(0)
#define RPL_ROUTE_CLEAR_DAO_PENDING(route) do { \
(route)->state.state_flags &= ~RPL_ROUTE_ENTRY_DAO_PENDING; \
} while(0)
#define RPL_ROUTE_IS_DAO_NACKED(route) \
((route->state.state_flags & RPL_ROUTE_ENTRY_DAO_NACK) != 0)
#define RPL_ROUTE_SET_DAO_NACKED(route) do { \
(route)->state.state_flags |= RPL_ROUTE_ENTRY_DAO_NACK; \
} while(0)
#define RPL_ROUTE_CLEAR_DAO_NACKED(route) do { \
(route)->state.state_flags &= ~RPL_ROUTE_ENTRY_DAO_NACK; \
} while(0)
#define RPL_ROUTE_CLEAR_DAO(route) do { \
(route)->state.state_flags &= ~(RPL_ROUTE_ENTRY_DAO_NACK|RPL_ROUTE_ENTRY_DAO_PENDING); \
} while(0)
struct rpl_dag;
typedef struct rpl_route_entry {
uint32_t lifetime;
void *dag;
uint8_t learned_from;
uint8_t nopath_received;
struct rpl_dag *dag;
uint8_t dao_seqno_out;
uint8_t dao_seqno_in;
uint8_t state_flags;
} rpl_route_entry_t;
#endif /* UIP_DS6_ROUTE_STATE_TYPE */
@ -166,7 +203,7 @@ uip_ipaddr_t *uip_ds6_route_nexthop(uip_ds6_route_t *);
int uip_ds6_route_num_routes(void);
uip_ds6_route_t *uip_ds6_route_head(void);
uip_ds6_route_t *uip_ds6_route_next(uip_ds6_route_t *);
int uip_ds6_route_is_nexthop(const uip_ipaddr_t *ipaddr);
/** @} */
#endif /* UIP_DS6_ROUTE_H */

View File

@ -186,7 +186,9 @@ uip_ds6_periodic(void)
}
#endif /* !UIP_CONF_ROUTER */
#if UIP_ND6_SEND_NA
uip_ds6_neighbor_periodic();
#endif /* UIP_ND6_SEND_RA */
#if UIP_CONF_ROUTER && UIP_ND6_SEND_RA
/* Periodic RA sending */

View File

@ -67,6 +67,36 @@
#endif
#define UIP_DS6_DEFRT_NB UIP_DS6_DEFRT_NBS + UIP_DS6_DEFRT_NBU
/* Default prefix */
#ifdef UIP_CONF_DS6_DEFAULT_PREFIX
#define UIP_DS6_DEFAULT_PREFIX UIP_CONF_DS6_DEFAULT_PREFIX
#else
/* From RFC4193, section 3.1:
* | 7 bits |1| 40 bits | 16 bits | 64 bits |
* +--------+-+------------+-----------+----------------------------+
* | Prefix |L| Global ID | Subnet ID | Interface ID |
* +--------+-+------------+-----------+----------------------------+
* Prefix FC00::/7 prefix to identify Local IPv6 unicast
* addresses.
* L Set to 1 if the prefix is locally assigned.
* Set to 0 may be defined in the future. See
* Section 3.2 for additional information.
* Global ID 40-bit global identifier used to create a
* globally unique prefix. See Section 3.2 for
* additional information.
*
* We set prefix to 0xfc00 and set the local bit, resulting in 0xfd00.
* For high probability of network uniqueness, Global ID must be generated
* pseudo-randomly. As this is a hard-coded default prefix, we simply use
* a Global ID of 0. For real deployments, make sure to install a pseudo-random
* Global ID, e.g. in a RPL network, by configuring it at the root.
*/
#define UIP_DS6_DEFAULT_PREFIX 0xfd00
#endif /* UIP_CONF_DS6_DEFAULT_PREFIX */
#define UIP_DS6_DEFAULT_PREFIX_0 ((UIP_DS6_DEFAULT_PREFIX >> 8) & 0xff)
#define UIP_DS6_DEFAULT_PREFIX_1 (UIP_DS6_DEFAULT_PREFIX & 0xff)
/* Prefix list */
#define UIP_DS6_PREFIX_NBS 1
#ifndef UIP_CONF_DS6_PREFIX_NBU

View File

@ -38,7 +38,7 @@
/**
* \file
* ICMPv6 (RFC 4443) implementation, with message and error handling
* \author Julien Abeille <jabeille@cisco.com>
* \author Julien Abeille <jabeille@cisco.com>
* \author Mathilde Durvy <mdurvy@cisco.com>
*/
@ -120,9 +120,6 @@ uip_icmp6_register_input_handler(uip_icmp6_input_handler_t *handler)
static void
echo_request_input(void)
{
#if UIP_CONF_IPV6_RPL
uint8_t temp_ext_len;
#endif /* UIP_CONF_IPV6_RPL */
/*
* we send an echo reply. It is trivial if there was no extension
* headers in the request otherwise we need to remove the extension
@ -147,44 +144,27 @@ echo_request_input(void)
}
if(uip_ext_len > 0) {
#if UIP_CONF_IPV6_RPL
if((temp_ext_len = rpl_invert_header())) {
/* If there were other extension headers*/
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
if (uip_ext_len != temp_ext_len) {
uip_len -= (uip_ext_len - temp_ext_len);
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
/* move the echo request payload (starting after the icmp header)
* to the new location in the reply.
* The shift is equal to the length of the remaining extension headers present
* Note: UIP_ICMP_BUF still points to the echo request at this stage
*/
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
}
uip_ext_len = temp_ext_len;
} else {
#endif /* UIP_CONF_IPV6_RPL */
/* If there were extension headers*/
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
uip_len -= uip_ext_len;
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
/* move the echo request payload (starting after the icmp header)
* to the new location in the reply.
* The shift is equal to the length of the extension headers present
* Note: UIP_ICMP_BUF still points to the echo request at this stage
*/
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
uip_ext_len = 0;
#if UIP_CONF_IPV6_RPL
}
#endif /* UIP_CONF_IPV6_RPL */
/* Remove extension headers if any */
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
uip_len -= uip_ext_len;
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
/* move the echo request payload (starting after the icmp header)
* to the new location in the reply.
* The shift is equal to the length of the extension headers present
* Note: UIP_ICMP_BUF still points to the echo request at this stage
*/
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
uip_ext_len = 0;
}
/* Insert RPL extension headers */
#if UIP_CONF_IPV6_RPL
rpl_insert_header();
#endif /* UIP_CONF_IPV6_RPL */
/* Below is important for the correctness of UIP_ICMP_BUF and the
* checksum
*/
@ -206,23 +186,22 @@ echo_request_input(void)
/*---------------------------------------------------------------------------*/
void
uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
/* check if originating packet is not an ICMP error*/
if (uip_ext_len) {
if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
/* check if originating packet is not an ICMP error */
if(uip_ext_len) {
if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
uip_clear_buf();
return;
}
} else {
if(UIP_IP_BUF->proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
if(UIP_IP_BUF->proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
uip_clear_buf();
return;
}
}
#if UIP_CONF_IPV6_RPL
uip_ext_len = rpl_invert_header();
#else /* UIP_CONF_IPV6_RPL */
rpl_remove_header();
#else
uip_ext_len = 0;
#endif /* UIP_CONF_IPV6_RPL */
@ -231,8 +210,9 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
uip_len += UIP_IPICMPH_LEN + UIP_ICMP6_ERROR_LEN;
if(uip_len > UIP_LINK_MTU)
if(uip_len > UIP_LINK_MTU) {
uip_len = UIP_LINK_MTU;
}
memmove((uint8_t *)UIP_ICMP6_ERROR_BUF + uip_ext_len + UIP_ICMP6_ERROR_LEN,
(void *)UIP_IP_BUF, uip_len - UIP_IPICMPH_LEN - uip_ext_len - UIP_ICMP6_ERROR_LEN);
@ -280,6 +260,10 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
UIP_ICMP_BUF->icmpchksum = 0;
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
#if UIP_CONF_IPV6_RPL
rpl_insert_header();
#endif /* UIP_CONF_IPV6_RPL */
UIP_STAT(++uip_stat.icmp.sent);
PRINTF("Sending ICMPv6 ERROR message type %d code %d to ", type, code);
@ -313,6 +297,13 @@ uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
UIP_STAT(++uip_stat.icmp.sent);
UIP_STAT(++uip_stat.ip.sent);
#if UIP_CONF_IPV6_RPL
rpl_insert_header();
#endif /* UIP_CONF_IPV6_RPL */
tcpip_ipv6_output();
}
/*---------------------------------------------------------------------------*/
@ -321,53 +312,31 @@ echo_reply_input(void)
{
int ttl;
uip_ipaddr_t sender;
#if UIP_CONF_IPV6_RPL
uint8_t temp_ext_len;
#endif /* UIP_CONF_IPV6_RPL */
PRINTF("Received Echo Reply from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(" to ");
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
PRINTF("\n");
uip_ipaddr_copy(&sender, &UIP_IP_BUF->srcipaddr);
ttl = UIP_IP_BUF->ttl;
if(uip_ext_len > 0) {
#if UIP_CONF_IPV6_RPL
if((temp_ext_len = rpl_invert_header())) {
/* If there were other extension headers*/
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
if (uip_ext_len != temp_ext_len) {
uip_len -= (uip_ext_len - temp_ext_len);
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
/* move the echo reply payload (starting after the icmp
* header) to the new location in the reply. The shift is
* equal to the length of the remaining extension headers
* present Note: UIP_ICMP_BUF still points to the echo reply
* at this stage
*/
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
}
uip_ext_len = temp_ext_len;
uip_len -= uip_ext_len;
} else {
#endif /* UIP_CONF_IPV6_RPL */
/* If there were extension headers*/
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
uip_len -= uip_ext_len;
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
/* move the echo reply payload (starting after the icmp header)
* to the new location in the reply. The shift is equal to the
* length of the extension headers present Note: UIP_ICMP_BUF
* still points to the echo request at this stage
*/
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
uip_ext_len = 0;
#if UIP_CONF_IPV6_RPL
}
#endif /* UIP_CONF_IPV6_RPL */
/* Remove extension headers if any */
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
uip_len -= uip_ext_len;
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
/* move the echo reply payload (starting after the icmp header)
* to the new location in the reply. The shift is equal to the
* length of the extension headers present Note: UIP_ICMP_BUF
* still points to the echo request at this stage
*/
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
uip_ext_len = 0;
}
/* Call all registered applications to let them know an echo reply

View File

@ -135,11 +135,13 @@ static uip_ds6_prefix_t *prefix; /** Pointer to a prefix list entry */
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
/*------------------------------------------------------------------*/
/* Copy link-layer address from LLAO option to a word-aligned uip_lladdr_t */
static void
extract_lladdr_aligned(uip_lladdr_t *dest) {
static int
extract_lladdr_from_llao_aligned(uip_lladdr_t *dest) {
if(dest != NULL && nd6_opt_llao != NULL) {
memcpy(dest, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN);
return 1;
}
return 0;
}
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
/*------------------------------------------------------------------*/
@ -199,16 +201,23 @@ ns_input(void)
goto discard;
} else {
#endif /*UIP_CONF_IPV6_CHECKS */
uip_lladdr_t lladdr_aligned;
extract_lladdr_from_llao_aligned(&lladdr_aligned);
nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
if(nbr == NULL) {
uip_lladdr_t lladdr_aligned;
extract_lladdr_aligned(&lladdr_aligned);
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
} else {
uip_lladdr_t *lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
if(lladdr == NULL) {
goto discard;
}
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
lladdr, UIP_LLADDR_LEN) != 0) {
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN);
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
/* failed to update the lladdr */
goto discard;
}
nbr->state = NBR_STALE;
} else {
if(nbr->state == NBR_INCOMPLETE) {
@ -427,6 +436,7 @@ na_input(void)
uint8_t is_router;
uint8_t is_solicited;
uint8_t is_override;
uip_lladdr_t lladdr_aligned;
PRINTF("Received NA from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
@ -489,23 +499,29 @@ na_input(void)
PRINTF("NA received is bad\n");
goto discard;
} else {
uip_lladdr_t *lladdr;
const uip_lladdr_t *lladdr;
nbr = uip_ds6_nbr_lookup(&UIP_ND6_NA_BUF->tgtipaddr);
lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
if(nbr == NULL) {
goto discard;
}
if(nd6_opt_llao != 0) {
lladdr = uip_ds6_nbr_get_ll(nbr);
if(lladdr == NULL) {
goto discard;
}
if(nd6_opt_llao != NULL) {
is_llchange =
memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], (void *)lladdr,
memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], lladdr,
UIP_LLADDR_LEN);
}
if(nbr->state == NBR_INCOMPLETE) {
if(nd6_opt_llao == NULL) {
if(nd6_opt_llao == NULL || !extract_lladdr_from_llao_aligned(&lladdr_aligned)) {
goto discard;
}
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
UIP_LLADDR_LEN);
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
/* failed to update the lladdr */
goto discard;
}
if(is_solicited) {
nbr->state = NBR_REACHABLE;
nbr->nscount = 0;
@ -517,27 +533,29 @@ na_input(void)
nbr->state = NBR_STALE;
}
nbr->isrouter = is_router;
} else {
} else { /* NBR is not INCOMPLETE */
if(!is_override && is_llchange) {
if(nbr->state == NBR_REACHABLE) {
nbr->state = NBR_STALE;
}
goto discard;
} else {
if(is_override || (!is_override && nd6_opt_llao != 0 && !is_llchange)
|| nd6_opt_llao == 0) {
if(nd6_opt_llao != 0) {
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
UIP_LLADDR_LEN);
/**
* If this is an cache override, or same lladdr, or no llao -
* do updates of nbr states.
*/
if(is_override || !is_llchange || nd6_opt_llao == NULL) {
if(nd6_opt_llao != NULL && is_llchange) {
if(!extract_lladdr_from_llao_aligned(&lladdr_aligned) ||
nbr_table_update_lladdr((const linkaddr_t *) lladdr, (const linkaddr_t *) &lladdr_aligned, 1) == 0) {
/* failed to update the lladdr */
goto discard;
}
}
if(is_solicited) {
nbr->state = NBR_REACHABLE;
/* reachable time is stored in ms */
stimer_set(&(nbr->reachable), uip_ds6_if.reachable_time / 1000);
} else {
if(nd6_opt_llao != 0 && is_llchange) {
nbr->state = NBR_STALE;
}
}
}
}
@ -631,17 +649,23 @@ rs_input(void)
} else {
#endif /*UIP_CONF_IPV6_CHECKS */
uip_lladdr_t lladdr_aligned;
extract_lladdr_aligned(&lladdr_aligned);
extract_lladdr_from_llao_aligned(&lladdr_aligned);
if((nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr)) == NULL) {
/* we need to add the neighbor */
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
} else {
/* If LL address changed, set neighbor state to stale */
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
if(lladdr == NULL) {
goto discard;
}
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
uip_ds6_nbr_get_ll(nbr), UIP_LLADDR_LEN) != 0) {
lladdr, UIP_LLADDR_LEN) != 0) {
uip_ds6_nbr_t nbr_data = *nbr;
uip_ds6_nbr_rm(nbr);
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
nbr->reachable = nbr_data.reachable;
nbr->sendns = nbr_data.sendns;
nbr->nscount = nbr_data.nscount;
@ -823,6 +847,8 @@ uip_nd6_rs_output(void)
void
ra_input(void)
{
uip_lladdr_t lladdr_aligned;
PRINTF("Received RA from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(" to ");
@ -867,19 +893,28 @@ ra_input(void)
PRINTF("Processing SLLAO option in RA\n");
nd6_opt_llao = (uint8_t *) UIP_ND6_OPT_HDR_BUF;
nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
if(!extract_lladdr_from_llao_aligned(&lladdr_aligned)) {
/* failed to extract llao - discard packet */
goto discard;
}
if(nbr == NULL) {
uip_lladdr_t lladdr_aligned;
extract_lladdr_aligned(&lladdr_aligned);
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 1, NBR_STALE);
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
1, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
} else {
uip_lladdr_t *lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
if(lladdr == NULL) {
goto discard;
}
if(nbr->state == NBR_INCOMPLETE) {
nbr->state = NBR_STALE;
}
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
lladdr, UIP_LLADDR_LEN) != 0) {
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
UIP_LLADDR_LEN);
/* change of link layer address */
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
/* failed to update the lladdr */
goto discard;
}
nbr->state = NBR_STALE;
}
nbr->isrouter = 1;

File diff suppressed because it is too large Load Diff

211
core/net/link-stats.c Normal file
View File

@ -0,0 +1,211 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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.
*
*
* Authors: Simon Duquennoy <simonduq@sics.se>
*/
#include "contiki.h"
#include "sys/clock.h"
#include "net/packetbuf.h"
#include "net/nbr-table.h"
#include "net/link-stats.h"
#include <stdio.h>
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/* Half time for the freshness counter, in minutes */
#define FRESHNESS_HALF_LIFE 20
/* Statistics are fresh if the freshness counter is FRESHNESS_TARGET or more */
#define FRESHNESS_TARGET 4
/* Maximum value for the freshness counter */
#define FRESHNESS_MAX 16
/* Statistics with no update in FRESHNESS_EXPIRATION_TIMEOUT is not fresh */
#define FRESHNESS_EXPIRATION_TIME (10 * 60 * CLOCK_SECOND)
/* EWMA (exponential moving average) used to maintain statistics over time */
#define EWMA_SCALE 100
#define EWMA_ALPHA 15
#define EWMA_BOOTSTRAP_ALPHA 30
/* ETX fixed point divisor. 128 is the value used by RPL (RFC 6551 and RFC 6719) */
#define ETX_DIVISOR LINK_STATS_ETX_DIVISOR
/* Number of Tx used to update the ETX EWMA in case of no-ACK */
#define ETX_NOACK_PENALTY 10
/* Initial ETX value */
#define ETX_INIT 2
/* Per-neighbor link statistics table */
NBR_TABLE(struct link_stats, link_stats);
/* Called every FRESHNESS_HALF_LIFE minutes */
struct ctimer periodic_timer;
/* Used to initialize ETX before any transmission occurs. In order to
* infer the initial ETX from the RSSI of previously received packets, use: */
/* #define LINK_STATS_CONF_INIT_ETX(stats) guess_etx_from_rssi(stats) */
#ifdef LINK_STATS_CONF_INIT_ETX
#define LINK_STATS_INIT_ETX(stats) LINK_STATS_CONF_INIT_ETX(stats)
#else /* LINK_STATS_INIT_ETX */
#define LINK_STATS_INIT_ETX(stats) (ETX_INIT * ETX_DIVISOR)
#endif /* LINK_STATS_INIT_ETX */
/*---------------------------------------------------------------------------*/
/* Returns the neighbor's link stats */
const struct link_stats *
link_stats_from_lladdr(const linkaddr_t *lladdr)
{
return nbr_table_get_from_lladdr(link_stats, lladdr);
}
/*---------------------------------------------------------------------------*/
/* Are the statistics fresh? */
int
link_stats_is_fresh(const struct link_stats *stats)
{
return (stats != NULL)
&& clock_time() - stats->last_tx_time < FRESHNESS_EXPIRATION_TIME
&& stats->freshness >= FRESHNESS_TARGET;
}
/*---------------------------------------------------------------------------*/
uint16_t
guess_etx_from_rssi(const struct link_stats *stats)
{
if(stats != NULL) {
if(stats->rssi == 0) {
return ETX_INIT * ETX_DIVISOR;
} else {
/* A rough estimate of PRR from RSSI, as a linear function where:
* RSSI >= -60 results in PRR of 1
* RSSI <= -90 results in PRR of 0
* prr = (bounded_rssi - RSSI_LOW) / (RSSI_DIFF)
* etx = ETX_DIVOSOR / ((bounded_rssi - RSSI_LOW) / RSSI_DIFF)
* etx = (RSSI_DIFF * ETX_DIVOSOR) / (bounded_rssi - RSSI_LOW)
* */
#define ETX_INIT_MAX 3
#define RSSI_HIGH -60
#define RSSI_LOW -90
#define RSSI_DIFF (RSSI_HIGH - RSSI_LOW)
uint16_t etx;
int16_t bounded_rssi = stats->rssi;
bounded_rssi = MIN(bounded_rssi, RSSI_HIGH);
bounded_rssi = MAX(bounded_rssi, RSSI_LOW + 1);
etx = RSSI_DIFF * ETX_DIVISOR / (bounded_rssi - RSSI_LOW);
return MIN(etx, ETX_INIT_MAX * ETX_DIVISOR);
}
}
return 0xffff;
}
/*---------------------------------------------------------------------------*/
/* Packet sent callback. Updates stats for transmissions to lladdr */
void
link_stats_packet_sent(const linkaddr_t *lladdr, int status, int numtx)
{
struct link_stats *stats;
uint16_t packet_etx;
uint8_t ewma_alpha;
if(status != MAC_TX_OK && status != MAC_TX_NOACK) {
/* Do not penalize the ETX when collisions or transmission errors occur. */
return;
}
stats = nbr_table_get_from_lladdr(link_stats, lladdr);
if(stats == NULL) {
/* Add the neighbor */
stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
if(stats != NULL) {
stats->etx = LINK_STATS_INIT_ETX(stats);
} else {
return; /* No space left, return */
}
}
/* Update last timestamp and freshness */
stats->last_tx_time = clock_time();
stats->freshness = MIN(stats->freshness + numtx, FRESHNESS_MAX);
/* ETX used for this update */
packet_etx = ((status == MAC_TX_NOACK) ? ETX_NOACK_PENALTY : numtx) * ETX_DIVISOR;
/* ETX alpha used for this update */
ewma_alpha = link_stats_is_fresh(stats) ? EWMA_ALPHA : EWMA_BOOTSTRAP_ALPHA;
/* Compute EWMA and update ETX */
stats->etx = ((uint32_t)stats->etx * (EWMA_SCALE - ewma_alpha) +
(uint32_t)packet_etx * ewma_alpha) / EWMA_SCALE;
}
/*---------------------------------------------------------------------------*/
/* Packet input callback. Updates statistics for receptions on a given link */
void
link_stats_input_callback(const linkaddr_t *lladdr)
{
struct link_stats *stats;
int16_t packet_rssi = packetbuf_attr(PACKETBUF_ATTR_RSSI);
stats = nbr_table_get_from_lladdr(link_stats, lladdr);
if(stats == NULL) {
/* Add the neighbor */
stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
if(stats != NULL) {
/* Initialize */
stats->rssi = packet_rssi;
stats->etx = LINK_STATS_INIT_ETX(stats);
}
return;
}
/* Update RSSI EWMA */
stats->rssi = ((int32_t)stats->rssi * (EWMA_SCALE - EWMA_ALPHA) +
(int32_t)packet_rssi * EWMA_ALPHA) / EWMA_SCALE;
}
/*---------------------------------------------------------------------------*/
/* Periodic timer called every FRESHNESS_HALF_LIFE minutes */
static void
periodic(void *ptr)
{
/* Age (by halving) freshness counter of all neighbors */
struct link_stats *stats;
ctimer_reset(&periodic_timer);
for(stats = nbr_table_head(link_stats); stats != NULL; stats = nbr_table_next(link_stats, stats)) {
stats->freshness >>= 1;
}
}
/*---------------------------------------------------------------------------*/
/* Initializes link-stats module */
void
link_stats_init(void)
{
nbr_table_register(link_stats, NULL);
ctimer_set(&periodic_timer, 60 * CLOCK_SECOND * FRESHNESS_HALF_LIFE,
periodic, NULL);
}

65
core/net/link-stats.h Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* 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.
*
*
* Authors: Simon Duquennoy <simonduq@sics.se>
*/
#ifndef LINK_STATS_H_
#define LINK_STATS_H_
#include "core/net/linkaddr.h"
/* ETX fixed point divisor. 128 is the value used by RPL (RFC 6551 and RFC 6719) */
#ifdef LINK_STATS_CONF_ETX_DIVISOR
#define LINK_STATS_ETX_DIVISOR LINK_STATS_CONF_ETX_DIVISOR
#else /* LINK_STATS_CONF_ETX_DIVISOR */
#define LINK_STATS_ETX_DIVISOR 128
#endif /* LINK_STATS_CONF_ETX_DIVISOR */
/* All statistics of a given link */
struct link_stats {
uint16_t etx; /* ETX using ETX_DIVISOR as fixed point divisor */
int16_t rssi; /* RSSI (received signal strength) */
uint8_t freshness; /* Freshness of the statistics */
clock_time_t last_tx_time; /* Last Tx timestamp */
};
/* Returns the neighbor's link statistics */
const struct link_stats *link_stats_from_lladdr(const linkaddr_t *lladdr);
/* Are the statistics fresh? */
int link_stats_is_fresh(const struct link_stats *stats);
/* Initializes link-stats module */
void link_stats_init(void);
/* Packet sent callback. Updates statistics for transmissions on a given link */
void link_stats_packet_sent(const linkaddr_t *lladdr, int status, int numtx);
/* Packet input callback. Updates statistics for receptions on a given link */
void link_stats_input_callback(const linkaddr_t *lladdr);
#endif /* LINK_STATS_H_ */

5
core/net/llsec/README.md Normal file
View File

@ -0,0 +1,5 @@
Link layer security is implemented as a new netstack layer, which is located in between the MAC and the NETWORK layer. The interface of LLSEC drivers is defined in [llsec.h](llsec.h). Additionally, LLSEC drivers may define a special [FRAMER](../mac/framer.h), which calls the actual FRAMER underneath. By default, the LLSEC driver `nullsec` is used, which does nothing.
# TODO
* Most main files do not call LLSEC.init, yet

View File

@ -45,6 +45,9 @@
#include "net/llsec/anti-replay.h"
#include "net/packetbuf.h"
#include "net/llsec/llsec802154.h"
#if LLSEC802154_USES_FRAME_COUNTER
/* This node's current frame counter value */
static uint32_t counter;
@ -107,5 +110,6 @@ anti_replay_was_replayed(struct anti_replay_info *info)
}
}
/*---------------------------------------------------------------------------*/
#endif /* LLSEC802154_USES_FRAME_COUNTER */
/** @} */

View File

@ -41,8 +41,11 @@
#include "llsec/ccm-star-packetbuf.h"
#include "net/linkaddr.h"
#include "net/packetbuf.h"
#include "net/llsec/llsec802154.h"
#include <string.h>
#if LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_FRAME_COUNTER
/*---------------------------------------------------------------------------*/
static const uint8_t *
get_extended_address(const linkaddr_t *addr)
@ -76,3 +79,4 @@ ccm_star_packetbuf_set_nonce(uint8_t *nonce, int forward)
nonce[12] = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL);
}
/*---------------------------------------------------------------------------*/
#endif /* LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_FRAME_COUNTER */

View File

@ -56,28 +56,13 @@
#include "net/mac/frame802154.h"
#include "net/ip/uip.h"
#ifdef LLSEC802154_CONF_SECURITY_LEVEL
#define LLSEC802154_SECURITY_LEVEL LLSEC802154_CONF_SECURITY_LEVEL
#else /* LLSEC802154_CONF_SECURITY_LEVEL */
#define LLSEC802154_SECURITY_LEVEL FRAME802154_SECURITY_LEVEL_NONE
#endif /* LLSEC802154_CONF_SECURITY_LEVEL */
#ifdef LLSEC802154_CONF_ENABLED
#define LLSEC802154_ENABLED LLSEC802154_CONF_ENABLED
#else /* LLSEC802154_CONF_ENABLED */
#define LLSEC802154_ENABLED 0
#endif /* LLSEC802154_CONF_ENABLED */
#if ((LLSEC802154_SECURITY_LEVEL < 0) || (LLSEC802154_SECURITY_LEVEL > 7))
#error "unsupported security level"
#endif
#define LLSEC802154_SECURITY_LEVEL_MIC (LLSEC802154_SECURITY_LEVEL & 3)
#if LLSEC802154_SECURITY_LEVEL_MIC
#define LLSEC802154_MIC_LENGTH (2 << LLSEC802154_SECURITY_LEVEL_MIC)
#else
#define LLSEC802154_MIC_LENGTH 0
#endif
#ifdef LLSEC802154_CONF_USES_ENCRYPTION
#define LLSEC802154_USES_ENCRYPTION LLSEC802154_CONF_USES_ENCRYPTION
#else /* LLSEC802154_CONF_USES_ENCRYPTION */
#define LLSEC802154_USES_ENCRYPTION (LLSEC802154_SECURITY_LEVEL & (1 << 2))
#endif /* LLSEC802154_CONF_USES_ENCRYPTION */
#define LLSEC802154_MIC_LEN(sec_lvl) (2 << (sec_lvl & 3))
#ifdef LLSEC802154_CONF_USES_EXPLICIT_KEYS
#define LLSEC802154_USES_EXPLICIT_KEYS LLSEC802154_CONF_USES_EXPLICIT_KEYS
@ -88,9 +73,15 @@
#ifdef LLSEC802154_CONF_USES_FRAME_COUNTER
#define LLSEC802154_USES_FRAME_COUNTER LLSEC802154_CONF_USES_FRAME_COUNTER
#else /* LLSEC802154_CONF_USES_FRAME_COUNTER */
#define LLSEC802154_USES_FRAME_COUNTER (LLSEC802154_SECURITY_LEVEL != FRAME802154_SECURITY_LEVEL_NONE)
#define LLSEC802154_USES_FRAME_COUNTER LLSEC802154_ENABLED
#endif /* LLSEC802154_CONF_USES_FRAME_COUNTER */
#ifdef LLSEC802154_CONF_USES_AUX_HEADER
#define LLSEC802154_USES_AUX_HEADER LLSEC802154_CONF_USES_AUX_HEADER
#else /* LLSEC802154_CONF_USES_AUX_HEADER */
#define LLSEC802154_USES_AUX_HEADER LLSEC802154_ENABLED
#endif /* LLSEC802154_CONF_USES_AUX_HEADER */
#if UIP_BYTE_ORDER == UIP_LITTLE_ENDIAN
#define LLSEC802154_HTONS(n) (n)
#define LLSEC802154_HTONL(n) (n)

View File

@ -0,0 +1,21 @@
`noncoresec` is a noncompromise-resilient 802.15.4 security implementation, which uses a network-wide key. Add these lines to your `project_conf.h` to enable `noncoresec`:
```c
#undef LLSEC802154_CONF_ENABLED
#define LLSEC802154_CONF_ENABLED 1
#undef NETSTACK_CONF_FRAMER
#define NETSTACK_CONF_FRAMER noncoresec_framer
#undef NETSTACK_CONF_LLSEC
#define NETSTACK_CONF_LLSEC noncoresec_driver
#undef NONCORESEC_CONF_SEC_LVL
#define NONCORESEC_CONF_SEC_LVL 1
```
`NONCORESEC_CONF_SEC_LVL` defines the length of MICs and whether encryption is enabled or not.
Setting the network-wide key works as follows:
```c
#define NONCORESEC_CONF_KEY { 0x00 , 0x01 , 0x02 , 0x03 , \
0x04 , 0x05 , 0x06 , 0x07 , \
0x08 , 0x09 , 0x0A , 0x0B , \
0x0C , 0x0D , 0x0E , 0x0F }
```

View File

@ -47,7 +47,6 @@
#include "net/llsec/llsec802154.h"
#include "net/llsec/ccm-star-packetbuf.h"
#include "net/mac/frame802154.h"
#include "net/mac/framer-802154.h"
#include "net/netstack.h"
#include "net/packetbuf.h"
#include "net/nbr-table.h"
@ -55,7 +54,22 @@
#include "lib/ccm-star.h"
#include <string.h>
#define WITH_ENCRYPTION (LLSEC802154_SECURITY_LEVEL & (1 << 2))
#ifdef NONCORESEC_CONF_DECORATED_FRAMER
#define DECORATED_FRAMER NONCORESEC_CONF_DECORATED_FRAMER
#else /* NONCORESEC_CONF_DECORATED_FRAMER */
#define DECORATED_FRAMER framer_802154
#endif /* NONCORESEC_CONF_DECORATED_FRAMER */
extern const struct framer DECORATED_FRAMER;
#ifdef NONCORESEC_CONF_SEC_LVL
#define SEC_LVL NONCORESEC_CONF_SEC_LVL
#else /* NONCORESEC_CONF_SEC_LVL */
#define SEC_LVL 2
#endif /* NONCORESEC_CONF_SEC_LVL */
#define WITH_ENCRYPTION (SEC_LVL & (1 << 2))
#define MIC_LEN LLSEC802154_MIC_LEN(SEC_LVL)
#ifdef NONCORESEC_CONF_KEY
#define NONCORESEC_KEY NONCORESEC_CONF_KEY
@ -76,6 +90,8 @@
#define PRINTF(...)
#endif /* DEBUG */
#if LLSEC802154_USES_AUX_HEADER && SEC_LVL && LLSEC802154_USES_FRAME_COUNTER
/* network-wide CCM* key */
static uint8_t key[16] = NONCORESEC_KEY;
NBR_TABLE(struct anti_replay_info, anti_replay_table);
@ -91,7 +107,7 @@ aead(uint8_t hdrlen, int forward)
uint8_t *a;
uint8_t a_len;
uint8_t *result;
uint8_t generated_mic[LLSEC802154_MIC_LENGTH];
uint8_t generated_mic[MIC_LEN];
uint8_t *mic;
ccm_star_packetbuf_set_nonce(nonce, forward);
@ -113,30 +129,29 @@ aead(uint8_t hdrlen, int forward)
CCM_STAR.aead(nonce,
m, m_len,
a, a_len,
result, LLSEC802154_MIC_LENGTH,
result, MIC_LEN,
forward);
if(forward) {
packetbuf_set_datalen(packetbuf_datalen() + LLSEC802154_MIC_LENGTH);
packetbuf_set_datalen(packetbuf_datalen() + MIC_LEN);
return 1;
} else {
return (memcmp(generated_mic, mic, LLSEC802154_MIC_LENGTH) == 0);
return (memcmp(generated_mic, mic, MIC_LEN) == 0);
}
}
/*---------------------------------------------------------------------------*/
static void
add_security_header(void)
{
if(!packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) {
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_DATAFRAME);
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, LLSEC802154_SECURITY_LEVEL);
anti_replay_set_counter();
}
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_DATAFRAME);
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, SEC_LVL);
}
/*---------------------------------------------------------------------------*/
static void
send(mac_callback_t sent, void *ptr)
{
add_security_header();
anti_replay_set_counter();
NETSTACK_MAC.send(sent, ptr);
}
/*---------------------------------------------------------------------------*/
@ -145,8 +160,7 @@ create(void)
{
int result;
add_security_header();
result = framer_802154.create();
result = DECORATED_FRAMER.create();
if(result == FRAMER_FAILED) {
return result;
}
@ -163,12 +177,12 @@ parse(void)
const linkaddr_t *sender;
struct anti_replay_info* info;
result = framer_802154.parse();
result = DECORATED_FRAMER.parse();
if(result == FRAMER_FAILED) {
return result;
}
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) != LLSEC802154_SECURITY_LEVEL) {
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) != SEC_LVL) {
PRINTF("noncoresec: received frame with wrong security level\n");
return FRAMER_FAILED;
}
@ -178,17 +192,17 @@ parse(void)
return FRAMER_FAILED;
}
packetbuf_set_datalen(packetbuf_datalen() - LLSEC802154_MIC_LENGTH);
packetbuf_set_datalen(packetbuf_datalen() - MIC_LEN);
if(!aead(result, 0)) {
PRINTF("noncoresec: received unauthentic frame %"PRIu32"\n",
PRINTF("noncoresec: received unauthentic frame %lu\n",
anti_replay_get_counter());
return FRAMER_FAILED;
}
info = nbr_table_get_from_lladdr(anti_replay_table, sender);
if(!info) {
info = nbr_table_add_lladdr(anti_replay_table, sender);
info = nbr_table_add_lladdr(anti_replay_table, sender, NBR_TABLE_REASON_LLSEC, NULL);
if(!info) {
PRINTF("noncoresec: could not get nbr_table_item\n");
return FRAMER_FAILED;
@ -214,7 +228,7 @@ parse(void)
anti_replay_init_info(info);
} else {
if(anti_replay_was_replayed(info)) {
PRINTF("noncoresec: received replayed frame %"PRIu32"\n",
PRINTF("noncoresec: received replayed frame %lu\n",
anti_replay_get_counter());
return FRAMER_FAILED;
}
@ -233,7 +247,7 @@ static int
length(void)
{
add_security_header();
return framer_802154.length() + LLSEC802154_MIC_LENGTH;
return DECORATED_FRAMER.length() + MIC_LEN;
}
/*---------------------------------------------------------------------------*/
static void
@ -256,5 +270,6 @@ const struct framer noncoresec_framer = {
parse
};
/*---------------------------------------------------------------------------*/
#endif /* LLSEC802154_USES_AUX_HEADER && SEC_LVL && LLSEC802154_USES_FRAME_COUNTER */
/** @} */

View File

@ -63,26 +63,35 @@
#define PRINTF(...)
#endif /* DEBUG */
#ifndef CSMA_MAX_BACKOFF_EXPONENT
#ifdef CSMA_CONF_MAX_BACKOFF_EXPONENT
#define CSMA_MAX_BACKOFF_EXPONENT CSMA_CONF_MAX_BACKOFF_EXPONENT
#else
#define CSMA_MAX_BACKOFF_EXPONENT 3
#endif /* CSMA_CONF_MAX_BACKOFF_EXPONENT */
#endif /* CSMA_MAX_BACKOFF_EXPONENT */
/* Constants of the IEEE 802.15.4 standard */
#ifndef CSMA_MAX_MAC_TRANSMISSIONS
#ifdef CSMA_CONF_MAX_MAC_TRANSMISSIONS
#define CSMA_MAX_MAC_TRANSMISSIONS CSMA_CONF_MAX_MAC_TRANSMISSIONS
/* macMinBE: Initial backoff exponent. Range 0--CSMA_MAX_BE */
#ifdef CSMA_CONF_MIN_BE
#define CSMA_MIN_BE CSMA_CONF_MIN_BE
#else
#define CSMA_MAX_MAC_TRANSMISSIONS 3
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS */
#endif /* CSMA_MAX_MAC_TRANSMISSIONS */
#define CSMA_MIN_BE 0
#endif
#if CSMA_MAX_MAC_TRANSMISSIONS < 1
#error CSMA_CONF_MAX_MAC_TRANSMISSIONS must be at least 1.
#error Change CSMA_CONF_MAX_MAC_TRANSMISSIONS in contiki-conf.h or in your Makefile.
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS < 1 */
/* macMaxBE: Maximum backoff exponent. Range 3--8 */
#ifdef CSMA_CONF_MAX_BE
#define CSMA_MAX_BE CSMA_CONF_MAX_BE
#else
#define CSMA_MAX_BE 4
#endif
/* macMaxCSMABackoffs: Maximum number of backoffs in case of channel busy/collision. Range 0--5 */
#ifdef CSMA_CONF_MAX_BACKOFF
#define CSMA_MAX_BACKOFF CSMA_CONF_MAX_BACKOFF
#else
#define CSMA_MAX_BACKOFF 5
#endif
/* macMaxFrameRetries: Maximum number of re-transmissions attampts. Range 0--7 */
#ifdef CSMA_CONF_MAX_FRAME_RETRIES
#define CSMA_MAX_MAX_FRAME_RETRIES CSMA_CONF_MAX_FRAME_RETRIES
#else
#define CSMA_MAX_MAX_FRAME_RETRIES 7
#endif
/* Packet metadata */
struct qbuf_metadata {
@ -97,7 +106,7 @@ struct neighbor_queue {
linkaddr_t addr;
struct ctimer transmit_timer;
uint8_t transmissions;
uint8_t collisions, deferrals;
uint8_t collisions;
LIST_STRUCT(queued_packet_list);
};
@ -123,7 +132,6 @@ LIST(neighbor_list);
static void packet_sent(void *ptr, int status, int num_transmissions);
static void transmit_packet_list(void *ptr);
/*---------------------------------------------------------------------------*/
static struct neighbor_queue *
neighbor_queue_from_addr(const linkaddr_t *addr)
@ -139,18 +147,18 @@ neighbor_queue_from_addr(const linkaddr_t *addr)
}
/*---------------------------------------------------------------------------*/
static clock_time_t
default_timebase(void)
backoff_period(void)
{
clock_time_t time;
/* The retransmission time must be proportional to the channel
check interval of the underlying radio duty cycling layer. */
time = NETSTACK_RDC.channel_check_interval();
/* If the radio duty cycle has no channel check interval (i.e., it
does not turn the radio off), we make the retransmission time
proportional to the configured MAC channel check rate. */
/* If the radio duty cycle has no channel check interval, we use
* the default in IEEE 802.15.4: aUnitBackoffPeriod which is
* 20 symbols i.e. 320 usec. That is, 1/3125 second. */
if(time == 0) {
time = CLOCK_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE;
time = MAX(CLOCK_SECOND / 3125, 1);
}
return time;
}
@ -171,10 +179,28 @@ transmit_packet_list(void *ptr)
}
/*---------------------------------------------------------------------------*/
static void
schedule_transmission(struct neighbor_queue *n)
{
clock_time_t delay;
int backoff_exponent; /* BE in IEEE 802.15.4 */
backoff_exponent = MIN(n->collisions, CSMA_MAX_BE);
/* Compute max delay as per IEEE 802.15.4: 2^BE-1 backoff periods */
delay = ((1 << backoff_exponent) - 1) * backoff_period();
if(delay > 0) {
/* Pick a time for next transmission */
delay = random_rand() % delay;
}
PRINTF("csma: scheduling transmission in %u ticks, NB=%u, BE=%u\n",
(unsigned)delay, n->collisions, backoff_exponent);
ctimer_set(&n->transmit_timer, delay, transmit_packet_list, n);
}
/*---------------------------------------------------------------------------*/
static void
free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
{
clock_time_t tx_delay;
if(p != NULL) {
/* Remove packet from list and deallocate */
list_remove(n->queued_packet_list, p);
@ -187,11 +213,9 @@ free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
if(list_head(n->queued_packet_list) != NULL) {
/* There is a next packet. We reset current tx information */
n->transmissions = 0;
n->collisions = 0;
n->deferrals = 0;
/* Set a timer for next transmissions */
tx_delay = (status == MAC_TX_OK) ? 0 : default_timebase();
ctimer_set(&n->transmit_timer, tx_delay, transmit_packet_list, n);
n->collisions = CSMA_MIN_BE;
/* Schedule next transmissions */
schedule_transmission(n);
} else {
/* This was the last packet in the queue, we free the neighbor */
ctimer_stop(&n->transmit_timer);
@ -202,34 +226,103 @@ free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
}
/*---------------------------------------------------------------------------*/
static void
tx_done(int status, struct rdc_buf_list *q, struct neighbor_queue *n)
{
mac_callback_t sent;
struct qbuf_metadata *metadata;
void *cptr;
metadata = (struct qbuf_metadata *)q->ptr;
sent = metadata->sent;
cptr = metadata->cptr;
switch(status) {
case MAC_TX_OK:
PRINTF("csma: rexmit ok %d\n", n->transmissions);
break;
case MAC_TX_COLLISION:
case MAC_TX_NOACK:
PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n",
status, n->transmissions, n->collisions);
break;
default:
PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status);
break;
}
free_packet(n, q, status);
mac_call_sent_callback(sent, cptr, status, n->transmissions);
}
/*---------------------------------------------------------------------------*/
static void
rexmit(struct rdc_buf_list *q, struct neighbor_queue *n)
{
schedule_transmission(n);
/* This is needed to correctly attribute energy that we spent
transmitting this packet. */
queuebuf_update_attr_from_packetbuf(q->buf);
}
/*---------------------------------------------------------------------------*/
static void
collision(struct rdc_buf_list *q, struct neighbor_queue *n,
int num_transmissions)
{
struct qbuf_metadata *metadata;
metadata = (struct qbuf_metadata *)q->ptr;
n->collisions += num_transmissions;
if(n->collisions > CSMA_MAX_BACKOFF) {
n->collisions = CSMA_MIN_BE;
/* Increment to indicate a next retry */
n->transmissions++;
}
if(n->transmissions >= metadata->max_transmissions) {
tx_done(MAC_TX_COLLISION, q, n);
} else {
PRINTF("csma: rexmit collision %d\n", n->transmissions);
rexmit(q, n);
}
}
/*---------------------------------------------------------------------------*/
static void
noack(struct rdc_buf_list *q, struct neighbor_queue *n, int num_transmissions)
{
struct qbuf_metadata *metadata;
metadata = (struct qbuf_metadata *)q->ptr;
n->collisions = CSMA_MIN_BE;
n->transmissions += num_transmissions;
if(n->transmissions >= metadata->max_transmissions) {
tx_done(MAC_TX_NOACK, q, n);
} else {
PRINTF("csma: rexmit noack %d\n", n->transmissions);
rexmit(q, n);
}
}
/*---------------------------------------------------------------------------*/
static void
tx_ok(struct rdc_buf_list *q, struct neighbor_queue *n, int num_transmissions)
{
n->collisions = CSMA_MIN_BE;
n->transmissions += num_transmissions;
tx_done(MAC_TX_OK, q, n);
}
/*---------------------------------------------------------------------------*/
static void
packet_sent(void *ptr, int status, int num_transmissions)
{
struct neighbor_queue *n;
struct rdc_buf_list *q;
struct qbuf_metadata *metadata;
clock_time_t time = 0;
mac_callback_t sent;
void *cptr;
int num_tx;
int backoff_exponent;
int backoff_transmissions;
n = ptr;
if(n == NULL) {
return;
}
switch(status) {
case MAC_TX_OK:
case MAC_TX_NOACK:
n->transmissions += num_transmissions;
break;
case MAC_TX_COLLISION:
n->collisions += num_transmissions;
break;
case MAC_TX_DEFERRED:
n->deferrals += num_transmissions;
break;
}
/* Find out what packet this callback refers to */
for(q = list_head(n->queued_packet_list);
@ -240,78 +333,30 @@ packet_sent(void *ptr, int status, int num_transmissions)
}
}
if(q != NULL) {
metadata = (struct qbuf_metadata *)q->ptr;
if(q == NULL) {
PRINTF("csma: seqno %d not found\n",
packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
return;
} else if(q->ptr == NULL) {
PRINTF("csma: no metadata\n");
return;
}
if(metadata != NULL) {
sent = metadata->sent;
cptr = metadata->cptr;
num_tx = n->transmissions;
if(status == MAC_TX_COLLISION ||
status == MAC_TX_NOACK) {
/* If the transmission was not performed because of a
collision or noack, we must retransmit the packet. */
switch(status) {
case MAC_TX_COLLISION:
PRINTF("csma: rexmit collision %d\n", n->transmissions);
break;
case MAC_TX_NOACK:
PRINTF("csma: rexmit noack %d\n", n->transmissions);
break;
default:
PRINTF("csma: rexmit err %d, %d\n", status, n->transmissions);
}
/* The retransmission time must be proportional to the channel
check interval of the underlying radio duty cycling layer. */
time = default_timebase();
/* The retransmission time uses a truncated exponential backoff
* so that the interval between the transmissions increase with
* each retransmit. */
backoff_exponent = num_tx;
/* Truncate the exponent if needed. */
if(backoff_exponent > CSMA_MAX_BACKOFF_EXPONENT) {
backoff_exponent = CSMA_MAX_BACKOFF_EXPONENT;
}
/* Proceed to exponentiation. */
backoff_transmissions = 1 << backoff_exponent;
/* Pick a time for next transmission, within the interval:
* [time, time + 2^backoff_exponent * time[ */
time = time + (random_rand() % (backoff_transmissions * time));
if(n->transmissions < metadata->max_transmissions) {
PRINTF("csma: retransmitting with time %lu %p\n", time, q);
ctimer_set(&n->transmit_timer, time,
transmit_packet_list, n);
/* This is needed to correctly attribute energy that we spent
transmitting this packet. */
queuebuf_update_attr_from_packetbuf(q->buf);
} else {
PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n",
status, n->transmissions, n->collisions);
free_packet(n, q, status);
mac_call_sent_callback(sent, cptr, status, num_tx);
}
} else {
if(status == MAC_TX_OK) {
PRINTF("csma: rexmit ok %d\n", n->transmissions);
} else {
PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status);
}
free_packet(n, q, status);
mac_call_sent_callback(sent, cptr, status, num_tx);
}
} else {
PRINTF("csma: no metadata\n");
}
} else {
PRINTF("csma: seqno %d not found\n", packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
switch(status) {
case MAC_TX_OK:
tx_ok(q, n, num_transmissions);
break;
case MAC_TX_NOACK:
noack(q, n, num_transmissions);
break;
case MAC_TX_COLLISION:
collision(q, n, num_transmissions);
break;
case MAC_TX_DEFERRED:
break;
default:
tx_done(status, q, n);
break;
}
}
/*---------------------------------------------------------------------------*/
@ -346,8 +391,7 @@ send_packet(mac_callback_t sent, void *ptr)
/* Init neighbor entry */
linkaddr_copy(&n->addr, addr);
n->transmissions = 0;
n->collisions = 0;
n->deferrals = 0;
n->collisions = CSMA_MIN_BE;
/* Init packet list for this neighbor */
LIST_STRUCT_INIT(n, queued_packet_list);
/* Add neighbor to the list */
@ -368,7 +412,7 @@ send_packet(mac_callback_t sent, void *ptr)
/* Neighbor and packet successfully allocated */
if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) {
/* Use default configuration for max transmissions */
metadata->max_transmissions = CSMA_MAX_MAC_TRANSMISSIONS;
metadata->max_transmissions = CSMA_MAX_MAX_FRAME_RETRIES + 1;
} else {
metadata->max_transmissions =
packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS);
@ -389,7 +433,7 @@ send_packet(mac_callback_t sent, void *ptr)
list_length(n->queued_packet_list), memb_numfree(&packet_memb));
/* If q is the first packet in the neighbor's queue, send asap */
if(list_head(n->queued_packet_list) == q) {
ctimer_set(&n->transmit_timer, 0, transmit_packet_list, n);
schedule_transmission(n);
}
return;
}

View File

@ -102,7 +102,7 @@ addr_len(uint8_t mode)
}
}
/*----------------------------------------------------------------------------*/
#if LLSEC802154_USES_EXPLICIT_KEYS
#if LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_EXPLICIT_KEYS
static uint8_t
get_key_id_len(uint8_t key_id_mode)
{
@ -117,7 +117,7 @@ get_key_id_len(uint8_t key_id_mode)
return 0;
}
}
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
#endif /* LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_EXPLICIT_KEYS */
/*---------------------------------------------------------------------------*/
/* Get current PAN ID */
uint16_t
@ -317,7 +317,7 @@ field_len(frame802154_t *p, field_length_t *flen)
flen->dest_addr_len = addr_len(p->fcf.dest_addr_mode & 3);
flen->src_addr_len = addr_len(p->fcf.src_addr_mode & 3);
#if LLSEC802154_SECURITY_LEVEL
#if LLSEC802154_USES_AUX_HEADER
/* Aux security header */
if(p->fcf.security_enabled & 1) {
flen->aux_sec_len = 1; /* FCF + possibly frame counter and key ID */
@ -333,7 +333,7 @@ field_len(frame802154_t *p, field_length_t *flen)
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
;
}
#endif /* LLSEC802154_SECURITY_LEVEL */
#endif /* LLSEC802154_USES_AUX_HEADER */
}
/*----------------------------------------------------------------------------*/
/**
@ -418,7 +418,7 @@ frame802154_create(frame802154_t *p, uint8_t *buf)
for(c = flen.src_addr_len; c > 0; c--) {
buf[pos++] = p->src_addr[c - 1];
}
#if LLSEC802154_SECURITY_LEVEL
#if LLSEC802154_USES_AUX_HEADER
/* Aux header */
if(flen.aux_sec_len) {
buf[pos++] = p->aux_hdr.security_control.security_level
@ -447,7 +447,7 @@ frame802154_create(frame802154_t *p, uint8_t *buf)
}
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
}
#endif /* LLSEC802154_SECURITY_LEVEL */
#endif /* LLSEC802154_USES_AUX_HEADER */
return (int)pos;
}
@ -570,7 +570,7 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
pf->src_pid = 0;
}
#if LLSEC802154_SECURITY_LEVEL
#if LLSEC802154_USES_AUX_HEADER
if(fcf.security_enabled) {
pf->aux_hdr.security_control.security_level = p[0] & 7;
#if LLSEC802154_USES_EXPLICIT_KEYS
@ -599,7 +599,7 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
}
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
}
#endif /* LLSEC802154_SECURITY_LEVEL */
#endif /* LLSEC802154_USES_AUX_HEADER */
/* header length */
c = p - data;

View File

@ -41,7 +41,7 @@
#include "net/mac/frame802154e-ie.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/* c.f. IEEE 802.15.4e Table 4b */
enum ieee802154e_header_ie_id {

View File

@ -98,7 +98,7 @@ create_frame(int type, int do_create)
/* Insert IEEE 802.15.4 version bits. */
params.fcf.frame_version = FRAME802154_VERSION;
#if LLSEC802154_SECURITY_LEVEL
#if LLSEC802154_USES_AUX_HEADER
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) {
params.fcf.security_enabled = 1;
}
@ -116,7 +116,7 @@ create_frame(int type, int do_create)
params.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
params.aux_hdr.key_source.u16[0] = packetbuf_attr(PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1);
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
#endif /* LLSEC802154_SECURITY_LEVEL */
#endif /* LLSEC802154_USES_AUX_HEADER */
/* Increment and set the data sequence number. */
if(!do_create) {
@ -238,7 +238,7 @@ parse(void)
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, frame.seq);
#endif
#if LLSEC802154_SECURITY_LEVEL
#if LLSEC802154_USES_AUX_HEADER
if(frame.fcf.security_enabled) {
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, frame.aux_hdr.security_control.security_level);
#if LLSEC802154_USES_FRAME_COUNTER
@ -251,7 +251,7 @@ parse(void)
packetbuf_set_attr(PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1, frame.aux_hdr.key_source.u16[0]);
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
}
#endif /* LLSEC802154_SECURITY_LEVEL */
#endif /* LLSEC802154_USES_AUX_HEADER */
PRINTF("15.4-IN: %2X", frame.fcf.frame_type);
PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));

View File

@ -122,7 +122,7 @@ phase_update(const linkaddr_t *neighbor, rtimer_clock_t time,
} else {
/* No matching phase was found, so we allocate a new one. */
if(mac_status == MAC_TX_OK && e == NULL) {
e = nbr_table_add_lladdr(nbr_phase, neighbor);
e = nbr_table_add_lladdr(nbr_phase, neighbor, NBR_TABLE_REASON_MAC, NULL);
if(e) {
e->time = time;
#if PHASE_DRIFT_CORRECT

View File

@ -43,6 +43,7 @@
#include "contiki-conf.h"
#include "net/mac/mac.h"
#include "net/llsec/llsec802154.h"
#ifdef RDC_CONF_WITH_DUPLICATE_DETECTION
#define RDC_WITH_DUPLICATE_DETECTION RDC_CONF_WITH_DUPLICATE_DETECTION
@ -51,7 +52,7 @@
frame because it has seen its sequence number already. Replay
protection should be implemented at the LLSEC layer where the
authenticity of frames is verified. */
#define RDC_WITH_DUPLICATE_DETECTION !LLSEC802154_CONF_SECURITY_LEVEL
#define RDC_WITH_DUPLICATE_DETECTION !LLSEC802154_ENABLED
#endif /* RDC_CONF_WITH_DUPLICATE_DETECTION */
/* List of packets to be sent by RDC layer */

View File

@ -36,6 +36,9 @@ It has been tested on the following platforms:
* NXP JN516x (`jn516x`, tested on hardware)
* Tmote Sky (`sky`, tested on hardware and in cooja)
* Zolertia Z1 (`z1`, tested in cooja only)
* CC2538DK (`cc2538dk`, tested on hardware)
* Zolertia Zoul (`zoul`, tested on hardware)
* CC2650 (`srf06-cc26xx`, tested on hardware)
This implementation was present at the ETSI Plugtest
event in Prague in July 2015, and did successfully inter-operate with all
@ -76,7 +79,7 @@ Orchestra is implemented in:
A simple TSCH+RPL example is included under `examples/ipv6/rpl-tsch`.
To use TSCH, first make sure your platform supports it.
Currently, `jn516x`, `sky` and `z1` are the supported platforms.
Currently, `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul` and `srf06-cc26xx` are the supported platforms.
To add your own, we refer the reader to the next section.
To add TSCH to your application, first include the TSCH module from your makefile with:
@ -128,8 +131,8 @@ To configure TSCH, see the macros in `.h` files under `core/net/mac/tsch/` and r
To include TSCH standard-compliant security, set the following:
```
/* Enable security */
#undef LLSEC802154_CONF_SECURITY_LEVEL
#define LLSEC802154_CONF_SECURITY_LEVEL 1
#undef LLSEC802154_CONF_ENABLED
#define LLSEC802154_CONF_ENABLED 1
/* TSCH uses explicit keys to identify k1 and k2 */
#undef LLSEC802154_CONF_USES_EXPLICIT_KEYS
#define LLSEC802154_CONF_USES_EXPLICIT_KEYS 1
@ -162,7 +165,7 @@ Finally, one can also implement his own scheduler, centralized or distributed, b
## Porting TSCH to a new platform
Porting TSCH to a new platform requires a few new features in the radio driver, a number of timing-related configuration paramters.
The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`.
The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul`, `srf06-cc26xx`.
### Radio features required for TSCH

View File

@ -86,12 +86,16 @@ timesync_entry_add(int32_t val, uint32_t time_delta)
static void
timesync_learn_drift_ticks(uint32_t time_delta_asn, int32_t drift_ticks)
{
/* should fit in 32-bit unsigned integer */
uint32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length];
/* should fit in a 32-bit integer */
int32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length];
int32_t real_drift_ticks = drift_ticks + compensated_ticks;
int32_t last_drift_ppm = (int32_t)((int64_t)real_drift_ticks * TSCH_DRIFT_UNIT / time_delta_ticks);
drift_ppm = timesync_entry_add(last_drift_ppm, time_delta_ticks);
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"drift %ld", drift_ppm / 256));
}
/*---------------------------------------------------------------------------*/
/* Either reset or update the neighbor's drift */

View File

@ -53,6 +53,8 @@
#define TSCH_HOPPING_SEQUENCE_4_16 (uint8_t[]){ 20, 26, 25, 26, 15, 15, 25, 20, 26, 15, 26, 25, 20, 15, 20, 25 }
/* 4 channels, sequence length 4 */
#define TSCH_HOPPING_SEQUENCE_4_4 (uint8_t[]){ 15, 25, 26, 20 }
/* 2 channels, sequence length 2 */
#define TSCH_HOPPING_SEQUENCE_2_2 (uint8_t[]){ 20, 25 }
/* 1 channel, sequence length 1 */
#define TSCH_HOPPING_SEQUENCE_1_1 (uint8_t[]){ 20 }
@ -120,7 +122,7 @@
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 10000
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 15000
/* Default timeslot timing for platfroms requiring 15ms slots */
/* Default timeslot timing for platforms requiring 15ms slots */
#define TSCH_DEFAULT_TS_CCA_OFFSET 1800
#define TSCH_DEFAULT_TS_CCA 128
@ -135,7 +137,7 @@
#define TSCH_DEFAULT_TS_MAX_TX 4256
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 15000
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 65000
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 65000U
/* 65ms timeslot, i.e. nearly the max length allowed by standard (16-bit unsigned in micro-seconds).
* Useful for running link-layer security on sky or z1 in Cooja, where only S/W security is supported.
* Note: this slot timing would require a total of 120ms. If a slot overlaps with the next active slot,
@ -175,4 +177,25 @@
#define TSCH_ADAPTIVE_TIMESYNC 0
#endif
/* HW frame filtering enabled */
#ifdef TSCH_CONF_HW_FRAME_FILTERING
#define TSCH_HW_FRAME_FILTERING TSCH_CONF_HW_FRAME_FILTERING
#else /* TSCH_CONF_HW_FRAME_FILTERING */
#define TSCH_HW_FRAME_FILTERING 1
#endif /* TSCH_CONF_HW_FRAME_FILTERING */
/* Keep radio always on within TSCH timeslot (1) or turn it off between packet and ACK? (0) */
#ifdef TSCH_CONF_RADIO_ON_DURING_TIMESLOT
#define TSCH_RADIO_ON_DURING_TIMESLOT TSCH_CONF_RADIO_ON_DURING_TIMESLOT
#else
#define TSCH_RADIO_ON_DURING_TIMESLOT 0
#endif
/* How long to scan each channel in the scanning phase */
#ifdef TSCH_CONF_CHANNEL_SCAN_DURATION
#define TSCH_CHANNEL_SCAN_DURATION TSCH_CONF_CHANNEL_SCAN_DURATION
#else
#define TSCH_CHANNEL_SCAN_DURATION CLOCK_SECOND
#endif
#endif /* __TSCH_CONF_H__ */

View File

@ -56,7 +56,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
#if TSCH_LOG_LEVEL >= 2 /* Skip this file for log levels 0 or 1 */
@ -84,11 +84,15 @@ tsch_log_process_pending(void)
}
while((log_index = ringbufindex_peek_get(&log_ringbuf)) != -1) {
struct tsch_log_t *log = &log_array[log_index];
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
log->asn.ms1b, log->asn.ls4b,
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
tsch_calculate_channel(&log->asn, log->link->channel_offset));
if(log->link == NULL) {
printf("TSCH: {asn-%x.%lx link-NULL} ", log->asn.ms1b, log->asn.ls4b);
} else {
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
log->asn.ms1b, log->asn.ls4b,
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
tsch_calculate_channel(&log->asn, log->link->channel_offset));
}
switch(log->type) {
case tsch_log_tx:
printf("%s-%u-%u %u tx %d, st %d-%d",
@ -103,7 +107,7 @@ tsch_log_process_pending(void)
break;
case tsch_log_rx:
printf("%s-%u-%u %u rx %d",
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->tx.sec_level,
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->rx.sec_level,
log->rx.datalen,
log->rx.src);
if(log->rx.drift_used) {

View File

@ -60,7 +60,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/*---------------------------------------------------------------------------*/
/* Construct enhanced ACK packet and return ACK length */
@ -94,7 +94,7 @@ tsch_packet_create_eack(uint8_t *buf, int buf_size,
p.src_pid = IEEE802154_PANID;
linkaddr_copy((linkaddr_t *)&p.src_addr, &linkaddr_node_addr);
#endif
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(tsch_is_pan_secured) {
p.fcf.security_enabled = 1;
p.aux_hdr.security_control.security_level = TSCH_SECURITY_KEY_SEC_LEVEL_ACK;
@ -103,7 +103,7 @@ tsch_packet_create_eack(uint8_t *buf, int buf_size,
p.aux_hdr.security_control.frame_counter_size = 1;
p.aux_hdr.key_index = TSCH_SECURITY_KEY_INDEX_ACK;
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
if((curr_len = frame802154_create(&p, buf)) == 0) {
return 0;
@ -166,13 +166,13 @@ tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
if(frame->fcf.ie_list_present) {
int mic_len = 0;
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
/* Check if there is space for the security MIC (if any) */
mic_len = tsch_security_mic_len(frame);
if(buf_size < curr_len + mic_len) {
return 0;
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
return 0;
@ -222,7 +222,7 @@ tsch_packet_create_eb(uint8_t *buf, int buf_size, uint8_t seqno,
p.dest_addr[0] = 0xff;
p.dest_addr[1] = 0xff;
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(tsch_is_pan_secured) {
p.fcf.security_enabled = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) > 0;
p.aux_hdr.security_control.security_level = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL);
@ -231,7 +231,7 @@ tsch_packet_create_eb(uint8_t *buf, int buf_size, uint8_t seqno,
p.aux_hdr.security_control.frame_counter_size = 1;
p.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
if((curr_len = frame802154_create(&p, buf)) == 0) {
return 0;
@ -387,14 +387,14 @@ tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
if(frame->fcf.ie_list_present) {
/* Calculate space needed for the security MIC, if any, before attempting to parse IEs */
int mic_len = 0;
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(!frame_without_mic) {
mic_len = tsch_security_mic_len(frame);
if(buf_size < curr_len + mic_len) {
return 0;
}
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {

View File

@ -36,6 +36,7 @@
/********** Includes **********/
#include "contiki.h"
#include "net/packetbuf.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/frame802154.h"
#include "net/mac/frame802154e-ie.h"
@ -81,7 +82,7 @@ by default, useful in case of duplicate seqno */
/********** Constants *********/
/* Max TSCH packet lenght */
#define TSCH_PACKET_MAX_LEN 127
#define TSCH_PACKET_MAX_LEN MIN(127,PACKETBUF_SIZE)
/********** Functions *********/

View File

@ -61,7 +61,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/* Check if TSCH_QUEUE_NUM_PER_NEIGHBOR is power of two */
#if (TSCH_QUEUE_NUM_PER_NEIGHBOR & (TSCH_QUEUE_NUM_PER_NEIGHBOR - 1)) != 0
@ -302,14 +302,17 @@ tsch_queue_free_packet(struct tsch_packet *p)
/*---------------------------------------------------------------------------*/
/* Flush all neighbor queues */
void
tsch_queue_flush_all(void)
tsch_queue_reset(void)
{
/* Deallocate unneeded neighbors */
if(!tsch_is_locked()) {
struct tsch_neighbor *n = list_head(neighbor_list);
while(n != NULL) {
struct tsch_neighbor *next_n = list_item_next(n);
/* Flush queue */
tsch_queue_flush_nbr_queue(n);
/* Reset backoff exponent */
tsch_queue_backoff_reset(n);
n = next_n;
}
}
@ -379,6 +382,7 @@ tsch_queue_get_packet_for_dest_addr(const linkaddr_t *addr, struct tsch_link *li
}
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Returns the head packet of any neighbor queue with zero backoff counter.
* Writes pointer to the neighbor in *n */
struct tsch_packet *

View File

@ -39,6 +39,7 @@
#include "lib/ringbufindex.h"
#include "net/linkaddr.h"
#include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/mac.h"
/******** Configuration *******/
@ -166,8 +167,8 @@ int tsch_queue_packet_count(const linkaddr_t *addr);
struct tsch_packet *tsch_queue_remove_packet_from_queue(struct tsch_neighbor *n);
/* Free a packet */
void tsch_queue_free_packet(struct tsch_packet *p);
/* Flush all neighbor queues */
void tsch_queue_flush_all(void);
/* Reset neighbor queues */
void tsch_queue_reset(void);
/* Deallocate neighbors with empty queue */
void tsch_queue_free_unused_neighbors(void);
/* Is the neighbor queue empty? */

View File

@ -51,7 +51,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/*---------------------------------------------------------------------------*/
/* To use, set #define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network */
@ -73,7 +73,7 @@ tsch_rpl_callback_leaving_network(void)
}
/*---------------------------------------------------------------------------*/
/* Set TSCH EB period based on current RPL DIO period.
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_new_dio_interval */
* To use, set #define RPL_CALLBACK_NEW_DIO_INTERVAL tsch_rpl_callback_new_dio_interval */
void
tsch_rpl_callback_new_dio_interval(uint8_t dio_interval)
{

View File

@ -60,7 +60,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/* Pre-allocated space for links */
MEMB(link_memb, struct tsch_link, TSCH_SCHEDULE_MAX_LINKS);
@ -191,6 +191,7 @@ tsch_schedule_add_link(struct tsch_slotframe *slotframe,
l = memb_alloc(&link_memb);
if(l == NULL) {
PRINTF("TSCH-schedule:! add_link memb_alloc failed\n");
tsch_release_lock();
} else {
static int current_link_handle = 0;
struct tsch_neighbor *n;

View File

@ -58,7 +58,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/* The two keys K1 and K2 from 6TiSCH minimal configuration
* K1: well-known, used for EBs

View File

@ -39,22 +39,23 @@
#include "net/mac/tsch/tsch-asn.h"
#include "net/mac/tsch/tsch-private.h"
#include "net/mac/frame802154.h"
#include "net/llsec/llsec802154.h"
#include "net/mac/frame802154e-ie.h"
/******** Configuration *******/
/* To enable TSCH security:
* - set LLSEC802154_CONF_SECURITY_LEVEL
* - set LLSEC802154_CONF_ENABLED
* - set LLSEC802154_CONF_USES_EXPLICIT_KEYS
* - unset LLSEC802154_CONF_USES_FRAME_COUNTER
* */
#define TSCH_SECURITY_ENABLED (LLSEC802154_CONF_SECURITY_LEVEL != 0)
#if TSCH_SECURITY_ENABLED && !LLSEC802154_CONF_USES_EXPLICIT_KEYS
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_EXPLICIT_KEYS unset
#endif /* TSCH_SECURITY_ENABLED */
#if TSCH_SECURITY_ENABLED && LLSEC802154_CONF_USES_FRAME_COUNTER
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_FRAME_COUNTER set
#endif /* TSCH_SECURITY_ENABLED */
#if LLSEC802154_ENABLED && !LLSEC802154_USES_EXPLICIT_KEYS
#error LLSEC802154_ENABLED set but LLSEC802154_USES_EXPLICIT_KEYS unset
#endif /* LLSEC802154_ENABLED */
#if LLSEC802154_ENABLED && LLSEC802154_USES_FRAME_COUNTER
#error LLSEC802154_ENABLED set but LLSEC802154_USES_FRAME_COUNTER set
#endif /* LLSEC802154_ENABLED */
/* K1, defined in 6TiSCH minimal, is well-known (offers no security) and used for EBs only */
#ifdef TSCH_SECURITY_CONF_K1

View File

@ -36,6 +36,7 @@
* \author
* Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se>
* Atis Elsts <atis.elsts@bristol.ac.uk>
*
*/
@ -59,7 +60,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/* TSCH debug macros, i.e. to set LEDs or GPIOs on various TSCH
* timeslot events */
@ -109,6 +110,18 @@
#define RTIMER_GUARD 2u
#endif
enum tsch_radio_state_on_cmd {
TSCH_RADIO_CMD_ON_START_OF_TIMESLOT,
TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT,
TSCH_RADIO_CMD_ON_FORCE,
};
enum tsch_radio_state_off_cmd {
TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT,
TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT,
TSCH_RADIO_CMD_OFF_FORCE,
};
/* A ringbuf storing outgoing packets after they were dequeued.
* Will be processed layer by tsch_tx_process_pending */
struct ringbufindex dequeued_ringbuf;
@ -370,6 +383,68 @@ update_neighbor_state(struct tsch_neighbor *n, struct tsch_packet *p,
return in_queue;
}
/*---------------------------------------------------------------------------*/
/**
* This function turns on the radio. Its semantics is dependent on
* the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
* - if enabled, the radio is turned on at the start of the slot
* - if disabled, the radio is turned on within the slot,
* directly before the packet Rx guard time and ACK Rx guard time.
*/
static void
tsch_radio_on(enum tsch_radio_state_on_cmd command)
{
int do_it = 0;
switch(command) {
case TSCH_RADIO_CMD_ON_START_OF_TIMESLOT:
if(TSCH_RADIO_ON_DURING_TIMESLOT) {
do_it = 1;
}
break;
case TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT:
if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
do_it = 1;
}
break;
case TSCH_RADIO_CMD_ON_FORCE:
do_it = 1;
break;
}
if(do_it) {
NETSTACK_RADIO.on();
}
}
/*---------------------------------------------------------------------------*/
/**
* This function turns off the radio. In the same way as for tsch_radio_on(),
* it depends on the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
* - if enabled, the radio is turned off at the end of the slot
* - if disabled, the radio is turned off within the slot,
* directly after Tx'ing or Rx'ing a packet or Tx'ing an ACK.
*/
static void
tsch_radio_off(enum tsch_radio_state_off_cmd command)
{
int do_it = 0;
switch(command) {
case TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT:
if(TSCH_RADIO_ON_DURING_TIMESLOT) {
do_it = 1;
}
break;
case TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT:
if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
do_it = 1;
}
break;
case TSCH_RADIO_CMD_OFF_FORCE:
do_it = 1;
break;
}
if(do_it) {
NETSTACK_RADIO.off();
}
}
/*---------------------------------------------------------------------------*/
static
PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
{
@ -404,10 +479,10 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
} else {
/* packet payload */
static void *packet;
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
/* encrypted payload */
static uint8_t encrypted_packet[TSCH_PACKET_MAX_LEN];
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
/* packet payload length */
static uint8_t packet_len;
/* packet seqno */
@ -434,7 +509,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
packet_ready = 1;
}
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(tsch_is_pan_secured) {
/* If we are going to encrypt, we need to generate the output in a separate buffer and keep
* the original untouched. This is to allow for future retransmissions. */
@ -445,7 +520,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
packet = encrypted_packet;
}
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
/* prepare packet to send: copy to radio buffer */
if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */
@ -456,7 +531,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
/* delay before CCA */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca");
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on();
tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
/* CCA */
BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()),
current_slot_start, TS_CCA_OFFSET + TS_CCA);
@ -480,7 +555,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
/* limit tx_time to its max value */
tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
/* turn tadio off -- will turn on again to wait for ACK if needed */
NETSTACK_RADIO.off();
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
if(mac_tx_status == RADIO_TX_OK) {
if(!is_broadcast) {
@ -488,35 +563,39 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
int ack_len;
rtimer_clock_t ack_start_time;
int is_time_source;
radio_value_t radio_rx_mode;
struct ieee802154_ies ack_ies;
uint8_t ack_hdrlen;
frame802154_t frame;
#if TSCH_HW_FRAME_FILTERING
radio_value_t radio_rx_mode;
/* Entering promiscuous mode so that the radio accepts the enhanced ACK */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER));
#endif /* TSCH_HW_FRAME_FILTERING */
/* Unicast: wait for ack after tx: sleep until ack time */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start,
tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on();
tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
/* Wait for ACK to come */
BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]);
TSCH_DEBUG_TX_EVENT();
ack_start_time = RTIMER_NOW();
ack_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
/* Wait for ACK to finish */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
ack_start_time, tsch_timing[tsch_ts_max_ack]);
TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.off();
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
#if TSCH_HW_FRAME_FILTERING
/* Leaving promiscuous mode */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
#endif /* TSCH_HW_FRAME_FILTERING */
/* Read ack frame */
ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf));
@ -530,7 +609,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
ack_len = 0;
}
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(ack_len != 0) {
if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
&frame, &current_neighbor->addr, &current_asn)) {
@ -544,7 +623,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
snprintf(log->message, sizeof(log->message),
"!failed to parse ACK"));
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
}
if(ack_len != 0) {
@ -584,6 +663,8 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
}
}
tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
current_packet->transmissions++;
current_packet->ret = mac_tx_status;
@ -604,7 +685,11 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
log->tx.drift = drift_correction;
log->tx.drift_used = is_drift_correction_used;
log->tx.is_data = ((((uint8_t *)(queuebuf_dataptr(current_packet->qb)))[0]) & 7) == FRAME802154_DATAFRAME;
#if LLSEC802154_ENABLED
log->tx.sec_level = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL);
#else /* LLSEC802154_ENABLED */
log->tx.sec_level = 0;
#endif /* LLSEC802154_ENABLED */
log->tx.dest = TSCH_LOG_ID_FROM_LINKADDR(queuebuf_addr(current_packet->qb, PACKETBUF_ADDR_RECEIVER));
);
@ -663,32 +748,26 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
TSCH_DEBUG_RX_EVENT();
/* Start radio for at least guard time */
NETSTACK_RADIO.on();
packet_seen = NETSTACK_RADIO.receiving_packet();
tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
packet_seen = NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet();
if(!packet_seen) {
/* Check if receiving within guard time */
BUSYWAIT_UNTIL_ABS((packet_seen = NETSTACK_RADIO.receiving_packet()),
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait]);
}
if(packet_seen) {
if(!packet_seen) {
/* no packets on air */
tsch_radio_off(TSCH_RADIO_CMD_OFF_FORCE);
} else {
TSCH_DEBUG_RX_EVENT();
/* Save packet timestamp */
rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
}
if(!NETSTACK_RADIO.receiving_packet() && !NETSTACK_RADIO.pending_packet()) {
NETSTACK_RADIO.off();
/* no packets on air */
} else {
/* Wait until packet is received, turn radio off */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]);
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.off();
#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
/* At the end of the reception, get an more accurate estimate of SFD arrival time */
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
#endif
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
if(NETSTACK_RADIO.pending_packet()) {
static int frame_valid;
@ -696,9 +775,9 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
static frame802154_t frame;
radio_value_t radio_last_rssi;
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
/* Read packet */
current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN);
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
current_input->rx_asn = current_asn;
current_input->rssi = (signed)radio_last_rssi;
header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame);
@ -706,9 +785,14 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
frame802154_check_dest_panid(&frame) &&
frame802154_extract_linkaddr(&frame, &source_address, &destination_address);
#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
/* At the end of the reception, get an more accurate estimate of SFD arrival time */
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
#endif
packet_duration = TSCH_PACKET_DURATION(current_input->len);
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
/* Decrypt and verify incoming frame */
if(frame_valid) {
if(tsch_security_parse_frame(
@ -727,17 +811,17 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
"!failed to parse frame %u %u", header_len, current_input->len));
frame_valid = 0;
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
if(frame_valid) {
if(linkaddr_cmp(&destination_address, &linkaddr_node_addr)
|| linkaddr_cmp(&destination_address, &linkaddr_null)) {
int do_nack = 0;
estimated_drift = ((int32_t)expected_rx_time - (int32_t)rx_start_time);
estimated_drift = RTIMER_CLOCK_DIFF(expected_rx_time, rx_start_time);
#if TSCH_TIMESYNC_REMOVE_JITTER
/* remove jitter due to measurement errors */
if(abs(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) {
if(ABS(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) {
estimated_drift = 0;
} else if(estimated_drift > 0) {
estimated_drift -= TSCH_TIMESYNC_MEASUREMENT_ERROR;
@ -761,12 +845,12 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
&source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(tsch_is_pan_secured) {
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, &current_asn);
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
/* Copy to radio buffer */
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
@ -776,6 +860,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.transmit(ack_len);
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
}
/* If the sender is a time source, proceed to clock drift compensation */
@ -805,18 +890,14 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
log->rx.sec_level = frame.aux_hdr.security_control.security_level;
log->rx.estimated_drift = estimated_drift;
);
} else {
TSCH_LOG_ADD(tsch_log_message,
snprintf(log->message, sizeof(log->message),
"!not for us %x:%x",
destination_address.u8[LINKADDR_SIZE - 2], destination_address.u8[LINKADDR_SIZE - 1]);
);
}
/* Poll process for processing of pending input and logs */
process_poll(&tsch_pending_events_process);
}
}
tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
}
if(input_queue_drop != 0) {
@ -857,8 +938,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
} else {
uint8_t current_channel;
int is_active_slot;
TSCH_DEBUG_SLOT_START();
tsch_in_slot_operation = 1;
/* Reset drift correction */
drift_correction = 0;
is_drift_correction_used = 0;
/* Get a packet ready to be sent */
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
/* There is no packet to send, and this link does not have Rx flag. Instead of doing
@ -867,26 +952,28 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
current_link = backup_link;
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
}
/* Hop channel */
current_channel = tsch_calculate_channel(&current_asn, current_link->channel_offset);
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
/* Reset drift correction */
drift_correction = 0;
is_drift_correction_used = 0;
/* Decide whether it is a TX/RX/IDLE or OFF slot */
/* Actual slot operation */
if(current_packet != NULL) {
/* We have something to transmit, do the following:
* 1. send
* 2. update_backoff_state(current_neighbor)
* 3. post tx callback
**/
static struct pt slot_tx_pt;
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
} else if((current_link->link_options & LINK_OPTION_RX)) {
/* Listen */
static struct pt slot_rx_pt;
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX);
if(is_active_slot) {
/* Hop channel */
current_channel = tsch_calculate_channel(&current_asn, current_link->channel_offset);
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
/* Turn the radio on already here if configured so; necessary for radios with slow startup */
tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
/* Decide whether it is a TX/RX/IDLE or OFF slot */
/* Actual slot operation */
if(current_packet != NULL) {
/* We have something to transmit, do the following:
* 1. send
* 2. update_backoff_state(current_neighbor)
* 3. post tx callback
**/
static struct pt slot_tx_pt;
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
} else {
/* Listen */
static struct pt slot_rx_pt;
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
}
}
TSCH_DEBUG_SLOT_END();
}

View File

@ -64,7 +64,7 @@
#else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */
#include "net/ip/uip-debug.h"
#include "net/net-debug.h"
/* Use to collect link statistics even on Keep-Alive, even though they were
* not sent from an upper layer and don't have a valid packet_sent callback */
@ -141,7 +141,7 @@ int tsch_is_coordinator = 0;
/* Are we associated to a TSCH network? */
int tsch_is_associated = 0;
/* Is the PAN running link-layer security? */
int tsch_is_pan_secured = TSCH_SECURITY_ENABLED;
int tsch_is_pan_secured = LLSEC802154_ENABLED;
/* The current Absolute Slot Number (ASN) */
struct asn_t current_asn;
/* Device rank or join priority:
@ -177,7 +177,7 @@ tsch_set_coordinator(int enable)
void
tsch_set_pan_secured(int enable)
{
tsch_is_pan_secured = TSCH_SECURITY_ENABLED && enable;
tsch_is_pan_secured = LLSEC802154_ENABLED && enable;
}
/*---------------------------------------------------------------------------*/
void
@ -199,8 +199,8 @@ tsch_reset(void)
frame802154_set_pan_id(0xffff);
/* First make sure pending packet callbacks are sent etc */
process_post_synch(&tsch_pending_events_process, PROCESS_EVENT_POLL, NULL);
/* Empty all neighbor queues */
/* tsch_queue_flush_all(); */
/* Reset neighbor queues */
tsch_queue_reset();
/* Remove unused neighbors */
tsch_queue_free_unused_neighbors();
tsch_queue_update_time_source(NULL);
@ -455,21 +455,21 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
}
#endif /* TSCH_JOIN_SECURED_ONLY */
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(!tsch_security_parse_frame(input_eb->payload, hdrlen,
input_eb->len - hdrlen - tsch_security_mic_len(&frame),
&frame, (linkaddr_t*)&frame.src_addr, &current_asn)) {
PRINTF("TSCH:! parse_eb: failed to authenticate\n");
return 0;
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
#if !TSCH_SECURITY_ENABLED
#if !LLSEC802154_ENABLED
if(frame.fcf.security_enabled == 1) {
PRINTF("TSCH:! parse_eb: we do not support security, but EB is secured\n");
return 0;
}
#endif /* !TSCH_SECURITY_ENABLED */
#endif /* !LLSEC802154_ENABLED */
#if TSCH_JOIN_MY_PANID_ONLY
/* Check if the EB comes from the PAN ID we expect */
@ -609,24 +609,25 @@ PT_THREAD(tsch_scan(struct pt *pt))
static struct input_packet input_eb;
static struct etimer scan_timer;
/* Time when we started scanning on current_channel */
static clock_time_t current_channel_since;
ASN_INIT(current_asn, 0, 0);
etimer_set(&scan_timer, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY);
current_channel_since = clock_time();
while(!tsch_is_associated && !tsch_is_coordinator) {
/* Hop to any channel offset */
static int current_channel = 0;
/* Time when we started scanning on current_channel */
static clock_time_t current_channel_since = 0;
static uint8_t current_channel = 0;
/* We are not coordinator, try to associate */
rtimer_clock_t t0;
int is_packet_pending = 0;
clock_time_t now_seconds = clock_seconds();
clock_time_t now_time = clock_time();
/* Switch to a (new) channel for scanning */
if(current_channel == 0 || now_seconds != current_channel_since) {
if(current_channel == 0 || now_time - current_channel_since > TSCH_CHANNEL_SCAN_DURATION) {
/* Pick a channel at random in TSCH_JOIN_HOPPING_SEQUENCE */
uint8_t scan_channel = TSCH_JOIN_HOPPING_SEQUENCE[
random_rand() % sizeof(TSCH_JOIN_HOPPING_SEQUENCE)];
@ -635,7 +636,7 @@ PT_THREAD(tsch_scan(struct pt *pt))
current_channel = scan_channel;
PRINTF("TSCH: scanning on channel %u\n", scan_channel);
}
current_channel_since = now_seconds;
current_channel_since = now_time;
}
/* Turn radio on and wait for EB */
@ -649,12 +650,12 @@ PT_THREAD(tsch_scan(struct pt *pt))
}
if(is_packet_pending) {
/* Save packet timestamp */
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t));
/* Read packet */
input_eb.len = NETSTACK_RADIO.read(input_eb.payload, TSCH_PACKET_MAX_LEN);
/* Save packet timestamp */
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t));
/* Parse EB and attempt to associate */
PRINTF("TSCH: association: received packet (%u bytes) on channel %u\n", input_eb.len, current_channel);
@ -746,14 +747,14 @@ PROCESS_THREAD(tsch_send_eb_process, ev, data)
}
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_BEACONFRAME);
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno);
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(tsch_is_pan_secured) {
/* Set security level, key id and index */
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, TSCH_SECURITY_KEY_SEC_LEVEL_EB);
packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, FRAME802154_1_BYTE_KEY_ID_MODE); /* Use 1-byte key index */
packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, TSCH_SECURITY_KEY_INDEX_EB);
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
eb_len = tsch_packet_create_eb(packetbuf_dataptr(), PACKETBUF_SIZE,
tsch_packet_seqno, &hdr_len, &tsch_sync_ie_offset);
if(eb_len != 0) {
@ -907,14 +908,14 @@ send_packet(mac_callback_t sent, void *ptr)
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_DATAFRAME);
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno);
#if TSCH_SECURITY_ENABLED
#if LLSEC802154_ENABLED
if(tsch_is_pan_secured) {
/* Set security level, key id and index */
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, TSCH_SECURITY_KEY_SEC_LEVEL_OTHER);
packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, FRAME802154_1_BYTE_KEY_ID_MODE); /* Use 1-byte key index */
packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, TSCH_SECURITY_KEY_INDEX_OTHER);
}
#endif /* TSCH_SECURITY_ENABLED */
#endif /* LLSEC802154_ENABLED */
packet_count_before = tsch_queue_packet_count(addr);
@ -936,6 +937,7 @@ send_packet(mac_callback_t sent, void *ptr)
tsch_queue_packet_count(addr),
p->header_len,
queuebuf_datalen(p->qb));
(void)packet_count_before; /* Discard "variable set but unused" warning in case of TSCH_LOG_LEVEL of 0 */
}
}
if(ret != MAC_TX_DEFERRED) {

View File

@ -84,8 +84,8 @@
#ifdef TSCH_CONF_JOIN_SECURED_ONLY
#define TSCH_JOIN_SECURED_ONLY TSCH_CONF_JOIN_SECURED_ONLY
#else
/* By default, set if TSCH_SECURITY_ENABLED is also non-zero */
#define TSCH_JOIN_SECURED_ONLY TSCH_SECURITY_ENABLED
/* By default, set if LLSEC802154_ENABLED is also non-zero */
#define TSCH_JOIN_SECURED_ONLY LLSEC802154_ENABLED
#endif
/* By default, join any PAN ID. Otherwise, wait for an EB from IEEE802154_PANID */

View File

@ -47,11 +47,20 @@
static void handle_periodic_timer(void *ptr);
static struct ctimer periodic_timer;
static uint8_t initialized = 0;
static void print_table();
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/* This is the callback function that will be called when there is a
* nbr-policy active
**/
#ifdef NBR_TABLE_FIND_REMOVABLE
const linkaddr_t *NBR_TABLE_FIND_REMOVABLE(nbr_table_reason_t reason, void *data);
#endif /* NBR_TABLE_FIND_REMOVABLE */
/* List of link-layer addresses of the neighbors, used as key in the tables */
typedef struct nbr_table_key {
struct nbr_table_key *next;
@ -169,8 +178,27 @@ nbr_set_bit(uint8_t *bitmap, nbr_table_t *table, nbr_table_item_t *item, int val
return 0;
}
/*---------------------------------------------------------------------------*/
static void
remove_key(nbr_table_key_t *least_used_key)
{
int i;
for(i = 0; i < MAX_NUM_TABLES; i++) {
if(all_tables[i] != NULL && all_tables[i]->callback != NULL) {
/* Call table callback for each table that uses this item */
nbr_table_item_t *removed_item = item_from_key(all_tables[i], least_used_key);
if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) {
all_tables[i]->callback(removed_item);
}
}
}
/* Empty used map */
used_map[index_from_key(least_used_key)] = 0;
/* Remove neighbor from list */
list_remove(nbr_table_keys, least_used_key);
}
/*---------------------------------------------------------------------------*/
static nbr_table_key_t *
nbr_table_allocate(void)
nbr_table_allocate(nbr_table_reason_t reason, void *data)
{
nbr_table_key_t *key;
int least_used_count = 0;
@ -179,59 +207,72 @@ nbr_table_allocate(void)
key = memb_alloc(&neighbor_addr_mem);
if(key != NULL) {
return key;
} else { /* No more space, try to free a neighbor.
* The replacement policy is the following: remove neighbor that is:
* (1) not locked
* (2) used by fewest tables
* (3) oldest (the list is ordered by insertion time)
* */
/* Get item from first key */
key = list_head(nbr_table_keys);
while(key != NULL) {
int item_index = index_from_key(key);
int locked = locked_map[item_index];
/* Never delete a locked item */
if(!locked) {
int used = used_map[item_index];
int used_count = 0;
/* Count how many tables are using this item */
while(used != 0) {
if((used & 1) == 1) {
used_count++;
}
used >>= 1;
}
/* Find least used item */
if(least_used_key == NULL || used_count < least_used_count) {
least_used_key = key;
least_used_count = used_count;
if(used_count == 0) { /* We won't find any least used item */
break;
}
}
} else {
#ifdef NBR_TABLE_FIND_REMOVABLE
const linkaddr_t *lladdr;
lladdr = NBR_TABLE_FIND_REMOVABLE(reason, data);
if(lladdr == NULL) {
/* Nothing found that can be deleted - return NULL to indicate failure */
PRINTF("*** Not removing entry to allocate new\n");
return NULL;
} else {
/* used least_used_key to indicate what is the least useful entry */
int index;
int locked = 0;
if((index = index_from_lladdr(lladdr)) != -1) {
least_used_key = key_from_index(index);
locked = locked_map[index];
}
/* Allow delete of locked item? */
if(least_used_key != NULL && locked) {
PRINTF("Deleting locked item!\n");
locked_map[index] = 0;
}
key = list_item_next(key);
}
#endif /* NBR_TABLE_FIND_REMOVABLE */
if(least_used_key == NULL) {
/* No more space, try to free a neighbor.
* The replacement policy is the following: remove neighbor that is:
* (1) not locked
* (2) used by fewest tables
* (3) oldest (the list is ordered by insertion time)
* */
/* Get item from first key */
key = list_head(nbr_table_keys);
while(key != NULL) {
int item_index = index_from_key(key);
int locked = locked_map[item_index];
/* Never delete a locked item */
if(!locked) {
int used = used_map[item_index];
int used_count = 0;
/* Count how many tables are using this item */
while(used != 0) {
if((used & 1) == 1) {
used_count++;
}
used >>= 1;
}
/* Find least used item */
if(least_used_key == NULL || used_count < least_used_count) {
least_used_key = key;
least_used_count = used_count;
if(used_count == 0) { /* We won't find any least used item */
break;
}
}
}
key = list_item_next(key);
}
}
if(least_used_key == NULL) {
/* We haven't found any unlocked item, allocation fails */
return NULL;
} else {
/* Reuse least used item */
int i;
for(i = 0; i<MAX_NUM_TABLES; i++) {
if(all_tables[i] != NULL && all_tables[i]->callback != NULL) {
/* Call table callback for each table that uses this item */
nbr_table_item_t *removed_item = item_from_key(all_tables[i], least_used_key);
if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) {
all_tables[i]->callback(removed_item);
}
}
}
/* Empty used map */
used_map[index_from_key(least_used_key)] = 0;
/* Remove neighbor from list */
list_remove(nbr_table_keys, least_used_key);
/* Return associated key */
remove_key(least_used_key);
return least_used_key;
}
}
@ -289,7 +330,7 @@ nbr_table_next(nbr_table_t *table, nbr_table_item_t *item)
/*---------------------------------------------------------------------------*/
/* Add a neighbor indexed with its link-layer address */
nbr_table_item_t *
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data)
{
int index;
nbr_table_item_t *item;
@ -303,7 +344,7 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
if((index = index_from_lladdr(lladdr)) == -1) {
/* Neighbor not yet in table, let's try to allocate one */
key = nbr_table_allocate();
key = nbr_table_allocate(reason, data);
/* No space available for new entry */
if(key == NULL) {
@ -327,6 +368,9 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
memset(item, 0, table->item_size);
nbr_set_bit(used_map, table, item, 1);
#if DEBUG
print_table();
#endif
return item;
}
/*---------------------------------------------------------------------------*/
@ -377,9 +421,42 @@ nbr_table_get_lladdr(nbr_table_t *table, const void *item)
return key != NULL ? &key->lladdr : NULL;
}
/*---------------------------------------------------------------------------*/
/* Update link-layer address of an item */
int
nbr_table_update_lladdr(const linkaddr_t *old_addr, const linkaddr_t *new_addr,
int remove_if_duplicate)
{
int index;
int new_index;
nbr_table_key_t *key;
index = index_from_lladdr(old_addr);
if(index == -1) {
/* Failure to change since there is nothing to change. */
return 0;
}
if((new_index = index_from_lladdr(new_addr)) != -1) {
/* check if it is a change or not - do not remove / fail if same */
if(new_index == index) {
return 1;
}
/* This new entry already exists - failure! - remove if requested. */
if(remove_if_duplicate) {
remove_key(key_from_index(index));
}
return 0;
}
key = key_from_index(index);
/**
* Copy the new lladdr into the key - since we know that there is no
* conflicting entry.
*/
memcpy(&key->lladdr, new_addr, sizeof(linkaddr_t));
return 1;
}
/*---------------------------------------------------------------------------*/
#if DEBUG
static void
handle_periodic_timer(void *ptr)
print_table()
{
int i, j;
/* Printout all neighbors and which tables they are used in */
@ -394,6 +471,12 @@ handle_periodic_timer(void *ptr)
PRINTF("\n");
}
}
}
/*---------------------------------------------------------------------------*/
static void
handle_periodic_timer(void *ptr)
{
print_table();
ctimer_reset(&periodic_timer);
}
#endif

View File

@ -75,6 +75,18 @@ typedef struct nbr_table {
/** \brief Declaration of non-static neighbor tables */
#define NBR_TABLE_DECLARE(name) extern nbr_table_t *name
typedef enum {
NBR_TABLE_REASON_UNDEFINED,
NBR_TABLE_REASON_RPL_DIO,
NBR_TABLE_REASON_RPL_DAO,
NBR_TABLE_REASON_RPL_DIS,
NBR_TABLE_REASON_ROUTE,
NBR_TABLE_REASON_IPV6_ND,
NBR_TABLE_REASON_MAC,
NBR_TABLE_REASON_LLSEC,
NBR_TABLE_REASON_LINK_STATS,
} nbr_table_reason_t;
/** \name Neighbor tables: register and loop through table elements */
/** @{ */
int nbr_table_register(nbr_table_t *table, nbr_table_callback *callback);
@ -84,7 +96,7 @@ nbr_table_item_t *nbr_table_next(nbr_table_t *table, nbr_table_item_t *item);
/** \name Neighbor tables: add and get data */
/** @{ */
nbr_table_item_t *nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr);
nbr_table_item_t *nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data);
nbr_table_item_t *nbr_table_get_from_lladdr(nbr_table_t *table, const linkaddr_t *lladdr);
/** @} */
@ -98,6 +110,7 @@ int nbr_table_unlock(nbr_table_t *table, nbr_table_item_t *item);
/** \name Neighbor tables: address manipulation */
/** @{ */
linkaddr_t *nbr_table_get_lladdr(nbr_table_t *table, const nbr_table_item_t *item);
int nbr_table_update_lladdr(const linkaddr_t *old_addr, const linkaddr_t *new_addr, int remove_if_duplicate);
/** @} */
#endif /* NBR_TABLE_H_ */

65
core/net/net-debug.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2010, 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.
*
*/
/**
* \file
* A set of debugging tools for the IP stack
* \author
* Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@sics.se>
* Joakim Eriksson <joakime@sics.se>
* Simon Duquennoy <simon.duquennoy@inria.fr>
*/
#include "net/net-debug.h"
/*---------------------------------------------------------------------------*/
void
net_debug_lladdr_print(const uip_lladdr_t *addr)
{
if(addr == NULL) {
PRINTA("(NULL LL addr)");
return;
} else {
#if NETSTACK_CONF_WITH_RIME
/* Rime uses traditionally a %u.%u format */
PRINTA("%u.%u", addr->addr[0], addr->addr[1]);
#else /* NETSTACK_CONF_WITH_RIME */
unsigned int i;
for(i = 0; i < LINKADDR_SIZE; i++) {
if(i > 0) {
PRINTA(":");
}
PRINTA("%02x", addr->addr[i]);
}
#endif /* NETSTACK_CONF_WITH_RIME */
}
}
/*---------------------------------------------------------------------------*/

86
core/net/net-debug.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2010, 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.
*
*/
/**
* \file
* A set of debugging macros for the netstack
*
* \author Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@sics.se>
* Joakim Eriksson <joakime@sics.se>
* Simon Duquennoy <simon.duquennoy@inria.fr>
*/
#ifndef NET_DEBUG_H
#define NET_DEBUG_H
#include "net/ip/uip.h"
#include "net/linkaddr.h"
#include <stdio.h>
void net_debug_lladdr_print(const uip_lladdr_t *addr);
#define DEBUG_NONE 0
#define DEBUG_PRINT 1
#define DEBUG_ANNOTATE 2
#define DEBUG_FULL DEBUG_ANNOTATE | DEBUG_PRINT
/* PRINTA will always print if the debug routines are called directly */
#ifdef __AVR__
#include <avr/pgmspace.h>
#define PRINTA(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define PRINTA(...) printf(__VA_ARGS__)
#endif
#if (DEBUG) & DEBUG_ANNOTATE
#ifdef __AVR__
#define ANNOTATE(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define ANNOTATE(...) printf(__VA_ARGS__)
#endif
#else
#define ANNOTATE(...)
#endif /* (DEBUG) & DEBUG_ANNOTATE */
#if (DEBUG) & DEBUG_PRINT
#ifdef __AVR__
#define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define PRINTF(...) printf(__VA_ARGS__)
#endif
#define PRINTLLADDR(lladdr) net_debug_lladdr_print(lladdr)
#else
#define PRINTF(...)
#define PRINTLLADDR(lladdr)
#endif /* (DEBUG) & DEBUG_PRINT */
#endif /* NET_DEBUG_H */

View File

@ -47,23 +47,22 @@
#include "contiki-net.h"
#include "net/packetbuf.h"
#include "net/rime/rime.h"
#include "sys/cc.h"
struct packetbuf_attr packetbuf_attrs[PACKETBUF_NUM_ATTRS];
struct packetbuf_addr packetbuf_addrs[PACKETBUF_NUM_ADDRS];
static uint16_t buflen, bufptr;
static uint8_t hdrptr;
static uint8_t hdrlen;
/* The declarations below ensure that the packet buffer is aligned on
an even 32-bit boundary. On some platforms (most notably the
msp430 or OpenRISC), having a potentially misaligned packet buffer may lead to
problems when accessing words. */
static uint32_t packetbuf_aligned[(PACKETBUF_SIZE + PACKETBUF_HDR_SIZE + 3) / 4];
static uint32_t packetbuf_aligned[(PACKETBUF_SIZE + 3) / 4];
static uint8_t *packetbuf = (uint8_t *)packetbuf_aligned;
static uint8_t *packetbufptr;
#define DEBUG 0
#if DEBUG
#include <stdio.h>
@ -77,26 +76,19 @@ void
packetbuf_clear(void)
{
buflen = bufptr = 0;
hdrptr = PACKETBUF_HDR_SIZE;
hdrlen = 0;
packetbufptr = &packetbuf[PACKETBUF_HDR_SIZE];
packetbuf_attr_clear();
}
/*---------------------------------------------------------------------------*/
void
packetbuf_clear_hdr(void)
{
hdrptr = PACKETBUF_HDR_SIZE;
}
/*---------------------------------------------------------------------------*/
int
packetbuf_copyfrom(const void *from, uint16_t len)
{
uint16_t l;
packetbuf_clear();
l = len > PACKETBUF_SIZE? PACKETBUF_SIZE: len;
memcpy(packetbufptr, from, l);
l = MIN(PACKETBUF_SIZE, len);
memcpy(packetbuf, from, l);
buflen = l;
return l;
}
@ -104,82 +96,43 @@ packetbuf_copyfrom(const void *from, uint16_t len)
void
packetbuf_compact(void)
{
int i, len;
int16_t i;
if(bufptr > 0) {
len = packetbuf_datalen() + PACKETBUF_HDR_SIZE;
for(i = PACKETBUF_HDR_SIZE; i < len; i++) {
packetbuf[i] = packetbuf[bufptr + i];
if(bufptr) {
/* shift data to the left */
for(i = 0; i < buflen; i++) {
packetbuf[hdrlen + i] = packetbuf[packetbuf_hdrlen() + i];
}
bufptr = 0;
}
}
/*---------------------------------------------------------------------------*/
int
packetbuf_copyto_hdr(uint8_t *to)
{
#if DEBUG_LEVEL > 0
{
int i;
PRINTF("packetbuf_write_hdr: header:\n");
for(i = hdrptr; i < PACKETBUF_HDR_SIZE; ++i) {
PRINTF("0x%02x, ", packetbuf[i]);
}
PRINTF("\n");
}
#endif /* DEBUG_LEVEL */
memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);
return PACKETBUF_HDR_SIZE - hdrptr;
}
/*---------------------------------------------------------------------------*/
int
packetbuf_copyto(void *to)
{
#if DEBUG_LEVEL > 0
{
int i;
char buffer[1000];
char *bufferptr = buffer;
int bufferlen = 0;
bufferptr[0] = 0;
for(i = hdrptr; i < PACKETBUF_HDR_SIZE; ++i) {
bufferptr += sprintf(bufferptr, "0x%02x, ", packetbuf[i]);
}
PRINTF("packetbuf_write: header: %s\n", buffer);
bufferptr = buffer;
bufferptr[0] = 0;
for(i = bufptr; ((i < buflen + bufptr) && (bufferlen < (sizeof(buffer) - 10))); ++i) {
bufferlen += sprintf(bufferptr + bufferlen, "0x%02x, ", packetbufptr[i]);
}
PRINTF("packetbuf_write: data: %s\n", buffer);
}
#endif /* DEBUG_LEVEL */
if(PACKETBUF_HDR_SIZE - hdrptr + buflen > PACKETBUF_SIZE) {
/* Too large packet */
if(hdrlen + buflen > PACKETBUF_SIZE) {
return 0;
}
memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);
memcpy((uint8_t *)to + PACKETBUF_HDR_SIZE - hdrptr, packetbufptr + bufptr,
buflen);
return PACKETBUF_HDR_SIZE - hdrptr + buflen;
memcpy(to, packetbuf_hdrptr(), hdrlen);
memcpy((uint8_t *)to + hdrlen, packetbuf_dataptr(), buflen);
return hdrlen + buflen;
}
/*---------------------------------------------------------------------------*/
int
packetbuf_hdralloc(int size)
{
if(hdrptr >= size && packetbuf_totlen() + size <= PACKETBUF_SIZE) {
hdrptr -= size;
return 1;
int16_t i;
if(size + packetbuf_totlen() > PACKETBUF_SIZE) {
return 0;
}
return 0;
}
/*---------------------------------------------------------------------------*/
void
packetbuf_hdr_remove(int size)
{
hdrptr += size;
/* shift data to the right */
for(i = packetbuf_totlen() - 1; i >= 0; i--) {
packetbuf[i + size] = packetbuf[i];
}
hdrlen += size;
return 1;
}
/*---------------------------------------------------------------------------*/
int
@ -204,13 +157,13 @@ packetbuf_set_datalen(uint16_t len)
void *
packetbuf_dataptr(void)
{
return (void *)(&packetbuf[bufptr + PACKETBUF_HDR_SIZE]);
return packetbuf + packetbuf_hdrlen();
}
/*---------------------------------------------------------------------------*/
void *
packetbuf_hdrptr(void)
{
return (void *)(&packetbuf[hdrptr]);
return packetbuf;
}
/*---------------------------------------------------------------------------*/
uint16_t
@ -222,16 +175,7 @@ packetbuf_datalen(void)
uint8_t
packetbuf_hdrlen(void)
{
uint8_t hdrlen;
hdrlen = PACKETBUF_HDR_SIZE - hdrptr;
if(hdrlen) {
/* outbound packet */
return hdrlen;
} else {
/* inbound packet */
return bufptr;
}
return bufptr + hdrlen;
}
/*---------------------------------------------------------------------------*/
uint16_t
@ -244,9 +188,7 @@ void
packetbuf_attr_clear(void)
{
int i;
for(i = 0; i < PACKETBUF_NUM_ATTRS; ++i) {
packetbuf_attrs[i].val = 0;
}
memset(packetbuf_attrs, 0, sizeof(packetbuf_attrs));
for(i = 0; i < PACKETBUF_NUM_ADDRS; ++i) {
linkaddr_copy(&packetbuf_addrs[i].addr, &linkaddr_null);
}
@ -272,7 +214,6 @@ packetbuf_attr_copyfrom(struct packetbuf_attr *attrs,
int
packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
{
/* packetbuf_attrs[type].type = type; */
packetbuf_attrs[type].val = val;
return 1;
}
@ -286,7 +227,6 @@ packetbuf_attr(uint8_t type)
int
packetbuf_set_addr(uint8_t type, const linkaddr_t *addr)
{
/* packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].type = type; */
linkaddr_copy(&packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr, addr);
return 1;
}

View File

@ -66,15 +66,6 @@
#define PACKETBUF_SIZE 128
#endif
/**
* \brief The size of the packetbuf header, in bytes
*/
#ifdef PACKETBUF_CONF_HDR_SIZE
#define PACKETBUF_HDR_SIZE PACKETBUF_CONF_HDR_SIZE
#else
#define PACKETBUF_HDR_SIZE 48
#endif
#ifdef PACKETBUF_CONF_WITH_PACKET_TYPE
#define PACKETBUF_WITH_PACKET_TYPE PACKETBUF_CONF_WITH_PACKET_TYPE
#else
@ -92,21 +83,6 @@
*/
void packetbuf_clear(void);
/**
* \brief Clear and reset the header of the packetbuf
*
* This function clears the header of the packetbuf and
* resets all the internal state pointers pertaining to
* the header (header size, header pointer, but not
* external data pointer). It is used before after sending
* a packet in the packetbuf, to be able to reuse the
* packet buffer for a later retransmission.
*
*/
void packetbuf_clear_hdr(void);
void packetbuf_hdr_remove(int bytes);
/**
* \brief Get a pointer to the data in the packetbuf
* \return Pointer to the packetbuf data
@ -115,15 +91,6 @@ void packetbuf_hdr_remove(int bytes);
* the packetbuf. The data is either stored in the packetbuf,
* or referenced to an external location.
*
* For outbound packets, the packetbuf consists of two
* parts: header and data. The header is accessed with the
* packetbuf_hdrptr() function.
*
* For incoming packets, both the packet header and the
* packet data is stored in the data portion of the
* packetbuf. Thus this function is used to get a pointer to
* the header for incoming packets.
*
*/
void *packetbuf_dataptr(void);
@ -131,11 +98,6 @@ void *packetbuf_dataptr(void);
* \brief Get a pointer to the header in the packetbuf, for outbound packets
* \return Pointer to the packetbuf header
*
* For outbound packets, the packetbuf consists of two
* parts: header and data. This function is used to get a
* pointer to the header in the packetbuf. The header is
* stored in the packetbuf.
*
*/
void *packetbuf_hdrptr(void);
@ -143,12 +105,6 @@ void *packetbuf_hdrptr(void);
* \brief Get the length of the header in the packetbuf
* \return Length of the header in the packetbuf
*
* For outbound packets, the packetbuf consists of two
* parts: header and data. This function is used to get
* the length of the header in the packetbuf. The header is
* stored in the packetbuf and accessed via the
* packetbuf_hdrptr() function.
*
*/
uint8_t packetbuf_hdrlen(void);
@ -157,17 +113,6 @@ uint8_t packetbuf_hdrlen(void);
* \brief Get the length of the data in the packetbuf
* \return Length of the data in the packetbuf
*
* For outbound packets, the packetbuf consists of two
* parts: header and data. This function is used to get
* the length of the data in the packetbuf. The data is
* stored in the packetbuf and accessed via the
* packetbuf_dataptr() function.
*
* For incoming packets, both the packet header and the
* packet data is stored in the data portion of the
* packetbuf. This function is then used to get the total
* length of the packet - both header and data.
*
*/
uint16_t packetbuf_datalen(void);
@ -181,10 +126,6 @@ uint16_t packetbuf_totlen(void);
/**
* \brief Set the length of the data in the packetbuf
* \param len The length of the data
*
* For outbound packets, the packetbuf consists of two
* parts: header and data. This function is used to set
* the length of the data in the packetbuf.
*/
void packetbuf_set_datalen(uint16_t len);
@ -228,29 +169,13 @@ int packetbuf_copyfrom(const void *from, uint16_t len);
*
* The external buffer to which the packetbuf is to be
* copied must be able to accomodate at least
* (PACKETBUF_SIZE + PACKETBUF_HDR_SIZE) bytes. The number of
* PACKETBUF_SIZE bytes. The number of
* bytes that was copied to the external buffer is
* returned.
*
*/
int packetbuf_copyto(void *to);
/**
* \brief Copy the header portion of the packetbuf to an external buffer
* \param to A pointer to the buffer to which the data is to be copied
* \retval The number of bytes that was copied to the external buffer
*
* This function copies the header portion of the packetbuf
* to an external buffer.
*
* The external buffer to which the packetbuf is to be
* copied must be able to accomodate at least
* PACKETBUF_HDR_SIZE bytes. The number of bytes that was
* copied to the external buffer is returned.
*
*/
int packetbuf_copyto_hdr(uint8_t *to);
/**
* \brief Extend the header of the packetbuf, for outbound packets
* \param size The number of bytes the header should be extended
@ -284,11 +209,9 @@ int packetbuf_hdrreduce(int size);
typedef uint16_t packetbuf_attr_t;
struct packetbuf_attr {
/* uint8_t type; */
packetbuf_attr_t val;
};
struct packetbuf_addr {
/* uint8_t type; */
linkaddr_t addr;
};
@ -332,9 +255,9 @@ enum {
#endif /* NETSTACK_CONF_WITH_RIME */
PACKETBUF_ATTR_PENDING,
PACKETBUF_ATTR_FRAME_TYPE,
#if LLSEC802154_SECURITY_LEVEL
#if LLSEC802154_USES_AUX_HEADER
PACKETBUF_ATTR_SECURITY_LEVEL,
#endif /* LLSEC802154_SECURITY_LEVEL */
#endif /* LLSEC802154_USES_AUX_HEADER */
#if LLSEC802154_USES_FRAME_COUNTER
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3,
@ -365,29 +288,6 @@ enum {
PACKETBUF_ATTR_MAX
};
/* Define surrogates when 802.15.4 security is off */
#if !LLSEC802154_SECURITY_LEVEL
enum {
PACKETBUF_ATTR_SECURITY_LEVEL,
};
#endif /* LLSEC802154_SECURITY_LEVEL */
#if !LLSEC802154_USES_FRAME_COUNTER
enum {
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3
};
#endif /* LLSEC802154_USES_FRAME_COUNTER */
/* Define surrogates when not using explicit keys */
#if !LLSEC802154_USES_EXPLICIT_KEYS
enum {
PACKETBUF_ATTR_KEY_ID_MODE,
PACKETBUF_ATTR_KEY_INDEX,
PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1
};
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
#if NETSTACK_CONF_WITH_RIME
#define PACKETBUF_NUM_ADDRS 4
#else /* NETSTACK_CONF_WITH_RIME */
@ -403,15 +303,9 @@ enum {
extern struct packetbuf_attr packetbuf_attrs[];
extern struct packetbuf_addr packetbuf_addrs[];
static int packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val);
static packetbuf_attr_t packetbuf_attr(uint8_t type);
static int packetbuf_set_addr(uint8_t type, const linkaddr_t *addr);
static const linkaddr_t *packetbuf_addr(uint8_t type);
static inline int
packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
{
/* packetbuf_attrs[type].type = type; */
packetbuf_attrs[type].val = val;
return 1;
}
@ -424,7 +318,6 @@ packetbuf_attr(uint8_t type)
static inline int
packetbuf_set_addr(uint8_t type, const linkaddr_t *addr)
{
/* packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].type = type; */
linkaddr_copy(&packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr, addr);
return 1;
}

5
core/net/rime/chameleon-raw.c Normal file → Executable file
View File

@ -205,10 +205,7 @@ hdrsize(const struct packetbuf_attrlist *a)
continue;
}
#endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */
len = a->len;
if(len < 8) {
len = 8;
}
len = (a->len & 0xf8) + ((a->len & 7) ? 8: 0);
size += len;
}
return size / 8;

View File

@ -45,31 +45,52 @@
#define RPL_CONF_STATS 0
#endif /* RPL_CONF_STATS */
/*
* Select routing metric supported at runtime. This must be a valid
* DAG Metric Container Object Type (see below). Currently, we only
* support RPL_DAG_MC_ETX and RPL_DAG_MC_ENERGY.
* When MRHOF (RFC6719) is used with ETX, no metric container must
* be used; instead the rank carries ETX directly.
/*
* The objective function (OF) used by a RPL root is configurable through
* the RPL_CONF_OF_OCP parameter. This is defined as the objective code
* point (OCP) of the OF, RPL_OCP_OF0 or RPL_OCP_MRHOF. This flag is of
* no relevance to non-root nodes, which run the OF advertised in the
* instance they join.
* Make sure the selected of is inRPL_SUPPORTED_OFS.
*/
#ifdef RPL_CONF_OF_OCP
#define RPL_OF_OCP RPL_CONF_OF_OCP
#else /* RPL_CONF_OF_OCP */
#define RPL_OF_OCP RPL_OCP_MRHOF
#endif /* RPL_CONF_OF_OCP */
/*
* The set of objective functions supported at runtime. Nodes are only
* able to join instances that advertise an OF in this set. To include
* both OF0 and MRHOF, use {&rpl_of0, &rpl_mrhof}.
*/
#ifdef RPL_CONF_SUPPORTED_OFS
#define RPL_SUPPORTED_OFS RPL_CONF_SUPPORTED_OFS
#else /* RPL_CONF_SUPPORTED_OFS */
#define RPL_SUPPORTED_OFS {&rpl_mrhof}
#endif /* RPL_CONF_SUPPORTED_OFS */
/*
* Enable/disable RPL Metric Containers (MC). The actual MC in use
* for a given DODAG is decided at runtime, when joining. Note that
* OF0 (RFC6552) operates without MC, and so does MRHOF (RFC6719) when
* used with ETX as a metric (the rank is the metric). We disable MC
* by default, but note it must be enabled to support joining a DODAG
* that requires MC (e.g., MRHOF with a metric other than ETX).
*/
#ifdef RPL_CONF_WITH_MC
#define RPL_WITH_MC RPL_CONF_WITH_MC
#else /* RPL_CONF_WITH_MC */
#define RPL_WITH_MC 0
#endif /* RPL_CONF_WITH_MC */
/* The MC advertised in DIOs and propagating from the root */
#ifdef RPL_CONF_DAG_MC
#define RPL_DAG_MC RPL_CONF_DAG_MC
#else
#define RPL_DAG_MC RPL_DAG_MC_NONE
#endif /* RPL_CONF_DAG_MC */
/*
* The objective function used by RPL is configurable through the
* RPL_CONF_OF parameter. This should be defined to be the name of an
* rpl_of object linked into the system image, e.g., rpl_of0.
*/
#ifdef RPL_CONF_OF
#define RPL_OF RPL_CONF_OF
#else
/* ETX is the default objective function. */
#define RPL_OF rpl_mrhof
#endif /* RPL_CONF_OF */
/* This value decides which DAG instance we should participate in by default. */
#ifdef RPL_CONF_DEFAULT_INSTANCE
#define RPL_DEFAULT_INSTANCE RPL_CONF_DEFAULT_INSTANCE
@ -118,7 +139,7 @@
#ifdef RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
#else
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 0
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 1
#endif /* RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME */
/*
@ -186,21 +207,12 @@
#define RPL_DIO_REDUNDANCY 10
#endif
/*
* Initial metric attributed to a link when the ETX is unknown
*/
#ifndef RPL_CONF_INIT_LINK_METRIC
#define RPL_INIT_LINK_METRIC 2
#else
#define RPL_INIT_LINK_METRIC RPL_CONF_INIT_LINK_METRIC
#endif
/*
* Default route lifetime unit. This is the granularity of time
* used in RPL lifetime values, in seconds.
*/
#ifndef RPL_CONF_DEFAULT_LIFETIME_UNIT
#define RPL_DEFAULT_LIFETIME_UNIT 0xffff
#define RPL_DEFAULT_LIFETIME_UNIT 60
#else
#define RPL_DEFAULT_LIFETIME_UNIT RPL_CONF_DEFAULT_LIFETIME_UNIT
#endif
@ -209,7 +221,7 @@
* Default route lifetime as a multiple of the lifetime unit.
*/
#ifndef RPL_CONF_DEFAULT_LIFETIME
#define RPL_DEFAULT_LIFETIME 0xff
#define RPL_DEFAULT_LIFETIME 30
#else
#define RPL_DEFAULT_LIFETIME RPL_CONF_DEFAULT_LIFETIME
#endif
@ -224,16 +236,38 @@
#endif
/*
* Hop-by-hop option
* This option control the insertion of the RPL Hop-by-Hop extension header
* into packets originating from this node. Incoming Hop-by-hop extension
* header are still processed and forwarded.
*/
#ifdef RPL_CONF_INSERT_HBH_OPTION
#define RPL_INSERT_HBH_OPTION RPL_CONF_INSERT_HBH_OPTION
* RPL DAO ACK support. When enabled, DAO ACK will be sent and requested.
* This will also enable retransmission of DAO when no ack is received.
* */
#ifdef RPL_CONF_WITH_DAO_ACK
#define RPL_WITH_DAO_ACK RPL_CONF_WITH_DAO_ACK
#else
#define RPL_INSERT_HBH_OPTION 1
#endif
#define RPL_WITH_DAO_ACK 0
#endif /* RPL_CONF_WITH_DAO_ACK */
/*
* RPL REPAIR ON DAO NACK. When enabled, DAO NACK will trigger a local
* repair in order to quickly find a new parent to send DAO's to.
* NOTE: this is too agressive in some cases so use with care.
* */
#ifdef RPL_CONF_RPL_REPAIR_ON_DAO_NACK
#define RPL_REPAIR_ON_DAO_NACK RPL_CONF_RPL_REPAIR_ON_DAO_NACK
#else
#define RPL_REPAIR_ON_DAO_NACK 0
#endif /* RPL_CONF_RPL_REPAIR_ON_DAO_NACK */
/*
* Setting the DIO_REFRESH_DAO_ROUTES will make the RPL root always
* increase the DTSN (Destination Advertisement Trigger Sequence Number)
* when sending multicast DIO. This is to get all children to re-register
* their DAO route. This is needed when DAO-ACK is not enabled to add
* reliability to route maintenance.
* */
#ifdef RPL_CONF_DIO_REFRESH_DAO_ROUTES
#define RPL_DIO_REFRESH_DAO_ROUTES RPL_CONF_DIO_REFRESH_DAO_ROUTES
#else
#define RPL_DIO_REFRESH_DAO_ROUTES 1
#endif /* RPL_CONF_DIO_REFRESH_DAO_ROUTES */
/*
* RPL probing. When enabled, probes will be sent periodically to keep
@ -253,23 +287,12 @@
#else
#define RPL_PROBING_INTERVAL (120 * CLOCK_SECOND)
#endif
/*
* RPL probing expiration time.
*/
#ifdef RPL_CONF_PROBING_EXPIRATION_TIME
#define RPL_PROBING_EXPIRATION_TIME RPL_CONF_PROBING_EXPIRATION_TIME
#else
#define RPL_PROBING_EXPIRATION_TIME (10L * 60 * CLOCK_SECOND)
#endif
/*
* Function used to select the next parent to be probed.
*/
#ifdef RPL_CONF_PROBING_SELECT_FUNC
#define RPL_PROBING_SELECT_FUNC RPL_CONF_PROBING_SELECT_FUNC
#else
#define RPL_PROBING_SELECT_FUNC(dag) get_probing_target((dag))
#define RPL_PROBING_SELECT_FUNC get_probing_target
#endif
/*
@ -292,8 +315,7 @@
#ifdef RPL_CONF_PROBING_DELAY_FUNC
#define RPL_PROBING_DELAY_FUNC RPL_CONF_PROBING_DELAY_FUNC
#else
#define RPL_PROBING_DELAY_FUNC() ((RPL_PROBING_INTERVAL / 2) \
+ random_rand() % (RPL_PROBING_INTERVAL))
#define RPL_PROBING_DELAY_FUNC get_probing_delay
#endif
/*

View File

@ -43,7 +43,9 @@
#define RPL_DAG_GRACE_PERIOD (CLOCK_SECOND * 20 * 1)
#if (UIP_CONF_MAX_ROUTES != 0)
static struct uip_ds6_notification n;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
static uint8_t to_become_root;
static struct ctimer c;
/*---------------------------------------------------------------------------*/
@ -121,6 +123,7 @@ create_dag_callback(void *ptr)
ctimer_set(&c, RPL_DAG_GRACE_PERIOD, create_dag_callback, NULL);
}
}
#if (UIP_CONF_MAX_ROUTES != 0)
/*---------------------------------------------------------------------------*/
static void
route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *ipaddr,
@ -136,6 +139,7 @@ route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *ipaddr,
}
}
}
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
/*---------------------------------------------------------------------------*/
static uip_ipaddr_t *
set_global_address(void)
@ -146,7 +150,7 @@ set_global_address(void)
/* Assign a unique local address (RFC4193,
http://tools.ietf.org/html/rfc4193). */
uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0);
uip_ip6addr(&ipaddr, UIP_DS6_DEFAULT_PREFIX, 0, 0, 0, 0, 0, 0, 0);
uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr);
uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF);
@ -171,7 +175,9 @@ rpl_dag_root_init(void)
if(!initialized) {
to_become_root = 0;
set_global_address();
#if (UIP_CONF_MAX_ROUTES != 0)
uip_ds6_notification_add(&n, route_callback);
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
initialized = 1;
}
}
@ -206,14 +212,16 @@ rpl_dag_root_init_dag_immediately(void)
/* If there are routes in this dag, we remove them all as we are
from now on the new dag root and the old routes are wrong */
rpl_remove_routes(dag);
if(RPL_IS_STORING(dag->instance)) {
rpl_remove_routes(dag);
}
if(dag->instance != NULL &&
dag->instance->def_route != NULL) {
uip_ds6_defrt_rm(dag->instance->def_route);
dag->instance->def_route = NULL;
}
uip_ip6addr(&prefix, 0xaaaa, 0, 0, 0, 0, 0, 0, 0);
uip_ip6addr(&prefix, UIP_DS6_DEFAULT_PREFIX, 0, 0, 0, 0, 0, 0, 0);
rpl_set_prefix(dag, &prefix, 64);
PRINTF("rpl_dag_root_init_dag: created a new RPL dag\n");
return 0;

View File

@ -44,6 +44,7 @@
*/
#include "contiki.h"
#include "net/link-stats.h"
#include "net/rpl/rpl-private.h"
#include "net/ip/uip.h"
#include "net/ipv6/uip-nd6.h"
@ -66,8 +67,8 @@ void RPL_CALLBACK_PARENT_SWITCH(rpl_parent_t *old, rpl_parent_t *new);
#endif /* RPL_CALLBACK_PARENT_SWITCH */
/*---------------------------------------------------------------------------*/
extern rpl_of_t RPL_OF;
static rpl_of_t * const objective_functions[] = {&RPL_OF};
extern rpl_of_t rpl_of0, rpl_mrhof;
static rpl_of_t * const objective_functions[] = RPL_SUPPORTED_OFS;
/*---------------------------------------------------------------------------*/
/* RPL definitions. */
@ -88,25 +89,29 @@ rpl_instance_t *default_instance;
/*---------------------------------------------------------------------------*/
void
rpl_print_neighbor_list()
rpl_print_neighbor_list(void)
{
if(default_instance != NULL && default_instance->current_dag != NULL &&
default_instance->of != NULL && default_instance->of->calculate_rank != NULL) {
default_instance->of != NULL) {
int curr_dio_interval = default_instance->dio_intcurrent;
int curr_rank = default_instance->current_dag->rank;
rpl_parent_t *p = nbr_table_head(rpl_parents);
clock_time_t now = clock_time();
clock_time_t clock_now = clock_time();
printf("RPL: rank %u dioint %u, %u nbr(s)\n", curr_rank, curr_dio_interval, uip_ds6_nbr_num());
printf("RPL: MOP %u OCP %u rank %u dioint %u, nbr count %u\n",
default_instance->mop, default_instance->of->ocp, curr_rank, curr_dio_interval, uip_ds6_nbr_num());
while(p != NULL) {
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
printf("RPL: nbr %3u %5u, %5u => %5u %c%c (last tx %u min ago)\n",
nbr_table_get_lladdr(rpl_parents, p)->u8[7],
p->rank, nbr ? nbr->link_metric : 0,
default_instance->of->calculate_rank(p, 0),
default_instance->current_dag == p->dag ? 'd' : ' ',
p == default_instance->current_dag->preferred_parent ? '*' : ' ',
(unsigned)((now - p->last_tx_time) / (60 * CLOCK_SECOND)));
const struct link_stats *stats = rpl_get_parent_link_stats(p);
printf("RPL: nbr %3u %5u, %5u => %5u -- %2u %c%c (last tx %u min ago)\n",
rpl_get_parent_ipaddr(p)->u8[15],
p->rank,
rpl_get_parent_link_metric(p),
rpl_rank_via_parent(p),
stats != NULL ? stats->freshness : 0,
link_stats_is_fresh(stats) ? 'f' : ' ',
p == default_instance->current_dag->preferred_parent ? 'p' : ' ',
(unsigned)((clock_now - stats->last_tx_time) / (60 * CLOCK_SECOND))
);
p = nbr_table_next(rpl_parents, p);
}
printf("RPL: end of list\n");
@ -116,8 +121,7 @@ rpl_print_neighbor_list()
uip_ds6_nbr_t *
rpl_get_nbr(rpl_parent_t *parent)
{
linkaddr_t *lladdr = NULL;
lladdr = nbr_table_get_lladdr(rpl_parents, parent);
const linkaddr_t *lladdr = rpl_get_parent_lladdr(parent);
if(lladdr != NULL) {
return nbr_table_get_from_lladdr(ds6_neighbors, lladdr);
} else {
@ -151,30 +155,78 @@ rpl_get_parent_rank(uip_lladdr_t *addr)
if(p != NULL) {
return p->rank;
} else {
return 0;
return INFINITE_RANK;
}
}
/*---------------------------------------------------------------------------*/
uint16_t
rpl_get_parent_link_metric(const uip_lladdr_t *addr)
rpl_get_parent_link_metric(rpl_parent_t *p)
{
uip_ds6_nbr_t *nbr;
nbr = nbr_table_get_from_lladdr(ds6_neighbors, (const linkaddr_t *)addr);
if(nbr != NULL) {
return nbr->link_metric;
} else {
return 0;
if(p != NULL && p->dag != NULL) {
rpl_instance_t *instance = p->dag->instance;
if(instance != NULL && instance->of != NULL && instance->of->parent_link_metric != NULL) {
return instance->of->parent_link_metric(p);
}
}
return 0xffff;
}
/*---------------------------------------------------------------------------*/
rpl_rank_t
rpl_rank_via_parent(rpl_parent_t *p)
{
if(p != NULL && p->dag != NULL) {
rpl_instance_t *instance = p->dag->instance;
if(instance != NULL && instance->of != NULL && instance->of->rank_via_parent != NULL) {
return instance->of->rank_via_parent(p);
}
}
return INFINITE_RANK;
}
/*---------------------------------------------------------------------------*/
const linkaddr_t *
rpl_get_parent_lladdr(rpl_parent_t *p)
{
return nbr_table_get_lladdr(rpl_parents, p);
}
/*---------------------------------------------------------------------------*/
uip_ipaddr_t *
rpl_get_parent_ipaddr(rpl_parent_t *p)
{
linkaddr_t *lladdr = nbr_table_get_lladdr(rpl_parents, p);
const linkaddr_t *lladdr = rpl_get_parent_lladdr(p);
return uip_ds6_nbr_ipaddr_from_lladdr((uip_lladdr_t *)lladdr);
}
/*---------------------------------------------------------------------------*/
const struct link_stats *
rpl_get_parent_link_stats(rpl_parent_t *p)
{
const linkaddr_t *lladdr = rpl_get_parent_lladdr(p);
return link_stats_from_lladdr(lladdr);
}
/*---------------------------------------------------------------------------*/
int
rpl_parent_is_fresh(rpl_parent_t *p)
{
const struct link_stats *stats = rpl_get_parent_link_stats(p);
return link_stats_is_fresh(stats);
}
/*---------------------------------------------------------------------------*/
int
rpl_parent_is_reachable(rpl_parent_t *p) {
if(p == NULL || p->dag == NULL || p->dag->instance == NULL || p->dag->instance->of == NULL) {
return 0;
} else {
#ifndef UIP_CONF_ND6_SEND_NA
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
/* Exclude links to a neighbor that is not reachable at a NUD level */
if(nbr == NULL || nbr->state != NBR_REACHABLE) {
return 0;
}
#endif /* UIP_CONF_ND6_SEND_NA */
/* If we don't have fresh link information, assume the parent is reachable. */
return !rpl_parent_is_fresh(p) || p->dag->instance->of->parent_has_usable_link(p);
}
}
/*---------------------------------------------------------------------------*/
static void
rpl_set_preferred_parent(rpl_dag_t *dag, rpl_parent_t *p)
{
@ -340,7 +392,12 @@ rpl_set_root(uint8_t instance_id, uip_ipaddr_t *dag_id)
dag->grounded = RPL_GROUNDED;
dag->preference = RPL_PREFERENCE;
instance->mop = RPL_MOP_DEFAULT;
instance->of = &RPL_OF;
instance->of = rpl_find_of(RPL_OF_OCP);
if(instance->of == NULL) {
PRINTF("RPL: OF with OCP %u not supported\n", RPL_OF_OCP);
return NULL;
}
rpl_set_preferred_parent(dag, NULL);
memcpy(&dag->dag_id, dag_id, sizeof(dag->dag_id));
@ -361,7 +418,9 @@ rpl_set_root(uint8_t instance_id, uip_ipaddr_t *dag_id)
if(instance->current_dag != dag && instance->current_dag != NULL) {
/* Remove routes installed by DAOs. */
rpl_remove_routes(instance->current_dag);
if(RPL_IS_STORING(instance)) {
rpl_remove_routes(instance->current_dag);
}
instance->current_dag->joined = 0;
}
@ -610,7 +669,9 @@ rpl_free_dag(rpl_dag_t *dag)
dag->joined = 0;
/* Remove routes installed by DAOs. */
rpl_remove_routes(dag);
if(RPL_IS_STORING(dag->instance)) {
rpl_remove_routes(dag);
}
/* Remove autoconfigured address */
if((dag->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) {
@ -634,25 +695,18 @@ rpl_add_parent(rpl_dag_t *dag, rpl_dio_t *dio, uip_ipaddr_t *addr)
PRINT6ADDR(addr);
PRINTF("\n");
if(lladdr != NULL) {
/* Add parent in rpl_parents */
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr);
/* Add parent in rpl_parents - again this is due to DIO */
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr,
NBR_TABLE_REASON_RPL_DIO, dio);
if(p == NULL) {
PRINTF("RPL: rpl_add_parent p NULL\n");
} else {
uip_ds6_nbr_t *nbr;
nbr = rpl_get_nbr(p);
p->dag = dag;
p->rank = dio->rank;
p->dtsn = dio->dtsn;
/* Check whether we have a neighbor that has not gotten a link metric yet */
if(nbr != NULL && nbr->link_metric == 0) {
nbr->link_metric = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
}
#if RPL_DAG_MC != RPL_DAG_MC_NONE
#if RPL_WITH_MC
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
#endif /* RPL_WITH_MC */
}
}
@ -737,7 +791,9 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
if(instance->current_dag != best_dag) {
/* Remove routes installed by DAOs. */
rpl_remove_routes(instance->current_dag);
if(RPL_IS_STORING(instance)) {
rpl_remove_routes(instance->current_dag);
}
PRINTF("RPL: New preferred DAG: ");
PRINT6ADDR(&best_dag->dag_id);
@ -756,13 +812,17 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
instance->of->update_metric_container(instance);
/* Update the DAG rank. */
best_dag->rank = instance->of->calculate_rank(best_dag->preferred_parent, 0);
best_dag->rank = rpl_rank_via_parent(best_dag->preferred_parent);
if(last_parent == NULL || best_dag->rank < best_dag->min_rank) {
/* This is a slight departure from RFC6550: if we had no preferred parent before,
* reset min_rank. This helps recovering from temporary bad link conditions. */
best_dag->min_rank = best_dag->rank;
} else if(!acceptable_rank(best_dag, best_dag->rank)) {
}
if(!acceptable_rank(best_dag, best_dag->rank)) {
PRINTF("RPL: New rank unacceptable!\n");
rpl_set_preferred_parent(instance->current_dag, NULL);
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES && last_parent != NULL) {
if(RPL_IS_STORING(instance) && last_parent != NULL) {
/* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME);
}
@ -774,15 +834,13 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
PRINTF("RPL: Changed preferred parent, rank changed from %u to %u\n",
(unsigned)old_rank, best_dag->rank);
RPL_STAT(rpl_stats.parent_switch++);
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
if(last_parent != NULL) {
/* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME);
}
/* The DAO parent set changed - schedule a DAO transmission. */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
rpl_schedule_dao(instance);
if(RPL_IS_STORING(instance) && last_parent != NULL) {
/* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME);
}
/* The DAO parent set changed - schedule a DAO transmission. */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
rpl_schedule_dao(instance);
rpl_reset_dio_timer(instance);
#if DEBUG
rpl_print_neighbor_list();
@ -795,22 +853,42 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
}
/*---------------------------------------------------------------------------*/
static rpl_parent_t *
best_parent(rpl_dag_t *dag)
best_parent(rpl_dag_t *dag, int fresh_only)
{
rpl_parent_t *p, *best;
rpl_parent_t *p;
rpl_of_t *of;
rpl_parent_t *best = NULL;
best = NULL;
if(dag == NULL || dag->instance == NULL || dag->instance->of == NULL) {
return NULL;
}
p = nbr_table_head(rpl_parents);
while(p != NULL) {
of = dag->instance->of;
/* Search for the best parent according to the OF */
for(p = nbr_table_head(rpl_parents); p != NULL; p = nbr_table_next(rpl_parents, p)) {
/* Exclude parents from other DAGs or announcing an infinite rank */
if(p->dag != dag || p->rank == INFINITE_RANK) {
/* ignore this neighbor */
} else if(best == NULL) {
best = p;
} else {
best = dag->instance->of->best_parent(best, p);
continue;
}
p = nbr_table_next(rpl_parents, p);
if(fresh_only && !rpl_parent_is_fresh(p)) {
/* Filter out non-fresh parents if fresh_only is set */
continue;
}
#ifndef UIP_CONF_ND6_SEND_NA
{
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
/* Exclude links to a neighbor that is not reachable at a NUD level */
if(nbr == NULL || nbr->state != NBR_REACHABLE) {
continue;
}
}
#endif /* UIP_CONF_ND6_SEND_NA */
/* Now we have an acceptable parent, check if it is the new best */
best = of->best_parent(best, p);
}
return best;
@ -819,16 +897,37 @@ best_parent(rpl_dag_t *dag)
rpl_parent_t *
rpl_select_parent(rpl_dag_t *dag)
{
rpl_parent_t *best = best_parent(dag);
/* Look for best parent (regardless of freshness) */
rpl_parent_t *best = best_parent(dag, 0);
if(best != NULL) {
rpl_set_preferred_parent(dag, best);
dag->rank = dag->instance->of->calculate_rank(dag->preferred_parent, 0);
#if RPL_WITH_PROBING
if(rpl_parent_is_fresh(best)) {
rpl_set_preferred_parent(dag, best);
} else {
/* The best is not fresh. Look for the best fresh now. */
rpl_parent_t *best_fresh = best_parent(dag, 1);
if(best_fresh == NULL) {
/* No fresh parent around, use best (non-fresh) */
rpl_set_preferred_parent(dag, best);
} else {
/* Use best fresh */
rpl_set_preferred_parent(dag, best_fresh);
}
/* Probe the best parent shortly in order to get a fresh estimate */
dag->instance->urgent_probing_target = best;
rpl_schedule_probing(dag->instance);
#else /* RPL_WITH_PROBING */
rpl_set_preferred_parent(dag, best);
dag->rank = rpl_rank_via_parent(dag->preferred_parent);
#endif /* RPL_WITH_PROBING */
}
} else {
dag->rank = INFINITE_RANK;
rpl_set_preferred_parent(dag, NULL);
}
return best;
dag->rank = rpl_rank_via_parent(dag->preferred_parent);
return dag->preferred_parent;
}
/*---------------------------------------------------------------------------*/
void
@ -859,9 +958,11 @@ rpl_nullify_parent(rpl_parent_t *parent)
uip_ds6_defrt_rm(dag->instance->def_route);
dag->instance->def_route = NULL;
}
/* Send No-Path DAO only to preferred parent, if any */
/* Send No-Path DAO only when nullifying preferred parent */
if(parent == dag->preferred_parent) {
dao_output(parent, RPL_ZERO_LIFETIME);
if(RPL_IS_STORING(dag->instance)) {
dao_output(parent, RPL_ZERO_LIFETIME);
}
rpl_set_preferred_parent(dag, NULL);
}
}
@ -887,8 +988,10 @@ rpl_move_parent(rpl_dag_t *dag_src, rpl_dag_t *dag_dst, rpl_parent_t *parent)
dag_src->instance->def_route = NULL;
}
} else if(dag_src->joined) {
/* Remove uIPv6 routes that have this parent as the next hop. */
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
if(RPL_IS_STORING(dag_src->instance)) {
/* Remove uIPv6 routes that have this parent as the next hop. */
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
}
}
PRINTF("RPL: Moving parent ");
@ -898,6 +1001,37 @@ rpl_move_parent(rpl_dag_t *dag_src, rpl_dag_t *dag_dst, rpl_parent_t *parent)
parent->dag = dag_dst;
}
/*---------------------------------------------------------------------------*/
int
rpl_has_downward_route(void)
{
int i;
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
if(instance_table[i].used && instance_table[i].has_downward_route) {
return 1;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
rpl_dag_t *
rpl_get_dag(const uip_ipaddr_t *addr)
{
int i, j;
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
if(instance_table[i].used) {
for(j = 0; j < RPL_MAX_DAG_PER_INSTANCE; ++j) {
if(instance_table[i].dag_table[j].joined
&& uip_ipaddr_prefixcmp(&instance_table[i].dag_table[j].dag_id, addr,
instance_table[i].dag_table[j].prefix_info.length)) {
return &instance_table[i].dag_table[j];
}
}
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
rpl_dag_t *
rpl_get_any_dag(void)
{
@ -948,6 +1082,12 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
rpl_parent_t *p;
rpl_of_t *of;
if((!RPL_WITH_NON_STORING && dio->mop == RPL_MOP_NON_STORING)
|| (!RPL_WITH_STORING && (dio->mop == RPL_MOP_STORING_NO_MULTICAST
|| dio->mop == RPL_MOP_STORING_MULTICAST))) {
PRINTF("RPL: DIO advertising a non-supported MOP %u\n", dio->mop);
}
/* Determine the objective function by using the
objective code point of the DIO. */
of = rpl_find_of(dio->ocp);
@ -990,6 +1130,10 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
instance->of = of;
instance->mop = dio->mop;
instance->mc.type = dio->mc.type;
instance->mc.flags = dio->mc.flags;
instance->mc.aggr = dio->mc.aggr;
instance->mc.prec = dio->mc.prec;
instance->current_dag = dag;
instance->dtsn_out = RPL_LOLLIPOP_INIT;
@ -1009,7 +1153,7 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
rpl_set_preferred_parent(dag, p);
instance->of->update_metric_container(instance);
dag->rank = instance->of->calculate_rank(p, 0);
dag->rank = rpl_rank_via_parent(p);
/* So far this is the lowest rank we are aware of. */
dag->min_rank = dag->rank;
@ -1032,6 +1176,8 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
} else {
PRINTF("RPL: The DIO does not meet the prerequisites for sending a DAO\n");
}
instance->of->reset(dag);
}
#if RPL_MAX_DAG_PER_INSTANCE > 1
@ -1102,7 +1248,7 @@ rpl_add_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
memcpy(&dag->prefix_info, &dio->prefix_info, sizeof(rpl_prefix_t));
rpl_set_preferred_parent(dag, p);
dag->rank = instance->of->calculate_rank(p, 0);
dag->rank = rpl_rank_via_parent(p);
dag->min_rank = dag->rank; /* So far this is the lowest rank we know of. */
PRINTF("RPL: Joined DAG with instance ID %u, rank %hu, DAG ID ",
@ -1144,7 +1290,7 @@ global_repair(uip_ipaddr_t *from, rpl_dag_t *dag, rpl_dio_t *dio)
PRINTF("RPL: Failed to add a parent during the global repair\n");
dag->rank = INFINITE_RANK;
} else {
dag->rank = dag->instance->of->calculate_rank(p, 0);
dag->rank = rpl_rank_via_parent(p);
dag->min_rank = dag->rank;
PRINTF("RPL: rpl_process_parent_event global repair\n");
rpl_process_parent_event(dag->instance, p);
@ -1155,6 +1301,7 @@ global_repair(uip_ipaddr_t *from, rpl_dag_t *dag, rpl_dio_t *dio)
RPL_STAT(rpl_stats.global_repairs++);
}
/*---------------------------------------------------------------------------*/
void
rpl_local_repair(rpl_instance_t *instance)
@ -1173,7 +1320,12 @@ rpl_local_repair(rpl_instance_t *instance)
}
}
/* no downward route anymore */
instance->has_downward_route = 0;
rpl_reset_dio_timer(instance);
/* Request refresh of DAO registrations next DIO */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
RPL_STAT(rpl_stats.local_repairs++);
}
@ -1205,6 +1357,7 @@ int
rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
{
int return_value;
rpl_parent_t *last_parent = instance->current_dag->preferred_parent;
#if DEBUG
rpl_rank_t old_rank;
@ -1213,10 +1366,20 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
return_value = 1;
if(RPL_IS_STORING(instance)
&& uip_ds6_route_is_nexthop(rpl_get_parent_ipaddr(p))
&& !rpl_parent_is_reachable(p) && instance->mop > RPL_MOP_NON_STORING) {
PRINTF("RPL: Unacceptable link %u, removing routes via: ", rpl_get_parent_link_metric(p));
PRINT6ADDR(rpl_get_parent_ipaddr(p));
PRINTF("\n");
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(p), p->dag);
}
if(!acceptable_rank(p->dag, p->rank)) {
/* The candidate parent is no longer valid: the rank increase resulting
from the choice of it as a parent would be too high. */
PRINTF("RPL: Unacceptable rank %u\n", (unsigned)p->rank);
PRINTF("RPL: Unacceptable rank %u (Current min %u, MaxRankInc %u)\n", (unsigned)p->rank,
p->dag->min_rank, p->dag->instance->max_rankinc);
rpl_nullify_parent(p);
if(p != instance->current_dag->preferred_parent) {
return 0;
@ -1226,10 +1389,12 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
}
if(rpl_select_dag(instance, p) == NULL) {
/* No suitable parent; trigger a local repair. */
PRINTF("RPL: No parents found in any DAG\n");
rpl_local_repair(instance);
return 0;
if(last_parent != NULL) {
/* No suitable parent anymore; trigger a local repair. */
PRINTF("RPL: No parents found in any DAG\n");
rpl_local_repair(instance);
return 0;
}
}
#if DEBUG
@ -1250,6 +1415,19 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
return return_value;
}
/*---------------------------------------------------------------------------*/
static int
add_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
/* add this to the neighbor cache if not already there */
if(rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIO, dio) == NULL) {
PRINTF("RPL: Out of memory, dropping DIO from ");
PRINT6ADDR(from);
PRINTF("\n");
return 0;
}
return 1;
}
/*---------------------------------------------------------------------------*/
void
rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
@ -1282,7 +1460,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
PRINTF("RPL: Global repair\n");
if(dio->prefix_info.length != 0) {
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
PRINTF("RPL : Prefix announced in DIO\n");
PRINTF("RPL: Prefix announced in DIO\n");
rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length);
}
}
@ -1303,7 +1481,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
if(instance == NULL) {
PRINTF("RPL: New instance detected (ID=%u): Joining...\n", dio->instance_id);
rpl_join_instance(from, dio);
if(add_nbr_from_dio(from, dio)) {
rpl_join_instance(from, dio);
} else {
PRINTF("RPL: Not joining since could not add parent\n");
}
return;
}
@ -1315,6 +1497,10 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
if(dag == NULL) {
#if RPL_MAX_DAG_PER_INSTANCE > 1
PRINTF("RPL: Adding new DAG to known instance.\n");
if(!add_nbr_from_dio(from, dio)) {
PRINTF("RPL: Could not add new DAG, could not add parent\n");
return;
}
dag = rpl_add_dag(from, dio);
if(dag == NULL) {
PRINTF("RPL: Failed to add DAG.\n");
@ -1331,18 +1517,21 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
PRINTF("RPL: Ignoring DIO with too low rank: %u\n",
(unsigned)dio->rank);
return;
} else if(dio->rank == INFINITE_RANK && dag->joined) {
rpl_reset_dio_timer(instance);
}
/* Prefix Information Option treated to add new prefix */
if(dio->prefix_info.length != 0) {
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
PRINTF("RPL : Prefix announced in DIO\n");
PRINTF("RPL: Prefix announced in DIO\n");
rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length);
}
}
if(!add_nbr_from_dio(from, dio)) {
PRINTF("RPL: Could not add parent based on DIO\n");
return;
}
if(dag->rank == ROOT_RANK(instance)) {
if(dio->rank != INFINITE_RANK) {
instance->dio_counter++;
@ -1394,6 +1583,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
}
p->rank = dio->rank;
if(dio->rank == INFINITE_RANK && p == dag->preferred_parent) {
/* Our preferred parent advertised an infinite rank, reset DIO timer */
rpl_reset_dio_timer(instance);
}
/* Parent info has been updated, trigger rank recalculation */
p->flags |= RPL_PARENT_FLAG_UPDATED;
@ -1401,14 +1595,14 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
PRINT6ADDR(&instance->current_dag->dag_id);
PRINTF(", rank %u, min_rank %u, ",
instance->current_dag->rank, instance->current_dag->min_rank);
PRINTF("parent rank %u, parent etx %u, link metric %u, instance etx %u\n",
p->rank, -1/*p->mc.obj.etx*/, rpl_get_nbr(p)->link_metric, instance->mc.obj.etx);
PRINTF("parent rank %u, link metric %u\n",
p->rank, rpl_get_parent_link_metric(p));
/* We have allocated a candidate parent; process the DIO further. */
#if RPL_DAG_MC != RPL_DAG_MC_NONE
#if RPL_WITH_MC
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
#endif /* RPL_WITH_MC */
if(rpl_process_parent_event(instance, p) == 0) {
PRINTF("RPL: The candidate parent is rejected\n");
return;

View File

@ -48,6 +48,7 @@
#include "net/ip/tcpip.h"
#include "net/ipv6/uip-ds6.h"
#include "net/rpl/rpl-private.h"
#include "net/rpl/rpl-ns.h"
#include "net/packetbuf.h"
#define DEBUG DEBUG_NONE
@ -61,12 +62,14 @@
#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])
#define UIP_HBHO_NEXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_HOP_BY_HOP_LEN])
#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])
#define UIP_EXT_HDR_OPT_BUF ((struct uip_ext_hdr_opt *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
#define UIP_EXT_HDR_OPT_PADN_BUF ((struct uip_ext_hdr_opt_padn *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
#define UIP_EXT_HDR_OPT_RPL_BUF ((struct uip_ext_hdr_opt_rpl *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
/*---------------------------------------------------------------------------*/
int
rpl_verify_header(int uip_ext_opt_offset)
rpl_verify_hbh_header(int uip_ext_opt_offset)
{
rpl_instance_t *instance;
int down;
@ -75,7 +78,7 @@ rpl_verify_header(int uip_ext_opt_offset)
uip_ds6_route_t *route;
rpl_parent_t *sender = NULL;
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
return 1;
}
@ -100,12 +103,14 @@ rpl_verify_header(int uip_ext_opt_offset)
if(UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_FWD_ERR) {
PRINTF("RPL: Forward error!\n");
/* We should try to repair it by removing the neighbor that caused
the packet to be forwareded in the first place. We drop any
routes that go through the neighbor that sent the packet to
us. */
route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
if(route != NULL) {
uip_ds6_route_rm(route);
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);
}
}
RPL_STAT(rpl_stats.forward_errors++);
/* Trigger DAO retransmission */
@ -125,23 +130,28 @@ rpl_verify_header(int uip_ext_opt_offset)
}
sender_rank = UIP_HTONS(UIP_EXT_HDR_OPT_RPL_BUF->senderrank);
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
);
sender = nbr_table_get_from_lladdr(rpl_parents, packetbuf_addr(PACKETBUF_ADDR_SENDER));
if(sender != NULL && (UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_RANK_ERR)) {
/* A rank error was signalled, attempt to repair it by updating
* the sender's rank from ext header */
sender->rank = sender_rank;
rpl_select_dag(instance, sender);
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);
}
}
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
);
if((down && !sender_closer) || (!down && sender_closer)) {
PRINTF("RPL: Loop detected - senderrank: %d my-rank: %d sender_closer: %d\n",
sender_rank, instance->current_dag->rank,
@ -166,35 +176,308 @@ rpl_verify_header(int uip_ext_opt_offset)
}
PRINTF("RPL: Rank OK\n");
return 0;
}
/*---------------------------------------------------------------------------*/
static void
set_rpl_opt(unsigned uip_ext_opt_offset)
#if RPL_WITH_NON_STORING
int
rpl_srh_get_next_hop(uip_ipaddr_t *ipaddr)
{
uint8_t temp_len;
uint8_t *uip_next_hdr;
int last_uip_ext_len = uip_ext_len;
rpl_dag_t *dag;
rpl_ns_node_t *dest_node;
rpl_ns_node_t *root_node;
memmove(UIP_HBHO_NEXT_BUF, UIP_EXT_BUF, uip_len - UIP_IPH_LEN);
memset(UIP_HBHO_BUF, 0, RPL_HOP_BY_HOP_LEN);
UIP_HBHO_BUF->next = UIP_IP_BUF->proto;
UIP_IP_BUF->proto = UIP_PROTO_HBHO;
UIP_HBHO_BUF->len = RPL_HOP_BY_HOP_LEN - 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;
UIP_EXT_HDR_OPT_RPL_BUF->instance = 0;
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = 0;
uip_len += RPL_HOP_BY_HOP_LEN;
temp_len = UIP_IP_BUF->len[1];
UIP_IP_BUF->len[1] += UIP_HBHO_BUF->len + 8;
if(UIP_IP_BUF->len[1] < temp_len) {
UIP_IP_BUF->len[0]++;
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_TCP:
case UIP_PROTO_UDP:
case UIP_PROTO_ICMP6:
case UIP_PROTO_NONE:
uip_next_hdr = NULL;
break;
case UIP_PROTO_HBHO:
case UIP_PROTO_DESTO:
case UIP_PROTO_FRAG:
/* Move to next header */
if(uip_next_hdr != &UIP_IP_BUF->proto) {
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
}
uip_next_hdr = &UIP_EXT_BUF->next;
break;
default:
break;
}
}
dag = rpl_get_dag(&UIP_IP_BUF->destipaddr);
root_node = rpl_ns_get_node(dag, &dag->dag_id);
dest_node = rpl_ns_get_node(dag, &UIP_IP_BUF->destipaddr);
if((uip_next_hdr != NULL && *uip_next_hdr == UIP_PROTO_ROUTING
&& UIP_RH_BUF->routing_type == RPL_RH_TYPE_SRH) ||
(dest_node != NULL && root_node != NULL &&
dest_node->parent == root_node)) {
/* 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
* address, via rpl_process_srh_header. We turn this address into a link-local to enable
* 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;
return 0;
}
/*---------------------------------------------------------------------------*/
int
rpl_update_header_empty(void)
rpl_process_srh_header(void)
{
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_TCP:
case UIP_PROTO_UDP:
case UIP_PROTO_ICMP6:
case UIP_PROTO_NONE:
uip_next_hdr = NULL;
break;
case UIP_PROTO_HBHO:
case UIP_PROTO_DESTO:
case UIP_PROTO_FRAG:
/* Move to next header */
if(uip_next_hdr != &UIP_IP_BUF->proto) {
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
}
uip_next_hdr = &UIP_EXT_BUF->next;
break;
default:
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;
}
/*---------------------------------------------------------------------------*/
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 */
uint8_t temp_len;
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;
}
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;
if(node == root_node) {
PRINTF("RPL: SRH no need to insert SRH\n");
return 0;
}
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;
PRINTF("RPL: SRH Hop ");
PRINT6ADDR(&node_addr);
PRINTF("\n");
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 */
if(uip_len + ext_len > UIP_BUFSIZE) {
PRINTF("RPL: Packet too long: impossible to add source routing header (%u bytes)\n", ext_len);
return 1;
}
/* 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 */
temp_len = UIP_IP_BUF->len[1];
UIP_IP_BUF->len[1] += ext_len;
if(UIP_IP_BUF->len[1] < temp_len) {
UIP_IP_BUF->len[0]++;
}
uip_ext_len += ext_len;
uip_len += ext_len;
return 1;
}
#else /* RPL_WITH_NON_STORING */
int insert_srh_header(void);
#endif /* RPL_WITH_NON_STORING */
/*---------------------------------------------------------------------------*/
static int
update_hbh_header(void)
{
rpl_instance_t *instance;
int uip_ext_opt_offset;
@ -209,39 +492,30 @@ rpl_update_header_empty(void)
switch(UIP_IP_BUF->proto) {
case UIP_PROTO_HBHO:
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
uip_ext_len = last_uip_ext_len;
return 0;
return 1;
}
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_type != UIP_EXT_HDR_OPT_RPL) {
PRINTF("RPL: Non RPL Hop-by-hop option support not implemented\n");
uip_ext_len = last_uip_ext_len;
return 0;
return 1;
}
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_len != RPL_HDR_OPT_LEN) {
PRINTF("RPL: RPL Hop-by-hop option has wrong length\n");
uip_ext_len = last_uip_ext_len;
return 0;
return 1;
}
instance = rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance);
if(instance == NULL || !instance->used || !instance->current_dag->joined) {
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect instance\n");
return 0;
return 1;
}
break;
default:
#if RPL_INSERT_HBH_OPTION
PRINTF("RPL: No hop-by-hop option found, creating it\n");
if(uip_len + RPL_HOP_BY_HOP_LEN > UIP_BUFSIZE) {
PRINTF("RPL: Packet too long: impossible to add hop-by-hop option\n");
uip_ext_len = last_uip_ext_len;
return 0;
}
set_rpl_opt(uip_ext_opt_offset);
uip_ext_len = last_uip_ext_len + RPL_HOP_BY_HOP_LEN;
#endif
return 0;
PRINTF("RPL: No hop-by-hop option found\n");
return 1;
}
switch(UIP_EXT_HDR_OPT_BUF->type) {
@ -249,51 +523,98 @@ rpl_update_header_empty(void)
PRINTF("RPL: Updating RPL option\n");
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(instance->current_dag->rank);
/* Check the direction of the down flag, as per Section 11.2.2.3,
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. */
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,
but it is not feasible yet, so we send a No-Path DAO instead */
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);
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,
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. */
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,
but it is not feasible yet, so we send a No-Path DAO instead */
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;
}
/* Drop packet */
return 1;
}
} else {
/* Set the down extension flag correctly as described in Section
11.2 of RFC6550. If the packet progresses along a DAO route,
the down flag should be set. */
if(uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr) == NULL) {
/* No route was found, so this packet will go towards the RPL
root. If so, we should not set the down flag. */
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");
/* Set the down extension flag correctly as described in Section
11.2 of RFC6550. If the packet progresses along a DAO route,
the down flag should be set. */
if(uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr) == NULL) {
/* No route was found, so this packet will go towards the RPL
root. If so, we should not set the down flag. */
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");
}
}
}
uip_ext_len = last_uip_ext_len;
return 0;
return 1;
default:
PRINTF("RPL: Multi Hop-by-hop options not implemented\n");
uip_ext_len = last_uip_ext_len;
return 0;
return 1;
}
}
/*---------------------------------------------------------------------------*/
static int
insert_hbh_header(void)
{
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");
if(uip_len + RPL_HOP_BY_HOP_LEN > UIP_BUFSIZE) {
PRINTF("RPL: Packet too long: impossible to add hop-by-hop option\n");
uip_ext_len = last_uip_ext_len;
return 0;
}
/* 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;
UIP_EXT_HDR_OPT_RPL_BUF->instance = 0;
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = 0;
uip_len += RPL_HOP_BY_HOP_LEN;
temp_len = UIP_IP_BUF->len[1];
UIP_IP_BUF->len[1] += RPL_HOP_BY_HOP_LEN;
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;
return 1;
}
/*---------------------------------------------------------------------------*/
int
rpl_update_header_final(uip_ipaddr_t *addr)
rpl_finalize_header(uip_ipaddr_t *addr)
{
rpl_parent_t *parent;
int uip_ext_opt_offset;
@ -304,10 +625,10 @@ rpl_update_header_final(uip_ipaddr_t *addr)
uip_ext_opt_offset = 2;
if(UIP_IP_BUF->proto == UIP_PROTO_HBHO) {
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
PRINTF("RPL: Non RPL Hop-by-hop options support not implemented\n");
uip_ext_len = last_uip_ext_len;
return 0;
return 1;
}
if(UIP_EXT_HDR_OPT_BUF->type == UIP_EXT_HDR_OPT_RPL) {
@ -315,7 +636,7 @@ rpl_update_header_final(uip_ipaddr_t *addr)
PRINTF("RPL: Updating RPL option\n");
if(default_instance == NULL || !default_instance->used || !default_instance->current_dag->joined) {
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect default instance\n");
return 1;
return 0;
}
parent = rpl_find_parent(default_instance->current_dag, addr);
if(parent == NULL || parent != parent->dag->preferred_parent) {
@ -326,78 +647,100 @@ rpl_update_header_final(uip_ipaddr_t *addr)
}
}
}
return 0;
return 1;
}
/*---------------------------------------------------------------------------*/
void
rpl_remove_header(void)
{
uint8_t temp_len;
uint8_t rpl_ext_hdr_len;
uint8_t *uip_next_hdr;
uip_ext_len = 0;
uip_next_hdr = &UIP_IP_BUF->proto;
PRINTF("RPL: Verifying the presence of the RPL header option\n");
switch(UIP_IP_BUF->proto){
case UIP_PROTO_HBHO:
PRINTF("RPL: Removing the RPL header option\n");
UIP_IP_BUF->proto = UIP_HBHO_BUF->next;
temp_len = UIP_IP_BUF->len[1];
uip_len -= UIP_HBHO_BUF->len + 8;
UIP_IP_BUF->len[1] -= UIP_HBHO_BUF->len + 8;
if(UIP_IP_BUF->len[1] > temp_len) {
UIP_IP_BUF->len[0]--;
PRINTF("RPL: Verifying the presence of RPL extension headers\n");
/* Look for hop-by-hop and routing headers */
while(uip_next_hdr != NULL) {
switch(*uip_next_hdr) {
case UIP_PROTO_TCP:
case UIP_PROTO_UDP:
case UIP_PROTO_ICMP6:
case UIP_PROTO_NONE:
return;
case UIP_PROTO_HBHO:
case UIP_PROTO_ROUTING:
/* 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);
break;
default:
/* Move to next header */
if(uip_next_hdr != &UIP_IP_BUF->proto) {
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
}
uip_next_hdr = &UIP_EXT_BUF->next;
break;
}
memmove(UIP_EXT_BUF, UIP_HBHO_NEXT_BUF, uip_len - UIP_IPH_LEN);
break;
default:
PRINTF("RPL: No hop-by-hop Option found\n");
}
}
/*---------------------------------------------------------------------------*/
uint8_t
rpl_invert_header(void)
{
uint8_t uip_ext_opt_offset;
uint8_t last_uip_ext_len;
last_uip_ext_len = uip_ext_len;
uip_ext_len = 0;
uip_ext_opt_offset = 2;
PRINTF("RPL: Verifying the presence of the RPL header option\n");
switch(UIP_IP_BUF->proto) {
case UIP_PROTO_HBHO:
break;
default:
PRINTF("RPL: No hop-by-hop Option found\n");
uip_ext_len = last_uip_ext_len;
return 0;
}
switch (UIP_EXT_HDR_OPT_BUF->type) {
case UIP_EXT_HDR_OPT_RPL:
PRINTF("RPL: Updating RPL option (switching direction)\n");
UIP_EXT_HDR_OPT_RPL_BUF->flags &= RPL_HDR_OPT_DOWN;
UIP_EXT_HDR_OPT_RPL_BUF->flags ^= RPL_HDR_OPT_DOWN;
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance)->current_dag->rank);
uip_ext_len = last_uip_ext_len;
return RPL_HOP_BY_HOP_LEN;
default:
PRINTF("RPL: Multi Hop-by-hop options not implemented\n");
uip_ext_len = last_uip_ext_len;
return 0;
}
}
/*---------------------------------------------------------------------------*/
void
rpl_insert_header(void)
{
#if RPL_INSERT_HBH_OPTION
if(default_instance != NULL && !uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
rpl_update_header_empty();
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)) {
return;
}
if(RPL_IS_STORING(default_instance)) {
insert_hbh_header();
}
if(RPL_IS_NON_STORING(default_instance)) {
if(default_instance->current_dag != NULL) {
if(default_instance->current_dag->rank == ROOT_RANK(default_instance)) {
insert_srh_header();
} else {
insert_hbh_header();
}
}
}
#endif
}
/*---------------------------------------------------------------------------*/
int
rpl_update_header(void)
{
if(default_instance == NULL) {
return 0;
}
if(default_instance->current_dag != NULL) {
if(default_instance->current_dag->rank == ROOT_RANK(default_instance)) {
/* At the root, remove headers if any, and insert SRH or HBH
* (SRH is inserted only if the destination is in the DODAG) */
rpl_remove_header();
if(RPL_IS_NON_STORING(default_instance)) {
return insert_srh_header();
} else {
return insert_hbh_header();
}
} else {
return update_hbh_header();
}
} else {
return 0;
}
}
/** @}*/

View File

@ -51,8 +51,10 @@
#include "net/ipv6/uip-nd6.h"
#include "net/ipv6/uip-icmp6.h"
#include "net/rpl/rpl-private.h"
#include "net/rpl/rpl-ns.h"
#include "net/packetbuf.h"
#include "net/ipv6/multicast/uip-mcast6.h"
#include "random.h"
#include <limits.h>
#include <string.h>
@ -76,6 +78,9 @@ static void dio_input(void);
static void dao_input(void);
static void dao_ack_input(void);
static void dao_output_target_seq(rpl_parent_t *parent, uip_ipaddr_t *prefix,
uint8_t lifetime, uint8_t seq_no);
/* some debug callbacks useful when debugging RPL networks */
#ifdef RPL_DEBUG_DIO_INPUT
void RPL_DEBUG_DIO_INPUT(uip_ipaddr_t *, rpl_dio_t *);
@ -87,8 +92,6 @@ void RPL_DEBUG_DAO_OUTPUT(rpl_parent_t *);
static uint8_t dao_sequence = RPL_LOLLIPOP_INIT;
extern rpl_of_t RPL_OF;
#if RPL_CONF_MULTICAST
static uip_mcast6_route_t *mcast_group;
#endif
@ -99,6 +102,40 @@ UIP_ICMP6_HANDLER(dio_handler, ICMP6_RPL, RPL_CODE_DIO, dio_input);
UIP_ICMP6_HANDLER(dao_handler, ICMP6_RPL, RPL_CODE_DAO, dao_input);
UIP_ICMP6_HANDLER(dao_ack_handler, ICMP6_RPL, RPL_CODE_DAO_ACK, dao_ack_input);
/*---------------------------------------------------------------------------*/
#if RPL_WITH_DAO_ACK
static uip_ds6_route_t *
find_route_entry_by_dao_ack(uint8_t seq)
{
uip_ds6_route_t *re;
re = uip_ds6_route_head();
while(re != NULL) {
if(re->state.dao_seqno_out == seq && RPL_ROUTE_IS_DAO_PENDING(re)) {
/* found it! */
return re;
}
re = uip_ds6_route_next(re);
}
return NULL;
}
#endif /* RPL_WITH_DAO_ACK */
#if RPL_WITH_STORING
/* prepare for forwarding of DAO */
static uint8_t
prepare_for_dao_fwd(uint8_t sequence, uip_ds6_route_t *rep)
{
/* not pending - or pending but not a retransmission */
RPL_LOLLIPOP_INCREMENT(dao_sequence);
/* set DAO pending and sequence numbers */
rep->state.dao_seqno_in = sequence;
rep->state.dao_seqno_out = dao_sequence;
RPL_ROUTE_SET_DAO_PENDING(rep);
return dao_sequence;
}
#endif /* RPL_WITH_STORING */
/*---------------------------------------------------------------------------*/
static int
get_global_addr(uip_ipaddr_t *addr)
{
@ -147,6 +184,34 @@ set16(uint8_t *buffer, int pos, uint16_t value)
buffer[pos++] = value & 0xff;
}
/*---------------------------------------------------------------------------*/
uip_ds6_nbr_t *
rpl_icmp6_update_nbr_table(uip_ipaddr_t *from, nbr_table_reason_t reason, void *data)
{
uip_ds6_nbr_t *nbr;
if((nbr = uip_ds6_nbr_lookup(from)) == NULL) {
if((nbr = uip_ds6_nbr_add(from, (uip_lladdr_t *)
packetbuf_addr(PACKETBUF_ADDR_SENDER),
0, NBR_REACHABLE, reason, data)) != NULL) {
PRINTF("RPL: Neighbor added to neighbor cache ");
PRINT6ADDR(from);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
}
}
if(nbr != NULL) {
#if UIP_ND6_SEND_NA
/* set reachable timer if we added or found the nbr entry - and update
neighbor entry to reachable to avoid sending NS/NA, etc. */
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
nbr->state = NBR_REACHABLE;
#endif /* UIP_ND6_SEND_NA */
}
return nbr;
}
/*---------------------------------------------------------------------------*/
static void
dis_input(void)
{
@ -170,8 +235,19 @@ dis_input(void)
rpl_reset_dio_timer(instance);
} else {
#endif /* !RPL_LEAF_ONLY */
PRINTF("RPL: Unicast DIS, reply to sender\n");
dio_output(instance, &UIP_IP_BUF->srcipaddr);
/* Check if this neighbor should be added according to the policy. */
if(rpl_icmp6_update_nbr_table(&UIP_IP_BUF->srcipaddr,
NBR_TABLE_REASON_RPL_DIS, NULL) == NULL) {
PRINTF("RPL: Out of Memory, not sending unicast DIO, DIS from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
} else {
PRINTF("RPL: Unicast DIS, reply to sender\n");
dio_output(instance, &UIP_IP_BUF->srcipaddr);
}
/* } */
}
}
}
@ -218,7 +294,6 @@ dio_input(void)
int i;
int len;
uip_ipaddr_t from;
uip_ds6_nbr_t *nbr;
memset(&dio, 0, sizeof(dio));
@ -228,7 +303,7 @@ dio_input(void)
dio.dag_redund = RPL_DIO_REDUNDANCY;
dio.dag_min_hoprankinc = RPL_MIN_HOPRANKINC;
dio.dag_max_rankinc = RPL_MAX_RANKINC;
dio.ocp = RPL_OF.ocp;
dio.ocp = RPL_OF_OCP;
dio.default_lifetime = RPL_DEFAULT_LIFETIME;
dio.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
@ -239,30 +314,6 @@ dio_input(void)
PRINT6ADDR(&from);
PRINTF("\n");
if((nbr = uip_ds6_nbr_lookup(&from)) == NULL) {
if((nbr = uip_ds6_nbr_add(&from, (uip_lladdr_t *)
packetbuf_addr(PACKETBUF_ADDR_SENDER),
0, NBR_REACHABLE)) != NULL) {
/* set reachable timer */
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
PRINTF("RPL: Neighbor added to neighbor cache ");
PRINT6ADDR(&from);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
} else {
PRINTF("RPL: Out of memory, dropping DIO from ");
PRINT6ADDR(&from);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
goto discard;
}
} else {
PRINTF("RPL: Neighbor already in neighbor cache\n");
}
buffer_length = uip_len - uip_l3_icmp_hdr_len;
/* Process the DIO base option. */
@ -428,6 +479,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
{
unsigned char *buffer;
int pos;
int is_root;
rpl_dag_t *dag = instance->current_dag;
#if !RPL_LEAF_ONLY
uip_ipaddr_t addr;
@ -448,6 +500,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
buffer = UIP_ICMP_PAYLOAD;
buffer[pos++] = instance->instance_id;
buffer[pos++] = dag->version;
is_root = (dag->rank == ROOT_RANK(instance));
#if RPL_LEAF_ONLY
PRINTF("RPL: LEAF ONLY DIO rank set to INFINITE_RANK\n");
@ -468,7 +521,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
buffer[pos++] = instance->dtsn_out;
if(uc_addr == NULL) {
if(RPL_DIO_REFRESH_DAO_ROUTES && is_root && uc_addr == NULL) {
/* Request new DAO to refresh route. We do not do this for unicast DIO
* in order to avoid DAO messages after a DIS-DIO update,
* or upon unicast DIO probing. */
@ -578,8 +631,9 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
}
/*---------------------------------------------------------------------------*/
static void
dao_input(void)
dao_input_storing(void)
{
#if RPL_WITH_STORING
uip_ipaddr_t dao_sender_addr;
rpl_dag_t *dag;
rpl_instance_t *instance;
@ -603,17 +657,13 @@ dao_input(void)
int learned_from;
rpl_parent_t *parent;
uip_ds6_nbr_t *nbr;
int is_root;
prefixlen = 0;
parent = NULL;
uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);
/* Destination Advertisement Object */
PRINTF("RPL: Received a DAO from ");
PRINT6ADDR(&dao_sender_addr);
PRINTF("\n");
buffer = UIP_ICMP_PAYLOAD;
buffer_length = uip_len - uip_l3_icmp_hdr_len;
@ -621,11 +671,6 @@ dao_input(void)
instance_id = buffer[pos++];
instance = rpl_get_instance(instance_id);
if(instance == NULL) {
PRINTF("RPL: Ignoring a DAO for an unknown RPL instance(%u)\n",
instance_id);
goto discard;
}
lifetime = instance->default_lifetime;
@ -635,11 +680,13 @@ dao_input(void)
sequence = buffer[pos++];
dag = instance->current_dag;
is_root = (dag->rank == ROOT_RANK(instance));
/* Is the DAG ID present? */
if(flags & RPL_DAO_D_FLAG) {
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
PRINTF("RPL: Ignoring a DAO for a DAG different from ours\n");
goto discard;
return;
}
pos += 16;
}
@ -647,8 +694,12 @@ dao_input(void)
learned_from = uip_is_addr_mcast(&dao_sender_addr) ?
RPL_ROUTE_FROM_MULTICAST_DAO : RPL_ROUTE_FROM_UNICAST_DAO;
PRINTF("RPL: DAO from %s\n",
learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast");
/* Destination Advertisement Object */
PRINTF("RPL: Received a (%s) DAO with sequence number %u from ",
learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast", sequence);
PRINT6ADDR(&dao_sender_addr);
PRINTF("\n");
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
/* Check whether this is a DAO forwarding loop. */
parent = rpl_find_parent(dag, &dao_sender_addr);
@ -660,7 +711,7 @@ dao_input(void)
DAG_RANK(parent->rank, instance), DAG_RANK(dag->rank, instance));
parent->rank = INFINITE_RANK;
parent->flags |= RPL_PARENT_FLAG_UPDATED;
goto discard;
return;
}
/* If we get the DAO from our parent, we also have a loop. */
@ -668,7 +719,7 @@ dao_input(void)
PRINTF("RPL: Loop detected when receiving a unicast DAO from our parent\n");
parent->rank = INFINITE_RANK;
parent->flags |= RPL_PARENT_FLAG_UPDATED;
goto discard;
return;
}
}
@ -721,91 +772,319 @@ dao_input(void)
PRINTF("RPL: No-Path DAO received\n");
/* No-Path DAO received; invoke the route purging routine. */
if(rep != NULL &&
rep->state.nopath_received == 0 &&
!RPL_ROUTE_IS_NOPATH_RECEIVED(rep) &&
rep->length == prefixlen &&
uip_ds6_route_nexthop(rep) != NULL &&
uip_ipaddr_cmp(uip_ds6_route_nexthop(rep), &dao_sender_addr)) {
PRINTF("RPL: Setting expiration timer for prefix ");
PRINT6ADDR(&prefix);
PRINTF("\n");
rep->state.nopath_received = 1;
RPL_ROUTE_SET_NOPATH_RECEIVED(rep);
rep->state.lifetime = RPL_NOPATH_REMOVAL_DELAY;
/* We forward the incoming No-Path DAO to our parent, if we have
one. */
if(dag->preferred_parent != NULL &&
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
PRINTF("RPL: Forwarding No-Path DAO to parent ");
uint8_t out_seq;
out_seq = prepare_for_dao_fwd(sequence, rep);
PRINTF("RPL: Forwarding No-path DAO to parent - out_seq:%d",
out_seq);
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
PRINTF("\n");
buffer = UIP_ICMP_PAYLOAD;
buffer[3] = out_seq; /* add an outgoing seq no before fwd */
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
}
if(flags & RPL_DAO_K_FLAG) {
dao_ack_output(instance, &dao_sender_addr, sequence);
}
}
goto discard;
/* independent if we remove or not - ACK the request */
if(flags & RPL_DAO_K_FLAG) {
/* indicate that we accepted the no-path DAO */
uip_clear_buf();
dao_ack_output(instance, &dao_sender_addr, sequence,
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
}
return;
}
PRINTF("RPL: adding DAO route\n");
PRINTF("RPL: Adding DAO route\n");
if((nbr = uip_ds6_nbr_lookup(&dao_sender_addr)) == NULL) {
if((nbr = uip_ds6_nbr_add(&dao_sender_addr,
(uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER),
0, NBR_REACHABLE)) != NULL) {
/* set reachable timer */
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
PRINTF("RPL: Neighbor added to neighbor cache ");
PRINT6ADDR(&dao_sender_addr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
} else {
PRINTF("RPL: Out of Memory, dropping DAO from ");
PRINT6ADDR(&dao_sender_addr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
goto discard;
/* Update and add neighbor - if no room - fail. */
if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr, NBR_TABLE_REASON_RPL_DAO, instance)) == NULL) {
PRINTF("RPL: Out of Memory, dropping DAO from ");
PRINT6ADDR(&dao_sender_addr);
PRINTF(", ");
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
PRINTF("\n");
if(flags & RPL_DAO_K_FLAG) {
/* signal the failure to add the node */
dao_ack_output(instance, &dao_sender_addr, sequence,
is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
RPL_DAO_ACK_UNABLE_TO_ACCEPT);
}
} else {
PRINTF("RPL: Neighbor already in neighbor cache\n");
return;
}
rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr);
if(rep == NULL) {
RPL_STAT(rpl_stats.mem_overflows++);
PRINTF("RPL: Could not add a route after receiving a DAO\n");
goto discard;
if(flags & RPL_DAO_K_FLAG) {
/* signal the failure to add the node */
dao_ack_output(instance, &dao_sender_addr, sequence,
is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
RPL_DAO_ACK_UNABLE_TO_ACCEPT);
}
return;
}
/* set lifetime and clear NOPATH bit */
rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
rep->state.learned_from = learned_from;
rep->state.nopath_received = 0;
RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);
#if RPL_CONF_MULTICAST
fwd_dao:
#endif
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
int should_ack = 0;
if(flags & RPL_DAO_K_FLAG) {
/*
* check if this route is already installed and we can ack now!
* not pending - and same seq-no means that we can ack.
* (e.g. the route is installed already so it will not take any
* more room that it already takes - so should be ok!)
*/
if((!RPL_ROUTE_IS_DAO_PENDING(rep) &&
rep->state.dao_seqno_in == sequence) ||
dag->rank == ROOT_RANK(instance)) {
should_ack = 1;
}
}
if(dag->preferred_parent != NULL &&
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
uint8_t out_seq = 0;
/* if this is pending and we get the same seq no it is a retrans */
if(RPL_ROUTE_IS_DAO_PENDING(rep) &&
rep->state.dao_seqno_in == sequence) {
/* keep the same seq-no as before for parent also */
out_seq = rep->state.dao_seqno_out;
} else {
out_seq = prepare_for_dao_fwd(sequence, rep);
}
PRINTF("RPL: Forwarding DAO to parent ");
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
PRINTF("\n");
PRINTF(" in seq: %d out seq: %d\n", sequence, out_seq);
buffer = UIP_ICMP_PAYLOAD;
buffer[3] = out_seq; /* add an outgoing seq no before fwd */
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
}
if(flags & RPL_DAO_K_FLAG) {
dao_ack_output(instance, &dao_sender_addr, sequence);
if(should_ack) {
PRINTF("RPL: Sending DAO ACK\n");
uip_clear_buf();
dao_ack_output(instance, &dao_sender_addr, sequence,
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
}
}
#endif /* RPL_WITH_STORING */
}
/*---------------------------------------------------------------------------*/
static void
dao_input_nonstoring(void)
{
#if RPL_WITH_NON_STORING
uip_ipaddr_t dao_sender_addr;
uip_ipaddr_t dao_parent_addr;
rpl_dag_t *dag;
rpl_instance_t *instance;
unsigned char *buffer;
uint16_t sequence;
uint8_t instance_id;
uint8_t lifetime;
uint8_t prefixlen;
uint8_t flags;
uint8_t subopt_type;
uip_ipaddr_t prefix;
uint8_t buffer_length;
int pos;
int len;
int i;
prefixlen = 0;
uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);
memset(&dao_parent_addr, 0, 16);
buffer = UIP_ICMP_PAYLOAD;
buffer_length = uip_len - uip_l3_icmp_hdr_len;
pos = 0;
instance_id = buffer[pos++];
instance = rpl_get_instance(instance_id);
lifetime = instance->default_lifetime;
flags = buffer[pos++];
/* reserved */
pos++;
sequence = buffer[pos++];
dag = instance->current_dag;
/* Is the DAG ID present? */
if(flags & RPL_DAO_D_FLAG) {
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
PRINTF("RPL: Ignoring a DAO for a DAG different from ours\n");
return;
}
pos += 16;
}
/* Check if there are any RPL options present. */
for(i = pos; i < buffer_length; i += len) {
subopt_type = buffer[i];
if(subopt_type == RPL_OPTION_PAD1) {
len = 1;
} else {
/* The option consists of a two-byte header and a payload. */
len = 2 + buffer[i + 1];
}
switch(subopt_type) {
case RPL_OPTION_TARGET:
/* Handle the target option. */
prefixlen = buffer[i + 3];
memset(&prefix, 0, sizeof(prefix));
memcpy(&prefix, buffer + i + 4, (prefixlen + 7) / CHAR_BIT);
break;
case RPL_OPTION_TRANSIT:
/* The path sequence and control are ignored. */
/* pathcontrol = buffer[i + 3];
pathsequence = buffer[i + 4];*/
lifetime = buffer[i + 5];
if(len >= 20) {
memcpy(&dao_parent_addr, buffer + i + 6, 16);
}
break;
}
}
PRINTF("RPL: DAO lifetime: %u, prefix length: %u prefix: ",
(unsigned)lifetime, (unsigned)prefixlen);
PRINT6ADDR(&prefix);
PRINTF(", parent: ");
PRINT6ADDR(&dao_parent_addr);
PRINTF(" \n");
if(lifetime == RPL_ZERO_LIFETIME) {
PRINTF("RPL: No-Path DAO received\n");
rpl_ns_expire_parent(dag, &prefix, &dao_parent_addr);
} else {
if(rpl_ns_update_node(dag, &prefix, &dao_parent_addr, RPL_LIFETIME(instance, lifetime)) == NULL) {
PRINTF("RPL: failed to add link\n");
return;
}
}
if(flags & RPL_DAO_K_FLAG) {
PRINTF("RPL: Sending DAO ACK\n");
uip_clear_buf();
dao_ack_output(instance, &dao_sender_addr, sequence,
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
}
#endif /* RPL_WITH_NON_STORING */
}
/*---------------------------------------------------------------------------*/
static void
dao_input(void)
{
rpl_instance_t *instance;
uint8_t instance_id;
/* Destination Advertisement Object */
PRINTF("RPL: Received a DAO from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF("\n");
instance_id = UIP_ICMP_PAYLOAD[0];
instance = rpl_get_instance(instance_id);
if(instance == NULL) {
PRINTF("RPL: Ignoring a DAO for an unknown RPL instance(%u)\n",
instance_id);
goto discard;
}
if(RPL_IS_STORING(instance)) {
dao_input_storing();
} else if(RPL_IS_NON_STORING(instance)) {
dao_input_nonstoring();
}
discard:
uip_clear_buf();
}
/*---------------------------------------------------------------------------*/
#if RPL_WITH_DAO_ACK
static void
handle_dao_retransmission(void *ptr)
{
rpl_parent_t *parent;
uip_ipaddr_t prefix;
rpl_instance_t *instance;
parent = ptr;
if(parent == NULL || parent->dag == NULL || parent->dag->instance == NULL) {
return;
}
instance = parent->dag->instance;
if(instance->my_dao_transmissions >= RPL_DAO_MAX_RETRANSMISSIONS) {
/* No more retransmissions - give up. */
if(instance->lifetime_unit == 0xffff && instance->default_lifetime == 0xff) {
/*
* ContikiRPL was previously using infinite lifetime for routes
* and no DAO_ACK configured. This probably means that the root
* and possibly other nodes might be running an old version that
* does not support DAO ack. Assume that everything is ok for
* now and let the normal repair mechanisms detect any problems.
*/
return;
}
if(RPL_IS_STORING(instance) && instance->of->dao_ack_callback) {
/* Inform the objective function about the timeout. */
instance->of->dao_ack_callback(parent, RPL_DAO_ACK_TIMEOUT);
}
/* Perform local repair and hope to find another parent. */
rpl_local_repair(instance);
return;
}
PRINTF("RPL: will retransmit DAO - seq:%d trans:%d\n", instance->my_dao_seqno,
instance->my_dao_transmissions);
if(get_global_addr(&prefix) == 0) {
return;
}
ctimer_set(&instance->dao_retransmit_timer,
RPL_DAO_RETRANSMISSION_TIMEOUT / 2 +
(random_rand() % (RPL_DAO_RETRANSMISSION_TIMEOUT / 2)),
handle_dao_retransmission, parent);
instance->my_dao_transmissions++;
dao_output_target_seq(parent, &prefix,
instance->default_lifetime, instance->my_dao_seqno);
}
#endif /* RPL_WITH_DAO_ACK */
/*---------------------------------------------------------------------------*/
void
dao_output(rpl_parent_t *parent, uint8_t lifetime)
{
@ -817,18 +1096,51 @@ dao_output(rpl_parent_t *parent, uint8_t lifetime)
return;
}
if(parent == NULL || parent->dag == NULL || parent->dag->instance == NULL) {
return;
}
RPL_LOLLIPOP_INCREMENT(dao_sequence);
#if RPL_WITH_DAO_ACK
/* set up the state since this will be the first transmission of DAO */
/* retransmissions will call directly to dao_output_target_seq */
/* keep track of my own sending of DAO for handling ack and loss of ack */
if(lifetime != RPL_ZERO_LIFETIME) {
rpl_instance_t *instance;
instance = parent->dag->instance;
instance->my_dao_seqno = dao_sequence;
instance->my_dao_transmissions = 1;
ctimer_set(&instance->dao_retransmit_timer, RPL_DAO_RETRANSMISSION_TIMEOUT,
handle_dao_retransmission, parent);
}
#else
/* We know that we have tried to register so now we are assuming
that we have a down-link - unless this is a zero lifetime one */
parent->dag->instance->has_downward_route = lifetime != RPL_ZERO_LIFETIME;
#endif /* RPL_WITH_DAO_ACK */
/* Sending a DAO with own prefix as target */
dao_output_target(parent, &prefix, lifetime);
}
/*---------------------------------------------------------------------------*/
void
dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
{
dao_output_target_seq(parent, prefix, lifetime, dao_sequence);
}
/*---------------------------------------------------------------------------*/
static void
dao_output_target_seq(rpl_parent_t *parent, uip_ipaddr_t *prefix,
uint8_t lifetime, uint8_t seq_no)
{
rpl_dag_t *dag;
rpl_instance_t *instance;
unsigned char *buffer;
uint8_t prefixlen;
int pos;
uip_ipaddr_t *parent_ipaddr = NULL;
uip_ipaddr_t *dest_ipaddr = NULL;
/* Destination Advertisement Object */
@ -842,6 +1154,12 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
return;
}
parent_ipaddr = rpl_get_parent_ipaddr(parent);
if(parent_ipaddr == NULL) {
PRINTF("RPL dao_output_target error parent IP address NULL\n");
return;
}
dag = parent->dag;
if(dag == NULL) {
PRINTF("RPL dao_output_target error dag NULL\n");
@ -863,8 +1181,6 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
#endif
buffer = UIP_ICMP_PAYLOAD;
RPL_LOLLIPOP_INCREMENT(dao_sequence);
pos = 0;
buffer[pos++] = instance->instance_id;
@ -872,12 +1188,14 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
#if RPL_DAO_SPECIFY_DAG
buffer[pos] |= RPL_DAO_D_FLAG;
#endif /* RPL_DAO_SPECIFY_DAG */
#if RPL_CONF_DAO_ACK
buffer[pos] |= RPL_DAO_K_FLAG;
#endif /* RPL_CONF_DAO_ACK */
#if RPL_WITH_DAO_ACK
if(lifetime != RPL_ZERO_LIFETIME) {
buffer[pos] |= RPL_DAO_K_FLAG;
}
#endif /* RPL_WITH_DAO_ACK */
++pos;
buffer[pos++] = 0; /* reserved */
buffer[pos++] = dao_sequence;
buffer[pos++] = seq_no;
#if RPL_DAO_SPECIFY_DAG
memcpy(buffer + pos, &dag->dag_id, sizeof(dag->dag_id));
pos+=sizeof(dag->dag_id);
@ -894,61 +1212,154 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
/* Create a transit information sub-option. */
buffer[pos++] = RPL_OPTION_TRANSIT;
buffer[pos++] = 4;
buffer[pos++] = (instance->mop != RPL_MOP_NON_STORING) ? 4 : 20;
buffer[pos++] = 0; /* flags - ignored */
buffer[pos++] = 0; /* path control - ignored */
buffer[pos++] = 0; /* path seq - ignored */
buffer[pos++] = lifetime;
PRINTF("RPL: Sending %sDAO with prefix ", lifetime == RPL_ZERO_LIFETIME ? "No-Path " : "");
if(instance->mop != RPL_MOP_NON_STORING) {
/* Send DAO to parent */
dest_ipaddr = parent_ipaddr;
} else {
/* Include parent global IP address */
memcpy(buffer + pos, &parent->dag->dag_id, 8); /* Prefix */
pos += 8;
memcpy(buffer + pos, ((const unsigned char *)parent_ipaddr) + 8, 8); /* Interface identifier */
pos += 8;
/* Send DAO to root */
dest_ipaddr = &parent->dag->dag_id;
}
PRINTF("RPL: Sending a %sDAO with sequence number %u, lifetime %u, prefix ",
lifetime == RPL_ZERO_LIFETIME ? "No-Path " : "", seq_no, lifetime);
PRINT6ADDR(prefix);
PRINTF(" to ");
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
PRINT6ADDR(dest_ipaddr);
PRINTF(" , parent ");
PRINT6ADDR(parent_ipaddr);
PRINTF("\n");
if(rpl_get_parent_ipaddr(parent) != NULL) {
uip_icmp6_send(rpl_get_parent_ipaddr(parent), ICMP6_RPL, RPL_CODE_DAO, pos);
if(dest_ipaddr != NULL) {
uip_icmp6_send(dest_ipaddr, ICMP6_RPL, RPL_CODE_DAO, pos);
}
}
/*---------------------------------------------------------------------------*/
static void
dao_ack_input(void)
{
#if DEBUG
unsigned char *buffer;
#if RPL_WITH_DAO_ACK
uint8_t *buffer;
uint8_t instance_id;
uint8_t sequence;
uint8_t status;
rpl_instance_t *instance;
rpl_parent_t *parent;
buffer = UIP_ICMP_PAYLOAD;
instance_id = buffer[0];
sequence = buffer[2];
status = buffer[3];
PRINTF("RPL: Received a DAO ACK with sequence number %d and status %d from ",
sequence, status);
instance = rpl_get_instance(instance_id);
if(instance == NULL) {
uip_clear_buf();
return;
}
if(RPL_IS_STORING(instance)) {
parent = rpl_find_parent(instance->current_dag, &UIP_IP_BUF->srcipaddr);
if(parent == NULL) {
/* not a known instance - drop the packet and ignore */
uip_clear_buf();
return;
}
} else {
parent = NULL;
}
PRINTF("RPL: Received a DAO %s with sequence number %d (%d) and status %d from ",
status < 128 ? "ACK" : "NACK",
sequence, instance->my_dao_seqno, status);
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF("\n");
#endif /* DEBUG */
if(sequence == instance->my_dao_seqno) {
instance->has_downward_route = status < 128;
/* always stop the retransmit timer when the ACK arrived */
ctimer_stop(&instance->dao_retransmit_timer);
/* Inform objective function on status of the DAO ACK */
if(RPL_IS_STORING(instance) && instance->of->dao_ack_callback) {
instance->of->dao_ack_callback(parent, status);
}
#if RPL_REPAIR_ON_DAO_NACK
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
/*
* Failed the DAO transmission - need to remove the default route.
* Trigger a local repair since we can not get our DAO in.
*/
rpl_local_repair(instance);
}
#endif
} else if(RPL_IS_STORING(instance)) {
/* this DAO ACK should be forwarded to another recently registered route */
uip_ds6_route_t *re;
uip_ipaddr_t *nexthop;
if((re = find_route_entry_by_dao_ack(sequence)) != NULL) {
/* pick the recorded seq no from that node and forward DAO ACK - and
clear the pending flag*/
RPL_ROUTE_CLEAR_DAO_PENDING(re);
nexthop = uip_ds6_route_nexthop(re);
if(nexthop == NULL) {
PRINTF("RPL: No next hop to fwd DAO ACK to\n");
} else {
PRINTF("RPL: Fwd DAO ACK to:");
PRINT6ADDR(nexthop);
PRINTF("\n");
buffer[2] = re->state.dao_seqno_in;
uip_icmp6_send(nexthop, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
}
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
/* this node did not get in to the routing tables above... - remove */
uip_ds6_route_rm(re);
}
} else {
PRINTF("RPL: No route entry found to forward DAO ACK (seqno %u)\n", sequence);
}
}
#endif /* RPL_WITH_DAO_ACK */
uip_clear_buf();
}
/*---------------------------------------------------------------------------*/
void
dao_ack_output(rpl_instance_t *instance, uip_ipaddr_t *dest, uint8_t sequence)
dao_ack_output(rpl_instance_t *instance, uip_ipaddr_t *dest, uint8_t sequence,
uint8_t status)
{
#if RPL_WITH_DAO_ACK
unsigned char *buffer;
PRINTF("RPL: Sending a DAO ACK with sequence number %d to ", sequence);
PRINTF("RPL: Sending a DAO %s with sequence number %d to ", status < 128 ? "ACK" : "NACK", sequence);
PRINT6ADDR(dest);
PRINTF("\n");
PRINTF(" with status %d\n", status);
buffer = UIP_ICMP_PAYLOAD;
buffer[0] = instance->instance_id;
buffer[1] = 0;
buffer[2] = sequence;
buffer[3] = 0;
buffer[3] = status;
uip_icmp6_send(dest, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
#endif /* RPL_WITH_DAO_ACK */
}
/*---------------------------------------------------------------------------*/
void

View File

@ -32,7 +32,7 @@
/**
* \file
* The Minimum Rank with Hysteresis Objective Function (MRHOF)
* The Minimum Rank with Hysteresis Objective Function (MRHOF), RFC6719
*
* This implementation uses the estimated number of
* transmissions (ETX) as the additive routing metric,
@ -46,150 +46,187 @@
* @{
*/
#include "net/rpl/rpl.h"
#include "net/rpl/rpl-private.h"
#include "net/nbr-table.h"
#include "net/link-stats.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
static void reset(rpl_dag_t *);
static void neighbor_link_callback(rpl_parent_t *, int, int);
static rpl_parent_t *best_parent(rpl_parent_t *, rpl_parent_t *);
static rpl_dag_t *best_dag(rpl_dag_t *, rpl_dag_t *);
static rpl_rank_t calculate_rank(rpl_parent_t *, rpl_rank_t);
static void update_metric_container(rpl_instance_t *);
/* RFC6551 and RFC6719 do not mandate the use of a specific formula to
* compute the ETX value. This MRHOF implementation relies on the value
* computed by the link-stats module. It has an optional feature,
* RPL_MRHOF_CONF_SQUARED_ETX, that consists in squaring this value.
* This basically penalizes bad links while preserving the semantics of ETX
* (1 = perfect link, more = worse link). As a result, MRHOF will favor
* good links over short paths. Recommended when reliability is a priority.
* Without this feature, a hop with 50% PRR (ETX=2) is equivalent to two
* perfect hops with 100% PRR (ETX=1+1=2). With this feature, the former
* path obtains ETX=2*2=4 and the former ETX=1*1+1*1=2. */
#ifdef RPL_MRHOF_CONF_SQUARED_ETX
#define RPL_MRHOF_SQUARED_ETX RPL_MRHOF_CONF_SQUARED_ETX
#else /* RPL_MRHOF_CONF_SQUARED_ETX */
#define RPL_MRHOF_SQUARED_ETX 0
#endif /* RPL_MRHOF_CONF_SQUARED_ETX */
rpl_of_t rpl_mrhof = {
reset,
neighbor_link_callback,
best_parent,
best_dag,
calculate_rank,
update_metric_container,
1
};
/* Constants for the ETX moving average */
#define ETX_SCALE 100
#define ETX_ALPHA 90
/* Reject parents that have a higher link metric than the following. */
#define MAX_LINK_METRIC 10
#if !RPL_MRHOF_SQUARED_ETX
/* Configuration parameters of RFC6719. Reject parents that have a higher
* link metric than the following. The default value is 512 but we use 1024. */
#define MAX_LINK_METRIC 1024 /* Eq ETX of 8 */
/* Hysteresis of MRHOF: the rank must differ more than PARENT_SWITCH_THRESHOLD_DIV
* in order to switch preferred parent. Default in RFC6719: 192, eq ETX of 1.5.
* We use a more aggressive setting: 96, eq ETX of 0.75.
*/
#define PARENT_SWITCH_THRESHOLD 96 /* Eq ETX of 0.75 */
#else /* !RPL_MRHOF_SQUARED_ETX */
#define MAX_LINK_METRIC 2048 /* Eq ETX of 4 */
#define PARENT_SWITCH_THRESHOLD 160 /* Eq ETX of 1.25 (results in a churn comparable
to the threshold of 96 in the non-squared case) */
#endif /* !RPL_MRHOF_SQUARED_ETX */
/* Reject parents that have a higher path cost than the following. */
#define MAX_PATH_COST 100
/*
* The rank must differ more than 1/PARENT_SWITCH_THRESHOLD_DIV in order
* to switch preferred parent.
*/
#define PARENT_SWITCH_THRESHOLD_DIV 2
typedef uint16_t rpl_path_metric_t;
static rpl_path_metric_t
calculate_path_metric(rpl_parent_t *p)
{
uip_ds6_nbr_t *nbr;
if(p == NULL) {
return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR;
}
nbr = rpl_get_nbr(p);
if(nbr == NULL) {
return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR;
}
#if RPL_DAG_MC == RPL_DAG_MC_NONE
{
return p->rank + (uint16_t)nbr->link_metric;
}
#elif RPL_DAG_MC == RPL_DAG_MC_ETX
return p->mc.obj.etx + (uint16_t)nbr->link_metric;
#elif RPL_DAG_MC == RPL_DAG_MC_ENERGY
return p->mc.obj.energy.energy_est + (uint16_t)nbr->link_metric;
#else
#error "Unsupported RPL_DAG_MC configured. See rpl.h."
#endif /* RPL_DAG_MC */
}
#define MAX_PATH_COST 32768 /* Eq path ETX of 256 */
/*---------------------------------------------------------------------------*/
static void
reset(rpl_dag_t *dag)
{
PRINTF("RPL: Reset MRHOF\n");
}
/*---------------------------------------------------------------------------*/
#if RPL_WITH_DAO_ACK
static void
neighbor_link_callback(rpl_parent_t *p, int status, int numtx)
dao_ack_callback(rpl_parent_t *p, int status)
{
uint16_t recorded_etx = 0;
uint16_t packet_etx = numtx * RPL_DAG_MC_ETX_DIVISOR;
uint16_t new_etx;
uip_ds6_nbr_t *nbr = NULL;
nbr = rpl_get_nbr(p);
if(nbr == NULL) {
/* No neighbor for this parent - something bad has occurred */
if(status == RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT) {
return;
}
recorded_etx = nbr->link_metric;
/* Do not penalize the ETX when collisions or transmission errors occur. */
if(status == MAC_TX_OK || status == MAC_TX_NOACK) {
if(status == MAC_TX_NOACK) {
packet_etx = MAX_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
}
if(p->flags & RPL_PARENT_FLAG_LINK_METRIC_VALID) {
/* We already have a valid link metric, use weighted moving average to update it */
new_etx = ((uint32_t)recorded_etx * ETX_ALPHA +
(uint32_t)packet_etx * (ETX_SCALE - ETX_ALPHA)) / ETX_SCALE;
} else {
/* We don't have a valid link metric, set it to the current packet's ETX */
new_etx = packet_etx;
/* Set link metric as valid */
p->flags |= RPL_PARENT_FLAG_LINK_METRIC_VALID;
}
PRINTF("RPL: ETX changed from %u to %u (packet ETX = %u)\n",
(unsigned)(recorded_etx / RPL_DAG_MC_ETX_DIVISOR),
(unsigned)(new_etx / RPL_DAG_MC_ETX_DIVISOR),
(unsigned)(packet_etx / RPL_DAG_MC_ETX_DIVISOR));
/* update the link metric for this nbr */
nbr->link_metric = new_etx;
/* here we need to handle failed DAO's and other stuff */
PRINTF("RPL: MRHOF - DAO ACK received with status: %d\n", status);
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
/* punish the ETX as if this was 10 packets lost */
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
} else if(status == RPL_DAO_ACK_TIMEOUT) { /* timeout = no ack */
/* punish the total lack of ACK with a similar punishment */
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
}
}
static rpl_rank_t
calculate_rank(rpl_parent_t *p, rpl_rank_t base_rank)
#endif /* RPL_WITH_DAO_ACK */
/*---------------------------------------------------------------------------*/
static uint16_t
parent_link_metric(rpl_parent_t *p)
{
rpl_rank_t new_rank;
rpl_rank_t rank_increase;
uip_ds6_nbr_t *nbr;
if(p == NULL || (nbr = rpl_get_nbr(p)) == NULL) {
if(base_rank == 0) {
return INFINITE_RANK;
}
rank_increase = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
} else {
rank_increase = nbr->link_metric;
if(base_rank == 0) {
base_rank = p->rank;
}
const struct link_stats *stats = rpl_get_parent_link_stats(p);
if(stats != NULL) {
#if RPL_MRHOF_SQUARED_ETX
uint32_t squared_etx = ((uint32_t)stats->etx * stats->etx) / LINK_STATS_ETX_DIVISOR;
return (uint16_t)MIN(squared_etx, 0xffff);
#else /* RPL_MRHOF_SQUARED_ETX */
return stats->etx;
#endif /* RPL_MRHOF_SQUARED_ETX */
}
if(INFINITE_RANK - base_rank < rank_increase) {
/* Reached the maximum rank. */
new_rank = INFINITE_RANK;
} else {
/* Calculate the rank based on the new rank information from DIO or
stored otherwise. */
new_rank = base_rank + rank_increase;
}
return new_rank;
return 0xffff;
}
/*---------------------------------------------------------------------------*/
static uint16_t
parent_path_cost(rpl_parent_t *p)
{
uint16_t base;
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
return 0xffff;
}
#if RPL_WITH_MC
/* Handle the different MC types */
switch(p->dag->instance->mc.type) {
case RPL_DAG_MC_ETX:
base = p->mc.obj.etx;
break;
case RPL_DAG_MC_ENERGY:
base = p->mc.obj.energy.energy_est << 8;
break;
default:
base = p->rank;
break;
}
#else /* RPL_WITH_MC */
base = p->rank;
#endif /* RPL_WITH_MC */
/* path cost upper bound: 0xffff */
return MIN((uint32_t)base + parent_link_metric(p), 0xffff);
}
/*---------------------------------------------------------------------------*/
static rpl_rank_t
rank_via_parent(rpl_parent_t *p)
{
uint16_t min_hoprankinc;
uint16_t path_cost;
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
return INFINITE_RANK;
}
min_hoprankinc = p->dag->instance->min_hoprankinc;
path_cost = parent_path_cost(p);
/* Rank lower-bound: parent rank + min_hoprankinc */
return MAX(MIN((uint32_t)p->rank + min_hoprankinc, 0xffff), path_cost);
}
/*---------------------------------------------------------------------------*/
static int
parent_is_acceptable(rpl_parent_t *p)
{
uint16_t link_metric = parent_link_metric(p);
uint16_t path_cost = parent_path_cost(p);
/* Exclude links with too high link metrics or path cost (RFC6719, 3.2.2) */
return link_metric <= MAX_LINK_METRIC && path_cost <= MAX_PATH_COST;
}
/*---------------------------------------------------------------------------*/
static int
parent_has_usable_link(rpl_parent_t *p)
{
uint16_t link_metric = parent_link_metric(p);
/* Exclude links with too high link metrics */
return link_metric <= MAX_LINK_METRIC;
}
/*---------------------------------------------------------------------------*/
static rpl_parent_t *
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
{
rpl_dag_t *dag;
uint16_t p1_cost;
uint16_t p2_cost;
int p1_is_acceptable;
int p2_is_acceptable;
p1_is_acceptable = p1 != NULL && parent_is_acceptable(p1);
p2_is_acceptable = p2 != NULL && parent_is_acceptable(p2);
if(!p1_is_acceptable) {
return p2_is_acceptable ? p2 : NULL;
}
if(!p2_is_acceptable) {
return p1_is_acceptable ? p1 : NULL;
}
dag = p1->dag; /* Both parents are in the same DAG. */
p1_cost = parent_path_cost(p1);
p2_cost = parent_path_cost(p2);
/* Maintain stability of the preferred parent in case of similar ranks. */
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
if(p1_cost < p2_cost + PARENT_SWITCH_THRESHOLD &&
p1_cost > p2_cost - PARENT_SWITCH_THRESHOLD) {
return dag->preferred_parent;
}
}
return p1_cost < p2_cost ? p1 : p2;
}
/*---------------------------------------------------------------------------*/
static rpl_dag_t *
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
{
@ -203,93 +240,77 @@ best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
return d1->rank < d2->rank ? d1 : d2;
}
static rpl_parent_t *
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
{
rpl_dag_t *dag;
rpl_path_metric_t min_diff;
rpl_path_metric_t p1_metric;
rpl_path_metric_t p2_metric;
dag = p1->dag; /* Both parents are in the same DAG. */
min_diff = RPL_DAG_MC_ETX_DIVISOR /
PARENT_SWITCH_THRESHOLD_DIV;
p1_metric = calculate_path_metric(p1);
p2_metric = calculate_path_metric(p2);
/* Maintain stability of the preferred parent in case of similar ranks. */
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
if(p1_metric < p2_metric + min_diff &&
p1_metric > p2_metric - min_diff) {
PRINTF("RPL: MRHOF hysteresis: %u <= %u <= %u\n",
p2_metric - min_diff,
p1_metric,
p2_metric + min_diff);
return dag->preferred_parent;
}
}
return p1_metric < p2_metric ? p1 : p2;
}
#if RPL_DAG_MC == RPL_DAG_MC_NONE
/*---------------------------------------------------------------------------*/
#if !RPL_WITH_MC
static void
update_metric_container(rpl_instance_t *instance)
{
instance->mc.type = RPL_DAG_MC;
instance->mc.type = RPL_DAG_MC_NONE;
}
#else
#else /* RPL_WITH_MC */
static void
update_metric_container(rpl_instance_t *instance)
{
rpl_path_metric_t path_metric;
rpl_dag_t *dag;
#if RPL_DAG_MC == RPL_DAG_MC_ENERGY
uint16_t path_cost;
uint8_t type;
#endif
instance->mc.type = RPL_DAG_MC;
instance->mc.flags = RPL_DAG_MC_FLAG_P;
instance->mc.aggr = RPL_DAG_MC_AGGR_ADDITIVE;
instance->mc.prec = 0;
dag = instance->current_dag;
if (!dag->joined) {
if(dag == NULL || !dag->joined) {
PRINTF("RPL: Cannot update the metric container when not joined\n");
return;
}
if(dag->rank == ROOT_RANK(instance)) {
path_metric = 0;
/* Configure MC at root only, other nodes are auto-configured when joining */
instance->mc.type = RPL_DAG_MC;
instance->mc.flags = 0;
instance->mc.aggr = RPL_DAG_MC_AGGR_ADDITIVE;
instance->mc.prec = 0;
path_cost = dag->rank;
} else {
path_metric = calculate_path_metric(dag->preferred_parent);
path_cost = parent_path_cost(dag->preferred_parent);
}
#if RPL_DAG_MC == RPL_DAG_MC_ETX
instance->mc.length = sizeof(instance->mc.obj.etx);
instance->mc.obj.etx = path_metric;
PRINTF("RPL: My path ETX to the root is %u.%u\n",
instance->mc.obj.etx / RPL_DAG_MC_ETX_DIVISOR,
(instance->mc.obj.etx % RPL_DAG_MC_ETX_DIVISOR * 100) /
RPL_DAG_MC_ETX_DIVISOR);
#elif RPL_DAG_MC == RPL_DAG_MC_ENERGY
instance->mc.length = sizeof(instance->mc.obj.energy);
if(dag->rank == ROOT_RANK(instance)) {
type = RPL_DAG_MC_ENERGY_TYPE_MAINS;
} else {
type = RPL_DAG_MC_ENERGY_TYPE_BATTERY;
/* Handle the different MC types */
switch(instance->mc.type) {
case RPL_DAG_MC_NONE:
break;
case RPL_DAG_MC_ETX:
instance->mc.length = sizeof(instance->mc.obj.etx);
instance->mc.obj.etx = path_cost;
break;
case RPL_DAG_MC_ENERGY:
instance->mc.length = sizeof(instance->mc.obj.energy);
if(dag->rank == ROOT_RANK(instance)) {
type = RPL_DAG_MC_ENERGY_TYPE_MAINS;
} else {
type = RPL_DAG_MC_ENERGY_TYPE_BATTERY;
}
instance->mc.obj.energy.flags = type << RPL_DAG_MC_ENERGY_TYPE;
/* Energy_est is only one byte, use the least significant byte of the path metric. */
instance->mc.obj.energy.energy_est = path_cost >> 8;
break;
default:
PRINTF("RPL: MRHOF, non-supported MC %u\n", instance->mc.type);
break;
}
instance->mc.obj.energy.flags = type << RPL_DAG_MC_ENERGY_TYPE;
instance->mc.obj.energy.energy_est = path_metric;
#endif /* RPL_DAG_MC == RPL_DAG_MC_ETX */
}
#endif /* RPL_DAG_MC == RPL_DAG_MC_NONE */
#endif /* RPL_WITH_MC */
/*---------------------------------------------------------------------------*/
rpl_of_t rpl_mrhof = {
reset,
#if RPL_WITH_DAO_ACK
dao_ack_callback,
#endif
parent_link_metric,
parent_has_usable_link,
parent_path_cost,
rank_via_parent,
best_parent,
best_dag,
update_metric_container,
RPL_OCP_MRHOF
};
/** @}*/

View File

@ -0,0 +1,256 @@
/*
* Copyright (c) 2014-2015, Yanzi Networks AB.
* 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 holders 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 HOLDERS 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.
*
*/
/**
* \addtogroup uip6
* @{
*/
/**
* \file
*
* Default RPL NBR policy
* decides when to add a new discovered node to the nbr table from RPL.
*
* \author Joakim Eriksson <joakime@sics.se>
* Contributors: Niclas Finne <nfi@sics.se>, Oriol Piñol <oriol@yanzi.se>,
*
*/
#include "net/rpl/rpl-private.h"
#include "net/nbr-table.h"
#include "net/ipv6/uip-ds6-nbr.h"
#include "net/ipv6/uip-ds6-route.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
/*
* Policy for neighbor adds
* - one node is locked (default route)
* - max X children (nexthops)
* - max Y "best parents"
* => at least MAX_NBRS - (Y + X + 1) free slots for other.
*
* NOTE: this policy assumes that all neighbors end up being IPv6
* neighbors and are not only MAC neighbors.
*/
#define MAX_CHILDREN (NBR_TABLE_MAX_NEIGHBORS - 2)
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
static int num_parents; /* any node that are possible parents */
static int num_children; /* all children that we have as nexthop */
static int num_free;
static linkaddr_t *worst_rank_nbr; /* the parent that has the worst rank */
static rpl_rank_t worst_rank;
/*---------------------------------------------------------------------------*/
#if DEBUG == DEBUG_FULL
/*
* This create a periodic call of the update_nbr function that will print
* useful debugging information when in DEBUG_FULL mode
*/
static void update_nbr(void);
static struct ctimer periodic_timer;
static int timer_init = 0;
static void
handle_periodic_timer(void *ptr)
{
update_nbr();
ctimer_restart(&periodic_timer);
}
#endif /* DEBUG == DEBUG_FULL */
/*---------------------------------------------------------------------------*/
static void
update_nbr(void)
{
uip_ds6_nbr_t *nbr;
rpl_parent_t *parent;
int num_used;
int is_used;
rpl_rank_t rank;
#if DEBUG == DEBUG_FULL
if(!timer_init) {
timer_init = 1;
ctimer_set(&periodic_timer, 60 * CLOCK_SECOND,
&handle_periodic_timer, NULL);
}
#endif /* DEBUG == DEBUG_FULL */
worst_rank = 0;
worst_rank_nbr = NULL;
num_used = 0;
num_parents = 0;
num_children = 0;
nbr = nbr_table_head(ds6_neighbors);
while(nbr != NULL) {
linkaddr_t *lladdr = nbr_table_get_lladdr(ds6_neighbors, nbr);
is_used = 0;
/*
* Check if this neighbor is used as nexthop and therefor being a
* RPL child.
*/
if(uip_ds6_route_is_nexthop(&nbr->ipaddr) != 0) {
is_used++;
num_children++;
}
parent = rpl_get_parent((uip_lladdr_t *)lladdr);
if(parent != NULL) {
num_parents++;
if(parent->dag != NULL && parent->dag->preferred_parent == parent) {
/*
* This is the preferred parent for the DAG and must not be removed
* Note: this assumes that only RPL adds default routes.
*/
} else if(is_used == 0 && worst_rank < INFINITE_RANK &&
parent->rank > 0 &&
parent->dag != NULL &&
parent->dag->instance != NULL &&
(rank = parent->dag->instance->of->rank_via_parent(parent)) > worst_rank) {
/* This is the worst-rank neighbor - this is a good candidate for removal */
worst_rank = rank;
worst_rank_nbr = lladdr;
}
/* add to is_used after evaluation of is_used above */
is_used++;
}
if(is_used == 0) {
/* This neighbor is neither parent or child and can be safely removed */
worst_rank_nbr = lladdr;
worst_rank = INFINITE_RANK;
} else if(is_used > 1) {
PRINTF("NBR-POLICY: *** Neighbor is both child and candidate parent: ");
PRINTLLADDR((uip_lladdr_t *)lladdr);
PRINTF("\n");
}
nbr = nbr_table_next(ds6_neighbors, nbr);
num_used++;
}
/* how many more IP neighbors can be have? */
num_free = NBR_TABLE_MAX_NEIGHBORS - num_used;
PRINTF("NBR-POLICY: Free: %d, Children: %d, Parents: %d Routes: %d\n",
num_free, num_children, num_parents, uip_ds6_route_num_routes());
}
/*---------------------------------------------------------------------------*/
/* Called whenever we get a unicast DIS - e.g. someone that already
have this node in its table - since it is a unicast */
const linkaddr_t *
find_removable_dis(uip_ipaddr_t *from)
{
update_nbr();
if(num_free > 0) {
/* there are free entries (e.g. unsused by RPL and ND6) but since it is
used by other modules we can not pick these entries for removal. */
PRINTF("Num-free > 0 = %d - Other for RPL/ND6 unused NBR entry exists .",
num_free);
}
if(num_children < MAX_CHILDREN) {
return worst_rank_nbr;
}
return NULL;
}
/*---------------------------------------------------------------------------*/
const linkaddr_t *
find_removable_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{
rpl_instance_t *instance;
update_nbr();
instance = rpl_get_instance(dio->instance_id);
if(instance == NULL || instance->current_dag == NULL) {
PRINTF("Did not find instance id: %d\n", dio->instance_id);
return NULL;
}
/* Add the new neighbor only if it is better than the worst parent. */
if(dio->rank + instance->min_hoprankinc < worst_rank - instance->min_hoprankinc / 2) {
/* Found *great* neighbor - add! */
PRINTF("Found better neighbor %d < %d - add to cache...\n",
rank, worst_rank);
return worst_rank_nbr;
}
PRINTF("Found worse neighbor with new %d and old %d - NOT add to cache.\n",
rank, worst_rank);
return NULL;
}
/*---------------------------------------------------------------------------*/
const linkaddr_t *
find_removable_dao(uip_ipaddr_t *from, rpl_instance_t *instance)
{
int max = MAX_CHILDREN;
update_nbr();
if(instance != NULL) {
/* No need to reserve space for parents for RPL ROOT */
if(instance->current_dag->rank == ROOT_RANK(instance)) {
max = NBR_TABLE_MAX_NEIGHBORS;
}
}
/* Check if this DAO sender is not yet neighbor and there is already too
many children. */
if(num_children >= max) {
PRINTF("Can not add another child - already at max.\n");
return NULL;
}
/* remove the worst ranked nbr */
return worst_rank_nbr;
}
/*---------------------------------------------------------------------------*/
const linkaddr_t *
rpl_nbr_policy_find_removable(nbr_table_reason_t reason,void * data)
{
/* When we get the DIO/DAO/DIS we know that UIP contains the
incoming packet */
switch(reason) {
case NBR_TABLE_REASON_RPL_DIO:
return find_removable_dio(&UIP_IP_BUF->srcipaddr, data);
case NBR_TABLE_REASON_RPL_DAO:
return find_removable_dao(&UIP_IP_BUF->srcipaddr, data);
case NBR_TABLE_REASON_RPL_DIS:
return find_removable_dis(&UIP_IP_BUF->srcipaddr);
default:
return NULL;
}
}
/*---------------------------------------------------------------------------*/
/** @}*/

230
core/net/rpl/rpl-ns.c Normal file
View File

@ -0,0 +1,230 @@
/*
* 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.
*/
/**
* \file
* RPL non-storing mode specific functions. Includes support for
* source routing.
*
* \author Simon Duquennoy <simon.duquennoy@inria.fr>
*/
#include "net/rpl/rpl-conf.h"
#include "net/ip/uip.h"
#include "net/ip/tcpip.h"
#include "net/ipv6/uip-ds6.h"
#include "net/ipv6/uip-icmp6.h"
#include "net/rpl/rpl-private.h"
#include "net/rpl/rpl-ns.h"
#include "lib/list.h"
#include "lib/memb.h"
#if RPL_WITH_NON_STORING
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
#include <limits.h>
#include <string.h>
/* Total number of nodes */
static int num_nodes;
/* Every known node in the network */
LIST(nodelist);
MEMB(nodememb, rpl_ns_node_t, RPL_NS_LINK_NUM);
/*---------------------------------------------------------------------------*/
int
rpl_ns_num_nodes(void)
{
return num_nodes;
}
/*---------------------------------------------------------------------------*/
static int
node_matches_address(const rpl_dag_t *dag, const rpl_ns_node_t *node, const uip_ipaddr_t *addr)
{
return addr != NULL
&& node != NULL
&& dag != NULL
&& dag == node->dag
&& !memcmp(addr, &node->dag->dag_id, 8)
&& !memcmp(((const unsigned char *)addr) + 8, node->link_identifier, 8);
}
/*---------------------------------------------------------------------------*/
rpl_ns_node_t *
rpl_ns_get_node(const rpl_dag_t *dag, const uip_ipaddr_t *addr)
{
rpl_ns_node_t *l;
for(l = list_head(nodelist); l != NULL; l = list_item_next(l)) {
/* Compare prefix and node identifier */
if(node_matches_address(dag, l, addr)) {
return l;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
int
rpl_ns_is_node_reachable(const rpl_dag_t *dag, const uip_ipaddr_t *addr)
{
int max_depth = RPL_NS_LINK_NUM;
rpl_ns_node_t *node = rpl_ns_get_node(dag, addr);
rpl_ns_node_t *root_node = rpl_ns_get_node(dag, dag != NULL ? &dag->dag_id : NULL);
while(node != NULL && node != root_node && max_depth > 0) {
node = node->parent;
max_depth--;
}
return node != NULL && node == root_node;
}
/*---------------------------------------------------------------------------*/
void
rpl_ns_expire_parent(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent)
{
rpl_ns_node_t *l = rpl_ns_get_node(dag, child);
/* Check if parent matches */
if(l != NULL && node_matches_address(dag, l->parent, parent)) {
l->lifetime = RPL_NOPATH_REMOVAL_DELAY;
}
}
/*---------------------------------------------------------------------------*/
rpl_ns_node_t *
rpl_ns_update_node(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent, uint32_t lifetime)
{
rpl_ns_node_t *child_node = rpl_ns_get_node(dag, child);
rpl_ns_node_t *parent_node = rpl_ns_get_node(dag, parent);
rpl_ns_node_t *old_parent_node;
if(parent != NULL) {
/* No node for the parent, add one with infinite lifetime */
if(parent_node == NULL) {
parent_node = rpl_ns_update_node(dag, parent, NULL, 0xffffffff);
if(parent_node == NULL) {
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) {
return NULL;
}
child_node->parent = NULL;
list_add(nodelist, child_node);
num_nodes++;
}
/* Initialize node */
child_node->dag = dag;
child_node->lifetime = lifetime;
memcpy(child_node->link_identifier, ((const unsigned char *)child) + 8, 8);
/* Is the node reachable before the update? */
if(rpl_ns_is_node_reachable(dag, 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(!rpl_ns_is_node_reachable(dag, 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;
}
return child_node;
}
/*---------------------------------------------------------------------------*/
void
rpl_ns_init(void)
{
num_nodes = 0;
memb_init(&nodememb);
list_init(nodelist);
}
/*---------------------------------------------------------------------------*/
rpl_ns_node_t *
rpl_ns_node_head(void)
{
return list_head(nodelist);
}
/*---------------------------------------------------------------------------*/
rpl_ns_node_t *
rpl_ns_node_next(rpl_ns_node_t *item)
{
return list_item_next(item);
}
/*---------------------------------------------------------------------------*/
void
rpl_ns_get_node_global_addr(uip_ipaddr_t *addr, rpl_ns_node_t *node)
{
if(addr != NULL && node != NULL && node->dag != NULL) {
memcpy(addr, &node->dag->dag_id, 8);
memcpy(((unsigned char *)addr) + 8, &node->link_identifier, 8);
}
}
/*---------------------------------------------------------------------------*/
void
rpl_ns_periodic(void)
{
rpl_ns_node_t *l;
/* First pass, decrement lifetime for all nodes with non-infinite lifetime */
for(l = list_head(nodelist); l != NULL; l = list_item_next(l)) {
/* Don't touch infinite lifetime nodes */
if(l->lifetime != 0xffffffff && l->lifetime > 0) {
l->lifetime--;
}
}
/* Second pass, for all expire nodes, deallocate them iff no child points to them */
for(l = list_head(nodelist); l != NULL; l = list_item_next(l)) {
if(l->lifetime == 0) {
rpl_ns_node_t *l2;
for(l2 = list_head(nodelist); l2 != NULL; l2 = list_item_next(l2)) {
if(l2->parent == l) {
break;
}
}
/* No child found, deallocate node */
list_remove(nodelist, l);
memb_free(&nodememb, l);
num_nodes--;
}
}
}
#endif /* RPL_WITH_NON_STORING */

72
core/net/rpl/rpl-ns.h Normal file
View File

@ -0,0 +1,72 @@
/*
* 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.
*/
/**
* \file
* RPL non-storing mode specific functions. Includes support for
* source routing.
*
* \author Simon Duquennoy <simon.duquennoy@inria.fr>
*/
#ifndef RPL_NS_H
#define RPL_NS_H
#include "rpl-conf.h"
#ifdef RPL_NS_CONF_LINK_NUM
#define RPL_NS_LINK_NUM RPL_NS_CONF_LINK_NUM
#else /* RPL_NS_CONF_LINK_NUM */
#define RPL_NS_LINK_NUM 32
#endif /* RPL_NS_CONF_LINK_NUM */
typedef struct rpl_ns_node {
struct rpl_ns_node *next;
uint32_t lifetime;
rpl_dag_t *dag;
/* Store only IPv6 link identifiers as all nodes in the DAG share the same prefix */
unsigned char link_identifier[8];
struct rpl_ns_node *parent;
} rpl_ns_node_t;
int rpl_ns_num_nodes(void);
void rpl_ns_expire_parent(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent);
rpl_ns_node_t *rpl_ns_update_node(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent, uint32_t lifetime);
void rpl_ns_init(void);
rpl_ns_node_t *rpl_ns_node_head(void);
rpl_ns_node_t *rpl_ns_node_next(rpl_ns_node_t *item);
rpl_ns_node_t *rpl_ns_get_node(const rpl_dag_t *dag, const uip_ipaddr_t *addr);
int rpl_ns_is_node_reachable(const rpl_dag_t *dag, const uip_ipaddr_t *addr);
void rpl_ns_get_node_global_addr(uip_ipaddr_t *addr, rpl_ns_node_t *node);
void rpl_ns_periodic();
#endif /* RPL_NS_H */

View File

@ -27,11 +27,12 @@
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* An implementation of RPL's objective function 0.
* An implementation of RPL's objective function 0, RFC6552
*
* \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
*/
@ -41,133 +42,197 @@
* @{
*/
#include "net/rpl/rpl.h"
#include "net/rpl/rpl-private.h"
#include "net/nbr-table.h"
#include "net/link-stats.h"
#define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h"
static void reset(rpl_dag_t *);
static rpl_parent_t *best_parent(rpl_parent_t *, rpl_parent_t *);
static rpl_dag_t *best_dag(rpl_dag_t *, rpl_dag_t *);
static rpl_rank_t calculate_rank(rpl_parent_t *, rpl_rank_t);
static void update_metric_container(rpl_instance_t *);
/* Constants from RFC6552. We use the default values. */
#define RANK_STRETCH 0 /* Must be in the range [0;5] */
#define RANK_FACTOR 1 /* Must be in the range [1;4] */
rpl_of_t rpl_of0 = {
reset,
NULL,
best_parent,
best_dag,
calculate_rank,
update_metric_container,
0
};
#define MIN_STEP_OF_RANK 1
#define MAX_STEP_OF_RANK 9
#define DEFAULT_RANK_INCREMENT RPL_MIN_HOPRANKINC
/* OF0 computes rank increase as follows:
* rank_increase = (RANK_FACTOR * STEP_OF_RANK + RANK_STRETCH) * min_hop_rank_increase
* STEP_OF_RANK is an implementation-specific scalar value in the range [1;9].
* RFC6552 provides a default value of 3 but recommends to use a dynamic link metric
* such as ETX.
* */
#define MIN_DIFFERENCE (RPL_MIN_HOPRANKINC + RPL_MIN_HOPRANKINC / 2)
#define RPL_OF0_FIXED_SR 0
#define RPL_OF0_ETX_BASED_SR 1
/* Select RPL_OF0_FIXED_SR or RPL_OF0_ETX_BASED_SR */
#ifdef RPL_OF0_CONF_SR
#define RPL_OF0_SR RPL_OF0_CONF_SR
#else /* RPL_OF0_CONF_SR */
#define RPL_OF0_SR RPL_OF0_ETX_BASED_SR
#endif /* RPL_OF0_CONF_SR */
#if RPL_OF0_FIXED_SR
#define STEP_OF_RANK(p) (3)
#endif /* RPL_OF0_FIXED_SR */
#if RPL_OF0_ETX_BASED_SR
/* Numbers suggested by P. Thubert for in the 6TiSCH WG. Anything that maps ETX to
* a step between 1 and 9 works. */
#define STEP_OF_RANK(p) (((3 * parent_link_metric(p)) / LINK_STATS_ETX_DIVISOR) - 2)
#endif /* RPL_OF0_ETX_BASED_SR */
/*---------------------------------------------------------------------------*/
static void
reset(rpl_dag_t *dag)
{
PRINTF("RPL: Resetting OF0\n");
PRINTF("RPL: Reset OF0\n");
}
static rpl_rank_t
calculate_rank(rpl_parent_t *p, rpl_rank_t base_rank)
/*---------------------------------------------------------------------------*/
#if RPL_WITH_DAO_ACK
static void
dao_ack_callback(rpl_parent_t *p, int status)
{
rpl_rank_t increment;
if(base_rank == 0) {
if(p == NULL) {
return INFINITE_RANK;
}
base_rank = p->rank;
if(status == RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT) {
return;
}
increment = p != NULL ?
p->dag->instance->min_hoprankinc :
DEFAULT_RANK_INCREMENT;
if((rpl_rank_t)(base_rank + increment) < base_rank) {
PRINTF("RPL: OF0 rank %d incremented to infinite rank due to wrapping\n",
base_rank);
/* here we need to handle failed DAO's and other stuff */
PRINTF("RPL: OF0 - DAO ACK received with status: %d\n", status);
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
/* punish the ETX as if this was 10 packets lost */
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
} else if(status == RPL_DAO_ACK_TIMEOUT) { /* timeout = no ack */
/* punish the total lack of ACK with a similar punishment */
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
}
}
#endif /* RPL_WITH_DAO_ACK */
/*---------------------------------------------------------------------------*/
static uint16_t
parent_link_metric(rpl_parent_t *p)
{
/* OF0 operates without metric container; the only metric we have is ETX */
const struct link_stats *stats = rpl_get_parent_link_stats(p);
return stats != NULL ? stats->etx : 0xffff;
}
/*---------------------------------------------------------------------------*/
static uint16_t
parent_rank_increase(rpl_parent_t *p)
{
uint16_t min_hoprankinc;
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
return INFINITE_RANK;
}
return base_rank + increment;
min_hoprankinc = p->dag->instance->min_hoprankinc;
return (RANK_FACTOR * STEP_OF_RANK(p) + RANK_STRETCH) * min_hoprankinc;
}
static rpl_dag_t *
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
/*---------------------------------------------------------------------------*/
static uint16_t
parent_path_cost(rpl_parent_t *p)
{
if(d1->grounded) {
if (!d2->grounded) {
return d1;
}
} else if(d2->grounded) {
return d2;
if(p == NULL) {
return 0xffff;
}
if(d1->preference < d2->preference) {
return d2;
/* path cost upper bound: 0xffff */
return MIN((uint32_t)p->rank + parent_link_metric(p), 0xffff);
}
/*---------------------------------------------------------------------------*/
static rpl_rank_t
rank_via_parent(rpl_parent_t *p)
{
if(p == NULL) {
return INFINITE_RANK;
} else {
if(d1->preference > d2->preference) {
return d1;
}
}
if(d2->rank < d1->rank) {
return d2;
} else {
return d1;
return MIN((uint32_t)p->rank + parent_rank_increase(p), INFINITE_RANK);
}
}
/*---------------------------------------------------------------------------*/
static int
parent_is_acceptable(rpl_parent_t *p)
{
return STEP_OF_RANK(p) >= MIN_STEP_OF_RANK
&& STEP_OF_RANK(p) <= MAX_STEP_OF_RANK;
}
/*---------------------------------------------------------------------------*/
static int
parent_has_usable_link(rpl_parent_t *p)
{
return parent_is_acceptable(p);
}
/*---------------------------------------------------------------------------*/
static rpl_parent_t *
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
{
rpl_rank_t r1, r2;
rpl_dag_t *dag;
uip_ds6_nbr_t *nbr1, *nbr2;
nbr1 = rpl_get_nbr(p1);
nbr2 = rpl_get_nbr(p2);
rpl_dag_t *dag;
uint16_t p1_cost;
uint16_t p2_cost;
int p1_is_acceptable;
int p2_is_acceptable;
dag = (rpl_dag_t *)p1->dag; /* Both parents must be in the same DAG. */
p1_is_acceptable = p1 != NULL && parent_is_acceptable(p1);
p2_is_acceptable = p2 != NULL && parent_is_acceptable(p2);
if(nbr1 == NULL || nbr2 == NULL) {
return dag->preferred_parent;
if(!p1_is_acceptable) {
return p2_is_acceptable ? p2 : NULL;
}
if(!p2_is_acceptable) {
return p1_is_acceptable ? p1 : NULL;
}
PRINTF("RPL: Comparing parent ");
PRINT6ADDR(rpl_get_parent_ipaddr(p1));
PRINTF(" (confidence %d, rank %d) with parent ",
nbr1->link_metric, p1->rank);
PRINT6ADDR(rpl_get_parent_ipaddr(p2));
PRINTF(" (confidence %d, rank %d)\n",
nbr2->link_metric, p2->rank);
dag = p1->dag; /* Both parents are in the same DAG. */
p1_cost = parent_path_cost(p1);
p2_cost = parent_path_cost(p2);
r1 = DAG_RANK(p1->rank, p1->dag->instance) * RPL_MIN_HOPRANKINC +
nbr1->link_metric;
r2 = DAG_RANK(p2->rank, p1->dag->instance) * RPL_MIN_HOPRANKINC +
nbr2->link_metric;
/* Compare two parents by looking both and their rank and at the ETX
for that parent. We choose the parent that has the most
favourable combination. */
if(r1 < r2 + MIN_DIFFERENCE &&
r1 > r2 - MIN_DIFFERENCE) {
return dag->preferred_parent;
} else if(r1 < r2) {
return p1;
/* Paths costs coarse-grained (multiple of min_hoprankinc), we operate without hysteresis */
if(p1_cost != p2_cost) {
/* Pick parent with lowest path cost */
return p1_cost < p2_cost ? p1 : p2;
} else {
return p2;
/* We have a tie! */
/* Stik to current preferred parent if possible */
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
return dag->preferred_parent;
}
/* None of the nodes is the current preferred parent,
* choose parent with best link metric */
return parent_link_metric(p1) < parent_link_metric(p2) ? p1 : p2;
}
}
/*---------------------------------------------------------------------------*/
static rpl_dag_t *
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
{
if(d1->grounded != d2->grounded) {
return d1->grounded ? d1 : d2;
}
if(d1->preference != d2->preference) {
return d1->preference > d2->preference ? d1 : d2;
}
return d1->rank < d2->rank ? d1 : d2;
}
/*---------------------------------------------------------------------------*/
static void
update_metric_container(rpl_instance_t *instance)
{
instance->mc.type = RPL_DAG_MC_NONE;
}
/*---------------------------------------------------------------------------*/
rpl_of_t rpl_of0 = {
reset,
#if RPL_WITH_DAO_ACK
dao_ack_callback,
#endif
parent_link_metric,
parent_has_usable_link,
parent_path_cost,
rank_via_parent,
best_parent,
best_dag,
update_metric_container,
RPL_OCP_OF0
};
/** @}*/

View File

@ -44,6 +44,8 @@
#include "sys/clock.h"
#include "sys/ctimer.h"
#include "net/ipv6/uip-ds6.h"
#include "net/ipv6/uip-ds6-route.h"
#include "net/rpl/rpl-ns.h"
#include "net/ipv6/multicast/uip-mcast6.h"
/*---------------------------------------------------------------------------*/
@ -90,10 +92,21 @@
#define RPL_DAO_K_FLAG 0x80 /* DAO ACK requested */
#define RPL_DAO_D_FLAG 0x40 /* DODAG ID present */
#define RPL_DAO_ACK_UNCONDITIONAL_ACCEPT 0
#define RPL_DAO_ACK_ACCEPT 1 /* 1 - 127 is OK but not good */
#define RPL_DAO_ACK_UNABLE_TO_ACCEPT 128 /* >127 is fail */
#define RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT 255 /* root can not accept */
#define RPL_DAO_ACK_TIMEOUT -1
/*---------------------------------------------------------------------------*/
/* RPL IPv6 extension header option. */
#define RPL_HDR_OPT_LEN 4
#define RPL_HOP_BY_HOP_LEN (RPL_HDR_OPT_LEN + 2 + 2)
#define RPL_RH_LEN 4
#define RPL_SRH_LEN 4
#define RPL_RH_TYPE_SRH 3
#define RPL_HDR_OPT_DOWN 0x80
#define RPL_HDR_OPT_DOWN_SHIFT 7
#define RPL_HDR_OPT_RANK_ERR 0x40
@ -117,6 +130,18 @@
#define RPL_NOPATH_REMOVAL_DELAY 60
#endif /* RPL_CONF_NOPATH_REMOVAL_DELAY */
#ifdef RPL_CONF_DAO_MAX_RETRANSMISSIONS
#define RPL_DAO_MAX_RETRANSMISSIONS RPL_CONF_DAO_MAX_RETRANSMISSIONS
#else
#define RPL_DAO_MAX_RETRANSMISSIONS 5
#endif /* RPL_CONF_DAO_MAX_RETRANSMISSIONS */
#ifdef RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
#define RPL_DAO_RETRANSMISSION_TIMEOUT RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
#else
#define RPL_DAO_RETRANSMISSION_TIMEOUT (5 * CLOCK_SECOND)
#endif /* RPL_CONF_DAO_RETRANSMISSION_TIMEOUT */
/* Special value indicating immediate removal. */
#define RPL_ZERO_LIFETIME 0
@ -124,11 +149,28 @@
((unsigned long)(instance)->lifetime_unit * (lifetime))
#ifndef RPL_CONF_MIN_HOPRANKINC
/* RFC6550 defines the default MIN_HOPRANKINC as 256.
* However, we use MRHOF as a default Objective Function (RFC6719),
* which recommends setting MIN_HOPRANKINC with care, in particular
* when used with ETX as a metric. ETX is computed as a fixed point
* real with a divisor of 128 (RFC6719, RFC6551). We choose to also
* use 128 for RPL_MIN_HOPRANKINC, resulting in a rank equal to the
* ETX path cost. Larger values may also be desirable, as discussed
* in section 6.1 of RFC6719. */
#if RPL_OF_OCP == RPL_OCP_MRHOF
#define RPL_MIN_HOPRANKINC 128
#else /* RPL_OF_OCP == RPL_OCP_MRHOF */
#define RPL_MIN_HOPRANKINC 256
#else
#endif /* RPL_OF_OCP == RPL_OCP_MRHOF */
#else /* RPL_CONF_MIN_HOPRANKINC */
#define RPL_MIN_HOPRANKINC RPL_CONF_MIN_HOPRANKINC
#endif
#endif /* RPL_CONF_MIN_HOPRANKINC */
#ifndef RPL_CONF_MAX_RANKINC
#define RPL_MAX_RANKINC (7 * RPL_MIN_HOPRANKINC)
#else /* RPL_CONF_MAX_RANKINC */
#define RPL_MAX_RANKINC RPL_CONF_MAX_RANKINC
#endif /* RPL_CONF_MAX_RANKINC */
#define DAG_RANK(fixpt_rank, instance) \
((fixpt_rank) / (instance)->min_hoprankinc)
@ -157,6 +199,7 @@
#define RPL_MOP_STORING_NO_MULTICAST 2
#define RPL_MOP_STORING_MULTICAST 3
/* RPL Mode of operation */
#ifdef RPL_CONF_MOP
#define RPL_MOP_DEFAULT RPL_CONF_MOP
#else /* RPL_CONF_MOP */
@ -167,6 +210,43 @@
#endif /* UIP_IPV6_MULTICAST_RPL */
#endif /* RPL_CONF_MOP */
/*
* Embed support for storing mode
*/
#ifdef RPL_CONF_WITH_STORING
#define RPL_WITH_STORING RPL_CONF_WITH_STORING
#else /* RPL_CONF_WITH_STORING */
/* By default: embed support for non-storing if and only if the configured MOP is not non-storing */
#define RPL_WITH_STORING (RPL_MOP_DEFAULT != RPL_MOP_NON_STORING)
#endif /* RPL_CONF_WITH_STORING */
/*
* Embed support for non-storing mode
*/
#ifdef RPL_CONF_WITH_NON_STORING
#define RPL_WITH_NON_STORING RPL_CONF_WITH_NON_STORING
#else /* RPL_CONF_WITH_NON_STORING */
/* By default: embed support for non-storing if and only if the configured MOP is non-storing */
#define RPL_WITH_NON_STORING (RPL_MOP_DEFAULT == RPL_MOP_NON_STORING)
#endif /* RPL_CONF_WITH_NON_STORING */
#if RPL_WITH_STORING && (UIP_DS6_ROUTE_NB == 0)
#error "RPL with storing mode included but #routes == 0. Set UIP_CONF_MAX_ROUTES accordingly."
#if !RPL_WITH_NON_STORING && (RPL_NS_LINK_NUM > 0)
#error "You might also want to set RPL_NS_CONF_LINK_NUM to 0."
#endif
#endif
#if RPL_WITH_NON_STORING && (RPL_NS_LINK_NUM == 0)
#error "RPL with non-storing mode included but #links == 0. Set RPL_NS_CONF_LINK_NUM accordingly."
#if !RPL_WITH_STORING && (UIP_DS6_ROUTE_NB > 0)
#error "You might also want to set UIP_CONF_MAX_ROUTES to 0."
#endif
#endif
#define RPL_IS_STORING(instance) (RPL_WITH_STORING && ((instance) != NULL) && ((instance)->mop > RPL_MOP_NON_STORING))
#define RPL_IS_NON_STORING(instance) (RPL_WITH_NON_STORING && ((instance) != NULL) && ((instance)->mop == RPL_MOP_NON_STORING))
/* Emit a pre-processor error if the user configured multicast with bad MOP */
#if RPL_CONF_MULTICAST && (RPL_MOP_DEFAULT != RPL_MOP_STORING_MULTICAST)
#error "RPL Multicast requires RPL_MOP_DEFAULT==3. Check contiki-conf.h"
@ -179,13 +259,6 @@
#define RPL_MCAST_LIFETIME 3
#endif
/*
* The ETX in the metric container is expressed as a fixed-point value
* whose integer part can be obtained by dividing the value by
* RPL_DAG_MC_ETX_DIVISOR.
*/
#define RPL_DAG_MC_ETX_DIVISOR 256
/* DIS related */
#define RPL_DIS_SEND 1
@ -250,6 +323,8 @@ typedef struct rpl_stats rpl_stats_t;
extern rpl_stats_t rpl_stats;
#endif
/*---------------------------------------------------------------------------*/
/* RPL macros. */
@ -268,8 +343,10 @@ void dis_output(uip_ipaddr_t *addr);
void dio_output(rpl_instance_t *, uip_ipaddr_t *uc_addr);
void dao_output(rpl_parent_t *, uint8_t lifetime);
void dao_output_target(rpl_parent_t *, uip_ipaddr_t *, uint8_t lifetime);
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t);
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t, uint8_t);
void rpl_icmp6_register_handlers(void);
uip_ds6_nbr_t *rpl_icmp6_update_nbr_table(uip_ipaddr_t *from,
nbr_table_reason_t r, void *data);
/* RPL logic functions. */
void rpl_join_dag(uip_ipaddr_t *from, rpl_dio_t *dio);

View File

@ -43,6 +43,8 @@
#include "contiki-conf.h"
#include "net/rpl/rpl-private.h"
#include "net/rpl/rpl-ns.h"
#include "net/link-stats.h"
#include "net/ipv6/multicast/uip-mcast6.h"
#include "lib/random.h"
#include "sys/ctimer.h"
@ -55,6 +57,14 @@
void RPL_CALLBACK_NEW_DIO_INTERVAL(uint8_t dio_interval);
#endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
#ifdef RPL_PROBING_SELECT_FUNC
rpl_parent_t *RPL_PROBING_SELECT_FUNC(rpl_dag_t *dag);
#endif /* RPL_PROBING_SELECT_FUNC */
#ifdef RPL_PROBING_DELAY_FUNC
clock_time_t RPL_PROBING_DELAY_FUNC(rpl_dag_t *dag);
#endif /* RPL_PROBING_DELAY_FUNC */
/*---------------------------------------------------------------------------*/
static struct ctimer periodic_timer;
@ -71,14 +81,23 @@ static uint8_t dio_send_ok;
static void
handle_periodic_timer(void *ptr)
{
rpl_dag_t *dag = rpl_get_any_dag();
rpl_purge_dags();
rpl_purge_routes();
if(dag != NULL) {
if(RPL_IS_STORING(dag->instance)) {
rpl_purge_routes();
}
if(RPL_IS_NON_STORING(dag->instance)) {
rpl_ns_periodic();
}
}
rpl_recalculate_ranks();
/* handle DIS */
#if RPL_DIS_SEND
next_dis++;
if(rpl_get_any_dag() == NULL && next_dis >= RPL_DIS_INTERVAL) {
if(dag == NULL && next_dis >= RPL_DIS_INTERVAL) {
next_dis = 0;
dis_output(NULL);
}
@ -161,7 +180,7 @@ handle_dio_timer(void *ptr)
#endif /* RPL_CONF_STATS */
dio_output(instance, NULL);
} else {
PRINTF("RPL: Supressing DIO transmission (%d >= %d)\n",
PRINTF("RPL: Suppressing DIO transmission (%d >= %d)\n",
instance->dio_counter, instance->dio_redundancy);
}
instance->dio_send = 0;
@ -224,6 +243,8 @@ set_dao_lifetime_timer(rpl_instance_t *instance)
expiration_time = (clock_time_t)instance->default_lifetime *
(clock_time_t)instance->lifetime_unit *
CLOCK_SECOND / 2;
/* make the time for the re registration be betwen 1/2 - 3/4 of lifetime */
expiration_time = expiration_time + (random_rand() % (expiration_time / 2));
PRINTF("RPL: Scheduling DAO lifetime timer %u ticks in the future\n",
(unsigned)expiration_time);
ctimer_set(&instance->dao_lifetime_timer, expiration_time,
@ -356,42 +377,57 @@ rpl_schedule_unicast_dio_immediately(rpl_instance_t *instance)
}
/*---------------------------------------------------------------------------*/
#if RPL_WITH_PROBING
static rpl_parent_t *
clock_time_t
get_probing_delay(rpl_dag_t *dag)
{
if(dag != NULL && dag->instance != NULL
&& dag->instance->urgent_probing_target != NULL) {
/* Urgent probing needed (to find out if a neighbor may become preferred parent) */
return random_rand() % (CLOCK_SECOND * 10);
} else {
/* Else, use normal probing interval */
return ((RPL_PROBING_INTERVAL) / 2) + random_rand() % (RPL_PROBING_INTERVAL);
}
}
/*---------------------------------------------------------------------------*/
rpl_parent_t *
get_probing_target(rpl_dag_t *dag)
{
/* Returns the next probing target. The current implementation probes the current
* preferred parent if we have not updated its link for RPL_PROBING_EXPIRATION_TIME.
/* Returns the next probing target. The current implementation probes the urgent
* probing target if any, or the preferred parent if its link statistics need refresh.
* Otherwise, it picks at random between:
* (1) selecting the best parent not updated for RPL_PROBING_EXPIRATION_TIME
* (1) selecting the best parent with non-fresh link statistics
* (2) selecting the least recently updated parent
*/
rpl_parent_t *p;
rpl_parent_t *probing_target = NULL;
rpl_rank_t probing_target_rank = INFINITE_RANK;
/* min_last_tx is the clock time RPL_PROBING_EXPIRATION_TIME in the past */
clock_time_t min_last_tx = clock_time();
min_last_tx = min_last_tx > 2 * RPL_PROBING_EXPIRATION_TIME
? min_last_tx - RPL_PROBING_EXPIRATION_TIME : 1;
clock_time_t probing_target_age = 0;
clock_time_t clock_now = clock_time();
if(dag == NULL ||
dag->instance == NULL ||
dag->preferred_parent == NULL) {
dag->instance == NULL) {
return NULL;
}
/* Our preferred parent needs probing */
if(dag->preferred_parent->last_tx_time < min_last_tx) {
probing_target = dag->preferred_parent;
/* There is an urgent probing target */
if(dag->instance->urgent_probing_target != NULL) {
return dag->instance->urgent_probing_target;
}
/* With 50% probability: probe best parent not updated for RPL_PROBING_EXPIRATION_TIME */
if(probing_target == NULL && (random_rand() % 2) == 0) {
/* The preferred parent needs probing */
if(dag->preferred_parent != NULL && !rpl_parent_is_fresh(dag->preferred_parent)) {
return dag->preferred_parent;
}
/* With 50% probability: probe best non-fresh parent */
if(random_rand() % 2 == 0) {
p = nbr_table_head(rpl_parents);
while(p != NULL) {
if(p->dag == dag && p->last_tx_time < min_last_tx) {
if(p->dag == dag && !rpl_parent_is_fresh(p)) {
/* p is in our dag and needs probing */
rpl_rank_t p_rank = dag->instance->of->calculate_rank(p, 0);
rpl_rank_t p_rank = rpl_rank_via_parent(p);
if(probing_target == NULL
|| p_rank < probing_target_rank) {
probing_target = p;
@ -402,14 +438,16 @@ get_probing_target(rpl_dag_t *dag)
}
}
/* The default probing target is the least recently updated parent */
/* If we still do not have a probing target: pick the least recently updated parent */
if(probing_target == NULL) {
p = nbr_table_head(rpl_parents);
while(p != NULL) {
if(p->dag == dag) {
const struct link_stats *stats =rpl_get_parent_link_stats(p);
if(p->dag == dag && stats != NULL) {
if(probing_target == NULL
|| p->last_tx_time < probing_target->last_tx_time) {
|| clock_now - stats->last_tx_time > probing_target_age) {
probing_target = p;
probing_target_age = clock_now - stats->last_tx_time;
}
}
p = nbr_table_next(rpl_parents, p);
@ -428,11 +466,17 @@ handle_probing_timer(void *ptr)
/* Perform probing */
if(target_ipaddr != NULL) {
PRINTF("RPL: probing %u ((last tx %u min ago))\n",
nbr_table_get_lladdr(rpl_parents, probing_target)->u8[7],
(unsigned)((clock_time() - probing_target->last_tx_time) / (60 * CLOCK_SECOND)));
/* Send probe (unicast DIO or DIS) */
const struct link_stats *stats = rpl_get_parent_link_stats(probing_target);
(void)stats;
PRINTF("RPL: probing %u %s last tx %u min ago\n",
rpl_get_parent_lladdr(probing_target)->u8[7],
instance->urgent_probing_target != NULL ? "(urgent)" : "",
probing_target != NULL ?
(unsigned)((clock_time() - stats->last_tx_time) / (60 * CLOCK_SECOND)) : 0
);
/* Send probe, e.g. unicast DIO or DIS */
RPL_PROBING_SEND_FUNC(instance, target_ipaddr);
instance->urgent_probing_target = NULL;
}
/* Schedule next probing */
@ -446,7 +490,7 @@ handle_probing_timer(void *ptr)
void
rpl_schedule_probing(rpl_instance_t *instance)
{
ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(),
ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(instance->current_dag),
handle_probing_timer, instance);
}
#endif /* RPL_WITH_PROBING */

View File

@ -47,6 +47,7 @@
#include "net/ipv6/uip-ds6.h"
#include "net/ipv6/uip-icmp6.h"
#include "net/rpl/rpl-private.h"
#include "net/rpl/rpl-ns.h"
#include "net/ipv6/multicast/uip-mcast6.h"
#define DEBUG DEBUG_NONE
@ -215,12 +216,10 @@ rpl_remove_routes_by_nexthop(uip_ipaddr_t *nexthop, rpl_dag_t *dag)
while(r != NULL) {
if(uip_ipaddr_cmp(uip_ds6_route_nexthop(r), nexthop) &&
r->state.dag == dag) {
uip_ds6_route_rm(r);
r = uip_ds6_route_head();
} else {
r = uip_ds6_route_next(r);
r->state.dag == dag) {
r->state.lifetime = 0;
}
r = uip_ds6_route_next(r);
}
ANNOTATE("#L %u 0\n", nexthop->u8[sizeof(uip_ipaddr_t) - 1]);
}
@ -238,7 +237,8 @@ rpl_add_route(rpl_dag_t *dag, uip_ipaddr_t *prefix, int prefix_len,
rep->state.dag = dag;
rep->state.lifetime = RPL_LIFETIME(dag->instance, dag->instance->default_lifetime);
rep->state.learned_from = RPL_ROUTE_FROM_INTERNAL;
/* always clear state flags for the no-path received when adding/refreshing */
RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);
PRINTF("RPL: Added a route to ");
PRINT6ADDR(prefix);
@ -267,10 +267,6 @@ rpl_link_neighbor_callback(const linkaddr_t *addr, int status, int numtx)
/* Trigger DAG rank recalculation. */
PRINTF("RPL: rpl_link_neighbor_callback triggering update\n");
parent->flags |= RPL_PARENT_FLAG_UPDATED;
if(instance->of->neighbor_link_callback != NULL) {
instance->of->neighbor_link_callback(parent, status, numtx);
parent->last_tx_time = clock_time();
}
}
}
}
@ -285,7 +281,11 @@ rpl_ipv6_neighbor_callback(uip_ds6_nbr_t *nbr)
PRINTF("RPL: Neighbor state changed for ");
PRINT6ADDR(&nbr->ipaddr);
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
PRINTF(", nscount=%u, state=%u\n", nbr->nscount, nbr->state);
#else /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
PRINTF(", state=%u\n", nbr->state);
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES; instance < end; ++instance) {
if(instance->used == 1 ) {
p = rpl_find_parent_any_dag(instance, &nbr->ipaddr);
@ -346,7 +346,9 @@ rpl_init(void)
memset(&rpl_stats, 0, sizeof(rpl_stats));
#endif
RPL_OF.reset(NULL);
#if RPL_WITH_NON_STORING
rpl_ns_init();
#endif /* RPL_WITH_NON_STORING */
}
/*---------------------------------------------------------------------------*/

Some files were not shown because too many files have changed in this diff Show More