Merge branch 'osd' into ico

Conflicts resolved:
	core/net/mac/contikimac/contikimac.c
This commit is contained in:
Ralf Schlatterbeck 2016-06-30 09:28:54 +02:00
commit 7d63a0f487
1020 changed files with 70078 additions and 10930 deletions

6
.gitignore vendored
View file

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

3
.gitmodules vendored
View file

@ -4,9 +4,6 @@
[submodule "tools/cc2538-bsl"] [submodule "tools/cc2538-bsl"]
path = tools/cc2538-bsl path = tools/cc2538-bsl
url = https://github.com/JelmerT/cc2538-bsl.git url = https://github.com/JelmerT/cc2538-bsl.git
[submodule "platform/osd-merkur/dev/LED_Strip_Suli"]
path = platform/osd-merkur/dev/LED_Strip_Suli
url = https://github.com/osdomotics/LED_Strip_Suli.git
[submodule "cpu/cc26xx-cc13xx/lib/cc26xxware"] [submodule "cpu/cc26xx-cc13xx/lib/cc26xxware"]
path = cpu/cc26xx-cc13xx/lib/cc26xxware path = cpu/cc26xx-cc13xx/lib/cc26xxware
url = https://github.com/g-oikonomou/cc26xxware.git url = https://github.com/g-oikonomou/cc26xxware.git

View file

@ -24,7 +24,11 @@ before_script:
msp430-gcc --version msp430-gcc --version
## Install avr toolchain ## 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 ## Install 32-bit compatibility libraries
- sudo apt-get -qq install libc6:i386 libgcc1:i386 gcc-4.6-base:i386 - 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 ; arm-none-eabi-gcc --version ;
fi fi
## Install mainline ARM toolchain. gcc-arm-none-eabi is available ## Install mainline ARM toolchain and srecord.
## in Ubuntu >= 14.04, but this external PPA is needed for 12.04.
## Install srecord
- if [ ${BUILD_ARCH:-0} = arm-aapcs ] ; then - if [ ${BUILD_ARCH:-0} = arm-aapcs ] ; then
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa && sudo apt-get -qq install srecord &&
sudo apt-get -qq update && $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 &&
sudo apt-get -qq install gcc-arm-embedded=5-2015q4-1~precise1 srecord && 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 ; arm-none-eabi-gcc --version ;
fi fi
@ -70,7 +74,6 @@ before_script:
git clone https://github.com/cc65/cc65 /tmp/cc65 && git clone https://github.com/cc65/cc65 /tmp/cc65 &&
make -C /tmp/cc65 bin apple2enh atarixl c64 c128 && make -C /tmp/cc65 bin apple2enh atarixl c64 c128 &&
sudo make -C /tmp/cc65 avail && sudo make -C /tmp/cc65 avail &&
export CC65_HOME=/tmp/cc65/ &&
cc65 --version ; cc65 --version ;
fi fi
@ -90,6 +93,18 @@ before_script:
ba-elf-gcc --version ; ba-elf-gcc --version ;
fi 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 ## Compile cooja.jar only when it's going to be needed
- if [ ${BUILD_CATEGORY:-sim} = sim ] ; then - if [ ${BUILD_CATEGORY:-sim} = sim ] ; then
java -version && java -version &&
@ -120,6 +135,7 @@ env:
- BUILD_TYPE='collect' - BUILD_TYPE='collect'
- BUILD_TYPE='collect-lossy' - BUILD_TYPE='collect-lossy'
- BUILD_TYPE='rpl' - BUILD_TYPE='rpl'
- BUILD_TYPE='rpl-non-storing'
- BUILD_TYPE='large-rpl' - BUILD_TYPE='large-rpl'
- BUILD_TYPE='rime' - BUILD_TYPE='rime'
- BUILD_TYPE='ipv6' - BUILD_TYPE='ipv6'
@ -138,5 +154,7 @@ env:
- BUILD_TYPE='compile-6502-ports' BUILD_CATEGORY='compile' BUILD_ARCH='6502' - 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-arm-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-aapcs'
- BUILD_TYPE='compile-nxp-ports' BUILD_CATEGORY='compile' BUILD_ARCH='jn516x' - 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='slip-radio' MAKE_TARGETS='cooja'
- BUILD_TYPE='llsec' 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)} $(CONTIKI_CPU_DIRS)}
SOURCEDIRS = . $(PROJECTDIRS) $(CONTIKI_TARGET_DIRS_CONCAT) \ 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 %.c $(SOURCEDIRS)
vpath %.cpp $(SOURCEDIRS) vpath %.cpp $(SOURCEDIRS)

View file

@ -54,6 +54,34 @@
#include "adc.h" #include "adc.h"
#include "hw-arduino.h" #include "hw-arduino.h"
extern volatile uint8_t mcusleepcycle;
volatile uint8_t mcusleepcycleval;
/*-------------- enabled sleep mode ----------------------------------------*/
void
mcu_sleep_init(void)
{
mcusleepcycleval=mcusleepcycle;
}
void
mcu_sleep_on(void)
{
mcusleepcycle= mcusleepcycleval;
}
/*--------------- disable sleep mode ---------------------------------------*/
void
mcu_sleep_off(void)
{
mcusleepcycle=0;
}
/*---------------- set duty cycle value ------------------------------------*/
void
mcu_sleep_set(uint8_t value)
{
mcusleepcycleval= value;
mcusleepcycle = mcusleepcycleval;
}
PROCESS(arduino_sketch, "Arduino Sketch Wrapper"); PROCESS(arduino_sketch, "Arduino Sketch Wrapper");
#ifndef LOOP_INTERVAL #ifndef LOOP_INTERVAL
@ -65,8 +93,8 @@ PROCESS_THREAD(arduino_sketch, ev, data)
static struct etimer loop_periodic_timer; static struct etimer loop_periodic_timer;
PROCESS_BEGIN(); PROCESS_BEGIN();
adc_init (); adc_init ();
mcu_sleep_init ();
setup (); setup ();
/* Define application-specific events here. */ /* Define application-specific events here. */
etimer_set(&loop_periodic_timer, LOOP_INTERVAL); etimer_set(&loop_periodic_timer, LOOP_INTERVAL);

View file

@ -51,6 +51,13 @@
#include "contiki.h" #include "contiki.h"
/*--------------- enable sleep mode ---------------------------------------*/
void mcu_sleep_on(void);
/*--------------- disable sleep mode ---------------------------------------*/
void mcu_sleep_off(void);
/*---------------- set sleep value ------------------------------------*/
void mcu_sleep_set(uint8_t value);
extern void loop (void); extern void loop (void);
extern void setup (void); extern void setup (void);
extern void arduino_init (void); extern void arduino_init (void);

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, int coap_separate_handler(resource_t *resource, void *request,
void *response); 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_accept(void *request, coap_separate_t *separate_store);
void coap_separate_resume(void *response, coap_separate_t *separate_store, void coap_separate_resume(void *response, coap_separate_t *separate_store,
uint8_t code); uint8_t code);

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,13 @@
oma-lwm2m_src = lwm2m-object.c lwm2m-engine.c \ oma-lwm2m_src = \
lwm2m-device.c lwm2m-server.c lwm2m-security.c \ lwm2m-object.c \
oma-tlv.c oma-tlv-reader.c oma-tlv-writer.c \ lwm2m-engine.c \
lwm2m-plain-text.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 CFLAGS += -DHAVE_OMA_LWM2M=1

View file

@ -46,10 +46,12 @@
#include "lwm2m-object.h" #include "lwm2m-object.h"
#include "lwm2m-device.h" #include "lwm2m-device.h"
#include "lwm2m-plain-text.h" #include "lwm2m-plain-text.h"
#include "lwm2m-json.h"
#include "rest-engine.h" #include "rest-engine.h"
#include "er-coap-constants.h" #include "er-coap-constants.h"
#include "er-coap-engine.h" #include "er-coap-engine.h"
#include "oma-tlv.h" #include "oma-tlv.h"
#include "oma-tlv-reader.h"
#include "oma-tlv-writer.h" #include "oma-tlv-writer.h"
#include "net/ipv6/uip-ds6.h" #include "net/ipv6/uip-ds6.h"
#include <stdio.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); value = lwm2m_object_get_resource_string(resource, context);
slen = lwm2m_object_get_resource_strlen(resource, context); slen = lwm2m_object_get_resource_strlen(resource, context);
if(value != NULL) { if(value != NULL) {
PRINTF("%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s, PRINTF("%s{\"n\":\"%u\",\"sv\":\"%.*s\"}", s,
resource->id, slen, value); resource->id, slen, value);
len = snprintf(&buffer[rdlen], size - rdlen, len = snprintf(&buffer[rdlen], size - rdlen,
"%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s, "%s{\"n\":\"%u\",\"sv\":\"%.*s\"}", s,
resource->id, slen, value); resource->id, slen, value);
} }
} else if(lwm2m_object_is_resource_int(resource)) { } 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)) { } else if(lwm2m_object_is_resource_boolean(resource)) {
int value; int value;
if(lwm2m_object_get_resource_boolean(resource, context, &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"); value ? "true" : "false");
len = snprintf(&buffer[rdlen], size - rdlen, 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"); value ? "true" : "false");
} }
} }
@ -707,6 +709,63 @@ write_rd_json_data(const lwm2m_context_t *context,
return rdlen; 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 void
lwm2m_engine_handler(const lwm2m_object_t *object, lwm2m_engine_handler(const lwm2m_object_t *object,
void *request, void *response, void *request, void *response,
@ -716,6 +775,8 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
int len; int len;
const char *url; const char *url;
unsigned int format; unsigned int format;
unsigned int accept;
unsigned int content_type;
int depth; int depth;
lwm2m_context_t context; lwm2m_context_t context;
rest_resource_flags_t method; 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 */ /* CoAP content format text plain - assume LWM2M text plain */
format = 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); depth = lwm2m_engine_parse_context(object, url, len, &context);
PRINTF("Context: %u/%u/%u found: %d\n", context.object_id, PRINTF("Context: %u/%u/%u found: %d\n", context.object_id,
context.object_instance_id, context.resource_id, depth); 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 #if (DEBUG) & DEBUG_PRINT
/* for debugging */ /* for debugging */
if(method == METHOD_GET) { if(method == METHOD_GET) {
@ -861,7 +930,7 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
if(depth == 3) { if(depth == 3) {
const lwm2m_resource_t *resource = get_resource(instance, &context); const lwm2m_resource_t *resource = get_resource(instance, &context);
size_t tlvlen = 0; size_t content_len = 0;
if(resource == NULL) { if(resource == NULL) {
PRINTF("Error - do not have resource %d\n", context.resource_id); PRINTF("Error - do not have resource %d\n", context.resource_id);
REST.set_response_status(response, NOT_FOUND_4_04); 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; context.reader = &lwm2m_plain_text_reader;
PRINTF("PUT Callback with data: '%.*s'\n", plen, data); PRINTF("PUT Callback with data: '%.*s'\n", plen, data);
/* no specific reader for plain text */ /* 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); 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); REST.set_response_status(response, CHANGED_2_04);
} else { } else {
PRINTF("PUT callback with format %d\n", format); 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) { } else if(method == METHOD_GET) {
if(lwm2m_object_is_resource_string(resource)) { if(lwm2m_object_is_resource_string(resource)) {
const uint8_t *value; const uint8_t *value;
uint16_t len;
value = lwm2m_object_get_resource_string(resource, &context); value = lwm2m_object_get_resource_string(resource, &context);
len = lwm2m_object_get_resource_strlen(resource, &context);
if(value != NULL) { if(value != NULL) {
uint16_t len = lwm2m_object_get_resource_strlen(resource, &context);
PRINTF("Get string value: %.*s\n", (int)len, (char *)value); PRINTF("Get string value: %.*s\n", (int)len, (char *)value);
/* TODO check format */ content_len = context.writer->write_string(&context, buffer,
REST.set_response_payload(response, value, len); preferred_size, (const char *)value, len);
REST.set_header_content_type(response, LWM2M_TEXT_PLAIN);
/* Done */
return;
} }
} else if(lwm2m_object_is_resource_int(resource)) { } else if(lwm2m_object_is_resource_int(resource)) {
int32_t value; int32_t value;
if(lwm2m_object_get_resource_int(resource, &context, &value)) { if(lwm2m_object_get_resource_int(resource, &context, &value)) {
/* export INT as TLV */ content_len = context.writer->write_int(&context, buffer, preferred_size, value);
tlvlen = oma_tlv_write_int32(resource->id, value, buffer, preferred_size);
PRINTF("Exporting int as TLV: %" PRId32 ", len: %u\n",
value, (unsigned int)tlvlen);
} }
} else if(lwm2m_object_is_resource_floatfix(resource)) { } else if(lwm2m_object_is_resource_floatfix(resource)) {
int32_t value; int32_t value;
if(lwm2m_object_get_resource_floatfix(resource, &context, &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", PRINTF("Exporting %d-bit fix as float: %" PRId32 "\n",
LWM2M_FLOAT32_BITS, value); LWM2M_FLOAT32_BITS, value);
tlvlen = oma_tlv_write_float32(resource->id, content_len = context.writer->write_float32fix(&context, buffer,
value, LWM2M_FLOAT32_BITS, preferred_size, value, LWM2M_FLOAT32_BITS);
buffer, preferred_size);
PRINTF("Exporting as TLV: len:%u\n", (unsigned int)tlvlen);
} }
} else if(lwm2m_object_is_resource_callback(resource)) { } else if(lwm2m_object_is_resource_callback(resource)) {
if(resource->value.callback.read != NULL) { if(resource->value.callback.read != NULL) {
tlvlen = resource->value.callback.read(&context, content_len = resource->value.callback.read(&context,
buffer, preferred_size); buffer, preferred_size);
} else { } else {
REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05); REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05);
return; return;
} }
} }
if(tlvlen > 0) { if(content_len > 0) {
REST.set_response_payload(response, buffer, tlvlen); REST.set_response_payload(response, buffer, content_len);
REST.set_header_content_type(response, LWM2M_TLV); REST.set_header_content_type(response, content_type);
} else { } else {
/* failed to produce output - it is an internal error */ /* failed to produce output - it is an internal error */
REST.set_response_status(response, INTERNAL_SERVER_ERROR_5_00); 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; const uint8_t *data;
int plen = REST.get_request_payload(request, &data); int plen = REST.get_request_payload(request, &data);
PRINTF("Execute Callback with data: '%.*s'\n", plen, 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, data, plen,
buffer, preferred_size); buffer, preferred_size);
REST.set_response_status(response, CHANGED_2_04); 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); REST.set_response_status(response, NOT_FOUND_4_04);
} else { } else {
int rdlen; int rdlen;
if(format == APPLICATION_LINK_FORMAT) { if(accept == APPLICATION_LINK_FORMAT) {
rdlen = write_rd_link_data(object, instance, rdlen = write_rd_link_data(object, instance,
(char *)buffer, preferred_size); (char *)buffer, preferred_size);
} else { } else {
@ -986,10 +1046,10 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
return; return;
} }
REST.set_response_payload(response, buffer, rdlen); 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); REST.set_header_content_type(response, REST.type.APPLICATION_LINK_FORMAT);
} else { } 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; break;
} }
} }
if(dot == 0) {
integerpart = counter;
counter = 0;
frac = 1;
}
*value = integerpart << bits; *value = integerpart << bits;
if(frac > 1) { if(frac > 1) {
*value += ((counter << bits) / frac); *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 sender-based or receiver-based slotframe for unicast to RPL parents and children
* - a common shared slotframe for any other traffic (mostly broadcast) * - a common shared slotframe for any other traffic (mostly broadcast)
* */ * */
#define ORCHESTRA_RULES { &eb_per_time_source, \ #define ORCHESTRA_RULES { &eb_per_time_source, &unicast_per_neighbor_rpl_storing, &default_common }
&unicast_per_neighbor, \ /* Example configuration for RPL non-storing mode: */
&default_common, \ /* #define ORCHESTRA_RULES { &eb_per_time_source, &unicast_per_neighbor_rpl_ns, &default_common } */
}
#endif /* ORCHESTRA_CONF_RULES */ #endif /* ORCHESTRA_CONF_RULES */
/* Length of the various slotframes. Tune to balance network capacity, /* 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 static void
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new) new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{ {
uint16_t old_ts = get_node_timeslot(&old->addr); uint16_t old_ts = old != NULL ? get_node_timeslot(&old->addr) : 0xffff;
uint16_t new_ts = get_node_timeslot(&new->addr); uint16_t new_ts = new != NULL ? get_node_timeslot(&new->addr) : 0xffff;
if(new_ts == old_ts) { if(new_ts == old_ts) {
return; return;
@ -83,14 +83,24 @@ new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new
if(old_ts != 0xffff) { if(old_ts != 0xffff) {
/* Stop listening to the old time source's EBs */ /* 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) { 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 */ /* Listen to the time source's EBs */
tsch_schedule_add_link(sf_eb, tsch_schedule_add_link(sf_eb, link_options, LINK_TYPE_ADVERTISING_ONLY,
LINK_OPTION_RX, &tsch_broadcast_address, new_ts, 0);
LINK_TYPE_ADVERTISING_ONLY, NULL,
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 * \file
* Orchestra: a slotframe dedicated to unicast data transmission. * Orchestra: a slotframe dedicated to unicast data transmission. Designed for
* If sender-based: * 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 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, * Nodes transmit at: for each nbr in RPL children and RPL preferred parent,
* hash(nbr.MAC) % ORCHESTRA_SB_UNICAST_PERIOD * hash(nbr.MAC) % ORCHESTRA_SB_UNICAST_PERIOD
* If receiver-based: the opposite * If sender-based: the opposite
* *
* \author Simon Duquennoy <simonduq@sics.se> * \author Simon Duquennoy <simonduq@sics.se>
*/ */
@ -43,6 +44,7 @@
#include "orchestra.h" #include "orchestra.h"
#include "net/ipv6/uip-ds6-route.h" #include "net/ipv6/uip-ds6-route.h"
#include "net/packetbuf.h" #include "net/packetbuf.h"
#include "net/rpl/rpl-conf.h"
#if ORCHESTRA_UNICAST_SENDER_BASED && ORCHESTRA_COLLISION_FREE_HASH #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) #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) { if(linkaddr != NULL) {
uint16_t timeslot = get_node_timeslot(linkaddr); uint16_t timeslot = get_node_timeslot(linkaddr);
tsch_schedule_add_link(sf_unicast, uint8_t link_options = ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG;
ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG,
LINK_TYPE_NORMAL, &tsch_broadcast_address, if(timeslot == get_node_timeslot(&linkaddr_node_addr)) {
timeslot, channel_offset); /* 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); 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 static void
@ -160,13 +178,14 @@ static void
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new) new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
{ {
if(new != old) { if(new != old) {
const linkaddr_t *old_addr = old != NULL ? &old->addr : NULL;
const linkaddr_t *new_addr = new != NULL ? &new->addr : NULL; const linkaddr_t *new_addr = new != NULL ? &new->addr : NULL;
if(new_addr != NULL) { if(new_addr != NULL) {
linkaddr_copy(&orchestra_parent_linkaddr, new_addr); linkaddr_copy(&orchestra_parent_linkaddr, new_addr);
} else { } else {
linkaddr_copy(&orchestra_parent_linkaddr, &linkaddr_null); linkaddr_copy(&orchestra_parent_linkaddr, &linkaddr_null);
} }
remove_uc_link(new_addr); remove_uc_link(old_addr);
add_uc_link(new_addr); add_uc_link(new_addr);
} }
} }
@ -185,7 +204,7 @@ init(uint16_t sf_handle)
timeslot, channel_offset); timeslot, channel_offset);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
struct orchestra_rule unicast_per_neighbor = { struct orchestra_rule unicast_per_neighbor_rpl_storing = {
init, init,
new_time_source, new_time_source,
select_packet, select_packet,

View file

@ -53,7 +53,8 @@ struct orchestra_rule {
}; };
struct orchestra_rule eb_per_time_source; 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; struct orchestra_rule default_common;
extern linkaddr_t orchestra_parent_linkaddr; 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); shell_output_str(&exec_command, print, symbol);
if(ret == ELFLOADER_OK) { if(ret == ELFLOADER_OK) {
#if !PROCESS_CONF_NO_PROCESS_NAMES
int i; int i;
for(i = 0; elfloader_autostart_processes[i] != NULL; ++i) { for(i = 0; elfloader_autostart_processes[i] != NULL; ++i) {
shell_output_str(&exec_command, "exec: starting process ", shell_output_str(&exec_command, "exec: starting process ",
elfloader_autostart_processes[i]->name); elfloader_autostart_processes[i]->name);
} }
#endif
autostart_start(elfloader_autostart_processes); autostart_start(elfloader_autostart_processes);
} }

View file

@ -75,12 +75,6 @@
#error "Cannot have COFFEE_APPEND_ONLY set when COFFEE_MICRO_LOGS is set." #error "Cannot have COFFEE_APPEND_ONLY set when COFFEE_MICRO_LOGS is set."
#endif #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. * Prevent sectors from being erased directly after file removal.
* This will level the wear across sectors better, but may lead * 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. #error COFFEE_START must point to the first byte in a sector.
#endif #endif
/* File descriptor flags. */
#define COFFEE_FD_FREE 0x0 #define COFFEE_FD_FREE 0x0
#define COFFEE_FD_READ 0x1 #define COFFEE_FD_READ 0x1
#define COFFEE_FD_WRITE 0x2 #define COFFEE_FD_WRITE 0x2
#define COFFEE_FD_APPEND 0x4 #define COFFEE_FD_APPEND 0x4
/* File object flags. */
#define COFFEE_FILE_MODIFIED 0x1 #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 UNKNOWN_OFFSET ((cfs_offset_t)-1)
#define REMOVE_LOG 1 /* File removal actions. They can have the same values because
#define CLOSE_FDS 1 they are passed as separate parameters. */
#define ALLOW_GC 1 #define REMOVE_LOG 1
#define CLOSE_FDS 1
#define ALLOW_GC 1
/* "Greedy" garbage collection erases as many sectors as possible. */ /* "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. */ /* "Reluctant" garbage collection stops after erasing one sector. */
#define GC_RELUCTANT 1 #define GC_RELUCTANT 1
/* File descriptor macros. */ /* File descriptor macros. */
#define FD_VALID(fd) \ #define FD_VALID(fd) ((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \ coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
#define FD_READABLE(fd) (coffee_fd_set[(fd)].flags & CFS_READ) #define FD_READABLE(fd) (coffee_fd_set[(fd)].flags & CFS_READ)
#define FD_WRITABLE(fd) (coffee_fd_set[(fd)].flags & CFS_WRITE) #define FD_WRITABLE(fd) (coffee_fd_set[(fd)].flags & CFS_WRITE)
#define FD_APPENDABLE(fd) (coffee_fd_set[(fd)].flags & CFS_APPEND) #define FD_APPENDABLE(fd) (coffee_fd_set[(fd)].flags & CFS_APPEND)
/* File object macros. */ /* File object macros. */
#define FILE_MODIFIED(file) ((file)->flags & COFFEE_FILE_MODIFIED) #define FILE_MODIFIED(file) ((file)->flags & COFFEE_FILE_MODIFIED)
#define FILE_FREE(file) ((file)->max_pages == 0) #define FILE_FREE(file) ((file)->max_pages == 0)
#define FILE_UNREFERENCED(file) ((file)->references == 0) #define FILE_UNREFERENCED(file) ((file)->references == 0)
/* File header flags. */ /* File header flags. */
#define HDR_FLAG_VALID 0x1 /* Completely written header. */ #define HDR_FLAG_VALID 0x01 /* Completely written header. */
#define HDR_FLAG_ALLOCATED 0x2 /* Allocated file. */ #define HDR_FLAG_ALLOCATED 0x02 /* Allocated file. */
#define HDR_FLAG_OBSOLETE 0x4 /* File marked for GC. */ #define HDR_FLAG_OBSOLETE 0x04 /* File marked for GC. */
#define HDR_FLAG_MODIFIED 0x8 /* Modified file, log exists. */ #define HDR_FLAG_MODIFIED 0x08 /* Modified file, log exists. */
#define HDR_FLAG_LOG 0x10 /* Log file. */ #define HDR_FLAG_LOG 0x10 /* Log file. */
#define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */ #define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */
/* File header macros. */ /* File header macros. */
#define CHECK_FLAG(hdr, flag) ((hdr).flags & (flag)) #define CHECK_FLAG(hdr, flag) ((hdr).flags & (flag))
#define HDR_VALID(hdr) CHECK_FLAG(hdr, HDR_FLAG_VALID) #define HDR_VALID(hdr) CHECK_FLAG(hdr, HDR_FLAG_VALID)
#define HDR_ALLOCATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED) #define HDR_ALLOCATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED)
#define HDR_FREE(hdr) !HDR_ALLOCATED(hdr) #define HDR_FREE(hdr) !HDR_ALLOCATED(hdr)
#define HDR_LOG(hdr) CHECK_FLAG(hdr, HDR_FLAG_LOG) #define HDR_LOG(hdr) CHECK_FLAG(hdr, HDR_FLAG_LOG)
#define HDR_MODIFIED(hdr) CHECK_FLAG(hdr, HDR_FLAG_MODIFIED) #define HDR_MODIFIED(hdr) CHECK_FLAG(hdr, HDR_FLAG_MODIFIED)
#define HDR_ISOLATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ISOLATED) #define HDR_ISOLATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ISOLATED)
#define HDR_OBSOLETE(hdr) CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE) #define HDR_OBSOLETE(hdr) CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE)
#define HDR_ACTIVE(hdr) (HDR_ALLOCATED(hdr) && \ #define HDR_ACTIVE(hdr) (HDR_ALLOCATED(hdr) && \
!HDR_OBSOLETE(hdr) && \ !HDR_OBSOLETE(hdr) && \
!HDR_ISOLATED(hdr)) !HDR_ISOLATED(hdr))
/* Shortcuts derived from the hardware-dependent configuration of Coffee. */ /* 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 \ #define COFFEE_PAGE_COUNT \
((coffee_page_t)(COFFEE_SIZE / COFFEE_PAGE_SIZE)) ((coffee_page_t)(COFFEE_SIZE / COFFEE_PAGE_SIZE))
#define COFFEE_PAGES_PER_SECTOR \ #define COFFEE_PAGES_PER_SECTOR \
@ -176,9 +175,7 @@ struct file_desc {
cfs_offset_t offset; cfs_offset_t offset;
struct file *file; struct file *file;
uint8_t flags; uint8_t flags;
#if COFFEE_IO_SEMANTICS
uint8_t io_flags; uint8_t io_flags;
#endif
}; };
/* The file header structure mimics the representation of file headers /* 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. */ /* This is needed because of a buggy compiler. */
struct log_param { struct log_param {
cfs_offset_t offset; cfs_offset_t offset;
const char *buf; char *buf;
uint16_t size; uint16_t size;
}; };
/* /*
* The protected memory consists of structures that should not be * Variables that keep track of opened files and internal
* overwritten during system checkpointing because they may be used by * optimization information for Coffee.
* the checkpointing implementation. These structures need not be
* protected if checkpointing is not used.
*/ */
static struct protected_mem_t { static struct file coffee_files[COFFEE_MAX_OPEN_FILES];
struct file coffee_files[COFFEE_MAX_OPEN_FILES]; static struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE]; static coffee_page_t next_free;
coffee_page_t next_free; static char gc_wait;
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 void static void
@ -229,11 +218,9 @@ static void
read_header(struct file_header *hdr, coffee_page_t page) read_header(struct file_header *hdr, coffee_page_t page)
{ {
COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE); COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
#if DEBUG if(DEBUG && HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
if(HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) { PRINTF("Coffee: Invalid header at page %u!\n", (unsigned)page);
PRINTF("Invalid header at page %u!\n", (unsigned)page);
} }
#endif
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static cfs_offset_t static cfs_offset_t
@ -243,7 +230,7 @@ absolute_offset(coffee_page_t page, cfs_offset_t offset)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static coffee_page_t 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 coffee_page_t skip_pages;
static char last_pages_are_active; 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 * Determine the amount of pages in the following sectors that
* should be remembered for the next iteration. This is necessary * 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 * 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 * amount is that there is no need to read in the headers of each
* of these pages from the storage. * of these pages from the storage.
@ -364,11 +351,11 @@ isolate_pages(coffee_page_t start, coffee_page_t skip_pages)
static void static void
collect_garbage(int mode) collect_garbage(int mode)
{ {
uint16_t sector; coffee_page_t sector;
struct sector_status stats; struct sector_status stats;
coffee_page_t first_page, isolation_count; 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"); mode == GC_RELUCTANT ? "reluctant" : "greedy");
/* /*
* The garbage collector erases as many sectors as possible. A sector is * 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++) { for(sector = 0; sector < COFFEE_SECTOR_COUNT; sector++) {
isolation_count = get_sector_status(sector, &stats); isolation_count = get_sector_status(sector, &stats);
PRINTF("Coffee: Sector %u has %u active, %u obsolete, and %u free pages.\n", 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); (unsigned)stats.obsolete, (unsigned)stats.free);
if(stats.active > 0) { if(stats.active > 0) {
@ -387,8 +374,8 @@ collect_garbage(int mode)
if((mode == GC_RELUCTANT && stats.free == 0) || if((mode == GC_RELUCTANT && stats.free == 0) ||
(mode == GC_GREEDY && stats.obsolete > 0)) { (mode == GC_GREEDY && stats.obsolete > 0)) {
first_page = sector * COFFEE_PAGES_PER_SECTOR; first_page = sector * COFFEE_PAGES_PER_SECTOR;
if(first_page < *next_free) { if(first_page < next_free) {
*next_free = first_page; next_free = first_page;
} }
if(isolation_count > 0) { 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 * 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 * algorithm to quickly jump over free areas and allocated extents
* after reading single headers and determining their status. * 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->page = start;
file->end = UNKNOWN_OFFSET; file->end = UNKNOWN_OFFSET;
file->max_pages = hdr->max_pages; file->max_pages = hdr->max_pages;
file->flags = 0; file->flags = HDR_MODIFIED(*hdr) ? COFFEE_FILE_MODIFIED : 0;
if(HDR_MODIFIED(*hdr)) {
file->flags |= COFFEE_FILE_MODIFIED;
}
/* We don't know the amount of records yet. */ /* We don't know the amount of records yet. */
file->record_count = -1; file->record_count = -1;
@ -539,7 +523,7 @@ find_contiguous_pages(coffee_page_t amount)
struct file_header hdr; struct file_header hdr;
start = INVALID_PAGE; start = INVALID_PAGE;
for(page = *next_free; page < COFFEE_PAGE_COUNT;) { for(page = next_free; page < COFFEE_PAGE_COUNT;) {
read_header(&hdr, page); read_header(&hdr, page);
if(HDR_FREE(hdr)) { if(HDR_FREE(hdr)) {
if(start == INVALID_PAGE) { if(start == INVALID_PAGE) {
@ -555,8 +539,8 @@ find_contiguous_pages(coffee_page_t amount)
page = next_file(page, &hdr); page = next_file(page, &hdr);
if(start + amount <= page) { if(start + amount <= page) {
if(start == *next_free) { if(start == next_free) {
*next_free = start + amount; next_free = start + amount;
} }
return start; return start;
} }
@ -569,8 +553,8 @@ find_contiguous_pages(coffee_page_t amount)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static int static int
remove_by_page(coffee_page_t page, int remove_log, int close_fds, remove_by_page(coffee_page_t page, int remove_log,
int gc_allowed) int close_fds, int gc_allowed)
{ {
struct file_header hdr; struct file_header hdr;
int i; int i;
@ -589,7 +573,7 @@ remove_by_page(coffee_page_t page, int remove_log, int close_fds,
hdr.flags |= HDR_FLAG_OBSOLETE; hdr.flags |= HDR_FLAG_OBSOLETE;
write_header(&hdr, page); write_header(&hdr, page);
*gc_wait = 0; gc_wait = 0;
/* Close all file descriptors that reference the removed file. */ /* Close all file descriptors that reference the removed file. */
if(close_fds) { 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(!COFFEE_EXTENDED_WEAR_LEVELLING && gc_allowed) {
if(gc_allowed) {
collect_garbage(GC_RELUCTANT); collect_garbage(GC_RELUCTANT);
} }
#endif
return 0; return 0;
} }
@ -638,13 +620,13 @@ reserve(const char *name, coffee_page_t pages,
page = find_contiguous_pages(pages); page = find_contiguous_pages(pages);
if(page == INVALID_PAGE) { if(page == INVALID_PAGE) {
if(*gc_wait) { if(gc_wait) {
return NULL; return NULL;
} }
collect_garbage(GC_GREEDY); collect_garbage(GC_GREEDY);
page = find_contiguous_pages(pages); page = find_contiguous_pages(pages);
if(page == INVALID_PAGE) { if(page == INVALID_PAGE) {
*gc_wait = 1; gc_wait = 1;
return NULL; return NULL;
} }
} }
@ -656,7 +638,7 @@ reserve(const char *name, coffee_page_t pages,
write_header(&hdr, page); write_header(&hdr, page);
PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n", 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); file = load_file(page, &hdr);
if(file != NULL) { if(file != NULL) {
@ -851,7 +833,7 @@ merge_log(coffee_page_t file_page, int extend)
return -1; return -1;
} }
/* Copy the log configuration and the EOF hint. */ /* Copy the log configuration. */
read_header(&hdr2, new_file->page); read_header(&hdr2, new_file->page);
hdr2.log_record_size = hdr.log_record_size; hdr2.log_record_size = hdr.log_record_size;
hdr2.log_records = hdr.log_records; hdr2.log_records = hdr.log_records;
@ -1005,6 +987,7 @@ cfs_open(const char *name, int flags)
fdp = &coffee_fd_set[fd]; fdp = &coffee_fd_set[fd];
fdp->flags = 0; fdp->flags = 0;
fdp->io_flags = 0;
fdp->file = find_file(name); fdp->file = find_file(name);
if(fdp->file == NULL) { if(fdp->file == NULL) {
@ -1063,7 +1046,12 @@ cfs_seek(int fd, cfs_offset_t offset, int whence)
} }
if(fdp->file->end < new_offset) { 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; return fdp->offset = new_offset;
@ -1106,11 +1094,16 @@ cfs_read(int fd, void *buf, unsigned size)
fdp = &coffee_fd_set[fd]; fdp = &coffee_fd_set[fd];
file = fdp->file; 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; 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)) { if(!FILE_MODIFIED(file)) {
COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset)); COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset));
fdp->offset += size; fdp->offset += size;
@ -1121,8 +1114,9 @@ cfs_read(int fd, void *buf, unsigned size)
read_header(&hdr, file->page); read_header(&hdr, file->page);
/* /*
* Fill the buffer by copying from the log in first hand, or the * Copy the contents of the most recent log record. If there is
* ordinary file if the page has no log record. * 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) { for(bytes_left = size; bytes_left > 0; bytes_left -= r) {
lp.offset = fdp->offset; lp.offset = fdp->offset;
@ -1164,32 +1158,24 @@ cfs_write(int fd, const void *buf, unsigned size)
file = fdp->file; file = fdp->file;
/* Attempt to extend the file if we try to write past the end. */ /* 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)) { if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) {
#endif while(size + fdp->offset + sizeof(struct file_header) >
while(size + fdp->offset + sizeof(struct file_header) > (file->max_pages * COFFEE_PAGE_SIZE)) {
(file->max_pages * COFFEE_PAGE_SIZE)) { if(merge_log(file->page, 1) < 0) {
if(merge_log(file->page, 1) < 0) { return -1;
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_MICRO_LOGS
#if COFFEE_IO_SEMANTICS
if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) && if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) &&
(FILE_MODIFIED(file) || fdp->offset < file->end)) { (FILE_MODIFIED(file) || fdp->offset < file->end)) {
#else
if(FILE_MODIFIED(file) || fdp->offset < file->end) {
#endif
need_dummy_write = 0; need_dummy_write = 0;
for(bytes_left = size; bytes_left > 0;) { for(bytes_left = size; bytes_left > 0;) {
lp.offset = fdp->offset; lp.offset = fdp->offset;
lp.buf = buf; lp.buf = (void *)buf;
lp.size = bytes_left; lp.size = bytes_left;
i = write_log_page(file, &lp); i = write_log_page(file, &lp);
if(i < 0) { if(i < 0) {
@ -1227,16 +1213,14 @@ cfs_write(int fd, const void *buf, unsigned size)
} }
} else { } else {
#endif /* COFFEE_MICRO_LOGS */ #endif /* COFFEE_MICRO_LOGS */
#if COFFEE_APPEND_ONLY if(COFFEE_APPEND_ONLY && fdp->offset < file->end) {
if(fdp->offset < file->end) { return -1;
return -1; }
}
#endif /* COFFEE_APPEND_ONLY */
COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset)); COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
fdp->offset += size; fdp->offset += size;
#if COFFEE_MICRO_LOGS #if COFFEE_MICRO_LOGS
} }
#endif /* COFFEE_MICRO_LOGS */ #endif /* COFFEE_MICRO_LOGS */
if(fdp->offset > file->end) { if(fdp->offset > file->end) {
@ -1250,10 +1234,10 @@ int
cfs_opendir(struct cfs_dir *dir, const char *name) cfs_opendir(struct cfs_dir *dir, const char *name)
{ {
/* /*
* Coffee is only guaranteed to support "/" and ".", but it does not * Coffee is only guaranteed to support the directory names "/" and ".",
* currently enforce this. * 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; return 0;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -1262,19 +1246,19 @@ cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
{ {
struct file_header hdr; struct file_header hdr;
coffee_page_t page; 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) { while(page < COFFEE_PAGE_COUNT) {
read_header(&hdr, page); read_header(&hdr, page);
if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) { if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) {
coffee_page_t next_page;
memcpy(record->name, hdr.name, sizeof(record->name)); memcpy(record->name, hdr.name, sizeof(record->name));
record->name[sizeof(record->name) - 1] = '\0'; record->name[sizeof(record->name) - 1] = '\0';
record->size = file_end(page); record->size = file_end(page);
next_page = next_file(page, &hdr); 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; return 0;
} }
page = next_file(page, &hdr); page = next_file(page, &hdr);
@ -1342,11 +1326,9 @@ cfs_coffee_set_io_semantics(int fd, unsigned flags)
int int
cfs_coffee_format(void) cfs_coffee_format(void)
{ {
unsigned i; coffee_page_t i;
PRINTF("Coffee: Formatting %u sectors", COFFEE_SECTOR_COUNT); PRINTF("Coffee: Formatting %u sectors", (unsigned)COFFEE_SECTOR_COUNT);
*next_free = 0;
for(i = 0; i < COFFEE_SECTOR_COUNT; i++) { for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
COFFEE_ERASE(i); COFFEE_ERASE(i);
@ -1354,16 +1336,13 @@ cfs_coffee_format(void)
} }
/* Formatting invalidates the file information. */ /* 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"); PRINTF(" done!\n");
return 0; 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. * invoke its own micro logs when file modifications occur.
* *
* This semantical I/O setting is useful when implementing flash storage * 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() * \sa cfs_coffee_set_io_semantics()
*/ */
#define CFS_COFFEE_IO_FLASH_AWARE 0x1 #define CFS_COFFEE_IO_FLASH_AWARE 0x1
/** /**
* Instruct Coffee not to attempt to extend the file when there is * Instruct Coffee not to attempt to extend the file upon a request
* an attempt to write past the reserved file size. * to write past the reserved file size.
* *
* A case when this is necessary is when the file has a firm size limit, * 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. * and a safeguard is needed to protect against writes beyond this limit.
@ -63,6 +63,15 @@
*/ */
#define CFS_COFFEE_IO_FIRM_SIZE 0x2 #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 * \file
* Header for the Coffee file system. * Header for the Coffee file system.
@ -75,8 +84,8 @@
/** /**
* \brief Reserve space for a file. * \brief Reserve space for a file.
* \param name The filename. * \param name The file name.
* \param size The size of the file. * \param size The initial size to be reserved for the file.
* \return 0 on success, -1 on failure. * \return 0 on success, -1 on failure.
* *
* Coffee uses sequential page structures for files. The sequential * 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. * \brief Configure the on-demand log file.
* \param file The filename. * \param file The file name.
* \param log_size The total log size. * \param log_size The total log file size.
* \param log_entry_size The log entry size. * \param log_entry_size The log entry size.
* \return 0 on success, -1 on failure. * \return 0 on success, -1 on failure.
* *
* When file data is first modified, Coffee creates a micro log for the * When file data is first modified, Coffee creates a micro log for the
* file. The micro log stores a table of modifications whose * file. The micro log stores a table of modifications whose parameters --
* parameters--the log size and the log entry size--can be modified * the log size and the log entry size -- can be modified through the
* through the cfs_coffee_configure_log function. * cfs_coffee_configure_log function.
*/ */
int cfs_coffee_configure_log(const char *file, unsigned log_size, int cfs_coffee_configure_log(const char *file, unsigned log_size,
unsigned log_entry_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 * 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 * I/O file semantics may not be optimal for the access pattern
* of a certain file. Hence, this functions allows programmers to * of a certain file. Hence, this function allows programmers to
* switch the /O semantics on a file that is accessed through a * switch the I/O semantics on a file that is accessed through a
* particular file descriptor. * 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. * Coffee formats the underlying storage by setting all bits to zero.
* Formatting must be done before using Coffee for the first time in * Formatting must be done before using Coffee for the first time in
* a mote. * 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); 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 #endif
struct cfs_dir { 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 { struct cfs_dirent {

View file

@ -78,13 +78,6 @@
#define NETSTACK_CONF_LLSEC nullsec_driver #define NETSTACK_CONF_LLSEC nullsec_driver
#endif /* NETSTACK_CONF_LLSEC */ #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 /* NETSTACK_CONF_NETWORK specifies the network layer and can be either
sicslowpan_driver, for IPv6 networking, or rime_driver, for the sicslowpan_driver, for IPv6 networking, or rime_driver, for the
custom Rime network stack. */ custom Rime network stack. */
@ -148,12 +141,32 @@
#define UIP_CONF_IPV6_RPL 1 #define UIP_CONF_IPV6_RPL 1
#endif /* UIP_CONF_IPV6_RPL */ #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 /* UIP_CONF_MAX_ROUTES specifies the maximum number of routes that each
node will be able to handle. */ node will be able to handle. */
#ifndef UIP_CONF_MAX_ROUTES #ifndef UIP_CONF_MAX_ROUTES
#define UIP_CONF_MAX_ROUTES 20 #define UIP_CONF_MAX_ROUTES 20
#endif /* UIP_CONF_MAX_ROUTES */ #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 /* UIP_CONF_UDP specifies if UDP support should be included or
not. Disabling UDP saves memory but breaks a lot of stuff. */ not. Disabling UDP saves memory but breaks a lot of stuff. */
#ifndef UIP_CONF_UDP #ifndef UIP_CONF_UDP
@ -220,14 +233,6 @@
* on the target platform, and are therefore platform-specific. * 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 /* SICSLOWPAN_CONF_FRAG specifies if 6lowpan fragmentation should be
used or not. Fragmentation is on by default. */ used or not. Fragmentation is on by default. */
#ifndef SICSLOWPAN_CONF_FRAG #ifndef SICSLOWPAN_CONF_FRAG

View file

@ -64,13 +64,13 @@ static unsigned char gcr_bits = 0;
static unsigned short gcr_val = 0; static unsigned short gcr_val = 0;
/* Call before starting encoding or decoding */ /* Call before starting encoding or decoding */
void gcr_init() { void gcr_init(void) {
gcr_val = 0; gcr_val = 0;
gcr_bits = 0; gcr_bits = 0;
} }
/* Use this to check if encoding / decoding is complete for now */ /* Use this to check if encoding / decoding is complete for now */
unsigned char gcr_finished() { unsigned char gcr_finished(void) {
return gcr_bits == 0; return gcr_bits == 0;
} }
@ -100,7 +100,7 @@ void gcr_decode(unsigned char gcr_data) {
} }
/* check if the current decoded stream is correct */ /* check if the current decoded stream is correct */
unsigned char gcr_valid() { unsigned char gcr_valid(void) {
if (gcr_bits >= 10) { if (gcr_bits >= 10) {
unsigned short val = gcr_val & 0x3ff; unsigned short val = gcr_val & 0x3ff;
if ((GCR_decode[val >> 5u] << 4u) == 0xff || 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; typedef eeprom_addr_t settings_iter_t;
/** Will return \ref SETTINGS_INVALID_ITER if the settings store is empty. */ /** 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. */ /** Will return \ref SETTINGS_INVALID_ITER if at the end of settings list. */
extern settings_iter_t settings_iter_next(settings_iter_t iter); 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 #if TRICKLE_TIMER_WIDE_RAND
/* Returns a 4-byte wide, unsigned random number */ /* Returns a 4-byte wide, unsigned random number */
static uint32_t static uint32_t
wide_rand() wide_rand(void)
{ {
return ((uint32_t)random_rand() << 16 | random_rand()); 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) { if(host != NULL) {
host[i] = 0; host[i] = 0;
} }
urlptr++;
break; break;
} }
if(host != NULL) { 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. */ /* Find the port. Default is 80. */
port = 80; port = 80;
if(*urlptr == ':') { 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, " HTTP/1.1\r\n");
tcp_socket_send_str(tcps, "Connection: close\r\n"); tcp_socket_send_str(tcps, "Connection: close\r\n");
tcp_socket_send_str(tcps, "Host: "); 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); tcp_socket_send_str(tcps, host);
if(memchr(host, ':', MAX_HOSTLEN)) {
tcp_socket_send_str(tcps, "]");
}
tcp_socket_send_str(tcps, "\r\n"); tcp_socket_send_str(tcps, "\r\n");
if(s->postdata != NULL) { if(s->postdata != NULL) {
if(s->content_type) { if(s->content_type) {

View file

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

View file

@ -119,7 +119,7 @@ simple_udp_register(struct simple_udp_connection *c,
PROCESS_CONTEXT_BEGIN(&simple_udp_process); PROCESS_CONTEXT_BEGIN(&simple_udp_process);
c->udp_conn = udp_new(remote_addr, UIP_HTONS(remote_port), c); 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)); udp_bind(c->udp_conn, UIP_HTONS(local_port));
} }
PROCESS_CONTEXT_END(); PROCESS_CONTEXT_END();

View file

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

View file

@ -30,7 +30,7 @@
/** /**
* \file * \file
* A set of debugging tools * A set of debugging tools for the IP stack
* \author * \author
* Nicolas Tsiftes <nvt@sics.se> * Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@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 */ #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 * \file
* A set of debugging macros. * A set of debugging macros for the IP stack
* *
* \author Nicolas Tsiftes <nvt@sics.se> * \author Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@sics.se> * Niclas Finne <nfi@sics.se>
* Joakim Eriksson <joakime@sics.se> * Joakim Eriksson <joakime@sics.se>
* Simon Duquennoy <simon.duquennoy@inria.fr>
*/ */
#ifndef UIP_DEBUG_H #ifndef UIP_DEBUG_H
#define UIP_DEBUG_H #define UIP_DEBUG_H
#include "net/net-debug.h"
#include "net/ip/uip.h" #include "net/ip/uip.h"
#include <stdio.h> #include <stdio.h>
void uip_debug_ipaddr_print(const uip_ipaddr_t *addr); 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 #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 PRINT6ADDR(addr) uip_debug_ipaddr_print(addr)
#define PRINTLLADDR(lladdr) uip_debug_lladdr_print(lladdr)
#else #else
#define PRINTF(...)
#define PRINT6ADDR(addr) #define PRINT6ADDR(addr)
#define PRINTLLADDR(lladdr)
#endif /* (DEBUG) & DEBUG_PRINT */ #endif /* (DEBUG) & DEBUG_PRINT */
#endif #endif /* UIP_DEBUG_H */

View file

@ -51,15 +51,13 @@ void
uip_udp_packet_send(struct uip_udp_conn *c, const void *data, int len) uip_udp_packet_send(struct uip_udp_conn *c, const void *data, int len)
{ {
#if UIP_UDP #if UIP_UDP
if(data != NULL) { if(data != NULL && len <= (UIP_BUFSIZE - (UIP_LLH_LEN + UIP_IPUDPH_LEN))) {
uip_udp_conn = c; uip_udp_conn = c;
uip_slen = len; uip_slen = len;
memmove(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data, memmove(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data, len);
len > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?
UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: len);
uip_process(UIP_UDP_SEND_CONN); uip_process(UIP_UDP_SEND_CONN);
#if UIP_CONF_IPV6_MULTICAST #if UIP_IPV6_MULTICAST
/* Let the multicast engine process the datagram before we send it */ /* Let the multicast engine process the datagram before we send it */
if(uip_is_addr_mcast_routable(&uip_udp_conn->ripaddr)) { if(uip_is_addr_mcast_routable(&uip_udp_conn->ripaddr)) {
UIP_MCAST6.out(); UIP_MCAST6.out();

View file

@ -1801,6 +1801,13 @@ typedef struct uip_routing_hdr {
uint8_t seg_left; uint8_t seg_left;
} uip_routing_hdr; } 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 */ /* fragmentation header */
typedef struct uip_frag_hdr { typedef struct uip_frag_hdr {
uint8_t next; uint8_t next;

View file

@ -98,7 +98,7 @@ In order to extend multicast with a new engine, perform the following steps:
- Open `uip-mcast6.h` and add a section in the `#if` spree. This aims to - Open `uip-mcast6.h` and add a section in the `#if` spree. This aims to
configure the uIPv6 core. More specifically, you need to: configure the uIPv6 core. More specifically, you need to:
* Specify if you want to put RPL in MOP3 by defining * Specify if you want to put RPL in MOP3 by defining
`RPL_CONF_MULTICAST`: 1: MOP 3, 0: non-multicast MOP `RPL_WITH_MULTICAST`: 1: MOP 3, 0: non-multicast MOP
* Define your engine details * Define your engine details
#define UIP_MCAST6 foo_driver #define UIP_MCAST6 foo_driver

View file

@ -0,0 +1,406 @@
/*
* Copyright (c) 2010, Loughborough University - 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
* This file shows the implementations of the Enhanced Stateless
* Multicast RPL Forwarding (ESMRF)
*
* It will only work in RPL networks in MOP 3 "Storing with Multicast"
*
* \author
* Khaled Qorany kqorany2@gmail.com
*/
#include "contiki.h"
#include "contiki-net.h"
#include "net/ipv6/multicast/uip-mcast6.h"
#include "net/ipv6/multicast/uip-mcast6-route.h"
#include "net/ipv6/multicast/uip-mcast6-stats.h"
#include "net/ipv6/multicast/esmrf.h"
#include "net/rpl/rpl.h"
#include "net/ip/uip.h"
#include "net/netstack.h"
#include <string.h>
extern uint16_t uip_slen;
#define DEBUG NONE
#include "net/ip/uip-debug.h"
#define ESMRF_VERBOSE NONE
#if DEBUG && ESMRF_VERBOSE
#define VERBOSE_PRINTF(...) PRINTF(__VA_ARGS__)
#define VERBOSE_PRINT_SEED(s) PRINT_SEED(s)
#else
#define VERBOSE_PRINTF(...)
#define VERBOSE_PRINT_SEED(...)
#endif
/*---------------------------------------------------------------------------*/
/* Maintain Stats */
#if UIP_MCAST6_STATS
static struct esmrf_stats stats;
#define ESMRF_STATS_ADD(x) stats.x++
#define ESMRF_STATS_INIT() do { memset(&stats, 0, sizeof(stats)); } while(0)
#else /* UIP_MCAST6_STATS */
#define ESMRF_STATS_ADD(x)
#define ESMRF_STATS_INIT()
#endif
/*---------------------------------------------------------------------------*/
/* Macros */
/*---------------------------------------------------------------------------*/
/* CCI */
#define ESMRF_FWD_DELAY() NETSTACK_RDC.channel_check_interval()
/* Number of slots in the next 500ms */
#define ESMRF_INTERVAL_COUNT ((CLOCK_SECOND >> 2) / fwd_delay)
/*---------------------------------------------------------------------------*/
/* Internal Data */
/*---------------------------------------------------------------------------*/
static struct ctimer mcast_periodic;
static uint8_t mcast_len;
static uip_buf_t mcast_buf;
static uint8_t fwd_delay;
static uint8_t fwd_spread;
static struct uip_udp_conn *c;
static uip_ipaddr_t src_ip;
static uip_ipaddr_t des_ip;
/*---------------------------------------------------------------------------*/
/* uIPv6 Pointers */
/*---------------------------------------------------------------------------*/
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
#define UIP_ICMP_PAYLOAD ((unsigned char *)&uip_buf[uip_l2_l3_icmp_hdr_len])
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN])
/*---------------------------------------------------------------------------*/
/* Local function prototypes */
/*---------------------------------------------------------------------------*/
static void icmp_input(void);
static void icmp_output(void);
static void mcast_fwd(void *p);
int remove_ext_hdr(void);
/*---------------------------------------------------------------------------*/
/* Internal Data Structures */
/*---------------------------------------------------------------------------*/
struct multicast_on_behalf{ /* ICMP message of multicast_on_behalf */
uint16_t mcast_port;
uip_ipaddr_t mcast_ip;
uint8_t mcast_payload[UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN];
};
#define UIP_ICMP_MOB 18 /* Size of multicast_on_behalf ICMP header */
/*---------------------------------------------------------------------------*/
/* Temporary Stores */
/*---------------------------------------------------------------------------*/
static struct multicast_on_behalf *locmobptr;
static int loclen;
/*---------------------------------------------------------------------------*/
/* ESMRF ICMPv6 handler declaration */
UIP_ICMP6_HANDLER(esmrf_icmp_handler, ICMP6_ESMRF,
UIP_ICMP6_HANDLER_CODE_ANY, icmp_input);
/*---------------------------------------------------------------------------*/
static void
icmp_output()
{
uint16_t payload_len=0;
rpl_dag_t *dag_t;
struct multicast_on_behalf *mob;
mob = (struct multicast_on_behalf *)UIP_ICMP_PAYLOAD;
memcpy(&mob->mcast_payload, &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], uip_slen);
UIP_IP_BUF->vtc = 0x60;
UIP_IP_BUF->tcflow = 0;
UIP_IP_BUF->flow = 0;
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
UIP_IP_BUF->ttl = ESMRF_IP_HOP_LIMIT;
mob->mcast_port = (uint16_t) uip_udp_conn->rport;
uip_ipaddr_copy(&mob->mcast_ip, &UIP_IP_BUF->destipaddr);
payload_len = UIP_ICMP_MOB + uip_slen;
dag_t = rpl_get_any_dag();
uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &dag_t->dag_id);
uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
VERBOSE_PRINTF("ESMRF: ICMPv6 Out - Hdr @ %p, payload @ %p to: ", UIP_ICMP_BUF, mob);
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
PRINTF("\n");
UIP_IP_BUF->len[0] = (UIP_ICMPH_LEN + payload_len) >> 8;
UIP_IP_BUF->len[1] = (UIP_ICMPH_LEN + payload_len) & 0xff;
UIP_ICMP_BUF->type = ICMP6_ESMRF;
UIP_ICMP_BUF->icode = ESMRF_ICMP_CODE;
UIP_ICMP_BUF->icmpchksum = 0;
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
VERBOSE_PRINTF("ESMRF: ICMPv6 Out - %u bytes, uip_len %u bytes, uip_ext_len %u bytes\n",
payload_len, uip_len, uip_ext_len);
tcpip_ipv6_output();
ESMRF_STATS_ADD(icmp_out);
return;
}
/*---------------------------------------------------------------------------*/
static void
icmp_input()
{
#if UIP_CONF_IPV6_CHECKS
if(UIP_ICMP_BUF->icode != ESMRF_ICMP_CODE) {
PRINTF("ESMRF: ICMPv6 In, bad ICMP code\n");
ESMRF_STATS_ADD(icmp_bad);
return;
}
if(UIP_IP_BUF->ttl <= 1) {
PRINTF("ESMRF: ICMPv6 In, bad TTL\n");
ESMRF_STATS_ADD(icmp_bad);
return;
}
#endif
remove_ext_hdr();
PRINTF("ESMRF: ICMPv6 In from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(" len %u, ext %u\n", uip_len, uip_ext_len);
ESMRF_STATS_ADD(icmp_in);
VERBOSE_PRINTF("ESMRF: ICMPv6 In, parse from %p to %p\n",
UIP_ICMP_PAYLOAD,
(uint8_t *)UIP_ICMP_PAYLOAD + uip_len -
uip_l2_l3_icmp_hdr_len);
locmobptr = (struct multicast_on_behalf *) UIP_ICMP_PAYLOAD;
loclen = uip_len - (uip_l2_l3_icmp_hdr_len + UIP_ICMP_MOB);
uip_ipaddr_copy(&src_ip, &UIP_IP_BUF->srcipaddr);
uip_ipaddr_copy(&des_ip, &UIP_IP_BUF->destipaddr);
/* Extract the original multicast message */
uip_ipaddr_copy(&c->ripaddr, &locmobptr->mcast_ip);
c->rport = locmobptr->mcast_port;
uip_slen = loclen;
uip_udp_conn=c;
memcpy(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], locmobptr->mcast_payload,
loclen > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?
UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: loclen);
uip_process(UIP_UDP_SEND_CONN);
memcpy(&mcast_buf, uip_buf, uip_len);
mcast_len = uip_len;
/* pass the packet to our uip_process to check if it is allowed to
* accept this packet or not */
uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &src_ip);
uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &des_ip);
UIP_UDP_BUF->udpchksum = 0;
uip_process(UIP_DATA);
memcpy(uip_buf, &mcast_buf, mcast_len);
uip_len = mcast_len;
/* Return the IP of the original Multicast sender */
uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &src_ip);
UIP_UDP_BUF->udpchksum = 0;
/* If we have an entry in the multicast routing table, something with
* a higher RPL rank (somewhere down the tree) is a group member */
if(uip_mcast6_route_lookup(&UIP_IP_BUF->destipaddr)) {
PRINTF("ESMRF: Forward this packet\n");
/* If we enter here, we will definitely forward */
tcpip_ipv6_output();
}
uip_clear_buf();
}
/*---------------------------------------------------------------------------*/
static void
mcast_fwd(void *p)
{
memcpy(uip_buf, &mcast_buf, mcast_len);
uip_len = mcast_len;
UIP_IP_BUF->ttl--;
tcpip_output(NULL);
uip_clear_buf();
}
/*---------------------------------------------------------------------------*/
static uint8_t
in()
{
rpl_dag_t *d; /* Our DODAG */
uip_ipaddr_t *parent_ipaddr; /* Our pref. parent's IPv6 address */
const uip_lladdr_t *parent_lladdr; /* Our pref. parent's LL address */
/*
* Fetch a pointer to the LL address of our preferred parent
*
* ToDo: This rpl_get_any_dag() call is a dirty replacement of the previous
* rpl_get_dag(RPL_DEFAULT_INSTANCE);
* so that things can compile with the new RPL code. This needs updated to
* read instance ID from the RPL HBHO and use the correct parent accordingly
*/
d = rpl_get_any_dag();
if(!d) {
PRINTF("ESMRF: No DODAG\n");
UIP_MCAST6_STATS_ADD(mcast_dropped);
return UIP_MCAST6_DROP;
}
/* Retrieve our preferred parent's LL address */
parent_ipaddr = rpl_get_parent_ipaddr(d->preferred_parent);
parent_lladdr = uip_ds6_nbr_lladdr_from_ipaddr(parent_ipaddr);
if(parent_lladdr == NULL) {
PRINTF("ESMRF: NO Parent exist \n");
UIP_MCAST6_STATS_ADD(mcast_dropped);
return UIP_MCAST6_DROP;
}
/*
* We accept a datagram if it arrived from our preferred parent, discard
* otherwise.
*/
if(memcmp(parent_lladdr, packetbuf_addr(PACKETBUF_ADDR_SENDER),
UIP_LLADDR_LEN)) {
PRINTF("ESMRF: Routable in but ESMRF ignored it\n");
UIP_MCAST6_STATS_ADD(mcast_dropped);
return UIP_MCAST6_DROP;
}
if(UIP_IP_BUF->ttl <= 1) {
UIP_MCAST6_STATS_ADD(mcast_dropped);
return UIP_MCAST6_DROP;
}
UIP_MCAST6_STATS_ADD(mcast_in_all);
UIP_MCAST6_STATS_ADD(mcast_in_unique);
/* If we have an entry in the mcast routing table, something with
* a higher RPL rank (somewhere down the tree) is a group member */
if(uip_mcast6_route_lookup(&UIP_IP_BUF->destipaddr)) {
/* If we enter here, we will definitely forward */
UIP_MCAST6_STATS_ADD(mcast_fwd);
/*
* Add a delay (D) of at least ESMRF_FWD_DELAY() to compensate for how
* contikimac handles broadcasts. We can't start our TX before the sender
* has finished its own.
*/
fwd_delay = ESMRF_FWD_DELAY();
/* Finalise D: D = min(ESMRF_FWD_DELAY(), ESMRF_MIN_FWD_DELAY) */
#if ESMRF_MIN_FWD_DELAY
if(fwd_delay < ESMRF_MIN_FWD_DELAY) {
fwd_delay = ESMRF_MIN_FWD_DELAY;
}
#endif
if(fwd_delay == 0) {
/* No delay required, send it, do it now, why wait? */
UIP_IP_BUF->ttl--;
tcpip_output(NULL);
UIP_IP_BUF->ttl++; /* Restore before potential upstack delivery */
} else {
/* Randomise final delay in [D , D*Spread], step D */
fwd_spread = ESMRF_INTERVAL_COUNT;
if(fwd_spread > ESMRF_MAX_SPREAD) {
fwd_spread = ESMRF_MAX_SPREAD;
}
if(fwd_spread) {
fwd_delay = fwd_delay * (1 + ((random_rand() >> 11) % fwd_spread));
}
memcpy(&mcast_buf, uip_buf, uip_len);
mcast_len = uip_len;
ctimer_set(&mcast_periodic, fwd_delay, mcast_fwd, NULL);
}
PRINTF("ESMRF: %u bytes: fwd in %u [%u]\n",
uip_len, fwd_delay, fwd_spread);
}
/* Done with this packet unless we are a member of the mcast group */
if(!uip_ds6_is_my_maddr(&UIP_IP_BUF->destipaddr)) {
PRINTF("ESMRF: Not a group member. No further processing\n");
return UIP_MCAST6_DROP;
} else {
PRINTF("ESMRF: Ours. Deliver to upper layers\n");
UIP_MCAST6_STATS_ADD(mcast_in_ours);
return UIP_MCAST6_ACCEPT;
}
}
/*---------------------------------------------------------------------------*/
static void
init()
{
UIP_MCAST6_STATS_INIT(NULL);
uip_mcast6_route_init();
/* Register the ICMPv6 input handler */
uip_icmp6_register_input_handler(&esmrf_icmp_handler);
c = udp_new(NULL, 0, NULL);
}
/*---------------------------------------------------------------------------*/
static void
out(void)
{
rpl_dag_t *dag_t;
dag_t = rpl_get_any_dag();
if (!dag_t){
PRINTF("ESMRF: There is no DODAG\n");
return;
}
if(dag_t->rank == 256){
PRINTF("ESMRF: I am the Root, thus send the multicast packet normally. \n");
return;
}
else{
PRINTF("ESMRF: I am not the Root\n");
PRINTF("Send multicast-on-befalf message (ICMPv6) instead to ");
PRINT6ADDR(&dag_t->dag_id);
PRINTF("\n");
icmp_output();
uip_slen=0;
return;
}
}
/*---------------------------------------------------------------------------*/
const struct uip_mcast6_driver esmrf_driver = {
"ESMRF",
init,
out,
in,
};
/*---------------------------------------------------------------------------*/

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2011, Loughborough University - 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 file for the Enhanced Stateless Multicast RPL Forwarding (ESMRF)
*
* \author
* Khaled Qorany kqorany2@gmail.com
*/
#ifndef ESMRF_H_
#define ESMRF_H_
#include "contiki-conf.h"
#include <stdint.h>
/*---------------------------------------------------------------------------*/
/* Protocol Constants */
/*---------------------------------------------------------------------------*/
#define ESMRF_ICMP_CODE 0 /* ICMPv6 code field */
#define ESMRF_IP_HOP_LIMIT 0xFF /* Hop limit for ICMP messages */
/*---------------------------------------------------------------------------*/
/* Configuration */
/*---------------------------------------------------------------------------*/
/* Fmin */
#ifdef ESMRF_CONF_MIN_FWD_DELAY
#define ESMRF_MIN_FWD_DELAY ESMRF_CONF_MIN_FWD_DELAY
#else
#define ESMRF_MIN_FWD_DELAY 4
#endif
/* Max Spread */
#ifdef ESMRF_CONF_MAX_SPREAD
#define ESMRF_MAX_SPREAD ESMRF_CONF_MAX_SPREAD
#else
#define ESMRF_MAX_SPREAD 4
#endif
/*---------------------------------------------------------------------------*/
/* Stats datatype */
/*---------------------------------------------------------------------------*/
struct esmrf_stats {
uint16_t mcast_in_unique;
uint16_t mcast_in_all; /* At layer 3 */
uint16_t mcast_in_ours; /* Unique and we are a group member */
uint16_t mcast_fwd; /* Forwarded by us but we are not the seed */
uint16_t mcast_out; /* We are the seed */
uint16_t mcast_bad;
uint16_t mcast_dropped;
uint16_t icmp_out;
uint16_t icmp_in;
uint16_t icmp_bad;
};
#endif /* ESMRF_H_ */

View file

@ -50,6 +50,7 @@
#define UIP_MCAST6_ENGINE_NONE 0 /**< Selecting this disables mcast */ #define UIP_MCAST6_ENGINE_NONE 0 /**< Selecting this disables mcast */
#define UIP_MCAST6_ENGINE_SMRF 1 /**< The SMRF engine */ #define UIP_MCAST6_ENGINE_SMRF 1 /**< The SMRF engine */
#define UIP_MCAST6_ENGINE_ROLL_TM 2 /**< The ROLL TM engine */ #define UIP_MCAST6_ENGINE_ROLL_TM 2 /**< The ROLL TM engine */
#define UIP_MCAST6_ENGINE_ESMRF 3 /**< The ESMRF engine */
#endif /* UIP_MCAST6_ENGINES_H_ */ #endif /* UIP_MCAST6_ENGINES_H_ */
/** @} */ /** @} */

View file

@ -63,6 +63,7 @@
#include "net/ipv6/multicast/uip-mcast6-engines.h" #include "net/ipv6/multicast/uip-mcast6-engines.h"
#include "net/ipv6/multicast/uip-mcast6-route.h" #include "net/ipv6/multicast/uip-mcast6-route.h"
#include "net/ipv6/multicast/smrf.h" #include "net/ipv6/multicast/smrf.h"
#include "net/ipv6/multicast/esmrf.h"
#include "net/ipv6/multicast/roll-tm.h" #include "net/ipv6/multicast/roll-tm.h"
#include <string.h> #include <string.h>
@ -147,17 +148,23 @@ struct uip_mcast6_driver {
#if UIP_MCAST6_ENGINE #if UIP_MCAST6_ENGINE
/* Enable Multicast hooks in the uip6 core */ /* Enable Multicast hooks in the uip6 core */
#define UIP_CONF_IPV6_MULTICAST 1 #define UIP_IPV6_MULTICAST 1
#if UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_ROLL_TM #if UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_ROLL_TM
#define RPL_CONF_MULTICAST 0 /* Not used by trickle */ #define RPL_WITH_MULTICAST 0 /* Not used by trickle */
#define UIP_CONF_IPV6_ROLL_TM 1 /* ROLL Trickle ICMP type support */ #define UIP_CONF_IPV6_ROLL_TM 1 /* ROLL Trickle ICMP type support */
#define UIP_MCAST6 roll_tm_driver #define UIP_MCAST6 roll_tm_driver
#elif UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_SMRF #elif UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_SMRF
#define RPL_CONF_MULTICAST 1 #define RPL_WITH_MULTICAST 1
#define UIP_MCAST6 smrf_driver #define UIP_MCAST6 smrf_driver
#elif UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_ESMRF
#define RPL_CONF_MULTICAST 1
#define UIP_MCAST6 esmrf_driver
#else #else
#error "Multicast Enabled with an Unknown Engine." #error "Multicast Enabled with an Unknown Engine."
#error "Check the value of UIP_MCAST6_CONF_ENGINE in conf files." #error "Check the value of UIP_MCAST6_CONF_ENGINE in conf files."
@ -168,7 +175,7 @@ extern const struct uip_mcast6_driver UIP_MCAST6;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Configuration Checks */ /* Configuration Checks */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#if RPL_CONF_MULTICAST && (!UIP_CONF_IPV6_RPL) #if RPL_WITH_MULTICAST && (!UIP_CONF_IPV6_RPL)
#error "The selected Multicast mode requires UIP_CONF_IPV6_RPL != 0" #error "The selected Multicast mode requires UIP_CONF_IPV6_RPL != 0"
#error "Check the value of UIP_CONF_IPV6_RPL in conf files." #error "Check the value of UIP_CONF_IPV6_RPL in conf files."
#endif #endif

View file

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

View file

@ -47,6 +47,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h> #include <stddef.h>
#include "lib/list.h" #include "lib/list.h"
#include "net/link-stats.h"
#include "net/linkaddr.h" #include "net/linkaddr.h"
#include "net/packetbuf.h" #include "net/packetbuf.h"
#include "net/ipv6/uip-ds6-nbr.h" #include "net/ipv6/uip-ds6-nbr.h"
@ -74,25 +75,32 @@ NBR_TABLE_GLOBAL(uip_ds6_nbr_t, ds6_neighbors);
void void
uip_ds6_neighbors_init(void) uip_ds6_neighbors_init(void)
{ {
link_stats_init();
nbr_table_register(ds6_neighbors, (nbr_table_callback *)uip_ds6_nbr_rm); nbr_table_register(ds6_neighbors, (nbr_table_callback *)uip_ds6_nbr_rm);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uip_ds6_nbr_t * uip_ds6_nbr_t *
uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr, 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) { if(nbr) {
uip_ipaddr_copy(&nbr->ipaddr, ipaddr); uip_ipaddr_copy(&nbr->ipaddr, ipaddr);
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
nbr->isrouter = isrouter; nbr->isrouter = isrouter;
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
nbr->state = state; nbr->state = state;
#if UIP_CONF_IPV6_QUEUE_PKT #if UIP_CONF_IPV6_QUEUE_PKT
uip_packetqueue_new(&nbr->packethandle); 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 */ /* timers are set separately, for now we put them in expired state */
stimer_set(&nbr->reachable, 0); stimer_set(&nbr->reachable, 0);
stimer_set(&nbr->sendns, 0); stimer_set(&nbr->sendns, 0);
nbr->nscount = 0; nbr->nscount = 0;
#endif /* UIP_ND6_SEND_NA */
PRINTF("Adding neighbor with ip addr "); PRINTF("Adding neighbor with ip addr ");
PRINT6ADDR(ipaddr); PRINT6ADDR(ipaddr);
PRINTF(" link addr "); 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) uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
{ {
if(nbr != NULL) { if(nbr != NULL) {
@ -119,9 +127,9 @@ uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
uip_packetqueue_free(&nbr->packethandle); uip_packetqueue_free(&nbr->packethandle);
#endif /* UIP_CONF_IPV6_QUEUE_PKT */ #endif /* UIP_CONF_IPV6_QUEUE_PKT */
NEIGHBOR_STATE_CHANGED(nbr); 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; 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); LINK_NEIGHBOR_CALLBACK(dest, status, numtx);
#if UIP_DS6_LL_NUD #if UIP_DS6_LL_NUD
@ -230,6 +241,7 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
#endif /* UIP_DS6_LL_NUD */ #endif /* UIP_DS6_LL_NUD */
} }
#if UIP_ND6_SEND_NA
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/** Periodic processing on neighbors */ /** Periodic processing on neighbors */
void void
@ -241,7 +253,7 @@ uip_ds6_neighbor_periodic(void)
case NBR_REACHABLE: case NBR_REACHABLE:
if(stimer_expired(&nbr->reachable)) { if(stimer_expired(&nbr->reachable)) {
#if UIP_CONF_IPV6_RPL #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 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 force a NUD on it. Otherwise, if there is no upward traffic, the
node never knows if the default router is still reachable. This 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 */ #endif /* UIP_CONF_IPV6_RPL */
} }
break; break;
#if UIP_ND6_SEND_NA
case NBR_INCOMPLETE: case NBR_INCOMPLETE:
if(nbr->nscount >= UIP_ND6_MAX_MULTICAST_SOLICIT) { if(nbr->nscount >= UIP_ND6_MAX_MULTICAST_SOLICIT) {
uip_ds6_nbr_rm(nbr); uip_ds6_nbr_rm(nbr);
@ -304,7 +315,6 @@ uip_ds6_neighbor_periodic(void)
stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000); stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000);
} }
break; break;
#endif /* UIP_ND6_SEND_NA */
default: default:
break; break;
} }
@ -330,5 +340,6 @@ uip_ds6_get_least_lifetime_neighbor(void)
} }
return nbr_expiring; 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 */ /** \brief An entry in the nbr cache */
typedef struct uip_ds6_nbr { typedef struct uip_ds6_nbr {
uip_ipaddr_t ipaddr; uip_ipaddr_t ipaddr;
uint8_t isrouter;
uint8_t state;
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
struct stimer reachable; struct stimer reachable;
struct stimer sendns; struct stimer sendns;
uint8_t nscount; uint8_t nscount;
uint8_t isrouter; #endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
uint8_t state;
uint16_t link_metric;
#if UIP_CONF_IPV6_QUEUE_PKT #if UIP_CONF_IPV6_QUEUE_PKT
struct uip_packetqueue_handle packethandle; struct uip_packetqueue_handle packethandle;
#define UIP_DS6_NBR_PACKET_LIFETIME CLOCK_SECOND * 4 #define UIP_DS6_NBR_PACKET_LIFETIME CLOCK_SECOND * 4
@ -84,9 +85,11 @@ typedef struct uip_ds6_nbr {
void uip_ds6_neighbors_init(void); void uip_ds6_neighbors_init(void);
/** \brief Neighbor Cache basic routines */ /** \brief Neighbor Cache basic routines */
uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr, uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr,
uint8_t isrouter, uint8_t state); const uip_lladdr_t *lladdr,
void uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr); 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_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); 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); 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); void NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK(const linkaddr_t *addr);
#endif /* NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK */ #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 /* The nbr_routes holds a neighbor table to be able to maintain
information about what routes go through what neighbor. This information about what routes go through what neighbor. This
neighbor table is registered with the central nbr-table repository 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); LIST(routelist);
MEMB(routememb, uip_ds6_route_t, UIP_DS6_ROUTE_NB); 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 /* Default routes are held on the defaultrouterlist and their
structures are allocated from the defaultroutermemb memory block.*/ structures are allocated from the defaultroutermemb memory block.*/
LIST(defaultrouterlist); LIST(defaultrouterlist);
@ -80,13 +86,10 @@ MEMB(defaultroutermemb, uip_ds6_defrt_t, UIP_DS6_DEFRT_NB);
LIST(notificationlist); LIST(notificationlist);
#endif #endif
static int num_routes = 0;
#undef DEBUG #undef DEBUG
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h" #include "net/ip/uip-debug.h"
static void rm_routelist_callback(nbr_table_item_t *ptr);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#if DEBUG != DEBUG_NONE #if DEBUG != DEBUG_NONE
static void static void
@ -156,10 +159,12 @@ uip_ds6_notification_rm(struct uip_ds6_notification *n)
void void
uip_ds6_route_init(void) uip_ds6_route_init(void)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
memb_init(&routememb); memb_init(&routememb);
list_init(routelist); list_init(routelist);
nbr_table_register(nbr_routes, nbr_table_register(nbr_routes,
(nbr_table_callback *)rm_routelist_callback); (nbr_table_callback *)rm_routelist_callback);
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
memb_init(&defaultroutermemb); memb_init(&defaultroutermemb);
list_init(defaultrouterlist); list_init(defaultrouterlist);
@ -168,6 +173,7 @@ uip_ds6_route_init(void)
list_init(notificationlist); list_init(notificationlist);
#endif #endif
} }
#if (UIP_CONF_MAX_ROUTES != 0)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static uip_lladdr_t * static uip_lladdr_t *
uip_ds6_route_nexthop_lladdr(uip_ds6_route_t *route) 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; return NULL;
} }
} }
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uip_ipaddr_t * uip_ipaddr_t *
uip_ds6_route_nexthop(uip_ds6_route_t *route) uip_ds6_route_nexthop(uip_ds6_route_t *route)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
if(route != NULL) { if(route != NULL) {
return uip_ds6_nbr_ipaddr_from_lladdr(uip_ds6_route_nexthop_lladdr(route)); return uip_ds6_nbr_ipaddr_from_lladdr(uip_ds6_route_nexthop_lladdr(route));
} else { } else {
return NULL; return NULL;
} }
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uip_ds6_route_t * uip_ds6_route_t *
uip_ds6_route_head(void) uip_ds6_route_head(void)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
return list_head(routelist); 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_t *
uip_ds6_route_next(uip_ds6_route_t *r) uip_ds6_route_next(uip_ds6_route_t *r)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
if(r != NULL) { if(r != NULL) {
uip_ds6_route_t *n = list_item_next(r); uip_ds6_route_t *n = list_item_next(r);
return n; return n;
} }
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL; return NULL;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
int 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) uip_ds6_route_num_routes(void)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
return num_routes; return num_routes;
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return 0;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uip_ds6_route_t * uip_ds6_route_t *
uip_ds6_route_lookup(uip_ipaddr_t *addr) uip_ds6_route_lookup(uip_ipaddr_t *addr)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
uip_ds6_route_t *r; uip_ds6_route_t *r;
uip_ds6_route_t *found_route; uip_ds6_route_t *found_route;
uint8_t longestmatch; uint8_t longestmatch;
@ -261,12 +300,16 @@ uip_ds6_route_lookup(uip_ipaddr_t *addr)
} }
return found_route; return found_route;
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uip_ds6_route_t * uip_ds6_route_t *
uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length, uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
uip_ipaddr_t *nexthop) uip_ipaddr_t *nexthop)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
uip_ds6_route_t *r; uip_ds6_route_t *r;
struct uip_ds6_route_neighbor_route *nbrr; 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. */ least recently used one we have. */
if(uip_ds6_route_num_routes() == UIP_DS6_ROUTE_NB) { 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 /* Removing the oldest route entry from the route table. The
least recently used route is the first route on the list. */ least recently used route is the first route on the list. */
uip_ds6_route_t *oldest; oldest = list_tail(routelist);
#endif
oldest = list_tail(routelist); /* uip_ds6_route_head(); */ if(oldest == NULL) {
return NULL;
}
PRINTF("uip_ds6_route_add: dropping route to "); PRINTF("uip_ds6_route_add: dropping route to ");
PRINT6ADDR(&oldest->ipaddr); PRINT6ADDR(&oldest->ipaddr);
PRINTF("\n"); 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 initialize this pointer with the list of routing entries that
are attached to this neighbor. */ are attached to this neighbor. */
routes = nbr_table_add_lladdr(nbr_routes, routes = nbr_table_add_lladdr(nbr_routes,
(linkaddr_t *)nexthop_lladdr); (linkaddr_t *)nexthop_lladdr,
NBR_TABLE_REASON_ROUTE, NULL);
if(routes == NULL) { if(routes == NULL) {
/* This should not happen, as we explicitly deallocated one /* This should not happen, as we explicitly deallocated one
route table entry above. */ route table entry above. */
@ -407,12 +456,17 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
assert_nbr_routes_list_sane(); assert_nbr_routes_list_sane();
#endif /* DEBUG != DEBUG_NONE */ #endif /* DEBUG != DEBUG_NONE */
return r; return r;
#else /* (UIP_CONF_MAX_ROUTES != 0) */
return NULL;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
uip_ds6_route_rm(uip_ds6_route_t *route) uip_ds6_route_rm(uip_ds6_route_t *route)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
struct uip_ds6_route_neighbor_route *neighbor_route; struct uip_ds6_route_neighbor_route *neighbor_route;
#if DEBUG != DEBUG_NONE #if DEBUG != DEBUG_NONE
assert_nbr_routes_list_sane(); assert_nbr_routes_list_sane();
@ -469,8 +523,11 @@ uip_ds6_route_rm(uip_ds6_route_t *route)
#if DEBUG != DEBUG_NONE #if DEBUG != DEBUG_NONE
assert_nbr_routes_list_sane(); assert_nbr_routes_list_sane();
#endif /* DEBUG != DEBUG_NONE */ #endif /* DEBUG != DEBUG_NONE */
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
return; return;
} }
#if (UIP_CONF_MAX_ROUTES != 0)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
rm_routelist(struct uip_ds6_route_neighbor_routes *routes) 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); rm_routelist((struct uip_ds6_route_neighbor_routes *)ptr);
} }
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop) uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop)
{ {
#if (UIP_CONF_MAX_ROUTES != 0)
/* Get routing entry list of this neighbor */ /* Get routing entry list of this neighbor */
const uip_lladdr_t *nexthop_lladdr; const uip_lladdr_t *nexthop_lladdr;
struct uip_ds6_route_neighbor_routes *routes; 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, routes = nbr_table_get_from_lladdr(nbr_routes,
(linkaddr_t *)nexthop_lladdr); (linkaddr_t *)nexthop_lladdr);
rm_routelist(routes); rm_routelist(routes);
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uip_ds6_defrt_t * uip_ds6_defrt_t *

View file

@ -50,7 +50,7 @@ NBR_TABLE_DECLARE(nbr_routes);
void uip_ds6_route_init(void); void uip_ds6_route_init(void);
#ifndef UIP_CONF_UIP_DS6_NOTIFICATIONS #ifndef UIP_CONF_UIP_DS6_NOTIFICATIONS
#define UIP_DS6_NOTIFICATIONS 1 #define UIP_DS6_NOTIFICATIONS (UIP_CONF_MAX_ROUTES != 0)
#else #else
#define UIP_DS6_NOTIFICATIONS UIP_CONF_UIP_DS6_NOTIFICATIONS #define UIP_DS6_NOTIFICATIONS UIP_CONF_UIP_DS6_NOTIFICATIONS
#endif #endif
@ -97,11 +97,48 @@ void uip_ds6_notification_rm(struct uip_ds6_notification *n);
#ifndef UIP_DS6_ROUTE_STATE_TYPE #ifndef UIP_DS6_ROUTE_STATE_TYPE
#define UIP_DS6_ROUTE_STATE_TYPE rpl_route_entry_t #define UIP_DS6_ROUTE_STATE_TYPE rpl_route_entry_t
/* Needed for the extended route entry state when using ContikiRPL */ /* 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 { typedef struct rpl_route_entry {
uint32_t lifetime; uint32_t lifetime;
void *dag; struct rpl_dag *dag;
uint8_t learned_from; uint8_t dao_seqno_out;
uint8_t nopath_received; uint8_t dao_seqno_in;
uint8_t state_flags;
} rpl_route_entry_t; } rpl_route_entry_t;
#endif /* UIP_DS6_ROUTE_STATE_TYPE */ #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); int uip_ds6_route_num_routes(void);
uip_ds6_route_t *uip_ds6_route_head(void); uip_ds6_route_t *uip_ds6_route_head(void);
uip_ds6_route_t *uip_ds6_route_next(uip_ds6_route_t *); 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 */ #endif /* UIP_DS6_ROUTE_H */

View file

@ -48,6 +48,7 @@
#include "lib/random.h" #include "lib/random.h"
#include "net/ipv6/uip-nd6.h" #include "net/ipv6/uip-nd6.h"
#include "net/ipv6/uip-ds6.h" #include "net/ipv6/uip-ds6.h"
#include "net/ipv6/multicast/uip-mcast6.h"
#include "net/ip/uip-packetqueue.h" #include "net/ip/uip-packetqueue.h"
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
@ -186,7 +187,9 @@ uip_ds6_periodic(void)
} }
#endif /* !UIP_CONF_ROUTER */ #endif /* !UIP_CONF_ROUTER */
#if UIP_ND6_SEND_NA
uip_ds6_neighbor_periodic(); uip_ds6_neighbor_periodic();
#endif /* UIP_ND6_SEND_RA */
#if UIP_CONF_ROUTER && UIP_ND6_SEND_RA #if UIP_CONF_ROUTER && UIP_ND6_SEND_RA
/* Periodic RA sending */ /* Periodic RA sending */

View file

@ -67,6 +67,36 @@
#endif #endif
#define UIP_DS6_DEFRT_NB UIP_DS6_DEFRT_NBS + UIP_DS6_DEFRT_NBU #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 */ /* Prefix list */
#define UIP_DS6_PREFIX_NBS 1 #define UIP_DS6_PREFIX_NBS 1
#ifndef UIP_CONF_DS6_PREFIX_NBU #ifndef UIP_CONF_DS6_PREFIX_NBU

View file

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

View file

@ -69,6 +69,7 @@
#define ICMP6_PRIV_EXP_200 200 /**< Private Experimentation */ #define ICMP6_PRIV_EXP_200 200 /**< Private Experimentation */
#define ICMP6_PRIV_EXP_201 201 /**< Private Experimentation */ #define ICMP6_PRIV_EXP_201 201 /**< Private Experimentation */
#define ICMP6_ROLL_TM ICMP6_PRIV_EXP_200 /**< ROLL Trickle Multicast */ #define ICMP6_ROLL_TM ICMP6_PRIV_EXP_200 /**< ROLL Trickle Multicast */
#define ICMP6_ESMRF ICMP6_PRIV_EXP_201 /**< ESMRF Multicast */
/** @} */ /** @} */

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 #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 */ /* Copy link-layer address from LLAO option to a word-aligned uip_lladdr_t */
static void static int
extract_lladdr_aligned(uip_lladdr_t *dest) { extract_lladdr_from_llao_aligned(uip_lladdr_t *dest) {
if(dest != NULL && nd6_opt_llao != NULL) { if(dest != NULL && nd6_opt_llao != NULL) {
memcpy(dest, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN); 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 */ #endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
@ -199,16 +201,23 @@ ns_input(void)
goto discard; goto discard;
} else { } else {
#endif /*UIP_CONF_IPV6_CHECKS */ #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); nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
if(nbr == NULL) { if(nbr == NULL) {
uip_lladdr_t lladdr_aligned; uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
extract_lladdr_aligned(&lladdr_aligned); 0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
} else { } 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], if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
lladdr, UIP_LLADDR_LEN) != 0) { 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; nbr->state = NBR_STALE;
} else { } else {
if(nbr->state == NBR_INCOMPLETE) { if(nbr->state == NBR_INCOMPLETE) {
@ -427,6 +436,7 @@ na_input(void)
uint8_t is_router; uint8_t is_router;
uint8_t is_solicited; uint8_t is_solicited;
uint8_t is_override; uint8_t is_override;
uip_lladdr_t lladdr_aligned;
PRINTF("Received NA from "); PRINTF("Received NA from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr); PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
@ -489,23 +499,29 @@ na_input(void)
PRINTF("NA received is bad\n"); PRINTF("NA received is bad\n");
goto discard; goto discard;
} else { } else {
uip_lladdr_t *lladdr; const uip_lladdr_t *lladdr;
nbr = uip_ds6_nbr_lookup(&UIP_ND6_NA_BUF->tgtipaddr); nbr = uip_ds6_nbr_lookup(&UIP_ND6_NA_BUF->tgtipaddr);
lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
if(nbr == NULL) { if(nbr == NULL) {
goto discard; 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 = 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); UIP_LLADDR_LEN);
} }
if(nbr->state == NBR_INCOMPLETE) { if(nbr->state == NBR_INCOMPLETE) {
if(nd6_opt_llao == NULL) { if(nd6_opt_llao == NULL || !extract_lladdr_from_llao_aligned(&lladdr_aligned)) {
goto discard; goto discard;
} }
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
UIP_LLADDR_LEN); /* failed to update the lladdr */
goto discard;
}
if(is_solicited) { if(is_solicited) {
nbr->state = NBR_REACHABLE; nbr->state = NBR_REACHABLE;
nbr->nscount = 0; nbr->nscount = 0;
@ -517,27 +533,29 @@ na_input(void)
nbr->state = NBR_STALE; nbr->state = NBR_STALE;
} }
nbr->isrouter = is_router; nbr->isrouter = is_router;
} else { } else { /* NBR is not INCOMPLETE */
if(!is_override && is_llchange) { if(!is_override && is_llchange) {
if(nbr->state == NBR_REACHABLE) { if(nbr->state == NBR_REACHABLE) {
nbr->state = NBR_STALE; nbr->state = NBR_STALE;
} }
goto discard; goto discard;
} else { } else {
if(is_override || (!is_override && nd6_opt_llao != 0 && !is_llchange) /**
|| nd6_opt_llao == 0) { * If this is an cache override, or same lladdr, or no llao -
if(nd6_opt_llao != 0) { * do updates of nbr states.
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], */
UIP_LLADDR_LEN); 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) { if(is_solicited) {
nbr->state = NBR_REACHABLE; nbr->state = NBR_REACHABLE;
/* reachable time is stored in ms */ /* reachable time is stored in ms */
stimer_set(&(nbr->reachable), uip_ds6_if.reachable_time / 1000); 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 { } else {
#endif /*UIP_CONF_IPV6_CHECKS */ #endif /*UIP_CONF_IPV6_CHECKS */
uip_lladdr_t lladdr_aligned; 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) { if((nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr)) == NULL) {
/* we need to add the neighbor */ /* 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 { } else {
/* If LL address changed, set neighbor state to stale */ /* 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], 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_t nbr_data = *nbr;
uip_ds6_nbr_rm(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->reachable = nbr_data.reachable;
nbr->sendns = nbr_data.sendns; nbr->sendns = nbr_data.sendns;
nbr->nscount = nbr_data.nscount; nbr->nscount = nbr_data.nscount;
@ -823,6 +847,8 @@ uip_nd6_rs_output(void)
void void
ra_input(void) ra_input(void)
{ {
uip_lladdr_t lladdr_aligned;
PRINTF("Received RA from "); PRINTF("Received RA from ");
PRINT6ADDR(&UIP_IP_BUF->srcipaddr); PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
PRINTF(" to "); PRINTF(" to ");
@ -867,19 +893,28 @@ ra_input(void)
PRINTF("Processing SLLAO option in RA\n"); PRINTF("Processing SLLAO option in RA\n");
nd6_opt_llao = (uint8_t *) UIP_ND6_OPT_HDR_BUF; nd6_opt_llao = (uint8_t *) UIP_ND6_OPT_HDR_BUF;
nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr); 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) { if(nbr == NULL) {
uip_lladdr_t lladdr_aligned; nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
extract_lladdr_aligned(&lladdr_aligned); 1, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 1, NBR_STALE);
} else { } 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) { if(nbr->state == NBR_INCOMPLETE) {
nbr->state = NBR_STALE; nbr->state = NBR_STALE;
} }
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
lladdr, UIP_LLADDR_LEN) != 0) { lladdr, UIP_LLADDR_LEN) != 0) {
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], /* change of link layer address */
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; nbr->state = NBR_STALE;
} }
nbr->isrouter = 1; 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/llsec/anti-replay.h"
#include "net/packetbuf.h" #include "net/packetbuf.h"
#include "net/llsec/llsec802154.h"
#if LLSEC802154_USES_FRAME_COUNTER
/* This node's current frame counter value */ /* This node's current frame counter value */
static uint32_t counter; 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 "llsec/ccm-star-packetbuf.h"
#include "net/linkaddr.h" #include "net/linkaddr.h"
#include "net/packetbuf.h" #include "net/packetbuf.h"
#include "net/llsec/llsec802154.h"
#include <string.h> #include <string.h>
#if LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_FRAME_COUNTER
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static const uint8_t * static const uint8_t *
get_extended_address(const linkaddr_t *addr) 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); 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/mac/frame802154.h"
#include "net/ip/uip.h" #include "net/ip/uip.h"
#ifdef LLSEC802154_CONF_SECURITY_LEVEL #ifdef LLSEC802154_CONF_ENABLED
#define LLSEC802154_SECURITY_LEVEL LLSEC802154_CONF_SECURITY_LEVEL #define LLSEC802154_ENABLED LLSEC802154_CONF_ENABLED
#else /* LLSEC802154_CONF_SECURITY_LEVEL */ #else /* LLSEC802154_CONF_ENABLED */
#define LLSEC802154_SECURITY_LEVEL FRAME802154_SECURITY_LEVEL_NONE #define LLSEC802154_ENABLED 0
#endif /* LLSEC802154_CONF_SECURITY_LEVEL */ #endif /* LLSEC802154_CONF_ENABLED */
#if ((LLSEC802154_SECURITY_LEVEL < 0) || (LLSEC802154_SECURITY_LEVEL > 7)) #define LLSEC802154_MIC_LEN(sec_lvl) (2 << (sec_lvl & 3))
#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 */
#ifdef LLSEC802154_CONF_USES_EXPLICIT_KEYS #ifdef LLSEC802154_CONF_USES_EXPLICIT_KEYS
#define LLSEC802154_USES_EXPLICIT_KEYS 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 #ifdef LLSEC802154_CONF_USES_FRAME_COUNTER
#define LLSEC802154_USES_FRAME_COUNTER LLSEC802154_CONF_USES_FRAME_COUNTER #define LLSEC802154_USES_FRAME_COUNTER LLSEC802154_CONF_USES_FRAME_COUNTER
#else /* 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 */ #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 #if UIP_BYTE_ORDER == UIP_LITTLE_ENDIAN
#define LLSEC802154_HTONS(n) (n) #define LLSEC802154_HTONS(n) (n)
#define LLSEC802154_HTONL(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/llsec802154.h"
#include "net/llsec/ccm-star-packetbuf.h" #include "net/llsec/ccm-star-packetbuf.h"
#include "net/mac/frame802154.h" #include "net/mac/frame802154.h"
#include "net/mac/framer-802154.h"
#include "net/netstack.h" #include "net/netstack.h"
#include "net/packetbuf.h" #include "net/packetbuf.h"
#include "net/nbr-table.h" #include "net/nbr-table.h"
@ -55,7 +54,22 @@
#include "lib/ccm-star.h" #include "lib/ccm-star.h"
#include <string.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 #ifdef NONCORESEC_CONF_KEY
#define NONCORESEC_KEY NONCORESEC_CONF_KEY #define NONCORESEC_KEY NONCORESEC_CONF_KEY
@ -76,6 +90,8 @@
#define PRINTF(...) #define PRINTF(...)
#endif /* DEBUG */ #endif /* DEBUG */
#if LLSEC802154_USES_AUX_HEADER && SEC_LVL && LLSEC802154_USES_FRAME_COUNTER
/* network-wide CCM* key */ /* network-wide CCM* key */
static uint8_t key[16] = NONCORESEC_KEY; static uint8_t key[16] = NONCORESEC_KEY;
NBR_TABLE(struct anti_replay_info, anti_replay_table); 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;
uint8_t a_len; uint8_t a_len;
uint8_t *result; uint8_t *result;
uint8_t generated_mic[LLSEC802154_MIC_LENGTH]; uint8_t generated_mic[MIC_LEN];
uint8_t *mic; uint8_t *mic;
ccm_star_packetbuf_set_nonce(nonce, forward); ccm_star_packetbuf_set_nonce(nonce, forward);
@ -113,30 +129,29 @@ aead(uint8_t hdrlen, int forward)
CCM_STAR.aead(nonce, CCM_STAR.aead(nonce,
m, m_len, m, m_len,
a, a_len, a, a_len,
result, LLSEC802154_MIC_LENGTH, result, MIC_LEN,
forward); forward);
if(forward) { if(forward) {
packetbuf_set_datalen(packetbuf_datalen() + LLSEC802154_MIC_LENGTH); packetbuf_set_datalen(packetbuf_datalen() + MIC_LEN);
return 1; return 1;
} else { } else {
return (memcmp(generated_mic, mic, LLSEC802154_MIC_LENGTH) == 0); return (memcmp(generated_mic, mic, MIC_LEN) == 0);
} }
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
add_security_header(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_FRAME_TYPE, FRAME802154_DATAFRAME); packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, SEC_LVL);
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, LLSEC802154_SECURITY_LEVEL);
anti_replay_set_counter();
}
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
send(mac_callback_t sent, void *ptr) send(mac_callback_t sent, void *ptr)
{ {
add_security_header();
anti_replay_set_counter();
NETSTACK_MAC.send(sent, ptr); NETSTACK_MAC.send(sent, ptr);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -145,8 +160,7 @@ create(void)
{ {
int result; int result;
add_security_header(); result = DECORATED_FRAMER.create();
result = framer_802154.create();
if(result == FRAMER_FAILED) { if(result == FRAMER_FAILED) {
return result; return result;
} }
@ -163,12 +177,12 @@ parse(void)
const linkaddr_t *sender; const linkaddr_t *sender;
struct anti_replay_info* info; struct anti_replay_info* info;
result = framer_802154.parse(); result = DECORATED_FRAMER.parse();
if(result == FRAMER_FAILED) { if(result == FRAMER_FAILED) {
return result; 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"); PRINTF("noncoresec: received frame with wrong security level\n");
return FRAMER_FAILED; return FRAMER_FAILED;
} }
@ -178,17 +192,17 @@ parse(void)
return FRAMER_FAILED; return FRAMER_FAILED;
} }
packetbuf_set_datalen(packetbuf_datalen() - LLSEC802154_MIC_LENGTH); packetbuf_set_datalen(packetbuf_datalen() - MIC_LEN);
if(!aead(result, 0)) { if(!aead(result, 0)) {
PRINTF("noncoresec: received unauthentic frame %"PRIu32"\n", PRINTF("noncoresec: received unauthentic frame %lu\n",
anti_replay_get_counter()); anti_replay_get_counter());
return FRAMER_FAILED; return FRAMER_FAILED;
} }
info = nbr_table_get_from_lladdr(anti_replay_table, sender); info = nbr_table_get_from_lladdr(anti_replay_table, sender);
if(!info) { 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) { if(!info) {
PRINTF("noncoresec: could not get nbr_table_item\n"); PRINTF("noncoresec: could not get nbr_table_item\n");
return FRAMER_FAILED; return FRAMER_FAILED;
@ -214,7 +228,7 @@ parse(void)
anti_replay_init_info(info); anti_replay_init_info(info);
} else { } else {
if(anti_replay_was_replayed(info)) { if(anti_replay_was_replayed(info)) {
PRINTF("noncoresec: received replayed frame %"PRIu32"\n", PRINTF("noncoresec: received replayed frame %lu\n",
anti_replay_get_counter()); anti_replay_get_counter());
return FRAMER_FAILED; return FRAMER_FAILED;
} }
@ -233,7 +247,7 @@ static int
length(void) length(void)
{ {
add_security_header(); add_security_header();
return framer_802154.length() + LLSEC802154_MIC_LENGTH; return DECORATED_FRAMER.length() + MIC_LEN;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
@ -256,5 +270,6 @@ const struct framer noncoresec_framer = {
parse parse
}; };
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
#endif /* LLSEC802154_USES_AUX_HEADER && SEC_LVL && LLSEC802154_USES_FRAME_COUNTER */
/** @} */ /** @} */

View file

@ -540,6 +540,7 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr,
rtimer_clock_t t0; rtimer_clock_t t0;
#if WITH_PHASE_OPTIMIZATION #if WITH_PHASE_OPTIMIZATION
rtimer_clock_t encounter_time = 0; rtimer_clock_t encounter_time = 0;
uint8_t is_known_receiver = 0;
#endif #endif
int strobes; int strobes;
uint8_t got_strobe_ack = 0; uint8_t got_strobe_ack = 0;

View file

@ -63,26 +63,35 @@
#define PRINTF(...) #define PRINTF(...)
#endif /* DEBUG */ #endif /* DEBUG */
#ifndef CSMA_MAX_BACKOFF_EXPONENT /* Constants of the IEEE 802.15.4 standard */
#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 */
#ifndef CSMA_MAX_MAC_TRANSMISSIONS /* macMinBE: Initial backoff exponent. Range 0--CSMA_MAX_BE */
#ifdef CSMA_CONF_MAX_MAC_TRANSMISSIONS #ifdef CSMA_CONF_MIN_BE
#define CSMA_MAX_MAC_TRANSMISSIONS CSMA_CONF_MAX_MAC_TRANSMISSIONS #define CSMA_MIN_BE CSMA_CONF_MIN_BE
#else #else
#define CSMA_MAX_MAC_TRANSMISSIONS 3 #define CSMA_MIN_BE 0
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS */ #endif
#endif /* CSMA_MAX_MAC_TRANSMISSIONS */
#if CSMA_MAX_MAC_TRANSMISSIONS < 1 /* macMaxBE: Maximum backoff exponent. Range 3--8 */
#error CSMA_CONF_MAX_MAC_TRANSMISSIONS must be at least 1. #ifdef CSMA_CONF_MAX_BE
#error Change CSMA_CONF_MAX_MAC_TRANSMISSIONS in contiki-conf.h or in your Makefile. #define CSMA_MAX_BE CSMA_CONF_MAX_BE
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS < 1 */ #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 */ /* Packet metadata */
struct qbuf_metadata { struct qbuf_metadata {
@ -97,7 +106,7 @@ struct neighbor_queue {
linkaddr_t addr; linkaddr_t addr;
struct ctimer transmit_timer; struct ctimer transmit_timer;
uint8_t transmissions; uint8_t transmissions;
uint8_t collisions, deferrals; uint8_t collisions;
LIST_STRUCT(queued_packet_list); 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 packet_sent(void *ptr, int status, int num_transmissions);
static void transmit_packet_list(void *ptr); static void transmit_packet_list(void *ptr);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static struct neighbor_queue * static struct neighbor_queue *
neighbor_queue_from_addr(const linkaddr_t *addr) neighbor_queue_from_addr(const linkaddr_t *addr)
@ -139,18 +147,18 @@ neighbor_queue_from_addr(const linkaddr_t *addr)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static clock_time_t static clock_time_t
default_timebase(void) backoff_period(void)
{ {
clock_time_t time; clock_time_t time;
/* The retransmission time must be proportional to the channel /* The retransmission time must be proportional to the channel
check interval of the underlying radio duty cycling layer. */ check interval of the underlying radio duty cycling layer. */
time = NETSTACK_RDC.channel_check_interval(); time = NETSTACK_RDC.channel_check_interval();
/* If the radio duty cycle has no channel check interval (i.e., it /* If the radio duty cycle has no channel check interval, we use
does not turn the radio off), we make the retransmission time * the default in IEEE 802.15.4: aUnitBackoffPeriod which is
proportional to the configured MAC channel check rate. */ * 20 symbols i.e. 320 usec. That is, 1/3125 second. */
if(time == 0) { if(time == 0) {
time = CLOCK_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE; time = MAX(CLOCK_SECOND / 3125, 1);
} }
return time; return time;
} }
@ -171,10 +179,28 @@ transmit_packet_list(void *ptr)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void 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) free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
{ {
clock_time_t tx_delay;
if(p != NULL) { if(p != NULL) {
/* Remove packet from list and deallocate */ /* Remove packet from list and deallocate */
list_remove(n->queued_packet_list, p); 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) { if(list_head(n->queued_packet_list) != NULL) {
/* There is a next packet. We reset current tx information */ /* There is a next packet. We reset current tx information */
n->transmissions = 0; n->transmissions = 0;
n->collisions = 0; n->collisions = CSMA_MIN_BE;
n->deferrals = 0; /* Schedule next transmissions */
/* Set a timer for next transmissions */ schedule_transmission(n);
tx_delay = (status == MAC_TX_OK) ? 0 : default_timebase();
ctimer_set(&n->transmit_timer, tx_delay, transmit_packet_list, n);
} else { } else {
/* This was the last packet in the queue, we free the neighbor */ /* This was the last packet in the queue, we free the neighbor */
ctimer_stop(&n->transmit_timer); ctimer_stop(&n->transmit_timer);
@ -202,34 +226,103 @@ free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void 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) packet_sent(void *ptr, int status, int num_transmissions)
{ {
struct neighbor_queue *n; struct neighbor_queue *n;
struct rdc_buf_list *q; 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; n = ptr;
if(n == NULL) { if(n == NULL) {
return; 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 */ /* Find out what packet this callback refers to */
for(q = list_head(n->queued_packet_list); for(q = list_head(n->queued_packet_list);
@ -240,78 +333,30 @@ packet_sent(void *ptr, int status, int num_transmissions)
} }
} }
if(q != NULL) { if(q == NULL) {
metadata = (struct qbuf_metadata *)q->ptr; 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) { switch(status) {
sent = metadata->sent; case MAC_TX_OK:
cptr = metadata->cptr; tx_ok(q, n, num_transmissions);
num_tx = n->transmissions; break;
if(status == MAC_TX_COLLISION || case MAC_TX_NOACK:
status == MAC_TX_NOACK) { noack(q, n, num_transmissions);
break;
/* If the transmission was not performed because of a case MAC_TX_COLLISION:
collision or noack, we must retransmit the packet. */ collision(q, n, num_transmissions);
break;
switch(status) { case MAC_TX_DEFERRED:
case MAC_TX_COLLISION: break;
PRINTF("csma: rexmit collision %d\n", n->transmissions); default:
break; tx_done(status, q, n);
case MAC_TX_NOACK: break;
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));
} }
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -346,8 +391,7 @@ send_packet(mac_callback_t sent, void *ptr)
/* Init neighbor entry */ /* Init neighbor entry */
linkaddr_copy(&n->addr, addr); linkaddr_copy(&n->addr, addr);
n->transmissions = 0; n->transmissions = 0;
n->collisions = 0; n->collisions = CSMA_MIN_BE;
n->deferrals = 0;
/* Init packet list for this neighbor */ /* Init packet list for this neighbor */
LIST_STRUCT_INIT(n, queued_packet_list); LIST_STRUCT_INIT(n, queued_packet_list);
/* Add neighbor to the list */ /* Add neighbor to the list */
@ -368,7 +412,7 @@ send_packet(mac_callback_t sent, void *ptr)
/* Neighbor and packet successfully allocated */ /* Neighbor and packet successfully allocated */
if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) { if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) {
/* Use default configuration for max transmissions */ /* Use default configuration for max transmissions */
metadata->max_transmissions = CSMA_MAX_MAC_TRANSMISSIONS; metadata->max_transmissions = CSMA_MAX_MAX_FRAME_RETRIES + 1;
} else { } else {
metadata->max_transmissions = metadata->max_transmissions =
packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_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)); list_length(n->queued_packet_list), memb_numfree(&packet_memb));
/* If q is the first packet in the neighbor's queue, send asap */ /* If q is the first packet in the neighbor's queue, send asap */
if(list_head(n->queued_packet_list) == q) { if(list_head(n->queued_packet_list) == q) {
ctimer_set(&n->transmit_timer, 0, transmit_packet_list, n); schedule_transmission(n);
} }
return; 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 static uint8_t
get_key_id_len(uint8_t key_id_mode) get_key_id_len(uint8_t key_id_mode)
{ {
@ -117,7 +117,7 @@ get_key_id_len(uint8_t key_id_mode)
return 0; return 0;
} }
} }
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_EXPLICIT_KEYS */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Get current PAN ID */ /* Get current PAN ID */
uint16_t 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->dest_addr_len = addr_len(p->fcf.dest_addr_mode & 3);
flen->src_addr_len = addr_len(p->fcf.src_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 */ /* Aux security header */
if(p->fcf.security_enabled & 1) { if(p->fcf.security_enabled & 1) {
flen->aux_sec_len = 1; /* FCF + possibly frame counter and key ID */ 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_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--) { for(c = flen.src_addr_len; c > 0; c--) {
buf[pos++] = p->src_addr[c - 1]; buf[pos++] = p->src_addr[c - 1];
} }
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_USES_AUX_HEADER
/* Aux header */ /* Aux header */
if(flen.aux_sec_len) { if(flen.aux_sec_len) {
buf[pos++] = p->aux_hdr.security_control.security_level 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_USES_EXPLICIT_KEYS */
} }
#endif /* LLSEC802154_SECURITY_LEVEL */ #endif /* LLSEC802154_USES_AUX_HEADER */
return (int)pos; return (int)pos;
} }
@ -570,7 +570,7 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
pf->src_pid = 0; pf->src_pid = 0;
} }
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_USES_AUX_HEADER
if(fcf.security_enabled) { if(fcf.security_enabled) {
pf->aux_hdr.security_control.security_level = p[0] & 7; pf->aux_hdr.security_control.security_level = p[0] & 7;
#if LLSEC802154_USES_EXPLICIT_KEYS #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_USES_EXPLICIT_KEYS */
} }
#endif /* LLSEC802154_SECURITY_LEVEL */ #endif /* LLSEC802154_USES_AUX_HEADER */
/* header length */ /* header length */
c = p - data; c = p - data;

View file

@ -41,7 +41,7 @@
#include "net/mac/frame802154e-ie.h" #include "net/mac/frame802154e-ie.h"
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#include "net/ip/uip-debug.h" #include "net/net-debug.h"
/* c.f. IEEE 802.15.4e Table 4b */ /* c.f. IEEE 802.15.4e Table 4b */
enum ieee802154e_header_ie_id { 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. */ /* Insert IEEE 802.15.4 version bits. */
params.fcf.frame_version = FRAME802154_VERSION; params.fcf.frame_version = FRAME802154_VERSION;
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_USES_AUX_HEADER
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) { if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) {
params.fcf.security_enabled = 1; 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_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
params.aux_hdr.key_source.u16[0] = packetbuf_attr(PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1); params.aux_hdr.key_source.u16[0] = packetbuf_attr(PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1);
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
#endif /* LLSEC802154_SECURITY_LEVEL */ #endif /* LLSEC802154_USES_AUX_HEADER */
/* Increment and set the data sequence number. */ /* Increment and set the data sequence number. */
if(!do_create) { if(!do_create) {
@ -238,7 +238,7 @@ parse(void)
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, frame.seq); packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, frame.seq);
#endif #endif
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_USES_AUX_HEADER
if(frame.fcf.security_enabled) { if(frame.fcf.security_enabled) {
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, frame.aux_hdr.security_control.security_level); packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, frame.aux_hdr.security_control.security_level);
#if LLSEC802154_USES_FRAME_COUNTER #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]); packetbuf_set_attr(PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1, frame.aux_hdr.key_source.u16[0]);
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */ #endif /* LLSEC802154_USES_EXPLICIT_KEYS */
} }
#endif /* LLSEC802154_SECURITY_LEVEL */ #endif /* LLSEC802154_USES_AUX_HEADER */
PRINTF("15.4-IN: %2X", frame.fcf.frame_type); PRINTF("15.4-IN: %2X", frame.fcf.frame_type);
PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER)); PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));

View file

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

View file

@ -43,6 +43,7 @@
#include "contiki-conf.h" #include "contiki-conf.h"
#include "net/mac/mac.h" #include "net/mac/mac.h"
#include "net/llsec/llsec802154.h"
#ifdef RDC_CONF_WITH_DUPLICATE_DETECTION #ifdef RDC_CONF_WITH_DUPLICATE_DETECTION
#define RDC_WITH_DUPLICATE_DETECTION 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 frame because it has seen its sequence number already. Replay
protection should be implemented at the LLSEC layer where the protection should be implemented at the LLSEC layer where the
authenticity of frames is verified. */ 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 */ #endif /* RDC_CONF_WITH_DUPLICATE_DETECTION */
/* List of packets to be sent by RDC layer */ /* 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) * NXP JN516x (`jn516x`, tested on hardware)
* Tmote Sky (`sky`, tested on hardware and in cooja) * Tmote Sky (`sky`, tested on hardware and in cooja)
* Zolertia Z1 (`z1`, tested in cooja only) * 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 This implementation was present at the ETSI Plugtest
event in Prague in July 2015, and did successfully inter-operate with all 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`. A simple TSCH+RPL example is included under `examples/ipv6/rpl-tsch`.
To use TSCH, first make sure your platform supports it. 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 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: 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: To include TSCH standard-compliant security, set the following:
``` ```
/* Enable security */ /* Enable security */
#undef LLSEC802154_CONF_SECURITY_LEVEL #undef LLSEC802154_CONF_ENABLED
#define LLSEC802154_CONF_SECURITY_LEVEL 1 #define LLSEC802154_CONF_ENABLED 1
/* TSCH uses explicit keys to identify k1 and k2 */ /* TSCH uses explicit keys to identify k1 and k2 */
#undef LLSEC802154_CONF_USES_EXPLICIT_KEYS #undef LLSEC802154_CONF_USES_EXPLICIT_KEYS
#define LLSEC802154_CONF_USES_EXPLICIT_KEYS 1 #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
Porting TSCH to a new platform requires a few new features in the radio driver, a number of timing-related configuration paramters. 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 ### Radio features required for TSCH

View file

@ -86,12 +86,16 @@ timesync_entry_add(int32_t val, uint32_t time_delta)
static void static void
timesync_learn_drift_ticks(uint32_t time_delta_asn, int32_t drift_ticks) timesync_learn_drift_ticks(uint32_t time_delta_asn, int32_t drift_ticks)
{ {
/* should fit in 32-bit unsigned integer */ /* should fit in a 32-bit integer */
uint32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length]; 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 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); 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); 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 */ /* 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 } #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 */ /* 4 channels, sequence length 4 */
#define TSCH_HOPPING_SEQUENCE_4_4 (uint8_t[]){ 15, 25, 26, 20 } #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 */ /* 1 channel, sequence length 1 */
#define TSCH_HOPPING_SEQUENCE_1_1 (uint8_t[]){ 20 } #define TSCH_HOPPING_SEQUENCE_1_1 (uint8_t[]){ 20 }
@ -120,7 +122,7 @@
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 10000 #define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 10000
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 15000 #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_OFFSET 1800
#define TSCH_DEFAULT_TS_CCA 128 #define TSCH_DEFAULT_TS_CCA 128
@ -135,7 +137,7 @@
#define TSCH_DEFAULT_TS_MAX_TX 4256 #define TSCH_DEFAULT_TS_MAX_TX 4256
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 15000 #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). /* 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. * 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, * 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 #define TSCH_ADAPTIVE_TIMESYNC 0
#endif #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__ */ #endif /* __TSCH_CONF_H__ */

View file

@ -56,7 +56,7 @@
#else /* TSCH_LOG_LEVEL */ #else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */ #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 */ #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) { while((log_index = ringbufindex_peek_get(&log_ringbuf)) != -1) {
struct tsch_log_t *log = &log_array[log_index]; struct tsch_log_t *log = &log_array[log_index];
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle); if(log->link == NULL) {
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ", printf("TSCH: {asn-%x.%lx link-NULL} ", log->asn.ms1b, log->asn.ls4b);
log->asn.ms1b, log->asn.ls4b, } else {
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset, struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
tsch_calculate_channel(&log->asn, log->link->channel_offset)); 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) { switch(log->type) {
case tsch_log_tx: case tsch_log_tx:
printf("%s-%u-%u %u tx %d, st %d-%d", printf("%s-%u-%u %u tx %d, st %d-%d",
@ -103,7 +107,7 @@ tsch_log_process_pending(void)
break; break;
case tsch_log_rx: case tsch_log_rx:
printf("%s-%u-%u %u rx %d", 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.datalen,
log->rx.src); log->rx.src);
if(log->rx.drift_used) { if(log->rx.drift_used) {

View file

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

View file

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

View file

@ -61,7 +61,7 @@
#else /* TSCH_LOG_LEVEL */ #else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */ #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 */ /* Check if TSCH_QUEUE_NUM_PER_NEIGHBOR is power of two */
#if (TSCH_QUEUE_NUM_PER_NEIGHBOR & (TSCH_QUEUE_NUM_PER_NEIGHBOR - 1)) != 0 #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 */ /* Flush all neighbor queues */
void void
tsch_queue_flush_all(void) tsch_queue_reset(void)
{ {
/* Deallocate unneeded neighbors */ /* Deallocate unneeded neighbors */
if(!tsch_is_locked()) { if(!tsch_is_locked()) {
struct tsch_neighbor *n = list_head(neighbor_list); struct tsch_neighbor *n = list_head(neighbor_list);
while(n != NULL) { while(n != NULL) {
struct tsch_neighbor *next_n = list_item_next(n); struct tsch_neighbor *next_n = list_item_next(n);
/* Flush queue */
tsch_queue_flush_nbr_queue(n); tsch_queue_flush_nbr_queue(n);
/* Reset backoff exponent */
tsch_queue_backoff_reset(n);
n = next_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; return NULL;
} }
/*---------------------------------------------------------------------------*/
/* Returns the head packet of any neighbor queue with zero backoff counter. /* Returns the head packet of any neighbor queue with zero backoff counter.
* Writes pointer to the neighbor in *n */ * Writes pointer to the neighbor in *n */
struct tsch_packet * struct tsch_packet *

View file

@ -39,6 +39,7 @@
#include "lib/ringbufindex.h" #include "lib/ringbufindex.h"
#include "net/linkaddr.h" #include "net/linkaddr.h"
#include "net/mac/tsch/tsch-schedule.h" #include "net/mac/tsch/tsch-schedule.h"
#include "net/mac/mac.h"
/******** Configuration *******/ /******** 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); struct tsch_packet *tsch_queue_remove_packet_from_queue(struct tsch_neighbor *n);
/* Free a packet */ /* Free a packet */
void tsch_queue_free_packet(struct tsch_packet *p); void tsch_queue_free_packet(struct tsch_packet *p);
/* Flush all neighbor queues */ /* Reset neighbor queues */
void tsch_queue_flush_all(void); void tsch_queue_reset(void);
/* Deallocate neighbors with empty queue */ /* Deallocate neighbors with empty queue */
void tsch_queue_free_unused_neighbors(void); void tsch_queue_free_unused_neighbors(void);
/* Is the neighbor queue empty? */ /* Is the neighbor queue empty? */

View file

@ -51,7 +51,7 @@
#else /* TSCH_LOG_LEVEL */ #else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */ #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 */ /* 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. /* 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 void
tsch_rpl_callback_new_dio_interval(uint8_t dio_interval) tsch_rpl_callback_new_dio_interval(uint8_t dio_interval)
{ {

View file

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

View file

@ -58,7 +58,7 @@
#else /* TSCH_LOG_LEVEL */ #else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */ #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 /* The two keys K1 and K2 from 6TiSCH minimal configuration
* K1: well-known, used for EBs * K1: well-known, used for EBs

View file

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

View file

@ -36,6 +36,7 @@
* \author * \author
* Simon Duquennoy <simonduq@sics.se> * Simon Duquennoy <simonduq@sics.se>
* Beshr Al Nahas <beshr@sics.se> * Beshr Al Nahas <beshr@sics.se>
* Atis Elsts <atis.elsts@bristol.ac.uk>
* *
*/ */
@ -59,7 +60,7 @@
#else /* TSCH_LOG_LEVEL */ #else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */ #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 /* TSCH debug macros, i.e. to set LEDs or GPIOs on various TSCH
* timeslot events */ * timeslot events */
@ -109,6 +110,18 @@
#define RTIMER_GUARD 2u #define RTIMER_GUARD 2u
#endif #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. /* A ringbuf storing outgoing packets after they were dequeued.
* Will be processed layer by tsch_tx_process_pending */ * Will be processed layer by tsch_tx_process_pending */
struct ringbufindex dequeued_ringbuf; struct ringbufindex dequeued_ringbuf;
@ -370,6 +383,68 @@ update_neighbor_state(struct tsch_neighbor *n, struct tsch_packet *p,
return in_queue; 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 static
PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) 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 { } else {
/* packet payload */ /* packet payload */
static void *packet; static void *packet;
#if TSCH_SECURITY_ENABLED #if LLSEC802154_ENABLED
/* encrypted payload */ /* encrypted payload */
static uint8_t encrypted_packet[TSCH_PACKET_MAX_LEN]; static uint8_t encrypted_packet[TSCH_PACKET_MAX_LEN];
#endif /* TSCH_SECURITY_ENABLED */ #endif /* LLSEC802154_ENABLED */
/* packet payload length */ /* packet payload length */
static uint8_t packet_len; static uint8_t packet_len;
/* packet seqno */ /* packet seqno */
@ -434,7 +509,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
packet_ready = 1; packet_ready = 1;
} }
#if TSCH_SECURITY_ENABLED #if LLSEC802154_ENABLED
if(tsch_is_pan_secured) { if(tsch_is_pan_secured) {
/* If we are going to encrypt, we need to generate the output in a separate buffer and keep /* 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. */ * 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; packet = encrypted_packet;
} }
} }
#endif /* TSCH_SECURITY_ENABLED */ #endif /* LLSEC802154_ENABLED */
/* prepare packet to send: copy to radio buffer */ /* prepare packet to send: copy to radio buffer */
if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */ 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 */ /* delay before CCA */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca"); TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca");
TSCH_DEBUG_TX_EVENT(); TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on(); tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
/* CCA */ /* CCA */
BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()), BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()),
current_slot_start, TS_CCA_OFFSET + TS_CCA); 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 */ /* limit tx_time to its max value */
tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]); tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
/* turn tadio off -- will turn on again to wait for ACK if needed */ /* 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(mac_tx_status == RADIO_TX_OK) {
if(!is_broadcast) { if(!is_broadcast) {
@ -488,35 +563,39 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
int ack_len; int ack_len;
rtimer_clock_t ack_start_time; rtimer_clock_t ack_start_time;
int is_time_source; int is_time_source;
radio_value_t radio_rx_mode;
struct ieee802154_ies ack_ies; struct ieee802154_ies ack_ies;
uint8_t ack_hdrlen; uint8_t ack_hdrlen;
frame802154_t frame; 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 */ /* Entering promiscuous mode so that the radio accepts the enhanced ACK */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_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)); 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 */ /* Unicast: wait for ack after tx: sleep until ack time */
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, 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_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
TSCH_DEBUG_TX_EVENT(); TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.on(); tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
/* Wait for ACK to come */ /* Wait for ACK to come */
BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(), 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]); tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]);
TSCH_DEBUG_TX_EVENT(); TSCH_DEBUG_TX_EVENT();
ack_start_time = RTIMER_NOW(); ack_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
/* Wait for ACK to finish */ /* Wait for ACK to finish */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(), BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
ack_start_time, tsch_timing[tsch_ts_max_ack]); ack_start_time, tsch_timing[tsch_ts_max_ack]);
TSCH_DEBUG_TX_EVENT(); TSCH_DEBUG_TX_EVENT();
NETSTACK_RADIO.off(); tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
#if TSCH_HW_FRAME_FILTERING
/* Leaving promiscuous mode */ /* Leaving promiscuous mode */
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_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); NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
#endif /* TSCH_HW_FRAME_FILTERING */
/* Read ack frame */ /* Read ack frame */
ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf)); 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; ack_len = 0;
} }
#if TSCH_SECURITY_ENABLED #if LLSEC802154_ENABLED
if(ack_len != 0) { if(ack_len != 0) {
if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame), if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
&frame, &current_neighbor->addr, &current_asn)) { &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), snprintf(log->message, sizeof(log->message),
"!failed to parse ACK")); "!failed to parse ACK"));
} }
#endif /* TSCH_SECURITY_ENABLED */ #endif /* LLSEC802154_ENABLED */
} }
if(ack_len != 0) { 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->transmissions++;
current_packet->ret = mac_tx_status; 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 = drift_correction;
log->tx.drift_used = is_drift_correction_used; log->tx.drift_used = is_drift_correction_used;
log->tx.is_data = ((((uint8_t *)(queuebuf_dataptr(current_packet->qb)))[0]) & 7) == FRAME802154_DATAFRAME; 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); 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)); 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(); TSCH_DEBUG_RX_EVENT();
/* Start radio for at least guard time */ /* Start radio for at least guard time */
NETSTACK_RADIO.on(); tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
packet_seen = NETSTACK_RADIO.receiving_packet(); packet_seen = NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet();
if(!packet_seen) { if(!packet_seen) {
/* Check if receiving within guard time */ /* Check if receiving within guard time */
BUSYWAIT_UNTIL_ABS((packet_seen = NETSTACK_RADIO.receiving_packet()), BUSYWAIT_UNTIL_ABS((packet_seen = NETSTACK_RADIO.receiving_packet()),
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait]); 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(); TSCH_DEBUG_RX_EVENT();
/* Save packet timestamp */ /* Save packet timestamp */
rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT; 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 */ /* Wait until packet is received, turn radio off */
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(), 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]); 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(); TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.off(); tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
#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
if(NETSTACK_RADIO.pending_packet()) { if(NETSTACK_RADIO.pending_packet()) {
static int frame_valid; static int frame_valid;
@ -696,9 +775,9 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
static frame802154_t frame; static frame802154_t frame;
radio_value_t radio_last_rssi; radio_value_t radio_last_rssi;
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
/* Read packet */ /* Read packet */
current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN); 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->rx_asn = current_asn;
current_input->rssi = (signed)radio_last_rssi; current_input->rssi = (signed)radio_last_rssi;
header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame); 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_check_dest_panid(&frame) &&
frame802154_extract_linkaddr(&frame, &source_address, &destination_address); 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); packet_duration = TSCH_PACKET_DURATION(current_input->len);
#if TSCH_SECURITY_ENABLED #if LLSEC802154_ENABLED
/* Decrypt and verify incoming frame */ /* Decrypt and verify incoming frame */
if(frame_valid) { if(frame_valid) {
if(tsch_security_parse_frame( 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)); "!failed to parse frame %u %u", header_len, current_input->len));
frame_valid = 0; frame_valid = 0;
} }
#endif /* TSCH_SECURITY_ENABLED */ #endif /* LLSEC802154_ENABLED */
if(frame_valid) { if(frame_valid) {
if(linkaddr_cmp(&destination_address, &linkaddr_node_addr) if(linkaddr_cmp(&destination_address, &linkaddr_node_addr)
|| linkaddr_cmp(&destination_address, &linkaddr_null)) { || linkaddr_cmp(&destination_address, &linkaddr_null)) {
int do_nack = 0; 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 #if TSCH_TIMESYNC_REMOVE_JITTER
/* remove jitter due to measurement errors */ /* 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; estimated_drift = 0;
} else if(estimated_drift > 0) { } else if(estimated_drift > 0) {
estimated_drift -= TSCH_TIMESYNC_MEASUREMENT_ERROR; 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), ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
&source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack); &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) { if(tsch_is_pan_secured) {
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */ /* 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); 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 */ /* Copy to radio buffer */
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len); 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"); packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
TSCH_DEBUG_RX_EVENT(); TSCH_DEBUG_RX_EVENT();
NETSTACK_RADIO.transmit(ack_len); 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 */ /* 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.sec_level = frame.aux_hdr.security_control.security_level;
log->rx.estimated_drift = estimated_drift; 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 */ /* Poll process for processing of pending input and logs */
process_poll(&tsch_pending_events_process); process_poll(&tsch_pending_events_process);
} }
} }
tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
} }
if(input_queue_drop != 0) { if(input_queue_drop != 0) {
@ -857,8 +938,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
} else { } else {
uint8_t current_channel; uint8_t current_channel;
int is_active_slot;
TSCH_DEBUG_SLOT_START(); TSCH_DEBUG_SLOT_START();
tsch_in_slot_operation = 1; tsch_in_slot_operation = 1;
/* Reset drift correction */
drift_correction = 0;
is_drift_correction_used = 0;
/* Get a packet ready to be sent */ /* Get a packet ready to be sent */
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor); 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 /* 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_link = backup_link;
current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor); current_packet = get_packet_and_neighbor_for_link(current_link, &current_neighbor);
} }
/* Hop channel */ is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX);
current_channel = tsch_calculate_channel(&current_asn, current_link->channel_offset); if(is_active_slot) {
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel); /* Hop channel */
/* Reset drift correction */ current_channel = tsch_calculate_channel(&current_asn, current_link->channel_offset);
drift_correction = 0; NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
is_drift_correction_used = 0; /* Turn the radio on already here if configured so; necessary for radios with slow startup */
/* Decide whether it is a TX/RX/IDLE or OFF slot */ tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
/* Actual slot operation */ /* Decide whether it is a TX/RX/IDLE or OFF slot */
if(current_packet != NULL) { /* Actual slot operation */
/* We have something to transmit, do the following: if(current_packet != NULL) {
* 1. send /* We have something to transmit, do the following:
* 2. update_backoff_state(current_neighbor) * 1. send
* 3. post tx callback * 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)); static struct pt slot_tx_pt;
} else if((current_link->link_options & LINK_OPTION_RX)) { PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
/* Listen */ } else {
static struct pt slot_rx_pt; /* Listen */
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t)); 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(); TSCH_DEBUG_SLOT_END();
} }

View file

@ -64,7 +64,7 @@
#else /* TSCH_LOG_LEVEL */ #else /* TSCH_LOG_LEVEL */
#define DEBUG DEBUG_NONE #define DEBUG DEBUG_NONE
#endif /* TSCH_LOG_LEVEL */ #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 /* 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 */ * 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? */ /* Are we associated to a TSCH network? */
int tsch_is_associated = 0; int tsch_is_associated = 0;
/* Is the PAN running link-layer security? */ /* 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) */ /* The current Absolute Slot Number (ASN) */
struct asn_t current_asn; struct asn_t current_asn;
/* Device rank or join priority: /* Device rank or join priority:
@ -177,7 +177,7 @@ tsch_set_coordinator(int enable)
void void
tsch_set_pan_secured(int enable) tsch_set_pan_secured(int enable)
{ {
tsch_is_pan_secured = TSCH_SECURITY_ENABLED && enable; tsch_is_pan_secured = LLSEC802154_ENABLED && enable;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
@ -199,8 +199,8 @@ tsch_reset(void)
frame802154_set_pan_id(0xffff); frame802154_set_pan_id(0xffff);
/* First make sure pending packet callbacks are sent etc */ /* First make sure pending packet callbacks are sent etc */
process_post_synch(&tsch_pending_events_process, PROCESS_EVENT_POLL, NULL); process_post_synch(&tsch_pending_events_process, PROCESS_EVENT_POLL, NULL);
/* Empty all neighbor queues */ /* Reset neighbor queues */
/* tsch_queue_flush_all(); */ tsch_queue_reset();
/* Remove unused neighbors */ /* Remove unused neighbors */
tsch_queue_free_unused_neighbors(); tsch_queue_free_unused_neighbors();
tsch_queue_update_time_source(NULL); 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 */ #endif /* TSCH_JOIN_SECURED_ONLY */
#if TSCH_SECURITY_ENABLED #if LLSEC802154_ENABLED
if(!tsch_security_parse_frame(input_eb->payload, hdrlen, if(!tsch_security_parse_frame(input_eb->payload, hdrlen,
input_eb->len - hdrlen - tsch_security_mic_len(&frame), input_eb->len - hdrlen - tsch_security_mic_len(&frame),
&frame, (linkaddr_t*)&frame.src_addr, &current_asn)) { &frame, (linkaddr_t*)&frame.src_addr, &current_asn)) {
PRINTF("TSCH:! parse_eb: failed to authenticate\n"); PRINTF("TSCH:! parse_eb: failed to authenticate\n");
return 0; return 0;
} }
#endif /* TSCH_SECURITY_ENABLED */ #endif /* LLSEC802154_ENABLED */
#if !TSCH_SECURITY_ENABLED #if !LLSEC802154_ENABLED
if(frame.fcf.security_enabled == 1) { if(frame.fcf.security_enabled == 1) {
PRINTF("TSCH:! parse_eb: we do not support security, but EB is secured\n"); PRINTF("TSCH:! parse_eb: we do not support security, but EB is secured\n");
return 0; return 0;
} }
#endif /* !TSCH_SECURITY_ENABLED */ #endif /* !LLSEC802154_ENABLED */
#if TSCH_JOIN_MY_PANID_ONLY #if TSCH_JOIN_MY_PANID_ONLY
/* Check if the EB comes from the PAN ID we expect */ /* 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 input_packet input_eb;
static struct etimer scan_timer; 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); ASN_INIT(current_asn, 0, 0);
etimer_set(&scan_timer, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY); etimer_set(&scan_timer, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY);
current_channel_since = clock_time();
while(!tsch_is_associated && !tsch_is_coordinator) { while(!tsch_is_associated && !tsch_is_coordinator) {
/* Hop to any channel offset */ /* Hop to any channel offset */
static int current_channel = 0; static uint8_t current_channel = 0;
/* Time when we started scanning on current_channel */
static clock_time_t current_channel_since = 0;
/* We are not coordinator, try to associate */ /* We are not coordinator, try to associate */
rtimer_clock_t t0; rtimer_clock_t t0;
int is_packet_pending = 0; 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 */ /* 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 */ /* Pick a channel at random in TSCH_JOIN_HOPPING_SEQUENCE */
uint8_t scan_channel = TSCH_JOIN_HOPPING_SEQUENCE[ uint8_t scan_channel = TSCH_JOIN_HOPPING_SEQUENCE[
random_rand() % sizeof(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; current_channel = scan_channel;
PRINTF("TSCH: scanning on channel %u\n", 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 */ /* Turn radio on and wait for EB */
@ -649,12 +650,12 @@ PT_THREAD(tsch_scan(struct pt *pt))
} }
if(is_packet_pending) { if(is_packet_pending) {
/* Save packet timestamp */
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t));
/* Read packet */ /* Read packet */
input_eb.len = NETSTACK_RADIO.read(input_eb.payload, TSCH_PACKET_MAX_LEN); 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 */ /* Parse EB and attempt to associate */
PRINTF("TSCH: association: received packet (%u bytes) on channel %u\n", input_eb.len, current_channel); 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_FRAME_TYPE, FRAME802154_BEACONFRAME);
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno); packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno);
#if TSCH_SECURITY_ENABLED #if LLSEC802154_ENABLED
if(tsch_is_pan_secured) { if(tsch_is_pan_secured) {
/* Set security level, key id and index */ /* 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_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_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); 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, eb_len = tsch_packet_create_eb(packetbuf_dataptr(), PACKETBUF_SIZE,
tsch_packet_seqno, &hdr_len, &tsch_sync_ie_offset); tsch_packet_seqno, &hdr_len, &tsch_sync_ie_offset);
if(eb_len != 0) { 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_FRAME_TYPE, FRAME802154_DATAFRAME);
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno); packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno);
#if TSCH_SECURITY_ENABLED #if LLSEC802154_ENABLED
if(tsch_is_pan_secured) { if(tsch_is_pan_secured) {
/* Set security level, key id and index */ /* 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_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_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); 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); 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), tsch_queue_packet_count(addr),
p->header_len, p->header_len,
queuebuf_datalen(p->qb)); 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) { if(ret != MAC_TX_DEFERRED) {

View file

@ -84,8 +84,8 @@
#ifdef TSCH_CONF_JOIN_SECURED_ONLY #ifdef TSCH_CONF_JOIN_SECURED_ONLY
#define TSCH_JOIN_SECURED_ONLY TSCH_CONF_JOIN_SECURED_ONLY #define TSCH_JOIN_SECURED_ONLY TSCH_CONF_JOIN_SECURED_ONLY
#else #else
/* By default, set if TSCH_SECURITY_ENABLED is also non-zero */ /* By default, set if LLSEC802154_ENABLED is also non-zero */
#define TSCH_JOIN_SECURED_ONLY TSCH_SECURITY_ENABLED #define TSCH_JOIN_SECURED_ONLY LLSEC802154_ENABLED
#endif #endif
/* By default, join any PAN ID. Otherwise, wait for an EB from IEEE802154_PANID */ /* 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 void handle_periodic_timer(void *ptr);
static struct ctimer periodic_timer; static struct ctimer periodic_timer;
static uint8_t initialized = 0; static uint8_t initialized = 0;
static void print_table();
#define PRINTF(...) printf(__VA_ARGS__) #define PRINTF(...) printf(__VA_ARGS__)
#else #else
#define PRINTF(...) #define PRINTF(...)
#endif #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 */ /* List of link-layer addresses of the neighbors, used as key in the tables */
typedef struct nbr_table_key { typedef struct nbr_table_key {
struct nbr_table_key *next; 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; 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 * static nbr_table_key_t *
nbr_table_allocate(void) nbr_table_allocate(nbr_table_reason_t reason, void *data)
{ {
nbr_table_key_t *key; nbr_table_key_t *key;
int least_used_count = 0; int least_used_count = 0;
@ -179,59 +207,72 @@ nbr_table_allocate(void)
key = memb_alloc(&neighbor_addr_mem); key = memb_alloc(&neighbor_addr_mem);
if(key != NULL) { if(key != NULL) {
return key; return key;
} else { /* No more space, try to free a neighbor. } else {
* The replacement policy is the following: remove neighbor that is: #ifdef NBR_TABLE_FIND_REMOVABLE
* (1) not locked const linkaddr_t *lladdr;
* (2) used by fewest tables lladdr = NBR_TABLE_FIND_REMOVABLE(reason, data);
* (3) oldest (the list is ordered by insertion time) if(lladdr == NULL) {
* */ /* Nothing found that can be deleted - return NULL to indicate failure */
/* Get item from first key */ PRINTF("*** Not removing entry to allocate new\n");
key = list_head(nbr_table_keys); return NULL;
while(key != NULL) { } else {
int item_index = index_from_key(key); /* used least_used_key to indicate what is the least useful entry */
int locked = locked_map[item_index]; int index;
/* Never delete a locked item */ int locked = 0;
if(!locked) { if((index = index_from_lladdr(lladdr)) != -1) {
int used = used_map[item_index]; least_used_key = key_from_index(index);
int used_count = 0; locked = locked_map[index];
/* Count how many tables are using this item */ }
while(used != 0) { /* Allow delete of locked item? */
if((used & 1) == 1) { if(least_used_key != NULL && locked) {
used_count++; PRINTF("Deleting locked item!\n");
} locked_map[index] = 0;
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);
} }
#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) { if(least_used_key == NULL) {
/* We haven't found any unlocked item, allocation fails */ /* We haven't found any unlocked item, allocation fails */
return NULL; return NULL;
} else { } else {
/* Reuse least used item */ /* Reuse least used item */
int i; remove_key(least_used_key);
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 */
return 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 */ /* Add a neighbor indexed with its link-layer address */
nbr_table_item_t * 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; int index;
nbr_table_item_t *item; 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) { if((index = index_from_lladdr(lladdr)) == -1) {
/* Neighbor not yet in table, let's try to allocate one */ /* 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 */ /* No space available for new entry */
if(key == NULL) { 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); memset(item, 0, table->item_size);
nbr_set_bit(used_map, table, item, 1); nbr_set_bit(used_map, table, item, 1);
#if DEBUG
print_table();
#endif
return item; return item;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -377,9 +421,42 @@ nbr_table_get_lladdr(nbr_table_t *table, const void *item)
return key != NULL ? &key->lladdr : NULL; 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 #if DEBUG
static void static void
handle_periodic_timer(void *ptr) print_table()
{ {
int i, j; int i, j;
/* Printout all neighbors and which tables they are used in */ /* Printout all neighbors and which tables they are used in */
@ -394,6 +471,12 @@ handle_periodic_timer(void *ptr)
PRINTF("\n"); PRINTF("\n");
} }
} }
}
/*---------------------------------------------------------------------------*/
static void
handle_periodic_timer(void *ptr)
{
print_table();
ctimer_reset(&periodic_timer); ctimer_reset(&periodic_timer);
} }
#endif #endif

View file

@ -75,6 +75,18 @@ typedef struct nbr_table {
/** \brief Declaration of non-static neighbor tables */ /** \brief Declaration of non-static neighbor tables */
#define NBR_TABLE_DECLARE(name) extern nbr_table_t *name #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 */ /** \name Neighbor tables: register and loop through table elements */
/** @{ */ /** @{ */
int nbr_table_register(nbr_table_t *table, nbr_table_callback *callback); 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 */ /** \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); 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 */ /** \name Neighbor tables: address manipulation */
/** @{ */ /** @{ */
linkaddr_t *nbr_table_get_lladdr(nbr_table_t *table, const nbr_table_item_t *item); 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_ */ #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 "contiki-net.h"
#include "net/packetbuf.h" #include "net/packetbuf.h"
#include "net/rime/rime.h" #include "net/rime/rime.h"
#include "sys/cc.h"
struct packetbuf_attr packetbuf_attrs[PACKETBUF_NUM_ATTRS]; struct packetbuf_attr packetbuf_attrs[PACKETBUF_NUM_ATTRS];
struct packetbuf_addr packetbuf_addrs[PACKETBUF_NUM_ADDRS]; struct packetbuf_addr packetbuf_addrs[PACKETBUF_NUM_ADDRS];
static uint16_t buflen, bufptr; static uint16_t buflen, bufptr;
static uint8_t hdrptr; static uint8_t hdrlen;
/* The declarations below ensure that the packet buffer is aligned on /* The declarations below ensure that the packet buffer is aligned on
an even 32-bit boundary. On some platforms (most notably the an even 32-bit boundary. On some platforms (most notably the
msp430 or OpenRISC), having a potentially misaligned packet buffer may lead to msp430 or OpenRISC), having a potentially misaligned packet buffer may lead to
problems when accessing words. */ 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 *packetbuf = (uint8_t *)packetbuf_aligned;
static uint8_t *packetbufptr;
#define DEBUG 0 #define DEBUG 0
#if DEBUG #if DEBUG
#include <stdio.h> #include <stdio.h>
@ -77,26 +76,19 @@ void
packetbuf_clear(void) packetbuf_clear(void)
{ {
buflen = bufptr = 0; buflen = bufptr = 0;
hdrptr = PACKETBUF_HDR_SIZE; hdrlen = 0;
packetbufptr = &packetbuf[PACKETBUF_HDR_SIZE];
packetbuf_attr_clear(); packetbuf_attr_clear();
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void
packetbuf_clear_hdr(void)
{
hdrptr = PACKETBUF_HDR_SIZE;
}
/*---------------------------------------------------------------------------*/
int int
packetbuf_copyfrom(const void *from, uint16_t len) packetbuf_copyfrom(const void *from, uint16_t len)
{ {
uint16_t l; uint16_t l;
packetbuf_clear(); packetbuf_clear();
l = len > PACKETBUF_SIZE? PACKETBUF_SIZE: len; l = MIN(PACKETBUF_SIZE, len);
memcpy(packetbufptr, from, l); memcpy(packetbuf, from, l);
buflen = l; buflen = l;
return l; return l;
} }
@ -104,82 +96,43 @@ packetbuf_copyfrom(const void *from, uint16_t len)
void void
packetbuf_compact(void) packetbuf_compact(void)
{ {
int i, len; int16_t i;
if(bufptr > 0) { if(bufptr) {
len = packetbuf_datalen() + PACKETBUF_HDR_SIZE; /* shift data to the left */
for(i = PACKETBUF_HDR_SIZE; i < len; i++) { for(i = 0; i < buflen; i++) {
packetbuf[i] = packetbuf[bufptr + i]; packetbuf[hdrlen + i] = packetbuf[packetbuf_hdrlen() + i];
} }
bufptr = 0; bufptr = 0;
} }
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
int 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) packetbuf_copyto(void *to)
{ {
#if DEBUG_LEVEL > 0 if(hdrlen + buflen > PACKETBUF_SIZE) {
{
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 */
return 0; return 0;
} }
memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr); memcpy(to, packetbuf_hdrptr(), hdrlen);
memcpy((uint8_t *)to + PACKETBUF_HDR_SIZE - hdrptr, packetbufptr + bufptr, memcpy((uint8_t *)to + hdrlen, packetbuf_dataptr(), buflen);
buflen); return hdrlen + buflen;
return PACKETBUF_HDR_SIZE - hdrptr + buflen;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
int int
packetbuf_hdralloc(int size) packetbuf_hdralloc(int size)
{ {
if(hdrptr >= size && packetbuf_totlen() + size <= PACKETBUF_SIZE) { int16_t i;
hdrptr -= size;
return 1; if(size + packetbuf_totlen() > PACKETBUF_SIZE) {
return 0;
} }
return 0;
} /* shift data to the right */
/*---------------------------------------------------------------------------*/ for(i = packetbuf_totlen() - 1; i >= 0; i--) {
void packetbuf[i + size] = packetbuf[i];
packetbuf_hdr_remove(int size) }
{ hdrlen += size;
hdrptr += size; return 1;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
int int
@ -204,13 +157,13 @@ packetbuf_set_datalen(uint16_t len)
void * void *
packetbuf_dataptr(void) packetbuf_dataptr(void)
{ {
return (void *)(&packetbuf[bufptr + PACKETBUF_HDR_SIZE]); return packetbuf + packetbuf_hdrlen();
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void * void *
packetbuf_hdrptr(void) packetbuf_hdrptr(void)
{ {
return (void *)(&packetbuf[hdrptr]); return packetbuf;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uint16_t uint16_t
@ -222,16 +175,7 @@ packetbuf_datalen(void)
uint8_t uint8_t
packetbuf_hdrlen(void) packetbuf_hdrlen(void)
{ {
uint8_t hdrlen; return bufptr + hdrlen;
hdrlen = PACKETBUF_HDR_SIZE - hdrptr;
if(hdrlen) {
/* outbound packet */
return hdrlen;
} else {
/* inbound packet */
return bufptr;
}
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uint16_t uint16_t
@ -244,9 +188,7 @@ void
packetbuf_attr_clear(void) packetbuf_attr_clear(void)
{ {
int i; int i;
for(i = 0; i < PACKETBUF_NUM_ATTRS; ++i) { memset(packetbuf_attrs, 0, sizeof(packetbuf_attrs));
packetbuf_attrs[i].val = 0;
}
for(i = 0; i < PACKETBUF_NUM_ADDRS; ++i) { for(i = 0; i < PACKETBUF_NUM_ADDRS; ++i) {
linkaddr_copy(&packetbuf_addrs[i].addr, &linkaddr_null); linkaddr_copy(&packetbuf_addrs[i].addr, &linkaddr_null);
} }
@ -272,7 +214,6 @@ packetbuf_attr_copyfrom(struct packetbuf_attr *attrs,
int int
packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val) packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
{ {
/* packetbuf_attrs[type].type = type; */
packetbuf_attrs[type].val = val; packetbuf_attrs[type].val = val;
return 1; return 1;
} }
@ -286,7 +227,6 @@ packetbuf_attr(uint8_t type)
int int
packetbuf_set_addr(uint8_t type, const linkaddr_t *addr) 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); linkaddr_copy(&packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr, addr);
return 1; return 1;
} }

View file

@ -66,15 +66,6 @@
#define PACKETBUF_SIZE 128 #define PACKETBUF_SIZE 128
#endif #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 #ifdef PACKETBUF_CONF_WITH_PACKET_TYPE
#define PACKETBUF_WITH_PACKET_TYPE PACKETBUF_CONF_WITH_PACKET_TYPE #define PACKETBUF_WITH_PACKET_TYPE PACKETBUF_CONF_WITH_PACKET_TYPE
#else #else
@ -92,21 +83,6 @@
*/ */
void packetbuf_clear(void); 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 * \brief Get a pointer to the data in the packetbuf
* \return Pointer to the packetbuf data * \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, * the packetbuf. The data is either stored in the packetbuf,
* or referenced to an external location. * 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); 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 * \brief Get a pointer to the header in the packetbuf, for outbound packets
* \return Pointer to the packetbuf header * \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); void *packetbuf_hdrptr(void);
@ -143,12 +105,6 @@ void *packetbuf_hdrptr(void);
* \brief Get the length of the header in the packetbuf * \brief Get the length of the header in the packetbuf
* \return 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); uint8_t packetbuf_hdrlen(void);
@ -157,17 +113,6 @@ uint8_t packetbuf_hdrlen(void);
* \brief Get the length of the data in the packetbuf * \brief Get the length of the data in the packetbuf
* \return 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); uint16_t packetbuf_datalen(void);
@ -181,10 +126,6 @@ uint16_t packetbuf_totlen(void);
/** /**
* \brief Set the length of the data in the packetbuf * \brief Set the length of the data in the packetbuf
* \param len The length of the data * \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); 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 * The external buffer to which the packetbuf is to be
* copied must be able to accomodate at least * 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 * bytes that was copied to the external buffer is
* returned. * returned.
* *
*/ */
int packetbuf_copyto(void *to); 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 * \brief Extend the header of the packetbuf, for outbound packets
* \param size The number of bytes the header should be extended * \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; typedef uint16_t packetbuf_attr_t;
struct packetbuf_attr { struct packetbuf_attr {
/* uint8_t type; */
packetbuf_attr_t val; packetbuf_attr_t val;
}; };
struct packetbuf_addr { struct packetbuf_addr {
/* uint8_t type; */
linkaddr_t addr; linkaddr_t addr;
}; };
@ -332,9 +255,9 @@ enum {
#endif /* NETSTACK_CONF_WITH_RIME */ #endif /* NETSTACK_CONF_WITH_RIME */
PACKETBUF_ATTR_PENDING, PACKETBUF_ATTR_PENDING,
PACKETBUF_ATTR_FRAME_TYPE, PACKETBUF_ATTR_FRAME_TYPE,
#if LLSEC802154_SECURITY_LEVEL #if LLSEC802154_USES_AUX_HEADER
PACKETBUF_ATTR_SECURITY_LEVEL, PACKETBUF_ATTR_SECURITY_LEVEL,
#endif /* LLSEC802154_SECURITY_LEVEL */ #endif /* LLSEC802154_USES_AUX_HEADER */
#if LLSEC802154_USES_FRAME_COUNTER #if LLSEC802154_USES_FRAME_COUNTER
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1, PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3, PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3,
@ -365,29 +288,6 @@ enum {
PACKETBUF_ATTR_MAX 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 #if NETSTACK_CONF_WITH_RIME
#define PACKETBUF_NUM_ADDRS 4 #define PACKETBUF_NUM_ADDRS 4
#else /* NETSTACK_CONF_WITH_RIME */ #else /* NETSTACK_CONF_WITH_RIME */
@ -403,15 +303,9 @@ enum {
extern struct packetbuf_attr packetbuf_attrs[]; extern struct packetbuf_attr packetbuf_attrs[];
extern struct packetbuf_addr packetbuf_addrs[]; 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 static inline int
packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val) packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
{ {
/* packetbuf_attrs[type].type = type; */
packetbuf_attrs[type].val = val; packetbuf_attrs[type].val = val;
return 1; return 1;
} }
@ -424,7 +318,6 @@ packetbuf_attr(uint8_t type)
static inline int static inline int
packetbuf_set_addr(uint8_t type, const linkaddr_t *addr) 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); linkaddr_copy(&packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr, addr);
return 1; 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; continue;
} }
#endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */ #endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */
len = a->len; len = (a->len & 0xf8) + ((a->len & 7) ? 8: 0);
if(len < 8) {
len = 8;
}
size += len; size += len;
} }
return size / 8; return size / 8;

View file

@ -46,30 +46,51 @@
#endif /* RPL_CONF_STATS */ #endif /* RPL_CONF_STATS */
/* /*
* Select routing metric supported at runtime. This must be a valid * The objective function (OF) used by a RPL root is configurable through
* DAG Metric Container Object Type (see below). Currently, we only * the RPL_CONF_OF_OCP parameter. This is defined as the objective code
* support RPL_DAG_MC_ETX and RPL_DAG_MC_ENERGY. * point (OCP) of the OF, RPL_OCP_OF0 or RPL_OCP_MRHOF. This flag is of
* When MRHOF (RFC6719) is used with ETX, no metric container must * no relevance to non-root nodes, which run the OF advertised in the
* be used; instead the rank carries ETX directly. * 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 #ifdef RPL_CONF_DAG_MC
#define RPL_DAG_MC RPL_CONF_DAG_MC #define RPL_DAG_MC RPL_CONF_DAG_MC
#else #else
#define RPL_DAG_MC RPL_DAG_MC_NONE #define RPL_DAG_MC RPL_DAG_MC_NONE
#endif /* RPL_CONF_DAG_MC */ #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. */ /* This value decides which DAG instance we should participate in by default. */
#ifdef RPL_CONF_DEFAULT_INSTANCE #ifdef RPL_CONF_DEFAULT_INSTANCE
#define RPL_DEFAULT_INSTANCE RPL_CONF_DEFAULT_INSTANCE #define RPL_DEFAULT_INSTANCE RPL_CONF_DEFAULT_INSTANCE
@ -118,7 +139,7 @@
#ifdef RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME #ifdef RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME #define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
#else #else
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 0 #define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 1
#endif /* RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME */ #endif /* RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME */
/* /*
@ -186,21 +207,12 @@
#define RPL_DIO_REDUNDANCY 10 #define RPL_DIO_REDUNDANCY 10
#endif #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 * Default route lifetime unit. This is the granularity of time
* used in RPL lifetime values, in seconds. * used in RPL lifetime values, in seconds.
*/ */
#ifndef RPL_CONF_DEFAULT_LIFETIME_UNIT #ifndef RPL_CONF_DEFAULT_LIFETIME_UNIT
#define RPL_DEFAULT_LIFETIME_UNIT 0xffff #define RPL_DEFAULT_LIFETIME_UNIT 60
#else #else
#define RPL_DEFAULT_LIFETIME_UNIT RPL_CONF_DEFAULT_LIFETIME_UNIT #define RPL_DEFAULT_LIFETIME_UNIT RPL_CONF_DEFAULT_LIFETIME_UNIT
#endif #endif
@ -209,7 +221,7 @@
* Default route lifetime as a multiple of the lifetime unit. * Default route lifetime as a multiple of the lifetime unit.
*/ */
#ifndef RPL_CONF_DEFAULT_LIFETIME #ifndef RPL_CONF_DEFAULT_LIFETIME
#define RPL_DEFAULT_LIFETIME 0xff #define RPL_DEFAULT_LIFETIME 30
#else #else
#define RPL_DEFAULT_LIFETIME RPL_CONF_DEFAULT_LIFETIME #define RPL_DEFAULT_LIFETIME RPL_CONF_DEFAULT_LIFETIME
#endif #endif
@ -224,16 +236,38 @@
#endif #endif
/* /*
* Hop-by-hop option * RPL DAO ACK support. When enabled, DAO ACK will be sent and requested.
* This option control the insertion of the RPL Hop-by-Hop extension header * This will also enable retransmission of DAO when no ack is received.
* into packets originating from this node. Incoming Hop-by-hop extension * */
* header are still processed and forwarded. #ifdef RPL_CONF_WITH_DAO_ACK
*/ #define RPL_WITH_DAO_ACK RPL_CONF_WITH_DAO_ACK
#ifdef RPL_CONF_INSERT_HBH_OPTION
#define RPL_INSERT_HBH_OPTION RPL_CONF_INSERT_HBH_OPTION
#else #else
#define RPL_INSERT_HBH_OPTION 1 #define RPL_WITH_DAO_ACK 0
#endif #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 * RPL probing. When enabled, probes will be sent periodically to keep
@ -254,22 +288,13 @@
#define RPL_PROBING_INTERVAL (120 * CLOCK_SECOND) #define RPL_PROBING_INTERVAL (120 * CLOCK_SECOND)
#endif #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 (10 * 60 * CLOCK_SECOND)
#endif
/* /*
* Function used to select the next parent to be probed. * Function used to select the next parent to be probed.
*/ */
#ifdef RPL_CONF_PROBING_SELECT_FUNC #ifdef RPL_CONF_PROBING_SELECT_FUNC
#define RPL_PROBING_SELECT_FUNC RPL_CONF_PROBING_SELECT_FUNC #define RPL_PROBING_SELECT_FUNC RPL_CONF_PROBING_SELECT_FUNC
#else #else
#define RPL_PROBING_SELECT_FUNC(dag) get_probing_target((dag)) #define RPL_PROBING_SELECT_FUNC get_probing_target
#endif #endif
/* /*
@ -292,8 +317,7 @@
#ifdef RPL_CONF_PROBING_DELAY_FUNC #ifdef RPL_CONF_PROBING_DELAY_FUNC
#define RPL_PROBING_DELAY_FUNC RPL_CONF_PROBING_DELAY_FUNC #define RPL_PROBING_DELAY_FUNC RPL_CONF_PROBING_DELAY_FUNC
#else #else
#define RPL_PROBING_DELAY_FUNC() ((RPL_PROBING_INTERVAL / 2) \ #define RPL_PROBING_DELAY_FUNC get_probing_delay
+ random_rand() % (RPL_PROBING_INTERVAL))
#endif #endif
/* /*

View file

@ -43,7 +43,9 @@
#define RPL_DAG_GRACE_PERIOD (CLOCK_SECOND * 20 * 1) #define RPL_DAG_GRACE_PERIOD (CLOCK_SECOND * 20 * 1)
#if (UIP_CONF_MAX_ROUTES != 0)
static struct uip_ds6_notification n; static struct uip_ds6_notification n;
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
static uint8_t to_become_root; static uint8_t to_become_root;
static struct ctimer c; static struct ctimer c;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -121,6 +123,7 @@ create_dag_callback(void *ptr)
ctimer_set(&c, RPL_DAG_GRACE_PERIOD, create_dag_callback, NULL); ctimer_set(&c, RPL_DAG_GRACE_PERIOD, create_dag_callback, NULL);
} }
} }
#if (UIP_CONF_MAX_ROUTES != 0)
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *ipaddr, 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 * static uip_ipaddr_t *
set_global_address(void) set_global_address(void)
@ -146,7 +150,7 @@ set_global_address(void)
/* Assign a unique local address (RFC4193, /* Assign a unique local address (RFC4193,
http://tools.ietf.org/html/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_set_addr_iid(&ipaddr, &uip_lladdr);
uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF);
@ -171,7 +175,9 @@ rpl_dag_root_init(void)
if(!initialized) { if(!initialized) {
to_become_root = 0; to_become_root = 0;
set_global_address(); set_global_address();
#if (UIP_CONF_MAX_ROUTES != 0)
uip_ds6_notification_add(&n, route_callback); uip_ds6_notification_add(&n, route_callback);
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
initialized = 1; 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 /* 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 */ 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 && if(dag->instance != NULL &&
dag->instance->def_route != NULL) { dag->instance->def_route != NULL) {
uip_ds6_defrt_rm(dag->instance->def_route); uip_ds6_defrt_rm(dag->instance->def_route);
dag->instance->def_route = NULL; 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); rpl_set_prefix(dag, &prefix, 64);
PRINTF("rpl_dag_root_init_dag: created a new RPL dag\n"); PRINTF("rpl_dag_root_init_dag: created a new RPL dag\n");
return 0; return 0;

View file

@ -44,6 +44,7 @@
*/ */
#include "contiki.h" #include "contiki.h"
#include "net/link-stats.h"
#include "net/rpl/rpl-private.h" #include "net/rpl/rpl-private.h"
#include "net/ip/uip.h" #include "net/ip/uip.h"
#include "net/ipv6/uip-nd6.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 */ #endif /* RPL_CALLBACK_PARENT_SWITCH */
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
extern rpl_of_t RPL_OF; extern rpl_of_t rpl_of0, rpl_mrhof;
static rpl_of_t * const objective_functions[] = {&RPL_OF}; static rpl_of_t * const objective_functions[] = RPL_SUPPORTED_OFS;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* RPL definitions. */ /* RPL definitions. */
@ -88,25 +89,29 @@ rpl_instance_t *default_instance;
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
rpl_print_neighbor_list() rpl_print_neighbor_list(void)
{ {
if(default_instance != NULL && default_instance->current_dag != NULL && 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_dio_interval = default_instance->dio_intcurrent;
int curr_rank = default_instance->current_dag->rank; int curr_rank = default_instance->current_dag->rank;
rpl_parent_t *p = nbr_table_head(rpl_parents); 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) { while(p != NULL) {
uip_ds6_nbr_t *nbr = rpl_get_nbr(p); const struct link_stats *stats = rpl_get_parent_link_stats(p);
printf("RPL: nbr %3u %5u, %5u => %5u %c%c (last tx %u min ago)\n", printf("RPL: nbr %3u %5u, %5u => %5u -- %2u %c%c (last tx %u min ago)\n",
nbr_table_get_lladdr(rpl_parents, p)->u8[7], rpl_get_parent_ipaddr(p)->u8[15],
p->rank, nbr ? nbr->link_metric : 0, p->rank,
default_instance->of->calculate_rank(p, 0), rpl_get_parent_link_metric(p),
default_instance->current_dag == p->dag ? 'd' : ' ', rpl_rank_via_parent(p),
p == default_instance->current_dag->preferred_parent ? '*' : ' ', stats != NULL ? stats->freshness : 0,
(unsigned)((now - p->last_tx_time) / (60 * CLOCK_SECOND))); 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); p = nbr_table_next(rpl_parents, p);
} }
printf("RPL: end of list\n"); printf("RPL: end of list\n");
@ -116,8 +121,7 @@ rpl_print_neighbor_list()
uip_ds6_nbr_t * uip_ds6_nbr_t *
rpl_get_nbr(rpl_parent_t *parent) rpl_get_nbr(rpl_parent_t *parent)
{ {
linkaddr_t *lladdr = NULL; const linkaddr_t *lladdr = rpl_get_parent_lladdr(parent);
lladdr = nbr_table_get_lladdr(rpl_parents, parent);
if(lladdr != NULL) { if(lladdr != NULL) {
return nbr_table_get_from_lladdr(ds6_neighbors, lladdr); return nbr_table_get_from_lladdr(ds6_neighbors, lladdr);
} else { } else {
@ -151,30 +155,78 @@ rpl_get_parent_rank(uip_lladdr_t *addr)
if(p != NULL) { if(p != NULL) {
return p->rank; return p->rank;
} else { } else {
return 0; return INFINITE_RANK;
} }
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
uint16_t 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; if(p != NULL && p->dag != NULL) {
nbr = nbr_table_get_from_lladdr(ds6_neighbors, (const linkaddr_t *)addr); rpl_instance_t *instance = p->dag->instance;
if(instance != NULL && instance->of != NULL && instance->of->parent_link_metric != NULL) {
if(nbr != NULL) { return instance->of->parent_link_metric(p);
return nbr->link_metric; }
} else {
return 0;
} }
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 * uip_ipaddr_t *
rpl_get_parent_ipaddr(rpl_parent_t *p) 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); 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 static void
rpl_set_preferred_parent(rpl_dag_t *dag, rpl_parent_t *p) 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->grounded = RPL_GROUNDED;
dag->preference = RPL_PREFERENCE; dag->preference = RPL_PREFERENCE;
instance->mop = RPL_MOP_DEFAULT; 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); rpl_set_preferred_parent(dag, NULL);
memcpy(&dag->dag_id, dag_id, sizeof(dag->dag_id)); 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) { if(instance->current_dag != dag && instance->current_dag != NULL) {
/* Remove routes installed by DAOs. */ /* 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; instance->current_dag->joined = 0;
} }
@ -610,7 +669,9 @@ rpl_free_dag(rpl_dag_t *dag)
dag->joined = 0; dag->joined = 0;
/* Remove routes installed by DAOs. */ /* Remove routes installed by DAOs. */
rpl_remove_routes(dag); if(RPL_IS_STORING(dag->instance)) {
rpl_remove_routes(dag);
}
/* Remove autoconfigured address */ /* Remove autoconfigured address */
if((dag->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) { 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); PRINT6ADDR(addr);
PRINTF("\n"); PRINTF("\n");
if(lladdr != NULL) { if(lladdr != NULL) {
/* Add parent in rpl_parents */ /* Add parent in rpl_parents - again this is due to DIO */
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr); p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr,
NBR_TABLE_REASON_RPL_DIO, dio);
if(p == NULL) { if(p == NULL) {
PRINTF("RPL: rpl_add_parent p NULL\n"); PRINTF("RPL: rpl_add_parent p NULL\n");
} else { } else {
uip_ds6_nbr_t *nbr;
nbr = rpl_get_nbr(p);
p->dag = dag; p->dag = dag;
p->rank = dio->rank; p->rank = dio->rank;
p->dtsn = dio->dtsn; p->dtsn = dio->dtsn;
#if RPL_WITH_MC
/* 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
memcpy(&p->mc, &dio->mc, sizeof(p->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) { if(instance->current_dag != best_dag) {
/* Remove routes installed by DAOs. */ /* 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: "); PRINTF("RPL: New preferred DAG: ");
PRINT6ADDR(&best_dag->dag_id); 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); instance->of->update_metric_container(instance);
/* Update the DAG rank. */ /* 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) { 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; 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"); PRINTF("RPL: New rank unacceptable!\n");
rpl_set_preferred_parent(instance->current_dag, NULL); 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. */ /* Send a No-Path DAO to the removed preferred parent. */
dao_output(last_parent, RPL_ZERO_LIFETIME); 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", PRINTF("RPL: Changed preferred parent, rank changed from %u to %u\n",
(unsigned)old_rank, best_dag->rank); (unsigned)old_rank, best_dag->rank);
RPL_STAT(rpl_stats.parent_switch++); RPL_STAT(rpl_stats.parent_switch++);
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES) { if(RPL_IS_STORING(instance) && last_parent != NULL) {
if(last_parent != NULL) { /* Send a No-Path DAO to the removed preferred parent. */
/* Send a No-Path DAO to the removed preferred parent. */ dao_output(last_parent, RPL_ZERO_LIFETIME);
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);
} }
/* The DAO parent set changed - schedule a DAO transmission. */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
rpl_schedule_dao(instance);
rpl_reset_dio_timer(instance); rpl_reset_dio_timer(instance);
#if DEBUG #if DEBUG
rpl_print_neighbor_list(); rpl_print_neighbor_list();
@ -795,22 +853,42 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static rpl_parent_t * 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); of = dag->instance->of;
while(p != NULL) { /* 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) { if(p->dag != dag || p->rank == INFINITE_RANK) {
/* ignore this neighbor */ continue;
} else if(best == NULL) {
best = p;
} else {
best = dag->instance->of->best_parent(best, p);
} }
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; return best;
@ -819,16 +897,37 @@ best_parent(rpl_dag_t *dag)
rpl_parent_t * rpl_parent_t *
rpl_select_parent(rpl_dag_t *dag) 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) { if(best != NULL) {
rpl_set_preferred_parent(dag, best); #if RPL_WITH_PROBING
dag->rank = dag->instance->of->calculate_rank(dag->preferred_parent, 0); 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 { } 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 void
@ -859,9 +958,11 @@ rpl_nullify_parent(rpl_parent_t *parent)
uip_ds6_defrt_rm(dag->instance->def_route); uip_ds6_defrt_rm(dag->instance->def_route);
dag->instance->def_route = NULL; 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) { 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); 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; dag_src->instance->def_route = NULL;
} }
} else if(dag_src->joined) { } else if(dag_src->joined) {
/* Remove uIPv6 routes that have this parent as the next hop. */ if(RPL_IS_STORING(dag_src->instance)) {
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src); /* 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 "); 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; 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_dag_t *
rpl_get_any_dag(void) 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_parent_t *p;
rpl_of_t *of; 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 /* Determine the objective function by using the
objective code point of the DIO. */ objective code point of the DIO. */
of = rpl_find_of(dio->ocp); 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->of = of;
instance->mop = dio->mop; 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->current_dag = dag;
instance->dtsn_out = RPL_LOLLIPOP_INIT; 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); rpl_set_preferred_parent(dag, p);
instance->of->update_metric_container(instance); 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. */ /* So far this is the lowest rank we are aware of. */
dag->min_rank = dag->rank; dag->min_rank = dag->rank;
@ -1032,6 +1176,8 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
} else { } else {
PRINTF("RPL: The DIO does not meet the prerequisites for sending a DAO\n"); 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 #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)); memcpy(&dag->prefix_info, &dio->prefix_info, sizeof(rpl_prefix_t));
rpl_set_preferred_parent(dag, p); 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. */ 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 ", 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"); PRINTF("RPL: Failed to add a parent during the global repair\n");
dag->rank = INFINITE_RANK; dag->rank = INFINITE_RANK;
} else { } else {
dag->rank = dag->instance->of->calculate_rank(p, 0); dag->rank = rpl_rank_via_parent(p);
dag->min_rank = dag->rank; dag->min_rank = dag->rank;
PRINTF("RPL: rpl_process_parent_event global repair\n"); PRINTF("RPL: rpl_process_parent_event global repair\n");
rpl_process_parent_event(dag->instance, p); 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++); RPL_STAT(rpl_stats.global_repairs++);
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void void
rpl_local_repair(rpl_instance_t *instance) 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); rpl_reset_dio_timer(instance);
/* Request refresh of DAO registrations next DIO */
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
RPL_STAT(rpl_stats.local_repairs++); RPL_STAT(rpl_stats.local_repairs++);
} }
@ -1205,6 +1357,7 @@ int
rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p) rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
{ {
int return_value; int return_value;
rpl_parent_t *last_parent = instance->current_dag->preferred_parent;
#if DEBUG #if DEBUG
rpl_rank_t old_rank; rpl_rank_t old_rank;
@ -1213,10 +1366,20 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
return_value = 1; 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)) { if(!acceptable_rank(p->dag, p->rank)) {
/* The candidate parent is no longer valid: the rank increase resulting /* The candidate parent is no longer valid: the rank increase resulting
from the choice of it as a parent would be too high. */ 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); rpl_nullify_parent(p);
if(p != instance->current_dag->preferred_parent) { if(p != instance->current_dag->preferred_parent) {
return 0; 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) { if(rpl_select_dag(instance, p) == NULL) {
/* No suitable parent; trigger a local repair. */ if(last_parent != NULL) {
PRINTF("RPL: No parents found in any DAG\n"); /* No suitable parent anymore; trigger a local repair. */
rpl_local_repair(instance); PRINTF("RPL: No parents found in any DAG\n");
return 0; rpl_local_repair(instance);
return 0;
}
} }
#if DEBUG #if DEBUG
@ -1250,6 +1415,19 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
return return_value; 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 void
rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio) rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
{ {
@ -1257,7 +1435,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
rpl_dag_t *dag, *previous_dag; rpl_dag_t *dag, *previous_dag;
rpl_parent_t *p; rpl_parent_t *p;
#if RPL_CONF_MULTICAST #if RPL_WITH_MULTICAST
/* If the root is advertising MOP 2 but we support MOP 3 we can still join /* If the root is advertising MOP 2 but we support MOP 3 we can still join
* In that scenario, we suppress DAOs for multicast targets */ * In that scenario, we suppress DAOs for multicast targets */
if(dio->mop < RPL_MOP_STORING_NO_MULTICAST) { if(dio->mop < RPL_MOP_STORING_NO_MULTICAST) {
@ -1282,7 +1460,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
PRINTF("RPL: Global repair\n"); PRINTF("RPL: Global repair\n");
if(dio->prefix_info.length != 0) { if(dio->prefix_info.length != 0) {
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) { 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); 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) { if(instance == NULL) {
PRINTF("RPL: New instance detected (ID=%u): Joining...\n", dio->instance_id); 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; return;
} }
@ -1315,6 +1497,10 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
if(dag == NULL) { if(dag == NULL) {
#if RPL_MAX_DAG_PER_INSTANCE > 1 #if RPL_MAX_DAG_PER_INSTANCE > 1
PRINTF("RPL: Adding new DAG to known instance.\n"); 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); dag = rpl_add_dag(from, dio);
if(dag == NULL) { if(dag == NULL) {
PRINTF("RPL: Failed to add DAG.\n"); 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", PRINTF("RPL: Ignoring DIO with too low rank: %u\n",
(unsigned)dio->rank); (unsigned)dio->rank);
return; return;
} else if(dio->rank == INFINITE_RANK && dag->joined) {
rpl_reset_dio_timer(instance);
} }
/* Prefix Information Option treated to add new prefix */ /* Prefix Information Option treated to add new prefix */
if(dio->prefix_info.length != 0) { if(dio->prefix_info.length != 0) {
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) { 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); 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(dag->rank == ROOT_RANK(instance)) {
if(dio->rank != INFINITE_RANK) { if(dio->rank != INFINITE_RANK) {
instance->dio_counter++; instance->dio_counter++;
@ -1394,6 +1583,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
} }
p->rank = dio->rank; 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 */ /* Parent info has been updated, trigger rank recalculation */
p->flags |= RPL_PARENT_FLAG_UPDATED; 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); PRINT6ADDR(&instance->current_dag->dag_id);
PRINTF(", rank %u, min_rank %u, ", PRINTF(", rank %u, min_rank %u, ",
instance->current_dag->rank, instance->current_dag->min_rank); instance->current_dag->rank, instance->current_dag->min_rank);
PRINTF("parent rank %u, parent etx %u, link metric %u, instance etx %u\n", PRINTF("parent rank %u, link metric %u\n",
p->rank, -1/*p->mc.obj.etx*/, rpl_get_nbr(p)->link_metric, instance->mc.obj.etx); p->rank, rpl_get_parent_link_metric(p));
/* We have allocated a candidate parent; process the DIO further. */ /* 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)); 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) { if(rpl_process_parent_event(instance, p) == 0) {
PRINTF("RPL: The candidate parent is rejected\n"); PRINTF("RPL: The candidate parent is rejected\n");
return; return;

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