cleanup confilicrts
This commit is contained in:
commit
3c8e91d74e
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -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
|
||||||
|
|
34
.travis.yml
34
.travis.yml
|
@ -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'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
1
apps/at-master/Makefile.at-master
Normal file
1
apps/at-master/Makefile.at-master
Normal file
|
@ -0,0 +1 @@
|
||||||
|
at-master_src = at-master.c
|
148
apps/at-master/at-master.c
Normal file
148
apps/at-master/at-master.c
Normal 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
123
apps/at-master/at-master.h
Normal 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_ */
|
|
@ -1 +0,0 @@
|
||||||
deluge_src = deluge.c
|
|
|
@ -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 = ¤t_object.pages[i];
|
|
||||||
init_page(¤t_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(¤t_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 = ¤t_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(¤t_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, ¤t_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(¤t_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, ¤t_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 = ¤t_object.pages[packet.pagenum];
|
|
||||||
if(packet.version == page->version && !(page->flags & PAGE_COMPLETE)) {
|
|
||||||
memcpy(¤t_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(¤t_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, ¤t_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 = ¤t_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(¤t_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, ¤t_object);
|
|
||||||
|
|
||||||
/* Deluge M.4 */
|
|
||||||
ctimer_set(&profile_timer, r_rand * CLOCK_SECOND,
|
|
||||||
(void *)(void *)send_profile, ¤t_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();
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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);
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
/* no other types for now... */
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 '{':
|
||||||
|
if((s == 0 && v == 0) || s == '[' || s == ':') {
|
||||||
push(state, c);
|
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;
|
||||||
default:
|
case 0:
|
||||||
if(s == ':' || s == '[') {
|
if(v == 0 || state->depth > 0) {
|
||||||
if(c <= '9' && c >= '0') {
|
state->error = JSON_ERROR_SYNTAX;
|
||||||
atomic(state, JSON_TYPE_NUMBER);
|
|
||||||
return JSON_TYPE_NUMBER;
|
|
||||||
}
|
}
|
||||||
|
return JSON_TYPE_ERROR;
|
||||||
|
default:
|
||||||
|
if(s == 0 || s == ':' || s == '[') {
|
||||||
|
if (v != 0 && v != ',') {
|
||||||
|
state->error = JSON_ERROR_SYNTAX;
|
||||||
|
return JSON_TYPE_ERROR;
|
||||||
|
}
|
||||||
|
if(c == '-' || (c <= '9' && c >= '0')) {
|
||||||
|
return atomic(state, JSON_TYPE_NUMBER);
|
||||||
|
} else if(c == 'n') {
|
||||||
|
return atomic(state, JSON_TYPE_NULL);
|
||||||
|
} else if(c == 't') {
|
||||||
|
return atomic(state, JSON_TYPE_TRUE);
|
||||||
|
} else if(c == 'f') {
|
||||||
|
return atomic(state, JSON_TYPE_FALSE);
|
||||||
|
} else {
|
||||||
|
state->error = JSON_ERROR_SYNTAX;
|
||||||
|
return JSON_TYPE_ERROR;
|
||||||
|
}
|
||||||
|
} else if(s == '{') {
|
||||||
|
state->error = JSON_ERROR_SYNTAX;
|
||||||
|
return JSON_TYPE_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
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;
|
||||||
}
|
}
|
||||||
str[i] = 0;
|
continue;
|
||||||
|
}
|
||||||
|
str[o++] = c;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
162
apps/oma-lwm2m/lwm2m-json.c
Normal 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
|
||||||
|
};
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/** @} */
|
51
apps/oma-lwm2m/lwm2m-json.h
Normal file
51
apps/oma-lwm2m/lwm2m-json.h
Normal 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_ */
|
||||||
|
/** @} */
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 */
|
||||||
|
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);
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
119
apps/orchestra/orchestra-rule-unicast-per-neighbor-rpl-ns.c
Normal file
119
apps/orchestra/orchestra-rule-unicast-per-neighbor-rpl-ns.c
Normal 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,
|
||||||
|
};
|
|
@ -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,9 +87,15 @@ 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)) {
|
||||||
|
/* 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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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);
|
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,
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,16 +88,21 @@
|
||||||
#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
|
||||||
|
|
||||||
|
/* Internal Coffee markers. */
|
||||||
#define INVALID_PAGE ((coffee_page_t)-1)
|
#define INVALID_PAGE ((coffee_page_t)-1)
|
||||||
#define UNKNOWN_OFFSET ((cfs_offset_t)-1)
|
#define UNKNOWN_OFFSET ((cfs_offset_t)-1)
|
||||||
|
|
||||||
|
/* File removal actions. They can have the same values because
|
||||||
|
they are passed as separate parameters. */
|
||||||
#define REMOVE_LOG 1
|
#define REMOVE_LOG 1
|
||||||
#define CLOSE_FDS 1
|
#define CLOSE_FDS 1
|
||||||
#define ALLOW_GC 1
|
#define ALLOW_GC 1
|
||||||
|
@ -114,8 +113,7 @@
|
||||||
#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)
|
||||||
|
@ -127,10 +125,10 @@
|
||||||
#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. */
|
||||||
|
|
||||||
|
@ -148,7 +146,8 @@
|
||||||
!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) {
|
||||||
|
if(FD_WRITABLE(fd)) {
|
||||||
fdp->file->end = new_offset;
|
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,9 +1158,7 @@ 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) {
|
||||||
|
@ -1175,21 +1167,15 @@ cfs_write(int fd, const void *buf, unsigned size)
|
||||||
file = fdp->file;
|
file = fdp->file;
|
||||||
PRINTF("Extended the file at page %u\n", (unsigned)file->page);
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ||
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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,7 +364,7 @@ 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;
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
@ -760,7 +778,7 @@ tcpip_uipcall(void)
|
||||||
|
|
||||||
#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
|
||||||
|
@ -794,7 +812,7 @@ PROCESS_THREAD(tcpip_process, ev, data)
|
||||||
|
|
||||||
#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;
|
||||||
|
@ -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 */
|
||||||
|
|
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -51,12 +51,10 @@ 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_CONF_IPV6_MULTICAST
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 *
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -186,7 +186,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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,27 +144,7 @@ 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())) {
|
|
||||||
/* If there were other extension headers*/
|
|
||||||
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
|
|
||||||
if (uip_ext_len != temp_ext_len) {
|
|
||||||
uip_len -= (uip_ext_len - temp_ext_len);
|
|
||||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
|
||||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
|
||||||
/* move the echo request payload (starting after the icmp header)
|
|
||||||
* to the new location in the reply.
|
|
||||||
* The shift is equal to the length of the remaining extension headers present
|
|
||||||
* Note: UIP_ICMP_BUF still points to the echo request at this stage
|
|
||||||
*/
|
|
||||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
|
|
||||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
|
||||||
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
|
|
||||||
}
|
|
||||||
uip_ext_len = temp_ext_len;
|
|
||||||
} else {
|
|
||||||
#endif /* UIP_CONF_IPV6_RPL */
|
|
||||||
/* If there were extension headers*/
|
|
||||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||||
uip_len -= uip_ext_len;
|
uip_len -= uip_ext_len;
|
||||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||||
|
@ -181,10 +158,13 @@ echo_request_input(void)
|
||||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||||
uip_ext_len = 0;
|
uip_ext_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert RPL extension headers */
|
||||||
#if UIP_CONF_IPV6_RPL
|
#if UIP_CONF_IPV6_RPL
|
||||||
}
|
rpl_insert_header();
|
||||||
#endif /* UIP_CONF_IPV6_RPL */
|
#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,37 +312,18 @@ 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())) {
|
|
||||||
/* If there were other extension headers*/
|
|
||||||
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
|
|
||||||
if (uip_ext_len != temp_ext_len) {
|
|
||||||
uip_len -= (uip_ext_len - temp_ext_len);
|
|
||||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
|
||||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
|
||||||
/* move the echo reply payload (starting after the icmp
|
|
||||||
* header) to the new location in the reply. The shift is
|
|
||||||
* equal to the length of the remaining extension headers
|
|
||||||
* present Note: UIP_ICMP_BUF still points to the echo reply
|
|
||||||
* at this stage
|
|
||||||
*/
|
|
||||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
|
|
||||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
|
||||||
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
|
|
||||||
}
|
|
||||||
uip_ext_len = temp_ext_len;
|
|
||||||
uip_len -= uip_ext_len;
|
|
||||||
} else {
|
|
||||||
#endif /* UIP_CONF_IPV6_RPL */
|
|
||||||
/* If there were extension headers*/
|
|
||||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||||
uip_len -= uip_ext_len;
|
uip_len -= uip_ext_len;
|
||||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||||
|
@ -365,9 +337,6 @@ echo_reply_input(void)
|
||||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||||
uip_ext_len = 0;
|
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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -79,6 +79,11 @@
|
||||||
#include "net/ipv6/uip-ds6.h"
|
#include "net/ipv6/uip-ds6.h"
|
||||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||||
|
|
||||||
|
#if UIP_CONF_IPV6_RPL
|
||||||
|
#include "rpl/rpl.h"
|
||||||
|
#include "rpl/rpl-private.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
@ -88,10 +93,6 @@
|
||||||
#define DEBUG DEBUG_NONE
|
#define DEBUG DEBUG_NONE
|
||||||
#include "net/ip/uip-debug.h"
|
#include "net/ip/uip-debug.h"
|
||||||
|
|
||||||
#if UIP_CONF_IPV6_RPL
|
|
||||||
#include "rpl/rpl.h"
|
|
||||||
#endif /* UIP_CONF_IPV6_RPL */
|
|
||||||
|
|
||||||
#if UIP_LOGGING == 1
|
#if UIP_LOGGING == 1
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
void uip_log(char *msg);
|
void uip_log(char *msg);
|
||||||
|
@ -207,11 +208,6 @@ uint8_t uip_flags;
|
||||||
/* uip_conn always points to the current connection (set to NULL for UDP). */
|
/* uip_conn always points to the current connection (set to NULL for UDP). */
|
||||||
struct uip_conn *uip_conn;
|
struct uip_conn *uip_conn;
|
||||||
|
|
||||||
/* Temporary variables. */
|
|
||||||
#if (UIP_TCP || UIP_UDP)
|
|
||||||
static uint8_t c;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UIP_ACTIVE_OPEN || UIP_UDP
|
#if UIP_ACTIVE_OPEN || UIP_UDP
|
||||||
/* Keeps track of the last port used for a new connection. */
|
/* Keeps track of the last port used for a new connection. */
|
||||||
static uint16_t lastport;
|
static uint16_t lastport;
|
||||||
|
@ -256,8 +252,6 @@ static uint8_t iss[4];
|
||||||
|
|
||||||
/* Temporary variables. */
|
/* Temporary variables. */
|
||||||
uint8_t uip_acc32[4];
|
uint8_t uip_acc32[4];
|
||||||
static uint8_t opt;
|
|
||||||
static uint16_t tmp16;
|
|
||||||
#endif /* UIP_TCP */
|
#endif /* UIP_TCP */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
@ -432,6 +426,7 @@ uip_udpchksum(void)
|
||||||
void
|
void
|
||||||
uip_init(void)
|
uip_init(void)
|
||||||
{
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
uip_ds6_init();
|
uip_ds6_init();
|
||||||
uip_icmp6_init();
|
uip_icmp6_init();
|
||||||
|
@ -466,6 +461,7 @@ struct uip_conn *
|
||||||
uip_connect(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
uip_connect(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
||||||
{
|
{
|
||||||
register struct uip_conn *conn, *cconn;
|
register struct uip_conn *conn, *cconn;
|
||||||
|
int c;
|
||||||
|
|
||||||
/* Find an unused local port. */
|
/* Find an unused local port. */
|
||||||
again:
|
again:
|
||||||
|
@ -560,6 +556,7 @@ remove_ext_hdr(void)
|
||||||
struct uip_udp_conn *
|
struct uip_udp_conn *
|
||||||
uip_udp_new(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
uip_udp_new(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
||||||
{
|
{
|
||||||
|
int c;
|
||||||
register struct uip_udp_conn *conn;
|
register struct uip_udp_conn *conn;
|
||||||
|
|
||||||
/* Find an unused local port. */
|
/* Find an unused local port. */
|
||||||
|
@ -605,6 +602,7 @@ uip_udp_new(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
||||||
void
|
void
|
||||||
uip_unlisten(uint16_t port)
|
uip_unlisten(uint16_t port)
|
||||||
{
|
{
|
||||||
|
int c;
|
||||||
for(c = 0; c < UIP_LISTENPORTS; ++c) {
|
for(c = 0; c < UIP_LISTENPORTS; ++c) {
|
||||||
if(uip_listenports[c] == port) {
|
if(uip_listenports[c] == port) {
|
||||||
uip_listenports[c] = 0;
|
uip_listenports[c] = 0;
|
||||||
|
@ -616,6 +614,7 @@ uip_unlisten(uint16_t port)
|
||||||
void
|
void
|
||||||
uip_listen(uint16_t port)
|
uip_listen(uint16_t port)
|
||||||
{
|
{
|
||||||
|
int c;
|
||||||
for(c = 0; c < UIP_LISTENPORTS; ++c) {
|
for(c = 0; c < UIP_LISTENPORTS; ++c) {
|
||||||
if(uip_listenports[c] == 0) {
|
if(uip_listenports[c] == 0) {
|
||||||
uip_listenports[c] = port;
|
uip_listenports[c] = port;
|
||||||
|
@ -891,7 +890,7 @@ ext_hdr_options_process(void)
|
||||||
*/
|
*/
|
||||||
#if UIP_CONF_IPV6_RPL
|
#if UIP_CONF_IPV6_RPL
|
||||||
PRINTF("Processing RPL option\n");
|
PRINTF("Processing RPL option\n");
|
||||||
if(rpl_verify_header(uip_ext_opt_offset)) {
|
if(rpl_verify_hbh_header(uip_ext_opt_offset)) {
|
||||||
PRINTF("RPL Option Error: Dropping Packet\n");
|
PRINTF("RPL Option Error: Dropping Packet\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -941,6 +940,9 @@ void
|
||||||
uip_process(uint8_t flag)
|
uip_process(uint8_t flag)
|
||||||
{
|
{
|
||||||
#if UIP_TCP
|
#if UIP_TCP
|
||||||
|
int c;
|
||||||
|
uint16_t tmp16;
|
||||||
|
uint8_t opt;
|
||||||
register struct uip_conn *uip_connr = uip_conn;
|
register struct uip_conn *uip_connr = uip_conn;
|
||||||
#endif /* UIP_TCP */
|
#endif /* UIP_TCP */
|
||||||
#if UIP_UDP
|
#if UIP_UDP
|
||||||
|
@ -1227,9 +1229,9 @@ uip_process(uint8_t flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UIP_CONF_IPV6_RPL
|
#if UIP_CONF_IPV6_RPL
|
||||||
if(rpl_update_header_empty()) {
|
if(!rpl_update_header()) {
|
||||||
/* Packet can not be forwarded */
|
/* Packet can not be forwarded */
|
||||||
PRINTF("RPL Forward Option Error\n");
|
PRINTF("RPL header update error\n");
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
#endif /* UIP_CONF_IPV6_RPL */
|
#endif /* UIP_CONF_IPV6_RPL */
|
||||||
|
@ -1367,6 +1369,11 @@ uip_process(uint8_t flag)
|
||||||
|
|
||||||
PRINTF("Processing Routing header\n");
|
PRINTF("Processing Routing header\n");
|
||||||
if(UIP_ROUTING_BUF->seg_left > 0) {
|
if(UIP_ROUTING_BUF->seg_left > 0) {
|
||||||
|
#if UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING
|
||||||
|
if(rpl_process_srh_header()) {
|
||||||
|
goto send; /* Proceed to forwarding */
|
||||||
|
}
|
||||||
|
#endif /* UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING */
|
||||||
uip_icmp6_error_output(ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, UIP_IPH_LEN + uip_ext_len + 2);
|
uip_icmp6_error_output(ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, UIP_IPH_LEN + uip_ext_len + 2);
|
||||||
UIP_STAT(++uip_stat.ip.drop);
|
UIP_STAT(++uip_stat.ip.drop);
|
||||||
UIP_LOG("ip6: unrecognized routing type");
|
UIP_LOG("ip6: unrecognized routing type");
|
||||||
|
@ -1473,6 +1480,7 @@ uip_process(uint8_t flag)
|
||||||
udp_input:
|
udp_input:
|
||||||
|
|
||||||
remove_ext_hdr();
|
remove_ext_hdr();
|
||||||
|
UIP_IP_BUF->proto = UIP_PROTO_UDP;
|
||||||
|
|
||||||
PRINTF("Receiving UDP packet\n");
|
PRINTF("Receiving UDP packet\n");
|
||||||
|
|
||||||
|
@ -1481,8 +1489,6 @@ uip_process(uint8_t flag)
|
||||||
work. If the application sets uip_slen, it has a packet to
|
work. If the application sets uip_slen, it has a packet to
|
||||||
send. */
|
send. */
|
||||||
#if UIP_UDP_CHECKSUMS
|
#if UIP_UDP_CHECKSUMS
|
||||||
uip_len = uip_len - UIP_IPUDPH_LEN;
|
|
||||||
uip_appdata = &uip_buf[UIP_IPUDPH_LEN + UIP_LLH_LEN];
|
|
||||||
/* XXX hack: UDP/IPv6 receivers should drop packets with UDP
|
/* XXX hack: UDP/IPv6 receivers should drop packets with UDP
|
||||||
checksum 0. Here, we explicitly receive UDP packets with checksum
|
checksum 0. Here, we explicitly receive UDP packets with checksum
|
||||||
0. This is to be able to debug code that for one reason or
|
0. This is to be able to debug code that for one reason or
|
||||||
|
@ -1495,8 +1501,6 @@ uip_process(uint8_t flag)
|
||||||
uip_udpchksum());
|
uip_udpchksum());
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
#else /* UIP_UDP_CHECKSUMS */
|
|
||||||
uip_len = uip_len - UIP_IPUDPH_LEN;
|
|
||||||
#endif /* UIP_UDP_CHECKSUMS */
|
#endif /* UIP_UDP_CHECKSUMS */
|
||||||
|
|
||||||
/* Make sure that the UDP destination port number is not zero. */
|
/* Make sure that the UDP destination port number is not zero. */
|
||||||
|
@ -1528,17 +1532,15 @@ uip_process(uint8_t flag)
|
||||||
PRINTF("udp: no matching connection found\n");
|
PRINTF("udp: no matching connection found\n");
|
||||||
UIP_STAT(++uip_stat.udp.drop);
|
UIP_STAT(++uip_stat.udp.drop);
|
||||||
|
|
||||||
#if UIP_UDP_SEND_UNREACH_NOPORT
|
|
||||||
uip_icmp6_error_output(ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
|
uip_icmp6_error_output(ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
|
||||||
goto send;
|
goto send;
|
||||||
#else
|
|
||||||
goto drop;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
udp_found:
|
udp_found:
|
||||||
PRINTF("In udp_found\n");
|
PRINTF("In udp_found\n");
|
||||||
UIP_STAT(++uip_stat.udp.recv);
|
UIP_STAT(++uip_stat.udp.recv);
|
||||||
|
|
||||||
|
uip_len = uip_len - UIP_IPUDPH_LEN;
|
||||||
|
uip_appdata = &uip_buf[UIP_IPUDPH_LEN + UIP_LLH_LEN];
|
||||||
uip_conn = NULL;
|
uip_conn = NULL;
|
||||||
uip_flags = UIP_NEWDATA;
|
uip_flags = UIP_NEWDATA;
|
||||||
uip_sappdata = uip_appdata = &uip_buf[UIP_IPUDPH_LEN + UIP_LLH_LEN];
|
uip_sappdata = uip_appdata = &uip_buf[UIP_IPUDPH_LEN + UIP_LLH_LEN];
|
||||||
|
@ -1593,6 +1595,7 @@ uip_process(uint8_t flag)
|
||||||
tcp_input:
|
tcp_input:
|
||||||
|
|
||||||
remove_ext_hdr();
|
remove_ext_hdr();
|
||||||
|
UIP_IP_BUF->proto = UIP_PROTO_TCP;
|
||||||
|
|
||||||
UIP_STAT(++uip_stat.tcp.recv);
|
UIP_STAT(++uip_stat.tcp.recv);
|
||||||
PRINTF("Receiving TCP packet\n");
|
PRINTF("Receiving TCP packet\n");
|
||||||
|
|
211
core/net/link-stats.c
Normal file
211
core/net/link-stats.c
Normal 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
65
core/net/link-stats.h
Normal 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
5
core/net/llsec/README.md
Normal 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
|
|
@ -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 */
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
21
core/net/llsec/noncoresec/README.md
Normal file
21
core/net/llsec/noncoresec/README.md
Normal 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 }
|
||||||
|
```
|
|
@ -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, LLSEC802154_SECURITY_LEVEL);
|
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, SEC_LVL);
|
||||||
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 */
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
|
@ -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));
|
||||||
if(metadata != NULL) {
|
return;
|
||||||
sent = metadata->sent;
|
} else if(q->ptr == NULL) {
|
||||||
cptr = metadata->cptr;
|
PRINTF("csma: no metadata\n");
|
||||||
num_tx = n->transmissions;
|
return;
|
||||||
if(status == MAC_TX_COLLISION ||
|
}
|
||||||
status == MAC_TX_NOACK) {
|
|
||||||
|
|
||||||
/* If the transmission was not performed because of a
|
|
||||||
collision or noack, we must retransmit the packet. */
|
|
||||||
|
|
||||||
switch(status) {
|
switch(status) {
|
||||||
case MAC_TX_COLLISION:
|
case MAC_TX_OK:
|
||||||
PRINTF("csma: rexmit collision %d\n", n->transmissions);
|
tx_ok(q, n, num_transmissions);
|
||||||
break;
|
break;
|
||||||
case MAC_TX_NOACK:
|
case MAC_TX_NOACK:
|
||||||
PRINTF("csma: rexmit noack %d\n", n->transmissions);
|
noack(q, n, num_transmissions);
|
||||||
|
break;
|
||||||
|
case MAC_TX_COLLISION:
|
||||||
|
collision(q, n, num_transmissions);
|
||||||
|
break;
|
||||||
|
case MAC_TX_DEFERRED:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PRINTF("csma: rexmit err %d, %d\n", status, n->transmissions);
|
tx_done(status, q, n);
|
||||||
}
|
break;
|
||||||
|
|
||||||
/* 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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__ */
|
||||||
|
|
|
@ -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];
|
||||||
|
if(log->link == NULL) {
|
||||||
|
printf("TSCH: {asn-%x.%lx link-NULL} ", log->asn.ms1b, log->asn.ls4b);
|
||||||
|
} else {
|
||||||
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
|
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
|
||||||
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
|
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
|
||||||
log->asn.ms1b, log->asn.ls4b,
|
log->asn.ms1b, log->asn.ls4b,
|
||||||
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
|
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));
|
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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 *********/
|
||||||
|
|
||||||
|
|
|
@ -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 *
|
||||||
|
|
|
@ -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? */
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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, ¤t_neighbor->addr, ¤t_asn)) {
|
&frame, ¤t_neighbor->addr, ¤t_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, ¤t_asn);
|
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, ¤t_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, ¤t_neighbor);
|
current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_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,12 +952,13 @@ 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, ¤t_neighbor);
|
current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor);
|
||||||
}
|
}
|
||||||
|
is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX);
|
||||||
|
if(is_active_slot) {
|
||||||
/* Hop channel */
|
/* Hop channel */
|
||||||
current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset);
|
current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset);
|
||||||
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
|
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
|
||||||
/* Reset drift correction */
|
/* Turn the radio on already here if configured so; necessary for radios with slow startup */
|
||||||
drift_correction = 0;
|
tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
|
||||||
is_drift_correction_used = 0;
|
|
||||||
/* Decide whether it is a TX/RX/IDLE or OFF slot */
|
/* Decide whether it is a TX/RX/IDLE or OFF slot */
|
||||||
/* Actual slot operation */
|
/* Actual slot operation */
|
||||||
if(current_packet != NULL) {
|
if(current_packet != NULL) {
|
||||||
|
@ -883,11 +969,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
|
||||||
**/
|
**/
|
||||||
static struct pt slot_tx_pt;
|
static struct pt slot_tx_pt;
|
||||||
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
|
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
|
||||||
} else if((current_link->link_options & LINK_OPTION_RX)) {
|
} else {
|
||||||
/* Listen */
|
/* Listen */
|
||||||
static struct pt slot_rx_pt;
|
static struct pt slot_rx_pt;
|
||||||
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
|
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
TSCH_DEBUG_SLOT_END();
|
TSCH_DEBUG_SLOT_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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, ¤t_asn)) {
|
&frame, (linkaddr_t*)&frame.src_addr, ¤t_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) {
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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,7 +207,32 @@ 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 {
|
||||||
|
#ifdef NBR_TABLE_FIND_REMOVABLE
|
||||||
|
const linkaddr_t *lladdr;
|
||||||
|
lladdr = NBR_TABLE_FIND_REMOVABLE(reason, data);
|
||||||
|
if(lladdr == NULL) {
|
||||||
|
/* Nothing found that can be deleted - return NULL to indicate failure */
|
||||||
|
PRINTF("*** Not removing entry to allocate new\n");
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
/* used least_used_key to indicate what is the least useful entry */
|
||||||
|
int index;
|
||||||
|
int locked = 0;
|
||||||
|
if((index = index_from_lladdr(lladdr)) != -1) {
|
||||||
|
least_used_key = key_from_index(index);
|
||||||
|
locked = locked_map[index];
|
||||||
|
}
|
||||||
|
/* Allow delete of locked item? */
|
||||||
|
if(least_used_key != NULL && locked) {
|
||||||
|
PRINTF("Deleting locked item!\n");
|
||||||
|
locked_map[index] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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:
|
* The replacement policy is the following: remove neighbor that is:
|
||||||
* (1) not locked
|
* (1) not locked
|
||||||
* (2) used by fewest tables
|
* (2) used by fewest tables
|
||||||
|
@ -212,26 +265,14 @@ nbr_table_allocate(void)
|
||||||
}
|
}
|
||||||
key = list_item_next(key);
|
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
|
||||||
|
|
|
@ -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
65
core/net/net-debug.c
Normal 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
86
core/net/net-debug.h
Normal 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 */
|
|
@ -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;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
void
|
/* shift data to the right */
|
||||||
packetbuf_hdr_remove(int size)
|
for(i = packetbuf_totlen() - 1; i >= 0; i--) {
|
||||||
{
|
packetbuf[i + size] = packetbuf[i];
|
||||||
hdrptr += size;
|
}
|
||||||
|
hdrlen += 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
5
core/net/rime/chameleon-raw.c
Normal file → Executable 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;
|
||||||
|
|
|
@ -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
|
||||||
|
@ -253,23 +287,12 @@
|
||||||
#else
|
#else
|
||||||
#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 (10L * 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 +315,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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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 */
|
||||||
|
if(RPL_IS_STORING(dag->instance)) {
|
||||||
rpl_remove_routes(dag);
|
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;
|
||||||
|
|
|
@ -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. */
|
||||||
|
if(RPL_IS_STORING(instance)) {
|
||||||
rpl_remove_routes(instance->current_dag);
|
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. */
|
||||||
|
if(RPL_IS_STORING(dag->instance)) {
|
||||||
rpl_remove_routes(dag);
|
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. */
|
||||||
|
if(RPL_IS_STORING(instance)) {
|
||||||
rpl_remove_routes(instance->current_dag);
|
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. */
|
/* The DAO parent set changed - schedule a DAO transmission. */
|
||||||
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
|
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
|
||||||
rpl_schedule_dao(instance);
|
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);
|
|
||||||
while(p != NULL) {
|
|
||||||
if(p->dag != dag || p->rank == INFINITE_RANK) {
|
|
||||||
/* ignore this neighbor */
|
|
||||||
} else if(best == NULL) {
|
|
||||||
best = p;
|
|
||||||
} else {
|
|
||||||
best = dag->instance->of->best_parent(best, p);
|
|
||||||
}
|
}
|
||||||
p = nbr_table_next(rpl_parents, p);
|
|
||||||
|
of = dag->instance->of;
|
||||||
|
/* Search for the best parent according to the OF */
|
||||||
|
for(p = nbr_table_head(rpl_parents); p != NULL; p = nbr_table_next(rpl_parents, p)) {
|
||||||
|
|
||||||
|
/* Exclude parents from other DAGs or announcing an infinite rank */
|
||||||
|
if(p->dag != dag || p->rank == INFINITE_RANK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
#if RPL_WITH_PROBING
|
||||||
|
if(rpl_parent_is_fresh(best)) {
|
||||||
rpl_set_preferred_parent(dag, best);
|
rpl_set_preferred_parent(dag, best);
|
||||||
dag->rank = dag->instance->of->calculate_rank(dag->preferred_parent, 0);
|
|
||||||
} else {
|
} else {
|
||||||
dag->rank = INFINITE_RANK;
|
/* 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 {
|
||||||
|
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) {
|
||||||
|
if(RPL_IS_STORING(dag->instance)) {
|
||||||
dao_output(parent, RPL_ZERO_LIFETIME);
|
dao_output(parent, RPL_ZERO_LIFETIME);
|
||||||
|
}
|
||||||
rpl_set_preferred_parent(dag, NULL);
|
rpl_set_preferred_parent(dag, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -887,9 +988,11 @@ 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) {
|
||||||
|
if(RPL_IS_STORING(dag_src->instance)) {
|
||||||
/* Remove uIPv6 routes that have this parent as the next hop. */
|
/* Remove uIPv6 routes that have this parent as the next hop. */
|
||||||
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
|
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PRINTF("RPL: Moving parent ");
|
PRINTF("RPL: Moving parent ");
|
||||||
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
|
PRINT6ADDR(rpl_get_parent_ipaddr(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,11 +1389,13 @@ 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) {
|
||||||
|
/* No suitable parent anymore; trigger a local repair. */
|
||||||
PRINTF("RPL: No parents found in any DAG\n");
|
PRINTF("RPL: No parents found in any DAG\n");
|
||||||
rpl_local_repair(instance);
|
rpl_local_repair(instance);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if(DAG_RANK(old_rank, instance) != DAG_RANK(instance->current_dag->rank, instance)) {
|
if(DAG_RANK(old_rank, instance) != DAG_RANK(instance->current_dag->rank, instance)) {
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
if(add_nbr_from_dio(from, dio)) {
|
||||||
rpl_join_instance(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;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "net/ip/tcpip.h"
|
#include "net/ip/tcpip.h"
|
||||||
#include "net/ipv6/uip-ds6.h"
|
#include "net/ipv6/uip-ds6.h"
|
||||||
#include "net/rpl/rpl-private.h"
|
#include "net/rpl/rpl-private.h"
|
||||||
|
#include "net/rpl/rpl-ns.h"
|
||||||
#include "net/packetbuf.h"
|
#include "net/packetbuf.h"
|
||||||
|
|
||||||
#define DEBUG DEBUG_NONE
|
#define DEBUG DEBUG_NONE
|
||||||
|
@ -61,12 +62,14 @@
|
||||||
#define UIP_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
#define UIP_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
||||||
#define UIP_HBHO_BUF ((struct uip_hbho_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
#define UIP_HBHO_BUF ((struct uip_hbho_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
||||||
#define UIP_HBHO_NEXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_HOP_BY_HOP_LEN])
|
#define UIP_HBHO_NEXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_HOP_BY_HOP_LEN])
|
||||||
|
#define UIP_RH_BUF ((struct uip_routing_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
||||||
|
#define UIP_RPL_SRH_BUF ((struct uip_rpl_srh_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_RH_LEN])
|
||||||
#define UIP_EXT_HDR_OPT_BUF ((struct uip_ext_hdr_opt *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
#define UIP_EXT_HDR_OPT_BUF ((struct uip_ext_hdr_opt *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
||||||
#define UIP_EXT_HDR_OPT_PADN_BUF ((struct uip_ext_hdr_opt_padn *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
#define UIP_EXT_HDR_OPT_PADN_BUF ((struct uip_ext_hdr_opt_padn *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
||||||
#define UIP_EXT_HDR_OPT_RPL_BUF ((struct uip_ext_hdr_opt_rpl *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
#define UIP_EXT_HDR_OPT_RPL_BUF ((struct uip_ext_hdr_opt_rpl *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
int
|
int
|
||||||
rpl_verify_header(int uip_ext_opt_offset)
|
rpl_verify_hbh_header(int uip_ext_opt_offset)
|
||||||
{
|
{
|
||||||
rpl_instance_t *instance;
|
rpl_instance_t *instance;
|
||||||
int down;
|
int down;
|
||||||
|
@ -75,7 +78,7 @@ rpl_verify_header(int uip_ext_opt_offset)
|
||||||
uip_ds6_route_t *route;
|
uip_ds6_route_t *route;
|
||||||
rpl_parent_t *sender = NULL;
|
rpl_parent_t *sender = NULL;
|
||||||
|
|
||||||
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
|
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
|
||||||
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
|
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -103,10 +106,12 @@ rpl_verify_header(int uip_ext_opt_offset)
|
||||||
the packet to be forwareded in the first place. We drop any
|
the packet to be forwareded in the first place. We drop any
|
||||||
routes that go through the neighbor that sent the packet to
|
routes that go through the neighbor that sent the packet to
|
||||||
us. */
|
us. */
|
||||||
|
if(RPL_IS_STORING(instance)) {
|
||||||
route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
|
route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
|
||||||
if(route != NULL) {
|
if(route != NULL) {
|
||||||
uip_ds6_route_rm(route);
|
uip_ds6_route_rm(route);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RPL_STAT(rpl_stats.forward_errors++);
|
RPL_STAT(rpl_stats.forward_errors++);
|
||||||
/* Trigger DAO retransmission */
|
/* Trigger DAO retransmission */
|
||||||
rpl_reset_dio_timer(instance);
|
rpl_reset_dio_timer(instance);
|
||||||
|
@ -125,6 +130,20 @@ rpl_verify_header(int uip_ext_opt_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
sender_rank = UIP_HTONS(UIP_EXT_HDR_OPT_RPL_BUF->senderrank);
|
sender_rank = UIP_HTONS(UIP_EXT_HDR_OPT_RPL_BUF->senderrank);
|
||||||
|
sender = nbr_table_get_from_lladdr(rpl_parents, packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||||
|
|
||||||
|
if(sender != NULL && (UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_RANK_ERR)) {
|
||||||
|
/* A rank error was signalled, attempt to repair it by updating
|
||||||
|
* the sender's rank from ext header */
|
||||||
|
sender->rank = sender_rank;
|
||||||
|
if(RPL_IS_NON_STORING(instance)) {
|
||||||
|
/* Select DAG and preferred parent only in non-storing mode. In storing mode,
|
||||||
|
* a parent switch would result in an immediate No-path DAO transmission, dropping
|
||||||
|
* current incoming packet. */
|
||||||
|
rpl_select_dag(instance, sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sender_closer = sender_rank < instance->current_dag->rank;
|
sender_closer = sender_rank < instance->current_dag->rank;
|
||||||
|
|
||||||
PRINTF("RPL: Packet going %s, sender closer %d (%d < %d)\n", down == 1 ? "down" : "up",
|
PRINTF("RPL: Packet going %s, sender closer %d (%d < %d)\n", down == 1 ? "down" : "up",
|
||||||
|
@ -133,15 +152,6 @@ rpl_verify_header(int uip_ext_opt_offset)
|
||||||
instance->current_dag->rank
|
instance->current_dag->rank
|
||||||
);
|
);
|
||||||
|
|
||||||
sender = nbr_table_get_from_lladdr(rpl_parents, packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
|
||||||
|
|
||||||
if(sender != NULL && (UIP_EXT_HDR_OPT_RPL_BUF->flags & RPL_HDR_OPT_RANK_ERR)) {
|
|
||||||
/* A rank error was signalled, attempt to repair it by updating
|
|
||||||
* the sender's rank from ext header */
|
|
||||||
sender->rank = sender_rank;
|
|
||||||
rpl_select_dag(instance, sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
if((down && !sender_closer) || (!down && sender_closer)) {
|
if((down && !sender_closer) || (!down && sender_closer)) {
|
||||||
PRINTF("RPL: Loop detected - senderrank: %d my-rank: %d sender_closer: %d\n",
|
PRINTF("RPL: Loop detected - senderrank: %d my-rank: %d sender_closer: %d\n",
|
||||||
sender_rank, instance->current_dag->rank,
|
sender_rank, instance->current_dag->rank,
|
||||||
|
@ -166,35 +176,308 @@ rpl_verify_header(int uip_ext_opt_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
PRINTF("RPL: Rank OK\n");
|
PRINTF("RPL: Rank OK\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
#if RPL_WITH_NON_STORING
|
||||||
set_rpl_opt(unsigned uip_ext_opt_offset)
|
int
|
||||||
|
rpl_srh_get_next_hop(uip_ipaddr_t *ipaddr)
|
||||||
{
|
{
|
||||||
uint8_t temp_len;
|
uint8_t *uip_next_hdr;
|
||||||
|
int last_uip_ext_len = uip_ext_len;
|
||||||
|
rpl_dag_t *dag;
|
||||||
|
rpl_ns_node_t *dest_node;
|
||||||
|
rpl_ns_node_t *root_node;
|
||||||
|
|
||||||
memmove(UIP_HBHO_NEXT_BUF, UIP_EXT_BUF, uip_len - UIP_IPH_LEN);
|
uip_ext_len = 0;
|
||||||
memset(UIP_HBHO_BUF, 0, RPL_HOP_BY_HOP_LEN);
|
uip_next_hdr = &UIP_IP_BUF->proto;
|
||||||
UIP_HBHO_BUF->next = UIP_IP_BUF->proto;
|
|
||||||
UIP_IP_BUF->proto = UIP_PROTO_HBHO;
|
/* Look for routing header */
|
||||||
UIP_HBHO_BUF->len = RPL_HOP_BY_HOP_LEN - 8;
|
while(uip_next_hdr != NULL && *uip_next_hdr != UIP_PROTO_ROUTING) {
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->opt_type = UIP_EXT_HDR_OPT_RPL;
|
switch(*uip_next_hdr) {
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->opt_len = RPL_HDR_OPT_LEN;
|
case UIP_PROTO_TCP:
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->flags = 0;
|
case UIP_PROTO_UDP:
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->instance = 0;
|
case UIP_PROTO_ICMP6:
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = 0;
|
case UIP_PROTO_NONE:
|
||||||
uip_len += RPL_HOP_BY_HOP_LEN;
|
uip_next_hdr = NULL;
|
||||||
temp_len = UIP_IP_BUF->len[1];
|
break;
|
||||||
UIP_IP_BUF->len[1] += UIP_HBHO_BUF->len + 8;
|
case UIP_PROTO_HBHO:
|
||||||
if(UIP_IP_BUF->len[1] < temp_len) {
|
case UIP_PROTO_DESTO:
|
||||||
UIP_IP_BUF->len[0]++;
|
case UIP_PROTO_FRAG:
|
||||||
|
/* Move to next header */
|
||||||
|
if(uip_next_hdr != &UIP_IP_BUF->proto) {
|
||||||
|
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
|
||||||
}
|
}
|
||||||
|
uip_next_hdr = &UIP_EXT_BUF->next;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dag = rpl_get_dag(&UIP_IP_BUF->destipaddr);
|
||||||
|
root_node = rpl_ns_get_node(dag, &dag->dag_id);
|
||||||
|
dest_node = rpl_ns_get_node(dag, &UIP_IP_BUF->destipaddr);
|
||||||
|
|
||||||
|
if((uip_next_hdr != NULL && *uip_next_hdr == UIP_PROTO_ROUTING
|
||||||
|
&& UIP_RH_BUF->routing_type == RPL_RH_TYPE_SRH) ||
|
||||||
|
(dest_node != NULL && root_node != NULL &&
|
||||||
|
dest_node->parent == root_node)) {
|
||||||
|
/* Routing header found or the packet destined for a direct child of the root.
|
||||||
|
* The next hop should be already copied as the IPv6 destination
|
||||||
|
* address, via rpl_process_srh_header. We turn this address into a link-local to enable
|
||||||
|
* forwarding to next hop */
|
||||||
|
uip_ipaddr_copy(ipaddr, &UIP_IP_BUF->destipaddr);
|
||||||
|
uip_create_linklocal_prefix(ipaddr);
|
||||||
|
uip_ext_len = last_uip_ext_len;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uip_ext_len = last_uip_ext_len;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
int
|
int
|
||||||
rpl_update_header_empty(void)
|
rpl_process_srh_header(void)
|
||||||
|
{
|
||||||
|
uint8_t *uip_next_hdr;
|
||||||
|
int last_uip_ext_len = uip_ext_len;
|
||||||
|
|
||||||
|
uip_ext_len = 0;
|
||||||
|
uip_next_hdr = &UIP_IP_BUF->proto;
|
||||||
|
|
||||||
|
/* Look for routing header */
|
||||||
|
while(uip_next_hdr != NULL && *uip_next_hdr != UIP_PROTO_ROUTING) {
|
||||||
|
switch(*uip_next_hdr) {
|
||||||
|
case UIP_PROTO_TCP:
|
||||||
|
case UIP_PROTO_UDP:
|
||||||
|
case UIP_PROTO_ICMP6:
|
||||||
|
case UIP_PROTO_NONE:
|
||||||
|
uip_next_hdr = NULL;
|
||||||
|
break;
|
||||||
|
case UIP_PROTO_HBHO:
|
||||||
|
case UIP_PROTO_DESTO:
|
||||||
|
case UIP_PROTO_FRAG:
|
||||||
|
/* Move to next header */
|
||||||
|
if(uip_next_hdr != &UIP_IP_BUF->proto) {
|
||||||
|
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
|
||||||
|
}
|
||||||
|
uip_next_hdr = &UIP_EXT_BUF->next;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(uip_next_hdr != NULL && *uip_next_hdr == UIP_PROTO_ROUTING
|
||||||
|
&& UIP_RH_BUF->routing_type == RPL_RH_TYPE_SRH) {
|
||||||
|
/* SRH found, now look for next hop */
|
||||||
|
uint8_t cmpri, cmpre;
|
||||||
|
uint8_t ext_len;
|
||||||
|
uint8_t padding;
|
||||||
|
uint8_t path_len;
|
||||||
|
uint8_t segments_left;
|
||||||
|
uip_ipaddr_t current_dest_addr;
|
||||||
|
|
||||||
|
segments_left = UIP_RH_BUF->seg_left;
|
||||||
|
ext_len = (UIP_RH_BUF->len * 8) + 8;
|
||||||
|
cmpri = UIP_RPL_SRH_BUF->cmpr >> 4;
|
||||||
|
cmpre = UIP_RPL_SRH_BUF->cmpr & 0x0f;
|
||||||
|
padding = UIP_RPL_SRH_BUF->pad >> 4;
|
||||||
|
path_len = ((ext_len - padding - RPL_RH_LEN - RPL_SRH_LEN - (16 - cmpre)) / (16 - cmpri)) + 1;
|
||||||
|
(void)path_len;
|
||||||
|
|
||||||
|
PRINTF("RPL: read SRH, path len %u, segments left %u, Cmpri %u, Cmpre %u, ext len %u (padding %u)\n",
|
||||||
|
path_len, segments_left, cmpri, cmpre, ext_len, padding);
|
||||||
|
|
||||||
|
if(segments_left == 0) {
|
||||||
|
/* We are the final destination, do nothing */
|
||||||
|
} else {
|
||||||
|
uint8_t i = path_len - segments_left; /* The index of the next address to be visited */
|
||||||
|
uint8_t *addr_ptr = ((uint8_t *)UIP_RH_BUF) + RPL_RH_LEN + RPL_SRH_LEN + (i * (16 - cmpri));
|
||||||
|
uint8_t cmpr = segments_left == 1 ? cmpre : cmpri;
|
||||||
|
|
||||||
|
/* As per RFC6554: swap the IPv6 destination address and address[i] */
|
||||||
|
|
||||||
|
/* First, copy the current IPv6 destination address */
|
||||||
|
uip_ipaddr_copy(¤t_dest_addr, &UIP_IP_BUF->destipaddr);
|
||||||
|
/* Second, update the IPv6 destination address with addresses[i] */
|
||||||
|
memcpy(((uint8_t *)&UIP_IP_BUF->destipaddr) + cmpr, addr_ptr, 16 - cmpr);
|
||||||
|
/* Third, write current_dest_addr to addresses[i] */
|
||||||
|
memcpy(addr_ptr, ((uint8_t *)¤t_dest_addr) + cmpr, 16 - cmpr);
|
||||||
|
|
||||||
|
/* Update segments left field */
|
||||||
|
UIP_RH_BUF->seg_left--;
|
||||||
|
|
||||||
|
PRINTF("RPL: SRH next hop ");
|
||||||
|
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
|
||||||
|
PRINTF("\n");
|
||||||
|
}
|
||||||
|
uip_ext_len = last_uip_ext_len;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uip_ext_len = last_uip_ext_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
count_matching_bytes(const void *p1, const void *p2, size_t n)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for(i = 0; i < n; i++) {
|
||||||
|
if(((uint8_t *)p1)[i] != ((uint8_t *)p2)[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
insert_srh_header(void)
|
||||||
|
{
|
||||||
|
/* Implementation of RFC6554 */
|
||||||
|
uint8_t temp_len;
|
||||||
|
uint8_t path_len;
|
||||||
|
uint8_t ext_len;
|
||||||
|
uint8_t cmpri, cmpre; /* ComprI and ComprE fields of the RPL Source Routing Header */
|
||||||
|
uint8_t *hop_ptr;
|
||||||
|
uint8_t padding;
|
||||||
|
rpl_ns_node_t *dest_node;
|
||||||
|
rpl_ns_node_t *root_node;
|
||||||
|
rpl_ns_node_t *node;
|
||||||
|
rpl_dag_t *dag;
|
||||||
|
uip_ipaddr_t node_addr;
|
||||||
|
|
||||||
|
PRINTF("RPL: SRH creating source routing header with destination ");
|
||||||
|
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
|
||||||
|
PRINTF(" \n");
|
||||||
|
|
||||||
|
/* Construct source route. We do not do this recursively to keep the runtime stack usage constant. */
|
||||||
|
|
||||||
|
/* Get link of the destination and root */
|
||||||
|
dag = rpl_get_dag(&UIP_IP_BUF->destipaddr);
|
||||||
|
|
||||||
|
if(dag == NULL) {
|
||||||
|
PRINTF("RPL: SRH DAG not found\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_node = rpl_ns_get_node(dag, &UIP_IP_BUF->destipaddr);
|
||||||
|
if(dest_node == NULL) {
|
||||||
|
/* The destination is not found, skip SRH insertion */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
root_node = rpl_ns_get_node(dag, &dag->dag_id);
|
||||||
|
if(root_node == NULL) {
|
||||||
|
PRINTF("RPL: SRH root node not found\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rpl_ns_is_node_reachable(dag, &UIP_IP_BUF->destipaddr)) {
|
||||||
|
PRINTF("RPL: SRH no path found to destination\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute path length and compression factors (we use cmpri == cmpre) */
|
||||||
|
path_len = 0;
|
||||||
|
node = dest_node->parent;
|
||||||
|
/* For simplicity, we use cmpri = cmpre */
|
||||||
|
cmpri = 15;
|
||||||
|
cmpre = 15;
|
||||||
|
|
||||||
|
if(node == root_node) {
|
||||||
|
PRINTF("RPL: SRH no need to insert SRH\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(node != NULL && node != root_node) {
|
||||||
|
|
||||||
|
rpl_ns_get_node_global_addr(&node_addr, node);
|
||||||
|
|
||||||
|
/* How many bytes in common between all nodes in the path? */
|
||||||
|
cmpri = MIN(cmpri, count_matching_bytes(&node_addr, &UIP_IP_BUF->destipaddr, 16));
|
||||||
|
cmpre = cmpri;
|
||||||
|
|
||||||
|
PRINTF("RPL: SRH Hop ");
|
||||||
|
PRINT6ADDR(&node_addr);
|
||||||
|
PRINTF("\n");
|
||||||
|
node = node->parent;
|
||||||
|
path_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extension header length: fixed headers + (n-1) * (16-ComprI) + (16-ComprE)*/
|
||||||
|
ext_len = RPL_RH_LEN + RPL_SRH_LEN
|
||||||
|
+ (path_len - 1) * (16 - cmpre)
|
||||||
|
+ (16 - cmpri);
|
||||||
|
|
||||||
|
padding = ext_len % 8 == 0 ? 0 : (8 - (ext_len % 8));
|
||||||
|
ext_len += padding;
|
||||||
|
|
||||||
|
PRINTF("RPL: SRH Path len: %u, ComprI %u, ComprE %u, ext len %u (padding %u)\n",
|
||||||
|
path_len, cmpri, cmpre, ext_len, padding);
|
||||||
|
|
||||||
|
/* Check if there is enough space to store the extension header */
|
||||||
|
if(uip_len + ext_len > UIP_BUFSIZE) {
|
||||||
|
PRINTF("RPL: Packet too long: impossible to add source routing header (%u bytes)\n", ext_len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move existing ext headers and payload uip_ext_len further */
|
||||||
|
memmove(uip_buf + uip_l2_l3_hdr_len + ext_len,
|
||||||
|
uip_buf + uip_l2_l3_hdr_len, uip_len - UIP_IPH_LEN);
|
||||||
|
memset(uip_buf + uip_l2_l3_hdr_len, 0, ext_len);
|
||||||
|
|
||||||
|
/* Insert source routing header */
|
||||||
|
UIP_RH_BUF->next = UIP_IP_BUF->proto;
|
||||||
|
UIP_IP_BUF->proto = UIP_PROTO_ROUTING;
|
||||||
|
|
||||||
|
/* Initialize IPv6 Routing Header */
|
||||||
|
UIP_RH_BUF->len = (ext_len - 8) / 8;
|
||||||
|
UIP_RH_BUF->routing_type = RPL_RH_TYPE_SRH;
|
||||||
|
UIP_RH_BUF->seg_left = path_len;
|
||||||
|
|
||||||
|
/* Initialize RPL Source Routing Header */
|
||||||
|
UIP_RPL_SRH_BUF->cmpr = (cmpri << 4) + cmpre;
|
||||||
|
UIP_RPL_SRH_BUF->pad = padding << 4;
|
||||||
|
|
||||||
|
/* Initialize addresses field (the actual source route).
|
||||||
|
* From last to first. */
|
||||||
|
node = dest_node;
|
||||||
|
hop_ptr = ((uint8_t *)UIP_RH_BUF) + ext_len - padding; /* Pointer where to write the next hop compressed address */
|
||||||
|
|
||||||
|
while(node != NULL && node->parent != root_node) {
|
||||||
|
rpl_ns_get_node_global_addr(&node_addr, node);
|
||||||
|
|
||||||
|
hop_ptr -= (16 - cmpri);
|
||||||
|
memcpy(hop_ptr, ((uint8_t*)&node_addr) + cmpri, 16 - cmpri);
|
||||||
|
|
||||||
|
node = node->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The next hop (i.e. node whose parent is the root) is placed as the current IPv6 destination */
|
||||||
|
rpl_ns_get_node_global_addr(&node_addr, node);
|
||||||
|
uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &node_addr);
|
||||||
|
|
||||||
|
/* In-place update of IPv6 length field */
|
||||||
|
temp_len = UIP_IP_BUF->len[1];
|
||||||
|
UIP_IP_BUF->len[1] += ext_len;
|
||||||
|
if(UIP_IP_BUF->len[1] < temp_len) {
|
||||||
|
UIP_IP_BUF->len[0]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uip_ext_len += ext_len;
|
||||||
|
uip_len += ext_len;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#else /* RPL_WITH_NON_STORING */
|
||||||
|
int insert_srh_header(void);
|
||||||
|
#endif /* RPL_WITH_NON_STORING */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
update_hbh_header(void)
|
||||||
{
|
{
|
||||||
rpl_instance_t *instance;
|
rpl_instance_t *instance;
|
||||||
int uip_ext_opt_offset;
|
int uip_ext_opt_offset;
|
||||||
|
@ -209,39 +492,30 @@ rpl_update_header_empty(void)
|
||||||
|
|
||||||
switch(UIP_IP_BUF->proto) {
|
switch(UIP_IP_BUF->proto) {
|
||||||
case UIP_PROTO_HBHO:
|
case UIP_PROTO_HBHO:
|
||||||
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
|
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
|
||||||
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
|
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
|
||||||
uip_ext_len = last_uip_ext_len;
|
uip_ext_len = last_uip_ext_len;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_type != UIP_EXT_HDR_OPT_RPL) {
|
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_type != UIP_EXT_HDR_OPT_RPL) {
|
||||||
PRINTF("RPL: Non RPL Hop-by-hop option support not implemented\n");
|
PRINTF("RPL: Non RPL Hop-by-hop option support not implemented\n");
|
||||||
uip_ext_len = last_uip_ext_len;
|
uip_ext_len = last_uip_ext_len;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_len != RPL_HDR_OPT_LEN) {
|
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_len != RPL_HDR_OPT_LEN) {
|
||||||
PRINTF("RPL: RPL Hop-by-hop option has wrong length\n");
|
PRINTF("RPL: RPL Hop-by-hop option has wrong length\n");
|
||||||
uip_ext_len = last_uip_ext_len;
|
uip_ext_len = last_uip_ext_len;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
instance = rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance);
|
instance = rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance);
|
||||||
if(instance == NULL || !instance->used || !instance->current_dag->joined) {
|
if(instance == NULL || !instance->used || !instance->current_dag->joined) {
|
||||||
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect instance\n");
|
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect instance\n");
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
#if RPL_INSERT_HBH_OPTION
|
PRINTF("RPL: No hop-by-hop option found\n");
|
||||||
PRINTF("RPL: No hop-by-hop option found, creating it\n");
|
return 1;
|
||||||
if(uip_len + RPL_HOP_BY_HOP_LEN > UIP_BUFSIZE) {
|
|
||||||
PRINTF("RPL: Packet too long: impossible to add hop-by-hop option\n");
|
|
||||||
uip_ext_len = last_uip_ext_len;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
set_rpl_opt(uip_ext_opt_offset);
|
|
||||||
uip_ext_len = last_uip_ext_len + RPL_HOP_BY_HOP_LEN;
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(UIP_EXT_HDR_OPT_BUF->type) {
|
switch(UIP_EXT_HDR_OPT_BUF->type) {
|
||||||
|
@ -249,6 +523,7 @@ rpl_update_header_empty(void)
|
||||||
PRINTF("RPL: Updating RPL option\n");
|
PRINTF("RPL: Updating RPL option\n");
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(instance->current_dag->rank);
|
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(instance->current_dag->rank);
|
||||||
|
|
||||||
|
if(RPL_IS_STORING(instance)) { /* In non-storing mode, downwards traffic does not have the HBH option */
|
||||||
/* Check the direction of the down flag, as per Section 11.2.2.3,
|
/* Check the direction of the down flag, as per Section 11.2.2.3,
|
||||||
which states that if a packet is going down it should in
|
which states that if a packet is going down it should in
|
||||||
general not go back up again. If this happens, a
|
general not go back up again. If this happens, a
|
||||||
|
@ -265,7 +540,7 @@ rpl_update_header_empty(void)
|
||||||
dao_output_target(parent, &UIP_IP_BUF->destipaddr, RPL_ZERO_LIFETIME);
|
dao_output_target(parent, &UIP_IP_BUF->destipaddr, RPL_ZERO_LIFETIME);
|
||||||
}
|
}
|
||||||
/* Drop packet */
|
/* Drop packet */
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Set the down extension flag correctly as described in Section
|
/* Set the down extension flag correctly as described in Section
|
||||||
|
@ -282,18 +557,64 @@ rpl_update_header_empty(void)
|
||||||
PRINTF("RPL option going down\n");
|
PRINTF("RPL option going down\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uip_ext_len = last_uip_ext_len;
|
uip_ext_len = last_uip_ext_len;
|
||||||
return 0;
|
return 1;
|
||||||
default:
|
default:
|
||||||
PRINTF("RPL: Multi Hop-by-hop options not implemented\n");
|
PRINTF("RPL: Multi Hop-by-hop options not implemented\n");
|
||||||
uip_ext_len = last_uip_ext_len;
|
uip_ext_len = last_uip_ext_len;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
insert_hbh_header(void)
|
||||||
|
{
|
||||||
|
int uip_ext_opt_offset;
|
||||||
|
int last_uip_ext_len;
|
||||||
|
uint8_t temp_len;
|
||||||
|
|
||||||
|
last_uip_ext_len = uip_ext_len;
|
||||||
|
uip_ext_len = 0;
|
||||||
|
uip_ext_opt_offset = 2;
|
||||||
|
|
||||||
|
/* Insert hop-by-hop header */
|
||||||
|
PRINTF("RPL: Creating hop-by-hop option\n");
|
||||||
|
if(uip_len + RPL_HOP_BY_HOP_LEN > UIP_BUFSIZE) {
|
||||||
|
PRINTF("RPL: Packet too long: impossible to add hop-by-hop option\n");
|
||||||
|
uip_ext_len = last_uip_ext_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move existing ext headers and payload UIP_EXT_BUF further */
|
||||||
|
memmove(UIP_HBHO_NEXT_BUF, UIP_EXT_BUF, uip_len - UIP_IPH_LEN);
|
||||||
|
memset(UIP_HBHO_BUF, 0, RPL_HOP_BY_HOP_LEN);
|
||||||
|
|
||||||
|
/* Update IP and HBH protocol and fields */
|
||||||
|
UIP_HBHO_BUF->next = UIP_IP_BUF->proto;
|
||||||
|
UIP_IP_BUF->proto = UIP_PROTO_HBHO;
|
||||||
|
|
||||||
|
/* Initialize HBH option */
|
||||||
|
UIP_HBHO_BUF->len = (RPL_HOP_BY_HOP_LEN - 8) / 8;
|
||||||
|
UIP_EXT_HDR_OPT_RPL_BUF->opt_type = UIP_EXT_HDR_OPT_RPL;
|
||||||
|
UIP_EXT_HDR_OPT_RPL_BUF->opt_len = RPL_HDR_OPT_LEN;
|
||||||
|
UIP_EXT_HDR_OPT_RPL_BUF->flags = 0;
|
||||||
|
UIP_EXT_HDR_OPT_RPL_BUF->instance = 0;
|
||||||
|
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = 0;
|
||||||
|
uip_len += RPL_HOP_BY_HOP_LEN;
|
||||||
|
temp_len = UIP_IP_BUF->len[1];
|
||||||
|
UIP_IP_BUF->len[1] += RPL_HOP_BY_HOP_LEN;
|
||||||
|
if(UIP_IP_BUF->len[1] < temp_len) {
|
||||||
|
UIP_IP_BUF->len[0]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uip_ext_len = last_uip_ext_len + RPL_HOP_BY_HOP_LEN;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
int
|
int
|
||||||
rpl_update_header_final(uip_ipaddr_t *addr)
|
rpl_finalize_header(uip_ipaddr_t *addr)
|
||||||
{
|
{
|
||||||
rpl_parent_t *parent;
|
rpl_parent_t *parent;
|
||||||
int uip_ext_opt_offset;
|
int uip_ext_opt_offset;
|
||||||
|
@ -304,10 +625,10 @@ rpl_update_header_final(uip_ipaddr_t *addr)
|
||||||
uip_ext_opt_offset = 2;
|
uip_ext_opt_offset = 2;
|
||||||
|
|
||||||
if(UIP_IP_BUF->proto == UIP_PROTO_HBHO) {
|
if(UIP_IP_BUF->proto == UIP_PROTO_HBHO) {
|
||||||
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
|
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
|
||||||
PRINTF("RPL: Non RPL Hop-by-hop options support not implemented\n");
|
PRINTF("RPL: Non RPL Hop-by-hop options support not implemented\n");
|
||||||
uip_ext_len = last_uip_ext_len;
|
uip_ext_len = last_uip_ext_len;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(UIP_EXT_HDR_OPT_BUF->type == UIP_EXT_HDR_OPT_RPL) {
|
if(UIP_EXT_HDR_OPT_BUF->type == UIP_EXT_HDR_OPT_RPL) {
|
||||||
|
@ -315,7 +636,7 @@ rpl_update_header_final(uip_ipaddr_t *addr)
|
||||||
PRINTF("RPL: Updating RPL option\n");
|
PRINTF("RPL: Updating RPL option\n");
|
||||||
if(default_instance == NULL || !default_instance->used || !default_instance->current_dag->joined) {
|
if(default_instance == NULL || !default_instance->used || !default_instance->current_dag->joined) {
|
||||||
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect default instance\n");
|
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect default instance\n");
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
parent = rpl_find_parent(default_instance->current_dag, addr);
|
parent = rpl_find_parent(default_instance->current_dag, addr);
|
||||||
if(parent == NULL || parent != parent->dag->preferred_parent) {
|
if(parent == NULL || parent != parent->dag->preferred_parent) {
|
||||||
|
@ -326,78 +647,100 @@ rpl_update_header_final(uip_ipaddr_t *addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
rpl_remove_header(void)
|
rpl_remove_header(void)
|
||||||
{
|
{
|
||||||
uint8_t temp_len;
|
uint8_t temp_len;
|
||||||
|
uint8_t rpl_ext_hdr_len;
|
||||||
|
uint8_t *uip_next_hdr;
|
||||||
|
|
||||||
uip_ext_len = 0;
|
uip_ext_len = 0;
|
||||||
|
uip_next_hdr = &UIP_IP_BUF->proto;
|
||||||
|
|
||||||
PRINTF("RPL: Verifying the presence of the RPL header option\n");
|
PRINTF("RPL: Verifying the presence of RPL extension headers\n");
|
||||||
switch(UIP_IP_BUF->proto){
|
|
||||||
|
/* Look for hop-by-hop and routing headers */
|
||||||
|
while(uip_next_hdr != NULL) {
|
||||||
|
switch(*uip_next_hdr) {
|
||||||
|
case UIP_PROTO_TCP:
|
||||||
|
case UIP_PROTO_UDP:
|
||||||
|
case UIP_PROTO_ICMP6:
|
||||||
|
case UIP_PROTO_NONE:
|
||||||
|
return;
|
||||||
case UIP_PROTO_HBHO:
|
case UIP_PROTO_HBHO:
|
||||||
PRINTF("RPL: Removing the RPL header option\n");
|
case UIP_PROTO_ROUTING:
|
||||||
UIP_IP_BUF->proto = UIP_HBHO_BUF->next;
|
/* Remove hop-by-hop and routing headers */
|
||||||
|
*uip_next_hdr = UIP_EXT_BUF->next;
|
||||||
|
rpl_ext_hdr_len = (UIP_EXT_BUF->len * 8) + 8;
|
||||||
temp_len = UIP_IP_BUF->len[1];
|
temp_len = UIP_IP_BUF->len[1];
|
||||||
uip_len -= UIP_HBHO_BUF->len + 8;
|
uip_len -= rpl_ext_hdr_len;
|
||||||
UIP_IP_BUF->len[1] -= UIP_HBHO_BUF->len + 8;
|
UIP_IP_BUF->len[1] -= rpl_ext_hdr_len;
|
||||||
if(UIP_IP_BUF->len[1] > temp_len) {
|
if(UIP_IP_BUF->len[1] > temp_len) {
|
||||||
UIP_IP_BUF->len[0]--;
|
UIP_IP_BUF->len[0]--;
|
||||||
}
|
}
|
||||||
memmove(UIP_EXT_BUF, UIP_HBHO_NEXT_BUF, uip_len - UIP_IPH_LEN);
|
PRINTF("RPL: Removing RPL extension header (type %u, len %u)\n", *uip_next_hdr, rpl_ext_hdr_len);
|
||||||
|
memmove(UIP_EXT_BUF, ((uint8_t *)UIP_EXT_BUF) + rpl_ext_hdr_len, uip_len - UIP_IPH_LEN);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PRINTF("RPL: No hop-by-hop Option found\n");
|
/* Move to next header */
|
||||||
|
if(uip_next_hdr != &UIP_IP_BUF->proto) {
|
||||||
|
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
|
||||||
}
|
}
|
||||||
}
|
uip_next_hdr = &UIP_EXT_BUF->next;
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
uint8_t
|
|
||||||
rpl_invert_header(void)
|
|
||||||
{
|
|
||||||
uint8_t uip_ext_opt_offset;
|
|
||||||
uint8_t last_uip_ext_len;
|
|
||||||
|
|
||||||
last_uip_ext_len = uip_ext_len;
|
|
||||||
uip_ext_len = 0;
|
|
||||||
uip_ext_opt_offset = 2;
|
|
||||||
|
|
||||||
PRINTF("RPL: Verifying the presence of the RPL header option\n");
|
|
||||||
switch(UIP_IP_BUF->proto) {
|
|
||||||
case UIP_PROTO_HBHO:
|
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
PRINTF("RPL: No hop-by-hop Option found\n");
|
|
||||||
uip_ext_len = last_uip_ext_len;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (UIP_EXT_HDR_OPT_BUF->type) {
|
|
||||||
case UIP_EXT_HDR_OPT_RPL:
|
|
||||||
PRINTF("RPL: Updating RPL option (switching direction)\n");
|
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->flags &= RPL_HDR_OPT_DOWN;
|
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->flags ^= RPL_HDR_OPT_DOWN;
|
|
||||||
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance)->current_dag->rank);
|
|
||||||
uip_ext_len = last_uip_ext_len;
|
|
||||||
return RPL_HOP_BY_HOP_LEN;
|
|
||||||
default:
|
|
||||||
PRINTF("RPL: Multi Hop-by-hop options not implemented\n");
|
|
||||||
uip_ext_len = last_uip_ext_len;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
rpl_insert_header(void)
|
rpl_insert_header(void)
|
||||||
{
|
{
|
||||||
#if RPL_INSERT_HBH_OPTION
|
if(default_instance == NULL || default_instance->current_dag == NULL
|
||||||
if(default_instance != NULL && !uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
|
|| uip_is_addr_linklocal(&UIP_IP_BUF->destipaddr) || uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
|
||||||
rpl_update_header_empty();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RPL_IS_STORING(default_instance)) {
|
||||||
|
insert_hbh_header();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RPL_IS_NON_STORING(default_instance)) {
|
||||||
|
if(default_instance->current_dag != NULL) {
|
||||||
|
if(default_instance->current_dag->rank == ROOT_RANK(default_instance)) {
|
||||||
|
insert_srh_header();
|
||||||
|
} else {
|
||||||
|
insert_hbh_header();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
int
|
||||||
|
rpl_update_header(void)
|
||||||
|
{
|
||||||
|
if(default_instance == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(default_instance->current_dag != NULL) {
|
||||||
|
if(default_instance->current_dag->rank == ROOT_RANK(default_instance)) {
|
||||||
|
/* At the root, remove headers if any, and insert SRH or HBH
|
||||||
|
* (SRH is inserted only if the destination is in the DODAG) */
|
||||||
|
rpl_remove_header();
|
||||||
|
if(RPL_IS_NON_STORING(default_instance)) {
|
||||||
|
return insert_srh_header();
|
||||||
|
} else {
|
||||||
|
return insert_hbh_header();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return update_hbh_header();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @}*/
|
/** @}*/
|
||||||
|
|
|
@ -51,8 +51,10 @@
|
||||||
#include "net/ipv6/uip-nd6.h"
|
#include "net/ipv6/uip-nd6.h"
|
||||||
#include "net/ipv6/uip-icmp6.h"
|
#include "net/ipv6/uip-icmp6.h"
|
||||||
#include "net/rpl/rpl-private.h"
|
#include "net/rpl/rpl-private.h"
|
||||||
|
#include "net/rpl/rpl-ns.h"
|
||||||
#include "net/packetbuf.h"
|
#include "net/packetbuf.h"
|
||||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||||
|
#include "random.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -76,6 +78,9 @@ static void dio_input(void);
|
||||||
static void dao_input(void);
|
static void dao_input(void);
|
||||||
static void dao_ack_input(void);
|
static void dao_ack_input(void);
|
||||||
|
|
||||||
|
static void dao_output_target_seq(rpl_parent_t *parent, uip_ipaddr_t *prefix,
|
||||||
|
uint8_t lifetime, uint8_t seq_no);
|
||||||
|
|
||||||
/* some debug callbacks useful when debugging RPL networks */
|
/* some debug callbacks useful when debugging RPL networks */
|
||||||
#ifdef RPL_DEBUG_DIO_INPUT
|
#ifdef RPL_DEBUG_DIO_INPUT
|
||||||
void RPL_DEBUG_DIO_INPUT(uip_ipaddr_t *, rpl_dio_t *);
|
void RPL_DEBUG_DIO_INPUT(uip_ipaddr_t *, rpl_dio_t *);
|
||||||
|
@ -87,8 +92,6 @@ void RPL_DEBUG_DAO_OUTPUT(rpl_parent_t *);
|
||||||
|
|
||||||
static uint8_t dao_sequence = RPL_LOLLIPOP_INIT;
|
static uint8_t dao_sequence = RPL_LOLLIPOP_INIT;
|
||||||
|
|
||||||
extern rpl_of_t RPL_OF;
|
|
||||||
|
|
||||||
#if RPL_CONF_MULTICAST
|
#if RPL_CONF_MULTICAST
|
||||||
static uip_mcast6_route_t *mcast_group;
|
static uip_mcast6_route_t *mcast_group;
|
||||||
#endif
|
#endif
|
||||||
|
@ -99,6 +102,40 @@ UIP_ICMP6_HANDLER(dio_handler, ICMP6_RPL, RPL_CODE_DIO, dio_input);
|
||||||
UIP_ICMP6_HANDLER(dao_handler, ICMP6_RPL, RPL_CODE_DAO, dao_input);
|
UIP_ICMP6_HANDLER(dao_handler, ICMP6_RPL, RPL_CODE_DAO, dao_input);
|
||||||
UIP_ICMP6_HANDLER(dao_ack_handler, ICMP6_RPL, RPL_CODE_DAO_ACK, dao_ack_input);
|
UIP_ICMP6_HANDLER(dao_ack_handler, ICMP6_RPL, RPL_CODE_DAO_ACK, dao_ack_input);
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if RPL_WITH_DAO_ACK
|
||||||
|
static uip_ds6_route_t *
|
||||||
|
find_route_entry_by_dao_ack(uint8_t seq)
|
||||||
|
{
|
||||||
|
uip_ds6_route_t *re;
|
||||||
|
re = uip_ds6_route_head();
|
||||||
|
while(re != NULL) {
|
||||||
|
if(re->state.dao_seqno_out == seq && RPL_ROUTE_IS_DAO_PENDING(re)) {
|
||||||
|
/* found it! */
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
re = uip_ds6_route_next(re);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
|
|
||||||
|
#if RPL_WITH_STORING
|
||||||
|
/* prepare for forwarding of DAO */
|
||||||
|
static uint8_t
|
||||||
|
prepare_for_dao_fwd(uint8_t sequence, uip_ds6_route_t *rep)
|
||||||
|
{
|
||||||
|
/* not pending - or pending but not a retransmission */
|
||||||
|
RPL_LOLLIPOP_INCREMENT(dao_sequence);
|
||||||
|
|
||||||
|
/* set DAO pending and sequence numbers */
|
||||||
|
rep->state.dao_seqno_in = sequence;
|
||||||
|
rep->state.dao_seqno_out = dao_sequence;
|
||||||
|
RPL_ROUTE_SET_DAO_PENDING(rep);
|
||||||
|
return dao_sequence;
|
||||||
|
}
|
||||||
|
#endif /* RPL_WITH_STORING */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static int
|
static int
|
||||||
get_global_addr(uip_ipaddr_t *addr)
|
get_global_addr(uip_ipaddr_t *addr)
|
||||||
{
|
{
|
||||||
|
@ -147,6 +184,34 @@ set16(uint8_t *buffer, int pos, uint16_t value)
|
||||||
buffer[pos++] = value & 0xff;
|
buffer[pos++] = value & 0xff;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
uip_ds6_nbr_t *
|
||||||
|
rpl_icmp6_update_nbr_table(uip_ipaddr_t *from, nbr_table_reason_t reason, void *data)
|
||||||
|
{
|
||||||
|
uip_ds6_nbr_t *nbr;
|
||||||
|
|
||||||
|
if((nbr = uip_ds6_nbr_lookup(from)) == NULL) {
|
||||||
|
if((nbr = uip_ds6_nbr_add(from, (uip_lladdr_t *)
|
||||||
|
packetbuf_addr(PACKETBUF_ADDR_SENDER),
|
||||||
|
0, NBR_REACHABLE, reason, data)) != NULL) {
|
||||||
|
PRINTF("RPL: Neighbor added to neighbor cache ");
|
||||||
|
PRINT6ADDR(from);
|
||||||
|
PRINTF(", ");
|
||||||
|
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||||
|
PRINTF("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nbr != NULL) {
|
||||||
|
#if UIP_ND6_SEND_NA
|
||||||
|
/* set reachable timer if we added or found the nbr entry - and update
|
||||||
|
neighbor entry to reachable to avoid sending NS/NA, etc. */
|
||||||
|
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
|
||||||
|
nbr->state = NBR_REACHABLE;
|
||||||
|
#endif /* UIP_ND6_SEND_NA */
|
||||||
|
}
|
||||||
|
return nbr;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
dis_input(void)
|
dis_input(void)
|
||||||
{
|
{
|
||||||
|
@ -170,9 +235,20 @@ dis_input(void)
|
||||||
rpl_reset_dio_timer(instance);
|
rpl_reset_dio_timer(instance);
|
||||||
} else {
|
} else {
|
||||||
#endif /* !RPL_LEAF_ONLY */
|
#endif /* !RPL_LEAF_ONLY */
|
||||||
|
/* Check if this neighbor should be added according to the policy. */
|
||||||
|
if(rpl_icmp6_update_nbr_table(&UIP_IP_BUF->srcipaddr,
|
||||||
|
NBR_TABLE_REASON_RPL_DIS, NULL) == NULL) {
|
||||||
|
PRINTF("RPL: Out of Memory, not sending unicast DIO, DIS from ");
|
||||||
|
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||||
|
PRINTF(", ");
|
||||||
|
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||||
|
PRINTF("\n");
|
||||||
|
} else {
|
||||||
PRINTF("RPL: Unicast DIS, reply to sender\n");
|
PRINTF("RPL: Unicast DIS, reply to sender\n");
|
||||||
dio_output(instance, &UIP_IP_BUF->srcipaddr);
|
dio_output(instance, &UIP_IP_BUF->srcipaddr);
|
||||||
}
|
}
|
||||||
|
/* } */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uip_clear_buf();
|
uip_clear_buf();
|
||||||
|
@ -218,7 +294,6 @@ dio_input(void)
|
||||||
int i;
|
int i;
|
||||||
int len;
|
int len;
|
||||||
uip_ipaddr_t from;
|
uip_ipaddr_t from;
|
||||||
uip_ds6_nbr_t *nbr;
|
|
||||||
|
|
||||||
memset(&dio, 0, sizeof(dio));
|
memset(&dio, 0, sizeof(dio));
|
||||||
|
|
||||||
|
@ -228,7 +303,7 @@ dio_input(void)
|
||||||
dio.dag_redund = RPL_DIO_REDUNDANCY;
|
dio.dag_redund = RPL_DIO_REDUNDANCY;
|
||||||
dio.dag_min_hoprankinc = RPL_MIN_HOPRANKINC;
|
dio.dag_min_hoprankinc = RPL_MIN_HOPRANKINC;
|
||||||
dio.dag_max_rankinc = RPL_MAX_RANKINC;
|
dio.dag_max_rankinc = RPL_MAX_RANKINC;
|
||||||
dio.ocp = RPL_OF.ocp;
|
dio.ocp = RPL_OF_OCP;
|
||||||
dio.default_lifetime = RPL_DEFAULT_LIFETIME;
|
dio.default_lifetime = RPL_DEFAULT_LIFETIME;
|
||||||
dio.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
|
dio.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
|
||||||
|
|
||||||
|
@ -239,30 +314,6 @@ dio_input(void)
|
||||||
PRINT6ADDR(&from);
|
PRINT6ADDR(&from);
|
||||||
PRINTF("\n");
|
PRINTF("\n");
|
||||||
|
|
||||||
if((nbr = uip_ds6_nbr_lookup(&from)) == NULL) {
|
|
||||||
if((nbr = uip_ds6_nbr_add(&from, (uip_lladdr_t *)
|
|
||||||
packetbuf_addr(PACKETBUF_ADDR_SENDER),
|
|
||||||
0, NBR_REACHABLE)) != NULL) {
|
|
||||||
/* set reachable timer */
|
|
||||||
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
|
|
||||||
PRINTF("RPL: Neighbor added to neighbor cache ");
|
|
||||||
PRINT6ADDR(&from);
|
|
||||||
PRINTF(", ");
|
|
||||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
|
||||||
PRINTF("\n");
|
|
||||||
} else {
|
|
||||||
PRINTF("RPL: Out of memory, dropping DIO from ");
|
|
||||||
PRINT6ADDR(&from);
|
|
||||||
PRINTF(", ");
|
|
||||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
|
||||||
PRINTF("\n");
|
|
||||||
|
|
||||||
goto discard;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PRINTF("RPL: Neighbor already in neighbor cache\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
||||||
|
|
||||||
/* Process the DIO base option. */
|
/* Process the DIO base option. */
|
||||||
|
@ -428,6 +479,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
||||||
{
|
{
|
||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
int pos;
|
int pos;
|
||||||
|
int is_root;
|
||||||
rpl_dag_t *dag = instance->current_dag;
|
rpl_dag_t *dag = instance->current_dag;
|
||||||
#if !RPL_LEAF_ONLY
|
#if !RPL_LEAF_ONLY
|
||||||
uip_ipaddr_t addr;
|
uip_ipaddr_t addr;
|
||||||
|
@ -448,6 +500,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
||||||
buffer = UIP_ICMP_PAYLOAD;
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
buffer[pos++] = instance->instance_id;
|
buffer[pos++] = instance->instance_id;
|
||||||
buffer[pos++] = dag->version;
|
buffer[pos++] = dag->version;
|
||||||
|
is_root = (dag->rank == ROOT_RANK(instance));
|
||||||
|
|
||||||
#if RPL_LEAF_ONLY
|
#if RPL_LEAF_ONLY
|
||||||
PRINTF("RPL: LEAF ONLY DIO rank set to INFINITE_RANK\n");
|
PRINTF("RPL: LEAF ONLY DIO rank set to INFINITE_RANK\n");
|
||||||
|
@ -468,7 +521,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
||||||
|
|
||||||
buffer[pos++] = instance->dtsn_out;
|
buffer[pos++] = instance->dtsn_out;
|
||||||
|
|
||||||
if(uc_addr == NULL) {
|
if(RPL_DIO_REFRESH_DAO_ROUTES && is_root && uc_addr == NULL) {
|
||||||
/* Request new DAO to refresh route. We do not do this for unicast DIO
|
/* Request new DAO to refresh route. We do not do this for unicast DIO
|
||||||
* in order to avoid DAO messages after a DIS-DIO update,
|
* in order to avoid DAO messages after a DIS-DIO update,
|
||||||
* or upon unicast DIO probing. */
|
* or upon unicast DIO probing. */
|
||||||
|
@ -578,8 +631,9 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
dao_input(void)
|
dao_input_storing(void)
|
||||||
{
|
{
|
||||||
|
#if RPL_WITH_STORING
|
||||||
uip_ipaddr_t dao_sender_addr;
|
uip_ipaddr_t dao_sender_addr;
|
||||||
rpl_dag_t *dag;
|
rpl_dag_t *dag;
|
||||||
rpl_instance_t *instance;
|
rpl_instance_t *instance;
|
||||||
|
@ -603,17 +657,13 @@ dao_input(void)
|
||||||
int learned_from;
|
int learned_from;
|
||||||
rpl_parent_t *parent;
|
rpl_parent_t *parent;
|
||||||
uip_ds6_nbr_t *nbr;
|
uip_ds6_nbr_t *nbr;
|
||||||
|
int is_root;
|
||||||
|
|
||||||
prefixlen = 0;
|
prefixlen = 0;
|
||||||
parent = NULL;
|
parent = NULL;
|
||||||
|
|
||||||
uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);
|
uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);
|
||||||
|
|
||||||
/* Destination Advertisement Object */
|
|
||||||
PRINTF("RPL: Received a DAO from ");
|
|
||||||
PRINT6ADDR(&dao_sender_addr);
|
|
||||||
PRINTF("\n");
|
|
||||||
|
|
||||||
buffer = UIP_ICMP_PAYLOAD;
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
||||||
|
|
||||||
|
@ -621,11 +671,6 @@ dao_input(void)
|
||||||
instance_id = buffer[pos++];
|
instance_id = buffer[pos++];
|
||||||
|
|
||||||
instance = rpl_get_instance(instance_id);
|
instance = rpl_get_instance(instance_id);
|
||||||
if(instance == NULL) {
|
|
||||||
PRINTF("RPL: Ignoring a DAO for an unknown RPL instance(%u)\n",
|
|
||||||
instance_id);
|
|
||||||
goto discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
lifetime = instance->default_lifetime;
|
lifetime = instance->default_lifetime;
|
||||||
|
|
||||||
|
@ -635,11 +680,13 @@ dao_input(void)
|
||||||
sequence = buffer[pos++];
|
sequence = buffer[pos++];
|
||||||
|
|
||||||
dag = instance->current_dag;
|
dag = instance->current_dag;
|
||||||
|
is_root = (dag->rank == ROOT_RANK(instance));
|
||||||
|
|
||||||
/* Is the DAG ID present? */
|
/* Is the DAG ID present? */
|
||||||
if(flags & RPL_DAO_D_FLAG) {
|
if(flags & RPL_DAO_D_FLAG) {
|
||||||
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
|
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
|
||||||
PRINTF("RPL: Ignoring a DAO for a DAG different from ours\n");
|
PRINTF("RPL: Ignoring a DAO for a DAG different from ours\n");
|
||||||
goto discard;
|
return;
|
||||||
}
|
}
|
||||||
pos += 16;
|
pos += 16;
|
||||||
}
|
}
|
||||||
|
@ -647,8 +694,12 @@ dao_input(void)
|
||||||
learned_from = uip_is_addr_mcast(&dao_sender_addr) ?
|
learned_from = uip_is_addr_mcast(&dao_sender_addr) ?
|
||||||
RPL_ROUTE_FROM_MULTICAST_DAO : RPL_ROUTE_FROM_UNICAST_DAO;
|
RPL_ROUTE_FROM_MULTICAST_DAO : RPL_ROUTE_FROM_UNICAST_DAO;
|
||||||
|
|
||||||
PRINTF("RPL: DAO from %s\n",
|
/* Destination Advertisement Object */
|
||||||
learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast");
|
PRINTF("RPL: Received a (%s) DAO with sequence number %u from ",
|
||||||
|
learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast", sequence);
|
||||||
|
PRINT6ADDR(&dao_sender_addr);
|
||||||
|
PRINTF("\n");
|
||||||
|
|
||||||
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
|
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
|
||||||
/* Check whether this is a DAO forwarding loop. */
|
/* Check whether this is a DAO forwarding loop. */
|
||||||
parent = rpl_find_parent(dag, &dao_sender_addr);
|
parent = rpl_find_parent(dag, &dao_sender_addr);
|
||||||
|
@ -660,7 +711,7 @@ dao_input(void)
|
||||||
DAG_RANK(parent->rank, instance), DAG_RANK(dag->rank, instance));
|
DAG_RANK(parent->rank, instance), DAG_RANK(dag->rank, instance));
|
||||||
parent->rank = INFINITE_RANK;
|
parent->rank = INFINITE_RANK;
|
||||||
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||||
goto discard;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we get the DAO from our parent, we also have a loop. */
|
/* If we get the DAO from our parent, we also have a loop. */
|
||||||
|
@ -668,7 +719,7 @@ dao_input(void)
|
||||||
PRINTF("RPL: Loop detected when receiving a unicast DAO from our parent\n");
|
PRINTF("RPL: Loop detected when receiving a unicast DAO from our parent\n");
|
||||||
parent->rank = INFINITE_RANK;
|
parent->rank = INFINITE_RANK;
|
||||||
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||||
goto discard;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,91 +772,319 @@ dao_input(void)
|
||||||
PRINTF("RPL: No-Path DAO received\n");
|
PRINTF("RPL: No-Path DAO received\n");
|
||||||
/* No-Path DAO received; invoke the route purging routine. */
|
/* No-Path DAO received; invoke the route purging routine. */
|
||||||
if(rep != NULL &&
|
if(rep != NULL &&
|
||||||
rep->state.nopath_received == 0 &&
|
!RPL_ROUTE_IS_NOPATH_RECEIVED(rep) &&
|
||||||
rep->length == prefixlen &&
|
rep->length == prefixlen &&
|
||||||
uip_ds6_route_nexthop(rep) != NULL &&
|
uip_ds6_route_nexthop(rep) != NULL &&
|
||||||
uip_ipaddr_cmp(uip_ds6_route_nexthop(rep), &dao_sender_addr)) {
|
uip_ipaddr_cmp(uip_ds6_route_nexthop(rep), &dao_sender_addr)) {
|
||||||
PRINTF("RPL: Setting expiration timer for prefix ");
|
PRINTF("RPL: Setting expiration timer for prefix ");
|
||||||
PRINT6ADDR(&prefix);
|
PRINT6ADDR(&prefix);
|
||||||
PRINTF("\n");
|
PRINTF("\n");
|
||||||
rep->state.nopath_received = 1;
|
RPL_ROUTE_SET_NOPATH_RECEIVED(rep);
|
||||||
rep->state.lifetime = RPL_NOPATH_REMOVAL_DELAY;
|
rep->state.lifetime = RPL_NOPATH_REMOVAL_DELAY;
|
||||||
|
|
||||||
/* We forward the incoming No-Path DAO to our parent, if we have
|
/* We forward the incoming No-Path DAO to our parent, if we have
|
||||||
one. */
|
one. */
|
||||||
if(dag->preferred_parent != NULL &&
|
if(dag->preferred_parent != NULL &&
|
||||||
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
|
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
|
||||||
PRINTF("RPL: Forwarding No-Path DAO to parent ");
|
uint8_t out_seq;
|
||||||
|
out_seq = prepare_for_dao_fwd(sequence, rep);
|
||||||
|
|
||||||
|
PRINTF("RPL: Forwarding No-path DAO to parent - out_seq:%d",
|
||||||
|
out_seq);
|
||||||
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
|
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
|
||||||
PRINTF("\n");
|
PRINTF("\n");
|
||||||
|
|
||||||
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
|
buffer[3] = out_seq; /* add an outgoing seq no before fwd */
|
||||||
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
|
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
|
||||||
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
|
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* independent if we remove or not - ACK the request */
|
||||||
if(flags & RPL_DAO_K_FLAG) {
|
if(flags & RPL_DAO_K_FLAG) {
|
||||||
dao_ack_output(instance, &dao_sender_addr, sequence);
|
/* indicate that we accepted the no-path DAO */
|
||||||
|
uip_clear_buf();
|
||||||
|
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||||
|
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
|
||||||
}
|
}
|
||||||
}
|
return;
|
||||||
goto discard;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PRINTF("RPL: adding DAO route\n");
|
PRINTF("RPL: Adding DAO route\n");
|
||||||
|
|
||||||
if((nbr = uip_ds6_nbr_lookup(&dao_sender_addr)) == NULL) {
|
/* Update and add neighbor - if no room - fail. */
|
||||||
if((nbr = uip_ds6_nbr_add(&dao_sender_addr,
|
if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr, NBR_TABLE_REASON_RPL_DAO, instance)) == NULL) {
|
||||||
(uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER),
|
|
||||||
0, NBR_REACHABLE)) != NULL) {
|
|
||||||
/* set reachable timer */
|
|
||||||
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
|
|
||||||
PRINTF("RPL: Neighbor added to neighbor cache ");
|
|
||||||
PRINT6ADDR(&dao_sender_addr);
|
|
||||||
PRINTF(", ");
|
|
||||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
|
||||||
PRINTF("\n");
|
|
||||||
} else {
|
|
||||||
PRINTF("RPL: Out of Memory, dropping DAO from ");
|
PRINTF("RPL: Out of Memory, dropping DAO from ");
|
||||||
PRINT6ADDR(&dao_sender_addr);
|
PRINT6ADDR(&dao_sender_addr);
|
||||||
PRINTF(", ");
|
PRINTF(", ");
|
||||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||||
PRINTF("\n");
|
PRINTF("\n");
|
||||||
goto discard;
|
if(flags & RPL_DAO_K_FLAG) {
|
||||||
|
/* signal the failure to add the node */
|
||||||
|
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||||
|
is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
|
||||||
|
RPL_DAO_ACK_UNABLE_TO_ACCEPT);
|
||||||
}
|
}
|
||||||
} else {
|
return;
|
||||||
PRINTF("RPL: Neighbor already in neighbor cache\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr);
|
rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr);
|
||||||
if(rep == NULL) {
|
if(rep == NULL) {
|
||||||
RPL_STAT(rpl_stats.mem_overflows++);
|
RPL_STAT(rpl_stats.mem_overflows++);
|
||||||
PRINTF("RPL: Could not add a route after receiving a DAO\n");
|
PRINTF("RPL: Could not add a route after receiving a DAO\n");
|
||||||
goto discard;
|
if(flags & RPL_DAO_K_FLAG) {
|
||||||
|
/* signal the failure to add the node */
|
||||||
|
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||||
|
is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
|
||||||
|
RPL_DAO_ACK_UNABLE_TO_ACCEPT);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* set lifetime and clear NOPATH bit */
|
||||||
rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
|
rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
|
||||||
rep->state.learned_from = learned_from;
|
RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);
|
||||||
rep->state.nopath_received = 0;
|
|
||||||
|
|
||||||
#if RPL_CONF_MULTICAST
|
#if RPL_CONF_MULTICAST
|
||||||
fwd_dao:
|
fwd_dao:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
|
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
|
||||||
|
int should_ack = 0;
|
||||||
|
|
||||||
|
if(flags & RPL_DAO_K_FLAG) {
|
||||||
|
/*
|
||||||
|
* check if this route is already installed and we can ack now!
|
||||||
|
* not pending - and same seq-no means that we can ack.
|
||||||
|
* (e.g. the route is installed already so it will not take any
|
||||||
|
* more room that it already takes - so should be ok!)
|
||||||
|
*/
|
||||||
|
if((!RPL_ROUTE_IS_DAO_PENDING(rep) &&
|
||||||
|
rep->state.dao_seqno_in == sequence) ||
|
||||||
|
dag->rank == ROOT_RANK(instance)) {
|
||||||
|
should_ack = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(dag->preferred_parent != NULL &&
|
if(dag->preferred_parent != NULL &&
|
||||||
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
|
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
|
||||||
|
uint8_t out_seq = 0;
|
||||||
|
/* if this is pending and we get the same seq no it is a retrans */
|
||||||
|
if(RPL_ROUTE_IS_DAO_PENDING(rep) &&
|
||||||
|
rep->state.dao_seqno_in == sequence) {
|
||||||
|
/* keep the same seq-no as before for parent also */
|
||||||
|
out_seq = rep->state.dao_seqno_out;
|
||||||
|
} else {
|
||||||
|
out_seq = prepare_for_dao_fwd(sequence, rep);
|
||||||
|
}
|
||||||
|
|
||||||
PRINTF("RPL: Forwarding DAO to parent ");
|
PRINTF("RPL: Forwarding DAO to parent ");
|
||||||
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
|
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
|
||||||
PRINTF("\n");
|
PRINTF(" in seq: %d out seq: %d\n", sequence, out_seq);
|
||||||
|
|
||||||
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
|
buffer[3] = out_seq; /* add an outgoing seq no before fwd */
|
||||||
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
|
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
|
||||||
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
|
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
|
||||||
}
|
}
|
||||||
if(flags & RPL_DAO_K_FLAG) {
|
if(should_ack) {
|
||||||
dao_ack_output(instance, &dao_sender_addr, sequence);
|
PRINTF("RPL: Sending DAO ACK\n");
|
||||||
|
uip_clear_buf();
|
||||||
|
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||||
|
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* RPL_WITH_STORING */
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
dao_input_nonstoring(void)
|
||||||
|
{
|
||||||
|
#if RPL_WITH_NON_STORING
|
||||||
|
uip_ipaddr_t dao_sender_addr;
|
||||||
|
uip_ipaddr_t dao_parent_addr;
|
||||||
|
rpl_dag_t *dag;
|
||||||
|
rpl_instance_t *instance;
|
||||||
|
unsigned char *buffer;
|
||||||
|
uint16_t sequence;
|
||||||
|
uint8_t instance_id;
|
||||||
|
uint8_t lifetime;
|
||||||
|
uint8_t prefixlen;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t subopt_type;
|
||||||
|
uip_ipaddr_t prefix;
|
||||||
|
uint8_t buffer_length;
|
||||||
|
int pos;
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
prefixlen = 0;
|
||||||
|
|
||||||
|
uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);
|
||||||
|
memset(&dao_parent_addr, 0, 16);
|
||||||
|
|
||||||
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
|
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
instance_id = buffer[pos++];
|
||||||
|
instance = rpl_get_instance(instance_id);
|
||||||
|
lifetime = instance->default_lifetime;
|
||||||
|
|
||||||
|
flags = buffer[pos++];
|
||||||
|
/* reserved */
|
||||||
|
pos++;
|
||||||
|
sequence = buffer[pos++];
|
||||||
|
|
||||||
|
dag = instance->current_dag;
|
||||||
|
/* Is the DAG ID present? */
|
||||||
|
if(flags & RPL_DAO_D_FLAG) {
|
||||||
|
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
|
||||||
|
PRINTF("RPL: Ignoring a DAO for a DAG different from ours\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos += 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if there are any RPL options present. */
|
||||||
|
for(i = pos; i < buffer_length; i += len) {
|
||||||
|
subopt_type = buffer[i];
|
||||||
|
if(subopt_type == RPL_OPTION_PAD1) {
|
||||||
|
len = 1;
|
||||||
|
} else {
|
||||||
|
/* The option consists of a two-byte header and a payload. */
|
||||||
|
len = 2 + buffer[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(subopt_type) {
|
||||||
|
case RPL_OPTION_TARGET:
|
||||||
|
/* Handle the target option. */
|
||||||
|
prefixlen = buffer[i + 3];
|
||||||
|
memset(&prefix, 0, sizeof(prefix));
|
||||||
|
memcpy(&prefix, buffer + i + 4, (prefixlen + 7) / CHAR_BIT);
|
||||||
|
break;
|
||||||
|
case RPL_OPTION_TRANSIT:
|
||||||
|
/* The path sequence and control are ignored. */
|
||||||
|
/* pathcontrol = buffer[i + 3];
|
||||||
|
pathsequence = buffer[i + 4];*/
|
||||||
|
lifetime = buffer[i + 5];
|
||||||
|
if(len >= 20) {
|
||||||
|
memcpy(&dao_parent_addr, buffer + i + 6, 16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINTF("RPL: DAO lifetime: %u, prefix length: %u prefix: ",
|
||||||
|
(unsigned)lifetime, (unsigned)prefixlen);
|
||||||
|
PRINT6ADDR(&prefix);
|
||||||
|
PRINTF(", parent: ");
|
||||||
|
PRINT6ADDR(&dao_parent_addr);
|
||||||
|
PRINTF(" \n");
|
||||||
|
|
||||||
|
if(lifetime == RPL_ZERO_LIFETIME) {
|
||||||
|
PRINTF("RPL: No-Path DAO received\n");
|
||||||
|
rpl_ns_expire_parent(dag, &prefix, &dao_parent_addr);
|
||||||
|
} else {
|
||||||
|
if(rpl_ns_update_node(dag, &prefix, &dao_parent_addr, RPL_LIFETIME(instance, lifetime)) == NULL) {
|
||||||
|
PRINTF("RPL: failed to add link\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flags & RPL_DAO_K_FLAG) {
|
||||||
|
PRINTF("RPL: Sending DAO ACK\n");
|
||||||
|
uip_clear_buf();
|
||||||
|
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||||
|
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
|
||||||
|
}
|
||||||
|
#endif /* RPL_WITH_NON_STORING */
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
dao_input(void)
|
||||||
|
{
|
||||||
|
rpl_instance_t *instance;
|
||||||
|
uint8_t instance_id;
|
||||||
|
|
||||||
|
/* Destination Advertisement Object */
|
||||||
|
PRINTF("RPL: Received a DAO from ");
|
||||||
|
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||||
|
PRINTF("\n");
|
||||||
|
|
||||||
|
instance_id = UIP_ICMP_PAYLOAD[0];
|
||||||
|
instance = rpl_get_instance(instance_id);
|
||||||
|
if(instance == NULL) {
|
||||||
|
PRINTF("RPL: Ignoring a DAO for an unknown RPL instance(%u)\n",
|
||||||
|
instance_id);
|
||||||
|
goto discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RPL_IS_STORING(instance)) {
|
||||||
|
dao_input_storing();
|
||||||
|
} else if(RPL_IS_NON_STORING(instance)) {
|
||||||
|
dao_input_nonstoring();
|
||||||
|
}
|
||||||
|
|
||||||
discard:
|
discard:
|
||||||
uip_clear_buf();
|
uip_clear_buf();
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
#if RPL_WITH_DAO_ACK
|
||||||
|
static void
|
||||||
|
handle_dao_retransmission(void *ptr)
|
||||||
|
{
|
||||||
|
rpl_parent_t *parent;
|
||||||
|
uip_ipaddr_t prefix;
|
||||||
|
rpl_instance_t *instance;
|
||||||
|
|
||||||
|
parent = ptr;
|
||||||
|
if(parent == NULL || parent->dag == NULL || parent->dag->instance == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
instance = parent->dag->instance;
|
||||||
|
|
||||||
|
if(instance->my_dao_transmissions >= RPL_DAO_MAX_RETRANSMISSIONS) {
|
||||||
|
/* No more retransmissions - give up. */
|
||||||
|
if(instance->lifetime_unit == 0xffff && instance->default_lifetime == 0xff) {
|
||||||
|
/*
|
||||||
|
* ContikiRPL was previously using infinite lifetime for routes
|
||||||
|
* and no DAO_ACK configured. This probably means that the root
|
||||||
|
* and possibly other nodes might be running an old version that
|
||||||
|
* does not support DAO ack. Assume that everything is ok for
|
||||||
|
* now and let the normal repair mechanisms detect any problems.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RPL_IS_STORING(instance) && instance->of->dao_ack_callback) {
|
||||||
|
/* Inform the objective function about the timeout. */
|
||||||
|
instance->of->dao_ack_callback(parent, RPL_DAO_ACK_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform local repair and hope to find another parent. */
|
||||||
|
rpl_local_repair(instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINTF("RPL: will retransmit DAO - seq:%d trans:%d\n", instance->my_dao_seqno,
|
||||||
|
instance->my_dao_transmissions);
|
||||||
|
|
||||||
|
if(get_global_addr(&prefix) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctimer_set(&instance->dao_retransmit_timer,
|
||||||
|
RPL_DAO_RETRANSMISSION_TIMEOUT / 2 +
|
||||||
|
(random_rand() % (RPL_DAO_RETRANSMISSION_TIMEOUT / 2)),
|
||||||
|
handle_dao_retransmission, parent);
|
||||||
|
|
||||||
|
instance->my_dao_transmissions++;
|
||||||
|
dao_output_target_seq(parent, &prefix,
|
||||||
|
instance->default_lifetime, instance->my_dao_seqno);
|
||||||
|
}
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
dao_output(rpl_parent_t *parent, uint8_t lifetime)
|
dao_output(rpl_parent_t *parent, uint8_t lifetime)
|
||||||
{
|
{
|
||||||
|
@ -817,18 +1096,51 @@ dao_output(rpl_parent_t *parent, uint8_t lifetime)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(parent == NULL || parent->dag == NULL || parent->dag->instance == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RPL_LOLLIPOP_INCREMENT(dao_sequence);
|
||||||
|
#if RPL_WITH_DAO_ACK
|
||||||
|
/* set up the state since this will be the first transmission of DAO */
|
||||||
|
/* retransmissions will call directly to dao_output_target_seq */
|
||||||
|
/* keep track of my own sending of DAO for handling ack and loss of ack */
|
||||||
|
if(lifetime != RPL_ZERO_LIFETIME) {
|
||||||
|
rpl_instance_t *instance;
|
||||||
|
instance = parent->dag->instance;
|
||||||
|
|
||||||
|
instance->my_dao_seqno = dao_sequence;
|
||||||
|
instance->my_dao_transmissions = 1;
|
||||||
|
ctimer_set(&instance->dao_retransmit_timer, RPL_DAO_RETRANSMISSION_TIMEOUT,
|
||||||
|
handle_dao_retransmission, parent);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* We know that we have tried to register so now we are assuming
|
||||||
|
that we have a down-link - unless this is a zero lifetime one */
|
||||||
|
parent->dag->instance->has_downward_route = lifetime != RPL_ZERO_LIFETIME;
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
|
|
||||||
/* Sending a DAO with own prefix as target */
|
/* Sending a DAO with own prefix as target */
|
||||||
dao_output_target(parent, &prefix, lifetime);
|
dao_output_target(parent, &prefix, lifetime);
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
||||||
|
{
|
||||||
|
dao_output_target_seq(parent, prefix, lifetime, dao_sequence);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
dao_output_target_seq(rpl_parent_t *parent, uip_ipaddr_t *prefix,
|
||||||
|
uint8_t lifetime, uint8_t seq_no)
|
||||||
{
|
{
|
||||||
rpl_dag_t *dag;
|
rpl_dag_t *dag;
|
||||||
rpl_instance_t *instance;
|
rpl_instance_t *instance;
|
||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
uint8_t prefixlen;
|
uint8_t prefixlen;
|
||||||
int pos;
|
int pos;
|
||||||
|
uip_ipaddr_t *parent_ipaddr = NULL;
|
||||||
|
uip_ipaddr_t *dest_ipaddr = NULL;
|
||||||
|
|
||||||
/* Destination Advertisement Object */
|
/* Destination Advertisement Object */
|
||||||
|
|
||||||
|
@ -842,6 +1154,12 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent_ipaddr = rpl_get_parent_ipaddr(parent);
|
||||||
|
if(parent_ipaddr == NULL) {
|
||||||
|
PRINTF("RPL dao_output_target error parent IP address NULL\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dag = parent->dag;
|
dag = parent->dag;
|
||||||
if(dag == NULL) {
|
if(dag == NULL) {
|
||||||
PRINTF("RPL dao_output_target error dag NULL\n");
|
PRINTF("RPL dao_output_target error dag NULL\n");
|
||||||
|
@ -863,8 +1181,6 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
buffer = UIP_ICMP_PAYLOAD;
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
|
|
||||||
RPL_LOLLIPOP_INCREMENT(dao_sequence);
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
|
||||||
buffer[pos++] = instance->instance_id;
|
buffer[pos++] = instance->instance_id;
|
||||||
|
@ -872,12 +1188,14 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
||||||
#if RPL_DAO_SPECIFY_DAG
|
#if RPL_DAO_SPECIFY_DAG
|
||||||
buffer[pos] |= RPL_DAO_D_FLAG;
|
buffer[pos] |= RPL_DAO_D_FLAG;
|
||||||
#endif /* RPL_DAO_SPECIFY_DAG */
|
#endif /* RPL_DAO_SPECIFY_DAG */
|
||||||
#if RPL_CONF_DAO_ACK
|
#if RPL_WITH_DAO_ACK
|
||||||
|
if(lifetime != RPL_ZERO_LIFETIME) {
|
||||||
buffer[pos] |= RPL_DAO_K_FLAG;
|
buffer[pos] |= RPL_DAO_K_FLAG;
|
||||||
#endif /* RPL_CONF_DAO_ACK */
|
}
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
++pos;
|
++pos;
|
||||||
buffer[pos++] = 0; /* reserved */
|
buffer[pos++] = 0; /* reserved */
|
||||||
buffer[pos++] = dao_sequence;
|
buffer[pos++] = seq_no;
|
||||||
#if RPL_DAO_SPECIFY_DAG
|
#if RPL_DAO_SPECIFY_DAG
|
||||||
memcpy(buffer + pos, &dag->dag_id, sizeof(dag->dag_id));
|
memcpy(buffer + pos, &dag->dag_id, sizeof(dag->dag_id));
|
||||||
pos+=sizeof(dag->dag_id);
|
pos+=sizeof(dag->dag_id);
|
||||||
|
@ -894,61 +1212,154 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
||||||
|
|
||||||
/* Create a transit information sub-option. */
|
/* Create a transit information sub-option. */
|
||||||
buffer[pos++] = RPL_OPTION_TRANSIT;
|
buffer[pos++] = RPL_OPTION_TRANSIT;
|
||||||
buffer[pos++] = 4;
|
buffer[pos++] = (instance->mop != RPL_MOP_NON_STORING) ? 4 : 20;
|
||||||
buffer[pos++] = 0; /* flags - ignored */
|
buffer[pos++] = 0; /* flags - ignored */
|
||||||
buffer[pos++] = 0; /* path control - ignored */
|
buffer[pos++] = 0; /* path control - ignored */
|
||||||
buffer[pos++] = 0; /* path seq - ignored */
|
buffer[pos++] = 0; /* path seq - ignored */
|
||||||
buffer[pos++] = lifetime;
|
buffer[pos++] = lifetime;
|
||||||
|
|
||||||
PRINTF("RPL: Sending %sDAO with prefix ", lifetime == RPL_ZERO_LIFETIME ? "No-Path " : "");
|
if(instance->mop != RPL_MOP_NON_STORING) {
|
||||||
|
/* Send DAO to parent */
|
||||||
|
dest_ipaddr = parent_ipaddr;
|
||||||
|
} else {
|
||||||
|
/* Include parent global IP address */
|
||||||
|
memcpy(buffer + pos, &parent->dag->dag_id, 8); /* Prefix */
|
||||||
|
pos += 8;
|
||||||
|
memcpy(buffer + pos, ((const unsigned char *)parent_ipaddr) + 8, 8); /* Interface identifier */
|
||||||
|
pos += 8;
|
||||||
|
/* Send DAO to root */
|
||||||
|
dest_ipaddr = &parent->dag->dag_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINTF("RPL: Sending a %sDAO with sequence number %u, lifetime %u, prefix ",
|
||||||
|
lifetime == RPL_ZERO_LIFETIME ? "No-Path " : "", seq_no, lifetime);
|
||||||
|
|
||||||
PRINT6ADDR(prefix);
|
PRINT6ADDR(prefix);
|
||||||
PRINTF(" to ");
|
PRINTF(" to ");
|
||||||
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
|
PRINT6ADDR(dest_ipaddr);
|
||||||
|
PRINTF(" , parent ");
|
||||||
|
PRINT6ADDR(parent_ipaddr);
|
||||||
PRINTF("\n");
|
PRINTF("\n");
|
||||||
|
|
||||||
if(rpl_get_parent_ipaddr(parent) != NULL) {
|
if(dest_ipaddr != NULL) {
|
||||||
uip_icmp6_send(rpl_get_parent_ipaddr(parent), ICMP6_RPL, RPL_CODE_DAO, pos);
|
uip_icmp6_send(dest_ipaddr, ICMP6_RPL, RPL_CODE_DAO, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
dao_ack_input(void)
|
dao_ack_input(void)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if RPL_WITH_DAO_ACK
|
||||||
unsigned char *buffer;
|
|
||||||
|
uint8_t *buffer;
|
||||||
|
uint8_t instance_id;
|
||||||
uint8_t sequence;
|
uint8_t sequence;
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
|
rpl_instance_t *instance;
|
||||||
|
rpl_parent_t *parent;
|
||||||
|
|
||||||
buffer = UIP_ICMP_PAYLOAD;
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
|
|
||||||
|
instance_id = buffer[0];
|
||||||
sequence = buffer[2];
|
sequence = buffer[2];
|
||||||
status = buffer[3];
|
status = buffer[3];
|
||||||
|
|
||||||
PRINTF("RPL: Received a DAO ACK with sequence number %d and status %d from ",
|
instance = rpl_get_instance(instance_id);
|
||||||
sequence, status);
|
if(instance == NULL) {
|
||||||
|
uip_clear_buf();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RPL_IS_STORING(instance)) {
|
||||||
|
parent = rpl_find_parent(instance->current_dag, &UIP_IP_BUF->srcipaddr);
|
||||||
|
if(parent == NULL) {
|
||||||
|
/* not a known instance - drop the packet and ignore */
|
||||||
|
uip_clear_buf();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINTF("RPL: Received a DAO %s with sequence number %d (%d) and status %d from ",
|
||||||
|
status < 128 ? "ACK" : "NACK",
|
||||||
|
sequence, instance->my_dao_seqno, status);
|
||||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||||
PRINTF("\n");
|
PRINTF("\n");
|
||||||
#endif /* DEBUG */
|
|
||||||
|
if(sequence == instance->my_dao_seqno) {
|
||||||
|
instance->has_downward_route = status < 128;
|
||||||
|
|
||||||
|
/* always stop the retransmit timer when the ACK arrived */
|
||||||
|
ctimer_stop(&instance->dao_retransmit_timer);
|
||||||
|
|
||||||
|
/* Inform objective function on status of the DAO ACK */
|
||||||
|
if(RPL_IS_STORING(instance) && instance->of->dao_ack_callback) {
|
||||||
|
instance->of->dao_ack_callback(parent, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RPL_REPAIR_ON_DAO_NACK
|
||||||
|
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
|
||||||
|
/*
|
||||||
|
* Failed the DAO transmission - need to remove the default route.
|
||||||
|
* Trigger a local repair since we can not get our DAO in.
|
||||||
|
*/
|
||||||
|
rpl_local_repair(instance);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} else if(RPL_IS_STORING(instance)) {
|
||||||
|
/* this DAO ACK should be forwarded to another recently registered route */
|
||||||
|
uip_ds6_route_t *re;
|
||||||
|
uip_ipaddr_t *nexthop;
|
||||||
|
if((re = find_route_entry_by_dao_ack(sequence)) != NULL) {
|
||||||
|
/* pick the recorded seq no from that node and forward DAO ACK - and
|
||||||
|
clear the pending flag*/
|
||||||
|
RPL_ROUTE_CLEAR_DAO_PENDING(re);
|
||||||
|
|
||||||
|
nexthop = uip_ds6_route_nexthop(re);
|
||||||
|
if(nexthop == NULL) {
|
||||||
|
PRINTF("RPL: No next hop to fwd DAO ACK to\n");
|
||||||
|
} else {
|
||||||
|
PRINTF("RPL: Fwd DAO ACK to:");
|
||||||
|
PRINT6ADDR(nexthop);
|
||||||
|
PRINTF("\n");
|
||||||
|
buffer[2] = re->state.dao_seqno_in;
|
||||||
|
uip_icmp6_send(nexthop, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
|
||||||
|
/* this node did not get in to the routing tables above... - remove */
|
||||||
|
uip_ds6_route_rm(re);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PRINTF("RPL: No route entry found to forward DAO ACK (seqno %u)\n", sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
uip_clear_buf();
|
uip_clear_buf();
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
dao_ack_output(rpl_instance_t *instance, uip_ipaddr_t *dest, uint8_t sequence)
|
dao_ack_output(rpl_instance_t *instance, uip_ipaddr_t *dest, uint8_t sequence,
|
||||||
|
uint8_t status)
|
||||||
{
|
{
|
||||||
|
#if RPL_WITH_DAO_ACK
|
||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
|
|
||||||
PRINTF("RPL: Sending a DAO ACK with sequence number %d to ", sequence);
|
PRINTF("RPL: Sending a DAO %s with sequence number %d to ", status < 128 ? "ACK" : "NACK", sequence);
|
||||||
PRINT6ADDR(dest);
|
PRINT6ADDR(dest);
|
||||||
PRINTF("\n");
|
PRINTF(" with status %d\n", status);
|
||||||
|
|
||||||
buffer = UIP_ICMP_PAYLOAD;
|
buffer = UIP_ICMP_PAYLOAD;
|
||||||
|
|
||||||
buffer[0] = instance->instance_id;
|
buffer[0] = instance->instance_id;
|
||||||
buffer[1] = 0;
|
buffer[1] = 0;
|
||||||
buffer[2] = sequence;
|
buffer[2] = sequence;
|
||||||
buffer[3] = 0;
|
buffer[3] = status;
|
||||||
|
|
||||||
uip_icmp6_send(dest, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
|
uip_icmp6_send(dest, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
void
|
void
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \file
|
* \file
|
||||||
* The Minimum Rank with Hysteresis Objective Function (MRHOF)
|
* The Minimum Rank with Hysteresis Objective Function (MRHOF), RFC6719
|
||||||
*
|
*
|
||||||
* This implementation uses the estimated number of
|
* This implementation uses the estimated number of
|
||||||
* transmissions (ETX) as the additive routing metric,
|
* transmissions (ETX) as the additive routing metric,
|
||||||
|
@ -46,150 +46,187 @@
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "net/rpl/rpl.h"
|
||||||
#include "net/rpl/rpl-private.h"
|
#include "net/rpl/rpl-private.h"
|
||||||
#include "net/nbr-table.h"
|
#include "net/nbr-table.h"
|
||||||
|
#include "net/link-stats.h"
|
||||||
|
|
||||||
#define DEBUG DEBUG_NONE
|
#define DEBUG DEBUG_NONE
|
||||||
#include "net/ip/uip-debug.h"
|
#include "net/ip/uip-debug.h"
|
||||||
|
|
||||||
static void reset(rpl_dag_t *);
|
/* RFC6551 and RFC6719 do not mandate the use of a specific formula to
|
||||||
static void neighbor_link_callback(rpl_parent_t *, int, int);
|
* compute the ETX value. This MRHOF implementation relies on the value
|
||||||
static rpl_parent_t *best_parent(rpl_parent_t *, rpl_parent_t *);
|
* computed by the link-stats module. It has an optional feature,
|
||||||
static rpl_dag_t *best_dag(rpl_dag_t *, rpl_dag_t *);
|
* RPL_MRHOF_CONF_SQUARED_ETX, that consists in squaring this value.
|
||||||
static rpl_rank_t calculate_rank(rpl_parent_t *, rpl_rank_t);
|
* This basically penalizes bad links while preserving the semantics of ETX
|
||||||
static void update_metric_container(rpl_instance_t *);
|
* (1 = perfect link, more = worse link). As a result, MRHOF will favor
|
||||||
|
* good links over short paths. Recommended when reliability is a priority.
|
||||||
|
* Without this feature, a hop with 50% PRR (ETX=2) is equivalent to two
|
||||||
|
* perfect hops with 100% PRR (ETX=1+1=2). With this feature, the former
|
||||||
|
* path obtains ETX=2*2=4 and the former ETX=1*1+1*1=2. */
|
||||||
|
#ifdef RPL_MRHOF_CONF_SQUARED_ETX
|
||||||
|
#define RPL_MRHOF_SQUARED_ETX RPL_MRHOF_CONF_SQUARED_ETX
|
||||||
|
#else /* RPL_MRHOF_CONF_SQUARED_ETX */
|
||||||
|
#define RPL_MRHOF_SQUARED_ETX 0
|
||||||
|
#endif /* RPL_MRHOF_CONF_SQUARED_ETX */
|
||||||
|
|
||||||
rpl_of_t rpl_mrhof = {
|
#if !RPL_MRHOF_SQUARED_ETX
|
||||||
reset,
|
/* Configuration parameters of RFC6719. Reject parents that have a higher
|
||||||
neighbor_link_callback,
|
* link metric than the following. The default value is 512 but we use 1024. */
|
||||||
best_parent,
|
#define MAX_LINK_METRIC 1024 /* Eq ETX of 8 */
|
||||||
best_dag,
|
/* Hysteresis of MRHOF: the rank must differ more than PARENT_SWITCH_THRESHOLD_DIV
|
||||||
calculate_rank,
|
* in order to switch preferred parent. Default in RFC6719: 192, eq ETX of 1.5.
|
||||||
update_metric_container,
|
* We use a more aggressive setting: 96, eq ETX of 0.75.
|
||||||
1
|
*/
|
||||||
};
|
#define PARENT_SWITCH_THRESHOLD 96 /* Eq ETX of 0.75 */
|
||||||
|
#else /* !RPL_MRHOF_SQUARED_ETX */
|
||||||
/* Constants for the ETX moving average */
|
#define MAX_LINK_METRIC 2048 /* Eq ETX of 4 */
|
||||||
#define ETX_SCALE 100
|
#define PARENT_SWITCH_THRESHOLD 160 /* Eq ETX of 1.25 (results in a churn comparable
|
||||||
#define ETX_ALPHA 90
|
to the threshold of 96 in the non-squared case) */
|
||||||
|
#endif /* !RPL_MRHOF_SQUARED_ETX */
|
||||||
/* Reject parents that have a higher link metric than the following. */
|
|
||||||
#define MAX_LINK_METRIC 10
|
|
||||||
|
|
||||||
/* Reject parents that have a higher path cost than the following. */
|
/* Reject parents that have a higher path cost than the following. */
|
||||||
#define MAX_PATH_COST 100
|
#define MAX_PATH_COST 32768 /* Eq path ETX of 256 */
|
||||||
|
|
||||||
/*
|
|
||||||
* The rank must differ more than 1/PARENT_SWITCH_THRESHOLD_DIV in order
|
|
||||||
* to switch preferred parent.
|
|
||||||
*/
|
|
||||||
#define PARENT_SWITCH_THRESHOLD_DIV 2
|
|
||||||
|
|
||||||
typedef uint16_t rpl_path_metric_t;
|
|
||||||
|
|
||||||
static rpl_path_metric_t
|
|
||||||
calculate_path_metric(rpl_parent_t *p)
|
|
||||||
{
|
|
||||||
uip_ds6_nbr_t *nbr;
|
|
||||||
if(p == NULL) {
|
|
||||||
return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR;
|
|
||||||
}
|
|
||||||
nbr = rpl_get_nbr(p);
|
|
||||||
if(nbr == NULL) {
|
|
||||||
return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR;
|
|
||||||
}
|
|
||||||
#if RPL_DAG_MC == RPL_DAG_MC_NONE
|
|
||||||
{
|
|
||||||
return p->rank + (uint16_t)nbr->link_metric;
|
|
||||||
}
|
|
||||||
#elif RPL_DAG_MC == RPL_DAG_MC_ETX
|
|
||||||
return p->mc.obj.etx + (uint16_t)nbr->link_metric;
|
|
||||||
#elif RPL_DAG_MC == RPL_DAG_MC_ENERGY
|
|
||||||
return p->mc.obj.energy.energy_est + (uint16_t)nbr->link_metric;
|
|
||||||
#else
|
|
||||||
#error "Unsupported RPL_DAG_MC configured. See rpl.h."
|
|
||||||
#endif /* RPL_DAG_MC */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
reset(rpl_dag_t *dag)
|
reset(rpl_dag_t *dag)
|
||||||
{
|
{
|
||||||
PRINTF("RPL: Reset MRHOF\n");
|
PRINTF("RPL: Reset MRHOF\n");
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
#if RPL_WITH_DAO_ACK
|
||||||
static void
|
static void
|
||||||
neighbor_link_callback(rpl_parent_t *p, int status, int numtx)
|
dao_ack_callback(rpl_parent_t *p, int status)
|
||||||
{
|
{
|
||||||
uint16_t recorded_etx = 0;
|
if(status == RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT) {
|
||||||
uint16_t packet_etx = numtx * RPL_DAG_MC_ETX_DIVISOR;
|
|
||||||
uint16_t new_etx;
|
|
||||||
uip_ds6_nbr_t *nbr = NULL;
|
|
||||||
|
|
||||||
nbr = rpl_get_nbr(p);
|
|
||||||
if(nbr == NULL) {
|
|
||||||
/* No neighbor for this parent - something bad has occurred */
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* here we need to handle failed DAO's and other stuff */
|
||||||
recorded_etx = nbr->link_metric;
|
PRINTF("RPL: MRHOF - DAO ACK received with status: %d\n", status);
|
||||||
|
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
|
||||||
/* Do not penalize the ETX when collisions or transmission errors occur. */
|
/* punish the ETX as if this was 10 packets lost */
|
||||||
if(status == MAC_TX_OK || status == MAC_TX_NOACK) {
|
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
|
||||||
if(status == MAC_TX_NOACK) {
|
} else if(status == RPL_DAO_ACK_TIMEOUT) { /* timeout = no ack */
|
||||||
packet_etx = MAX_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
|
/* punish the total lack of ACK with a similar punishment */
|
||||||
}
|
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
|
||||||
|
|
||||||
if(p->flags & RPL_PARENT_FLAG_LINK_METRIC_VALID) {
|
|
||||||
/* We already have a valid link metric, use weighted moving average to update it */
|
|
||||||
new_etx = ((uint32_t)recorded_etx * ETX_ALPHA +
|
|
||||||
(uint32_t)packet_etx * (ETX_SCALE - ETX_ALPHA)) / ETX_SCALE;
|
|
||||||
} else {
|
|
||||||
/* We don't have a valid link metric, set it to the current packet's ETX */
|
|
||||||
new_etx = packet_etx;
|
|
||||||
/* Set link metric as valid */
|
|
||||||
p->flags |= RPL_PARENT_FLAG_LINK_METRIC_VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
PRINTF("RPL: ETX changed from %u to %u (packet ETX = %u)\n",
|
|
||||||
(unsigned)(recorded_etx / RPL_DAG_MC_ETX_DIVISOR),
|
|
||||||
(unsigned)(new_etx / RPL_DAG_MC_ETX_DIVISOR),
|
|
||||||
(unsigned)(packet_etx / RPL_DAG_MC_ETX_DIVISOR));
|
|
||||||
/* update the link metric for this nbr */
|
|
||||||
nbr->link_metric = new_etx;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
static rpl_rank_t
|
/*---------------------------------------------------------------------------*/
|
||||||
calculate_rank(rpl_parent_t *p, rpl_rank_t base_rank)
|
static uint16_t
|
||||||
|
parent_link_metric(rpl_parent_t *p)
|
||||||
{
|
{
|
||||||
rpl_rank_t new_rank;
|
const struct link_stats *stats = rpl_get_parent_link_stats(p);
|
||||||
rpl_rank_t rank_increase;
|
if(stats != NULL) {
|
||||||
uip_ds6_nbr_t *nbr;
|
#if RPL_MRHOF_SQUARED_ETX
|
||||||
|
uint32_t squared_etx = ((uint32_t)stats->etx * stats->etx) / LINK_STATS_ETX_DIVISOR;
|
||||||
|
return (uint16_t)MIN(squared_etx, 0xffff);
|
||||||
|
#else /* RPL_MRHOF_SQUARED_ETX */
|
||||||
|
return stats->etx;
|
||||||
|
#endif /* RPL_MRHOF_SQUARED_ETX */
|
||||||
|
}
|
||||||
|
return 0xffff;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static uint16_t
|
||||||
|
parent_path_cost(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
uint16_t base;
|
||||||
|
|
||||||
if(p == NULL || (nbr = rpl_get_nbr(p)) == NULL) {
|
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
|
||||||
if(base_rank == 0) {
|
return 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RPL_WITH_MC
|
||||||
|
/* Handle the different MC types */
|
||||||
|
switch(p->dag->instance->mc.type) {
|
||||||
|
case RPL_DAG_MC_ETX:
|
||||||
|
base = p->mc.obj.etx;
|
||||||
|
break;
|
||||||
|
case RPL_DAG_MC_ENERGY:
|
||||||
|
base = p->mc.obj.energy.energy_est << 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
base = p->rank;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else /* RPL_WITH_MC */
|
||||||
|
base = p->rank;
|
||||||
|
#endif /* RPL_WITH_MC */
|
||||||
|
|
||||||
|
/* path cost upper bound: 0xffff */
|
||||||
|
return MIN((uint32_t)base + parent_link_metric(p), 0xffff);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static rpl_rank_t
|
||||||
|
rank_via_parent(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
uint16_t min_hoprankinc;
|
||||||
|
uint16_t path_cost;
|
||||||
|
|
||||||
|
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
|
||||||
return INFINITE_RANK;
|
return INFINITE_RANK;
|
||||||
}
|
}
|
||||||
rank_increase = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
|
|
||||||
} else {
|
|
||||||
rank_increase = nbr->link_metric;
|
|
||||||
if(base_rank == 0) {
|
|
||||||
base_rank = p->rank;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(INFINITE_RANK - base_rank < rank_increase) {
|
min_hoprankinc = p->dag->instance->min_hoprankinc;
|
||||||
/* Reached the maximum rank. */
|
path_cost = parent_path_cost(p);
|
||||||
new_rank = INFINITE_RANK;
|
|
||||||
} else {
|
|
||||||
/* Calculate the rank based on the new rank information from DIO or
|
|
||||||
stored otherwise. */
|
|
||||||
new_rank = base_rank + rank_increase;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new_rank;
|
/* Rank lower-bound: parent rank + min_hoprankinc */
|
||||||
|
return MAX(MIN((uint32_t)p->rank + min_hoprankinc, 0xffff), path_cost);
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
parent_is_acceptable(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
uint16_t link_metric = parent_link_metric(p);
|
||||||
|
uint16_t path_cost = parent_path_cost(p);
|
||||||
|
/* Exclude links with too high link metrics or path cost (RFC6719, 3.2.2) */
|
||||||
|
return link_metric <= MAX_LINK_METRIC && path_cost <= MAX_PATH_COST;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
parent_has_usable_link(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
uint16_t link_metric = parent_link_metric(p);
|
||||||
|
/* Exclude links with too high link metrics */
|
||||||
|
return link_metric <= MAX_LINK_METRIC;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static rpl_parent_t *
|
||||||
|
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
|
||||||
|
{
|
||||||
|
rpl_dag_t *dag;
|
||||||
|
uint16_t p1_cost;
|
||||||
|
uint16_t p2_cost;
|
||||||
|
int p1_is_acceptable;
|
||||||
|
int p2_is_acceptable;
|
||||||
|
|
||||||
|
p1_is_acceptable = p1 != NULL && parent_is_acceptable(p1);
|
||||||
|
p2_is_acceptable = p2 != NULL && parent_is_acceptable(p2);
|
||||||
|
|
||||||
|
if(!p1_is_acceptable) {
|
||||||
|
return p2_is_acceptable ? p2 : NULL;
|
||||||
|
}
|
||||||
|
if(!p2_is_acceptable) {
|
||||||
|
return p1_is_acceptable ? p1 : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dag = p1->dag; /* Both parents are in the same DAG. */
|
||||||
|
p1_cost = parent_path_cost(p1);
|
||||||
|
p2_cost = parent_path_cost(p2);
|
||||||
|
|
||||||
|
/* Maintain stability of the preferred parent in case of similar ranks. */
|
||||||
|
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
|
||||||
|
if(p1_cost < p2_cost + PARENT_SWITCH_THRESHOLD &&
|
||||||
|
p1_cost > p2_cost - PARENT_SWITCH_THRESHOLD) {
|
||||||
|
return dag->preferred_parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p1_cost < p2_cost ? p1 : p2;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static rpl_dag_t *
|
static rpl_dag_t *
|
||||||
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
||||||
{
|
{
|
||||||
|
@ -203,93 +240,77 @@ best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
||||||
|
|
||||||
return d1->rank < d2->rank ? d1 : d2;
|
return d1->rank < d2->rank ? d1 : d2;
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static rpl_parent_t *
|
#if !RPL_WITH_MC
|
||||||
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
|
|
||||||
{
|
|
||||||
rpl_dag_t *dag;
|
|
||||||
rpl_path_metric_t min_diff;
|
|
||||||
rpl_path_metric_t p1_metric;
|
|
||||||
rpl_path_metric_t p2_metric;
|
|
||||||
|
|
||||||
dag = p1->dag; /* Both parents are in the same DAG. */
|
|
||||||
|
|
||||||
min_diff = RPL_DAG_MC_ETX_DIVISOR /
|
|
||||||
PARENT_SWITCH_THRESHOLD_DIV;
|
|
||||||
|
|
||||||
p1_metric = calculate_path_metric(p1);
|
|
||||||
p2_metric = calculate_path_metric(p2);
|
|
||||||
|
|
||||||
/* Maintain stability of the preferred parent in case of similar ranks. */
|
|
||||||
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
|
|
||||||
if(p1_metric < p2_metric + min_diff &&
|
|
||||||
p1_metric > p2_metric - min_diff) {
|
|
||||||
PRINTF("RPL: MRHOF hysteresis: %u <= %u <= %u\n",
|
|
||||||
p2_metric - min_diff,
|
|
||||||
p1_metric,
|
|
||||||
p2_metric + min_diff);
|
|
||||||
return dag->preferred_parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return p1_metric < p2_metric ? p1 : p2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if RPL_DAG_MC == RPL_DAG_MC_NONE
|
|
||||||
static void
|
static void
|
||||||
update_metric_container(rpl_instance_t *instance)
|
update_metric_container(rpl_instance_t *instance)
|
||||||
{
|
{
|
||||||
instance->mc.type = RPL_DAG_MC;
|
instance->mc.type = RPL_DAG_MC_NONE;
|
||||||
}
|
}
|
||||||
#else
|
#else /* RPL_WITH_MC */
|
||||||
static void
|
static void
|
||||||
update_metric_container(rpl_instance_t *instance)
|
update_metric_container(rpl_instance_t *instance)
|
||||||
{
|
{
|
||||||
rpl_path_metric_t path_metric;
|
|
||||||
rpl_dag_t *dag;
|
rpl_dag_t *dag;
|
||||||
#if RPL_DAG_MC == RPL_DAG_MC_ENERGY
|
uint16_t path_cost;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
#endif
|
|
||||||
|
|
||||||
instance->mc.type = RPL_DAG_MC;
|
|
||||||
instance->mc.flags = RPL_DAG_MC_FLAG_P;
|
|
||||||
instance->mc.aggr = RPL_DAG_MC_AGGR_ADDITIVE;
|
|
||||||
instance->mc.prec = 0;
|
|
||||||
|
|
||||||
dag = instance->current_dag;
|
dag = instance->current_dag;
|
||||||
|
if(dag == NULL || !dag->joined) {
|
||||||
if (!dag->joined) {
|
|
||||||
PRINTF("RPL: Cannot update the metric container when not joined\n");
|
PRINTF("RPL: Cannot update the metric container when not joined\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dag->rank == ROOT_RANK(instance)) {
|
if(dag->rank == ROOT_RANK(instance)) {
|
||||||
path_metric = 0;
|
/* Configure MC at root only, other nodes are auto-configured when joining */
|
||||||
|
instance->mc.type = RPL_DAG_MC;
|
||||||
|
instance->mc.flags = 0;
|
||||||
|
instance->mc.aggr = RPL_DAG_MC_AGGR_ADDITIVE;
|
||||||
|
instance->mc.prec = 0;
|
||||||
|
path_cost = dag->rank;
|
||||||
} else {
|
} else {
|
||||||
path_metric = calculate_path_metric(dag->preferred_parent);
|
path_cost = parent_path_cost(dag->preferred_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if RPL_DAG_MC == RPL_DAG_MC_ETX
|
/* Handle the different MC types */
|
||||||
|
switch(instance->mc.type) {
|
||||||
|
case RPL_DAG_MC_NONE:
|
||||||
|
break;
|
||||||
|
case RPL_DAG_MC_ETX:
|
||||||
instance->mc.length = sizeof(instance->mc.obj.etx);
|
instance->mc.length = sizeof(instance->mc.obj.etx);
|
||||||
instance->mc.obj.etx = path_metric;
|
instance->mc.obj.etx = path_cost;
|
||||||
|
break;
|
||||||
PRINTF("RPL: My path ETX to the root is %u.%u\n",
|
case RPL_DAG_MC_ENERGY:
|
||||||
instance->mc.obj.etx / RPL_DAG_MC_ETX_DIVISOR,
|
|
||||||
(instance->mc.obj.etx % RPL_DAG_MC_ETX_DIVISOR * 100) /
|
|
||||||
RPL_DAG_MC_ETX_DIVISOR);
|
|
||||||
#elif RPL_DAG_MC == RPL_DAG_MC_ENERGY
|
|
||||||
instance->mc.length = sizeof(instance->mc.obj.energy);
|
instance->mc.length = sizeof(instance->mc.obj.energy);
|
||||||
|
|
||||||
if(dag->rank == ROOT_RANK(instance)) {
|
if(dag->rank == ROOT_RANK(instance)) {
|
||||||
type = RPL_DAG_MC_ENERGY_TYPE_MAINS;
|
type = RPL_DAG_MC_ENERGY_TYPE_MAINS;
|
||||||
} else {
|
} else {
|
||||||
type = RPL_DAG_MC_ENERGY_TYPE_BATTERY;
|
type = RPL_DAG_MC_ENERGY_TYPE_BATTERY;
|
||||||
}
|
}
|
||||||
|
|
||||||
instance->mc.obj.energy.flags = type << RPL_DAG_MC_ENERGY_TYPE;
|
instance->mc.obj.energy.flags = type << RPL_DAG_MC_ENERGY_TYPE;
|
||||||
instance->mc.obj.energy.energy_est = path_metric;
|
/* Energy_est is only one byte, use the least significant byte of the path metric. */
|
||||||
#endif /* RPL_DAG_MC == RPL_DAG_MC_ETX */
|
instance->mc.obj.energy.energy_est = path_cost >> 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PRINTF("RPL: MRHOF, non-supported MC %u\n", instance->mc.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* RPL_DAG_MC == RPL_DAG_MC_NONE */
|
#endif /* RPL_WITH_MC */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
rpl_of_t rpl_mrhof = {
|
||||||
|
reset,
|
||||||
|
#if RPL_WITH_DAO_ACK
|
||||||
|
dao_ack_callback,
|
||||||
|
#endif
|
||||||
|
parent_link_metric,
|
||||||
|
parent_has_usable_link,
|
||||||
|
parent_path_cost,
|
||||||
|
rank_via_parent,
|
||||||
|
best_parent,
|
||||||
|
best_dag,
|
||||||
|
update_metric_container,
|
||||||
|
RPL_OCP_MRHOF
|
||||||
|
};
|
||||||
|
|
||||||
/** @}*/
|
/** @}*/
|
||||||
|
|
256
core/net/rpl/rpl-nbr-policy.c
Normal file
256
core/net/rpl/rpl-nbr-policy.c
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2015, Yanzi Networks AB.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holders nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||||
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* \addtogroup uip6
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
*
|
||||||
|
* Default RPL NBR policy
|
||||||
|
* decides when to add a new discovered node to the nbr table from RPL.
|
||||||
|
*
|
||||||
|
* \author Joakim Eriksson <joakime@sics.se>
|
||||||
|
* Contributors: Niclas Finne <nfi@sics.se>, Oriol Piñol <oriol@yanzi.se>,
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "net/rpl/rpl-private.h"
|
||||||
|
#include "net/nbr-table.h"
|
||||||
|
#include "net/ipv6/uip-ds6-nbr.h"
|
||||||
|
#include "net/ipv6/uip-ds6-route.h"
|
||||||
|
|
||||||
|
#define DEBUG DEBUG_NONE
|
||||||
|
#include "net/ip/uip-debug.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Policy for neighbor adds
|
||||||
|
* - one node is locked (default route)
|
||||||
|
* - max X children (nexthops)
|
||||||
|
* - max Y "best parents"
|
||||||
|
* => at least MAX_NBRS - (Y + X + 1) free slots for other.
|
||||||
|
*
|
||||||
|
* NOTE: this policy assumes that all neighbors end up being IPv6
|
||||||
|
* neighbors and are not only MAC neighbors.
|
||||||
|
*/
|
||||||
|
#define MAX_CHILDREN (NBR_TABLE_MAX_NEIGHBORS - 2)
|
||||||
|
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
|
||||||
|
|
||||||
|
static int num_parents; /* any node that are possible parents */
|
||||||
|
static int num_children; /* all children that we have as nexthop */
|
||||||
|
static int num_free;
|
||||||
|
static linkaddr_t *worst_rank_nbr; /* the parent that has the worst rank */
|
||||||
|
static rpl_rank_t worst_rank;
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
#if DEBUG == DEBUG_FULL
|
||||||
|
/*
|
||||||
|
* This create a periodic call of the update_nbr function that will print
|
||||||
|
* useful debugging information when in DEBUG_FULL mode
|
||||||
|
*/
|
||||||
|
static void update_nbr(void);
|
||||||
|
static struct ctimer periodic_timer;
|
||||||
|
static int timer_init = 0;
|
||||||
|
static void
|
||||||
|
handle_periodic_timer(void *ptr)
|
||||||
|
{
|
||||||
|
update_nbr();
|
||||||
|
ctimer_restart(&periodic_timer);
|
||||||
|
}
|
||||||
|
#endif /* DEBUG == DEBUG_FULL */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static void
|
||||||
|
update_nbr(void)
|
||||||
|
{
|
||||||
|
uip_ds6_nbr_t *nbr;
|
||||||
|
rpl_parent_t *parent;
|
||||||
|
int num_used;
|
||||||
|
int is_used;
|
||||||
|
rpl_rank_t rank;
|
||||||
|
|
||||||
|
#if DEBUG == DEBUG_FULL
|
||||||
|
if(!timer_init) {
|
||||||
|
timer_init = 1;
|
||||||
|
ctimer_set(&periodic_timer, 60 * CLOCK_SECOND,
|
||||||
|
&handle_periodic_timer, NULL);
|
||||||
|
}
|
||||||
|
#endif /* DEBUG == DEBUG_FULL */
|
||||||
|
|
||||||
|
worst_rank = 0;
|
||||||
|
worst_rank_nbr = NULL;
|
||||||
|
num_used = 0;
|
||||||
|
num_parents = 0;
|
||||||
|
num_children = 0;
|
||||||
|
|
||||||
|
nbr = nbr_table_head(ds6_neighbors);
|
||||||
|
while(nbr != NULL) {
|
||||||
|
linkaddr_t *lladdr = nbr_table_get_lladdr(ds6_neighbors, nbr);
|
||||||
|
is_used = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if this neighbor is used as nexthop and therefor being a
|
||||||
|
* RPL child.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(uip_ds6_route_is_nexthop(&nbr->ipaddr) != 0) {
|
||||||
|
is_used++;
|
||||||
|
num_children++;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = rpl_get_parent((uip_lladdr_t *)lladdr);
|
||||||
|
if(parent != NULL) {
|
||||||
|
num_parents++;
|
||||||
|
|
||||||
|
if(parent->dag != NULL && parent->dag->preferred_parent == parent) {
|
||||||
|
/*
|
||||||
|
* This is the preferred parent for the DAG and must not be removed
|
||||||
|
* Note: this assumes that only RPL adds default routes.
|
||||||
|
*/
|
||||||
|
} else if(is_used == 0 && worst_rank < INFINITE_RANK &&
|
||||||
|
parent->rank > 0 &&
|
||||||
|
parent->dag != NULL &&
|
||||||
|
parent->dag->instance != NULL &&
|
||||||
|
(rank = parent->dag->instance->of->rank_via_parent(parent)) > worst_rank) {
|
||||||
|
/* This is the worst-rank neighbor - this is a good candidate for removal */
|
||||||
|
worst_rank = rank;
|
||||||
|
worst_rank_nbr = lladdr;
|
||||||
|
}
|
||||||
|
/* add to is_used after evaluation of is_used above */
|
||||||
|
is_used++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_used == 0) {
|
||||||
|
/* This neighbor is neither parent or child and can be safely removed */
|
||||||
|
worst_rank_nbr = lladdr;
|
||||||
|
worst_rank = INFINITE_RANK;
|
||||||
|
} else if(is_used > 1) {
|
||||||
|
PRINTF("NBR-POLICY: *** Neighbor is both child and candidate parent: ");
|
||||||
|
PRINTLLADDR((uip_lladdr_t *)lladdr);
|
||||||
|
PRINTF("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
nbr = nbr_table_next(ds6_neighbors, nbr);
|
||||||
|
num_used++;
|
||||||
|
}
|
||||||
|
/* how many more IP neighbors can be have? */
|
||||||
|
num_free = NBR_TABLE_MAX_NEIGHBORS - num_used;
|
||||||
|
|
||||||
|
PRINTF("NBR-POLICY: Free: %d, Children: %d, Parents: %d Routes: %d\n",
|
||||||
|
num_free, num_children, num_parents, uip_ds6_route_num_routes());
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Called whenever we get a unicast DIS - e.g. someone that already
|
||||||
|
have this node in its table - since it is a unicast */
|
||||||
|
const linkaddr_t *
|
||||||
|
find_removable_dis(uip_ipaddr_t *from)
|
||||||
|
{
|
||||||
|
|
||||||
|
update_nbr();
|
||||||
|
if(num_free > 0) {
|
||||||
|
/* there are free entries (e.g. unsused by RPL and ND6) but since it is
|
||||||
|
used by other modules we can not pick these entries for removal. */
|
||||||
|
PRINTF("Num-free > 0 = %d - Other for RPL/ND6 unused NBR entry exists .",
|
||||||
|
num_free);
|
||||||
|
}
|
||||||
|
if(num_children < MAX_CHILDREN) {
|
||||||
|
return worst_rank_nbr;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
const linkaddr_t *
|
||||||
|
find_removable_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
||||||
|
{
|
||||||
|
rpl_instance_t *instance;
|
||||||
|
|
||||||
|
update_nbr();
|
||||||
|
|
||||||
|
instance = rpl_get_instance(dio->instance_id);
|
||||||
|
if(instance == NULL || instance->current_dag == NULL) {
|
||||||
|
PRINTF("Did not find instance id: %d\n", dio->instance_id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the new neighbor only if it is better than the worst parent. */
|
||||||
|
if(dio->rank + instance->min_hoprankinc < worst_rank - instance->min_hoprankinc / 2) {
|
||||||
|
/* Found *great* neighbor - add! */
|
||||||
|
PRINTF("Found better neighbor %d < %d - add to cache...\n",
|
||||||
|
rank, worst_rank);
|
||||||
|
|
||||||
|
return worst_rank_nbr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINTF("Found worse neighbor with new %d and old %d - NOT add to cache.\n",
|
||||||
|
rank, worst_rank);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
const linkaddr_t *
|
||||||
|
find_removable_dao(uip_ipaddr_t *from, rpl_instance_t *instance)
|
||||||
|
{
|
||||||
|
int max = MAX_CHILDREN;
|
||||||
|
update_nbr();
|
||||||
|
|
||||||
|
if(instance != NULL) {
|
||||||
|
/* No need to reserve space for parents for RPL ROOT */
|
||||||
|
if(instance->current_dag->rank == ROOT_RANK(instance)) {
|
||||||
|
max = NBR_TABLE_MAX_NEIGHBORS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if this DAO sender is not yet neighbor and there is already too
|
||||||
|
many children. */
|
||||||
|
if(num_children >= max) {
|
||||||
|
PRINTF("Can not add another child - already at max.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* remove the worst ranked nbr */
|
||||||
|
return worst_rank_nbr;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
const linkaddr_t *
|
||||||
|
rpl_nbr_policy_find_removable(nbr_table_reason_t reason,void * data)
|
||||||
|
{
|
||||||
|
/* When we get the DIO/DAO/DIS we know that UIP contains the
|
||||||
|
incoming packet */
|
||||||
|
switch(reason) {
|
||||||
|
case NBR_TABLE_REASON_RPL_DIO:
|
||||||
|
return find_removable_dio(&UIP_IP_BUF->srcipaddr, data);
|
||||||
|
case NBR_TABLE_REASON_RPL_DAO:
|
||||||
|
return find_removable_dao(&UIP_IP_BUF->srcipaddr, data);
|
||||||
|
case NBR_TABLE_REASON_RPL_DIS:
|
||||||
|
return find_removable_dis(&UIP_IP_BUF->srcipaddr);
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/** @}*/
|
230
core/net/rpl/rpl-ns.c
Normal file
230
core/net/rpl/rpl-ns.c
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016, Inria.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the Institute nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This file is part of the Contiki operating system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* RPL non-storing mode specific functions. Includes support for
|
||||||
|
* source routing.
|
||||||
|
*
|
||||||
|
* \author Simon Duquennoy <simon.duquennoy@inria.fr>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "net/rpl/rpl-conf.h"
|
||||||
|
|
||||||
|
#include "net/ip/uip.h"
|
||||||
|
#include "net/ip/tcpip.h"
|
||||||
|
#include "net/ipv6/uip-ds6.h"
|
||||||
|
#include "net/ipv6/uip-icmp6.h"
|
||||||
|
#include "net/rpl/rpl-private.h"
|
||||||
|
#include "net/rpl/rpl-ns.h"
|
||||||
|
#include "lib/list.h"
|
||||||
|
#include "lib/memb.h"
|
||||||
|
|
||||||
|
#if RPL_WITH_NON_STORING
|
||||||
|
|
||||||
|
#define DEBUG DEBUG_NONE
|
||||||
|
#include "net/ip/uip-debug.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Total number of nodes */
|
||||||
|
static int num_nodes;
|
||||||
|
|
||||||
|
/* Every known node in the network */
|
||||||
|
LIST(nodelist);
|
||||||
|
MEMB(nodememb, rpl_ns_node_t, RPL_NS_LINK_NUM);
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
int
|
||||||
|
rpl_ns_num_nodes(void)
|
||||||
|
{
|
||||||
|
return num_nodes;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
node_matches_address(const rpl_dag_t *dag, const rpl_ns_node_t *node, const uip_ipaddr_t *addr)
|
||||||
|
{
|
||||||
|
return addr != NULL
|
||||||
|
&& node != NULL
|
||||||
|
&& dag != NULL
|
||||||
|
&& dag == node->dag
|
||||||
|
&& !memcmp(addr, &node->dag->dag_id, 8)
|
||||||
|
&& !memcmp(((const unsigned char *)addr) + 8, node->link_identifier, 8);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
rpl_ns_node_t *
|
||||||
|
rpl_ns_get_node(const rpl_dag_t *dag, const uip_ipaddr_t *addr)
|
||||||
|
{
|
||||||
|
rpl_ns_node_t *l;
|
||||||
|
for(l = list_head(nodelist); l != NULL; l = list_item_next(l)) {
|
||||||
|
/* Compare prefix and node identifier */
|
||||||
|
if(node_matches_address(dag, l, addr)) {
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
int
|
||||||
|
rpl_ns_is_node_reachable(const rpl_dag_t *dag, const uip_ipaddr_t *addr)
|
||||||
|
{
|
||||||
|
int max_depth = RPL_NS_LINK_NUM;
|
||||||
|
rpl_ns_node_t *node = rpl_ns_get_node(dag, addr);
|
||||||
|
rpl_ns_node_t *root_node = rpl_ns_get_node(dag, dag != NULL ? &dag->dag_id : NULL);
|
||||||
|
while(node != NULL && node != root_node && max_depth > 0) {
|
||||||
|
node = node->parent;
|
||||||
|
max_depth--;
|
||||||
|
}
|
||||||
|
return node != NULL && node == root_node;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
rpl_ns_expire_parent(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent)
|
||||||
|
{
|
||||||
|
rpl_ns_node_t *l = rpl_ns_get_node(dag, child);
|
||||||
|
/* Check if parent matches */
|
||||||
|
if(l != NULL && node_matches_address(dag, l->parent, parent)) {
|
||||||
|
l->lifetime = RPL_NOPATH_REMOVAL_DELAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
rpl_ns_node_t *
|
||||||
|
rpl_ns_update_node(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent, uint32_t lifetime)
|
||||||
|
{
|
||||||
|
rpl_ns_node_t *child_node = rpl_ns_get_node(dag, child);
|
||||||
|
rpl_ns_node_t *parent_node = rpl_ns_get_node(dag, parent);
|
||||||
|
rpl_ns_node_t *old_parent_node;
|
||||||
|
|
||||||
|
if(parent != NULL) {
|
||||||
|
/* No node for the parent, add one with infinite lifetime */
|
||||||
|
if(parent_node == NULL) {
|
||||||
|
parent_node = rpl_ns_update_node(dag, parent, NULL, 0xffffffff);
|
||||||
|
if(parent_node == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No node for this child, add one */
|
||||||
|
if(child_node == NULL) {
|
||||||
|
child_node = memb_alloc(&nodememb);
|
||||||
|
/* No space left, abort */
|
||||||
|
if(child_node == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
child_node->parent = NULL;
|
||||||
|
list_add(nodelist, child_node);
|
||||||
|
num_nodes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize node */
|
||||||
|
child_node->dag = dag;
|
||||||
|
child_node->lifetime = lifetime;
|
||||||
|
memcpy(child_node->link_identifier, ((const unsigned char *)child) + 8, 8);
|
||||||
|
|
||||||
|
/* Is the node reachable before the update? */
|
||||||
|
if(rpl_ns_is_node_reachable(dag, child)) {
|
||||||
|
old_parent_node = child_node->parent;
|
||||||
|
/* Update node */
|
||||||
|
child_node->parent = parent_node;
|
||||||
|
/* Has the node become unreachable? May happen if we create a loop. */
|
||||||
|
if(!rpl_ns_is_node_reachable(dag, child)) {
|
||||||
|
/* The new parent makes the node unreachable, restore old parent.
|
||||||
|
* We will take the update next time, with chances we know more of
|
||||||
|
* the topology and the loop is gone. */
|
||||||
|
child_node->parent = old_parent_node;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
child_node->parent = parent_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return child_node;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
rpl_ns_init(void)
|
||||||
|
{
|
||||||
|
num_nodes = 0;
|
||||||
|
memb_init(&nodememb);
|
||||||
|
list_init(nodelist);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
rpl_ns_node_t *
|
||||||
|
rpl_ns_node_head(void)
|
||||||
|
{
|
||||||
|
return list_head(nodelist);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
rpl_ns_node_t *
|
||||||
|
rpl_ns_node_next(rpl_ns_node_t *item)
|
||||||
|
{
|
||||||
|
return list_item_next(item);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
rpl_ns_get_node_global_addr(uip_ipaddr_t *addr, rpl_ns_node_t *node)
|
||||||
|
{
|
||||||
|
if(addr != NULL && node != NULL && node->dag != NULL) {
|
||||||
|
memcpy(addr, &node->dag->dag_id, 8);
|
||||||
|
memcpy(((unsigned char *)addr) + 8, &node->link_identifier, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
void
|
||||||
|
rpl_ns_periodic(void)
|
||||||
|
{
|
||||||
|
rpl_ns_node_t *l;
|
||||||
|
/* First pass, decrement lifetime for all nodes with non-infinite lifetime */
|
||||||
|
for(l = list_head(nodelist); l != NULL; l = list_item_next(l)) {
|
||||||
|
/* Don't touch infinite lifetime nodes */
|
||||||
|
if(l->lifetime != 0xffffffff && l->lifetime > 0) {
|
||||||
|
l->lifetime--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Second pass, for all expire nodes, deallocate them iff no child points to them */
|
||||||
|
for(l = list_head(nodelist); l != NULL; l = list_item_next(l)) {
|
||||||
|
if(l->lifetime == 0) {
|
||||||
|
rpl_ns_node_t *l2;
|
||||||
|
for(l2 = list_head(nodelist); l2 != NULL; l2 = list_item_next(l2)) {
|
||||||
|
if(l2->parent == l) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* No child found, deallocate node */
|
||||||
|
list_remove(nodelist, l);
|
||||||
|
memb_free(&nodememb, l);
|
||||||
|
num_nodes--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* RPL_WITH_NON_STORING */
|
72
core/net/rpl/rpl-ns.h
Normal file
72
core/net/rpl/rpl-ns.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016, Inria.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the Institute nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This file is part of the Contiki operating system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* RPL non-storing mode specific functions. Includes support for
|
||||||
|
* source routing.
|
||||||
|
*
|
||||||
|
* \author Simon Duquennoy <simon.duquennoy@inria.fr>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef RPL_NS_H
|
||||||
|
#define RPL_NS_H
|
||||||
|
|
||||||
|
#include "rpl-conf.h"
|
||||||
|
|
||||||
|
#ifdef RPL_NS_CONF_LINK_NUM
|
||||||
|
#define RPL_NS_LINK_NUM RPL_NS_CONF_LINK_NUM
|
||||||
|
#else /* RPL_NS_CONF_LINK_NUM */
|
||||||
|
#define RPL_NS_LINK_NUM 32
|
||||||
|
#endif /* RPL_NS_CONF_LINK_NUM */
|
||||||
|
|
||||||
|
typedef struct rpl_ns_node {
|
||||||
|
struct rpl_ns_node *next;
|
||||||
|
uint32_t lifetime;
|
||||||
|
rpl_dag_t *dag;
|
||||||
|
/* Store only IPv6 link identifiers as all nodes in the DAG share the same prefix */
|
||||||
|
unsigned char link_identifier[8];
|
||||||
|
struct rpl_ns_node *parent;
|
||||||
|
} rpl_ns_node_t;
|
||||||
|
|
||||||
|
int rpl_ns_num_nodes(void);
|
||||||
|
void rpl_ns_expire_parent(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent);
|
||||||
|
rpl_ns_node_t *rpl_ns_update_node(rpl_dag_t *dag, const uip_ipaddr_t *child, const uip_ipaddr_t *parent, uint32_t lifetime);
|
||||||
|
void rpl_ns_init(void);
|
||||||
|
rpl_ns_node_t *rpl_ns_node_head(void);
|
||||||
|
rpl_ns_node_t *rpl_ns_node_next(rpl_ns_node_t *item);
|
||||||
|
rpl_ns_node_t *rpl_ns_get_node(const rpl_dag_t *dag, const uip_ipaddr_t *addr);
|
||||||
|
int rpl_ns_is_node_reachable(const rpl_dag_t *dag, const uip_ipaddr_t *addr);
|
||||||
|
void rpl_ns_get_node_global_addr(uip_ipaddr_t *addr, rpl_ns_node_t *node);
|
||||||
|
void rpl_ns_periodic();
|
||||||
|
|
||||||
|
#endif /* RPL_NS_H */
|
|
@ -27,11 +27,12 @@
|
||||||
* SUCH DAMAGE.
|
* SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
* This file is part of the Contiki operating system.
|
* This file is part of the Contiki operating system.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \file
|
* \file
|
||||||
* An implementation of RPL's objective function 0.
|
* An implementation of RPL's objective function 0, RFC6552
|
||||||
*
|
*
|
||||||
* \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
|
* \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
|
||||||
*/
|
*/
|
||||||
|
@ -41,133 +42,197 @@
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "net/rpl/rpl.h"
|
||||||
#include "net/rpl/rpl-private.h"
|
#include "net/rpl/rpl-private.h"
|
||||||
|
#include "net/nbr-table.h"
|
||||||
|
#include "net/link-stats.h"
|
||||||
|
|
||||||
#define DEBUG DEBUG_NONE
|
#define DEBUG DEBUG_NONE
|
||||||
#include "net/ip/uip-debug.h"
|
#include "net/ip/uip-debug.h"
|
||||||
|
|
||||||
static void reset(rpl_dag_t *);
|
/* Constants from RFC6552. We use the default values. */
|
||||||
static rpl_parent_t *best_parent(rpl_parent_t *, rpl_parent_t *);
|
#define RANK_STRETCH 0 /* Must be in the range [0;5] */
|
||||||
static rpl_dag_t *best_dag(rpl_dag_t *, rpl_dag_t *);
|
#define RANK_FACTOR 1 /* Must be in the range [1;4] */
|
||||||
static rpl_rank_t calculate_rank(rpl_parent_t *, rpl_rank_t);
|
|
||||||
static void update_metric_container(rpl_instance_t *);
|
|
||||||
|
|
||||||
rpl_of_t rpl_of0 = {
|
#define MIN_STEP_OF_RANK 1
|
||||||
reset,
|
#define MAX_STEP_OF_RANK 9
|
||||||
NULL,
|
|
||||||
best_parent,
|
|
||||||
best_dag,
|
|
||||||
calculate_rank,
|
|
||||||
update_metric_container,
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DEFAULT_RANK_INCREMENT RPL_MIN_HOPRANKINC
|
/* OF0 computes rank increase as follows:
|
||||||
|
* rank_increase = (RANK_FACTOR * STEP_OF_RANK + RANK_STRETCH) * min_hop_rank_increase
|
||||||
|
* STEP_OF_RANK is an implementation-specific scalar value in the range [1;9].
|
||||||
|
* RFC6552 provides a default value of 3 but recommends to use a dynamic link metric
|
||||||
|
* such as ETX.
|
||||||
|
* */
|
||||||
|
|
||||||
#define MIN_DIFFERENCE (RPL_MIN_HOPRANKINC + RPL_MIN_HOPRANKINC / 2)
|
#define RPL_OF0_FIXED_SR 0
|
||||||
|
#define RPL_OF0_ETX_BASED_SR 1
|
||||||
|
/* Select RPL_OF0_FIXED_SR or RPL_OF0_ETX_BASED_SR */
|
||||||
|
#ifdef RPL_OF0_CONF_SR
|
||||||
|
#define RPL_OF0_SR RPL_OF0_CONF_SR
|
||||||
|
#else /* RPL_OF0_CONF_SR */
|
||||||
|
#define RPL_OF0_SR RPL_OF0_ETX_BASED_SR
|
||||||
|
#endif /* RPL_OF0_CONF_SR */
|
||||||
|
|
||||||
|
#if RPL_OF0_FIXED_SR
|
||||||
|
#define STEP_OF_RANK(p) (3)
|
||||||
|
#endif /* RPL_OF0_FIXED_SR */
|
||||||
|
|
||||||
|
#if RPL_OF0_ETX_BASED_SR
|
||||||
|
/* Numbers suggested by P. Thubert for in the 6TiSCH WG. Anything that maps ETX to
|
||||||
|
* a step between 1 and 9 works. */
|
||||||
|
#define STEP_OF_RANK(p) (((3 * parent_link_metric(p)) / LINK_STATS_ETX_DIVISOR) - 2)
|
||||||
|
#endif /* RPL_OF0_ETX_BASED_SR */
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
reset(rpl_dag_t *dag)
|
reset(rpl_dag_t *dag)
|
||||||
{
|
{
|
||||||
PRINTF("RPL: Resetting OF0\n");
|
PRINTF("RPL: Reset OF0\n");
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static rpl_rank_t
|
#if RPL_WITH_DAO_ACK
|
||||||
calculate_rank(rpl_parent_t *p, rpl_rank_t base_rank)
|
static void
|
||||||
|
dao_ack_callback(rpl_parent_t *p, int status)
|
||||||
|
{
|
||||||
|
if(status == RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* here we need to handle failed DAO's and other stuff */
|
||||||
|
PRINTF("RPL: OF0 - DAO ACK received with status: %d\n", status);
|
||||||
|
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
|
||||||
|
/* punish the ETX as if this was 10 packets lost */
|
||||||
|
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
|
||||||
|
} else if(status == RPL_DAO_ACK_TIMEOUT) { /* timeout = no ack */
|
||||||
|
/* punish the total lack of ACK with a similar punishment */
|
||||||
|
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* RPL_WITH_DAO_ACK */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static uint16_t
|
||||||
|
parent_link_metric(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
/* OF0 operates without metric container; the only metric we have is ETX */
|
||||||
|
const struct link_stats *stats = rpl_get_parent_link_stats(p);
|
||||||
|
return stats != NULL ? stats->etx : 0xffff;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static uint16_t
|
||||||
|
parent_rank_increase(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
uint16_t min_hoprankinc;
|
||||||
|
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
|
||||||
|
return INFINITE_RANK;
|
||||||
|
}
|
||||||
|
min_hoprankinc = p->dag->instance->min_hoprankinc;
|
||||||
|
return (RANK_FACTOR * STEP_OF_RANK(p) + RANK_STRETCH) * min_hoprankinc;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static uint16_t
|
||||||
|
parent_path_cost(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
if(p == NULL) {
|
||||||
|
return 0xffff;
|
||||||
|
}
|
||||||
|
/* path cost upper bound: 0xffff */
|
||||||
|
return MIN((uint32_t)p->rank + parent_link_metric(p), 0xffff);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static rpl_rank_t
|
||||||
|
rank_via_parent(rpl_parent_t *p)
|
||||||
{
|
{
|
||||||
rpl_rank_t increment;
|
|
||||||
if(base_rank == 0) {
|
|
||||||
if(p == NULL) {
|
if(p == NULL) {
|
||||||
return INFINITE_RANK;
|
return INFINITE_RANK;
|
||||||
|
} else {
|
||||||
|
return MIN((uint32_t)p->rank + parent_rank_increase(p), INFINITE_RANK);
|
||||||
}
|
}
|
||||||
base_rank = p->rank;
|
|
||||||
}
|
|
||||||
|
|
||||||
increment = p != NULL ?
|
|
||||||
p->dag->instance->min_hoprankinc :
|
|
||||||
DEFAULT_RANK_INCREMENT;
|
|
||||||
|
|
||||||
if((rpl_rank_t)(base_rank + increment) < base_rank) {
|
|
||||||
PRINTF("RPL: OF0 rank %d incremented to infinite rank due to wrapping\n",
|
|
||||||
base_rank);
|
|
||||||
return INFINITE_RANK;
|
|
||||||
}
|
|
||||||
return base_rank + increment;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static rpl_dag_t *
|
static int
|
||||||
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
parent_is_acceptable(rpl_parent_t *p)
|
||||||
{
|
{
|
||||||
if(d1->grounded) {
|
return STEP_OF_RANK(p) >= MIN_STEP_OF_RANK
|
||||||
if (!d2->grounded) {
|
&& STEP_OF_RANK(p) <= MAX_STEP_OF_RANK;
|
||||||
return d1;
|
|
||||||
}
|
|
||||||
} else if(d2->grounded) {
|
|
||||||
return d2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(d1->preference < d2->preference) {
|
|
||||||
return d2;
|
|
||||||
} else {
|
|
||||||
if(d1->preference > d2->preference) {
|
|
||||||
return d1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(d2->rank < d1->rank) {
|
|
||||||
return d2;
|
|
||||||
} else {
|
|
||||||
return d1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static int
|
||||||
|
parent_has_usable_link(rpl_parent_t *p)
|
||||||
|
{
|
||||||
|
return parent_is_acceptable(p);
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static rpl_parent_t *
|
static rpl_parent_t *
|
||||||
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
|
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
|
||||||
{
|
{
|
||||||
rpl_rank_t r1, r2;
|
|
||||||
rpl_dag_t *dag;
|
rpl_dag_t *dag;
|
||||||
uip_ds6_nbr_t *nbr1, *nbr2;
|
uint16_t p1_cost;
|
||||||
nbr1 = rpl_get_nbr(p1);
|
uint16_t p2_cost;
|
||||||
nbr2 = rpl_get_nbr(p2);
|
int p1_is_acceptable;
|
||||||
|
int p2_is_acceptable;
|
||||||
|
|
||||||
dag = (rpl_dag_t *)p1->dag; /* Both parents must be in the same DAG. */
|
p1_is_acceptable = p1 != NULL && parent_is_acceptable(p1);
|
||||||
|
p2_is_acceptable = p2 != NULL && parent_is_acceptable(p2);
|
||||||
|
|
||||||
if(nbr1 == NULL || nbr2 == NULL) {
|
if(!p1_is_acceptable) {
|
||||||
return dag->preferred_parent;
|
return p2_is_acceptable ? p2 : NULL;
|
||||||
|
}
|
||||||
|
if(!p2_is_acceptable) {
|
||||||
|
return p1_is_acceptable ? p1 : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PRINTF("RPL: Comparing parent ");
|
dag = p1->dag; /* Both parents are in the same DAG. */
|
||||||
PRINT6ADDR(rpl_get_parent_ipaddr(p1));
|
p1_cost = parent_path_cost(p1);
|
||||||
PRINTF(" (confidence %d, rank %d) with parent ",
|
p2_cost = parent_path_cost(p2);
|
||||||
nbr1->link_metric, p1->rank);
|
|
||||||
PRINT6ADDR(rpl_get_parent_ipaddr(p2));
|
|
||||||
PRINTF(" (confidence %d, rank %d)\n",
|
|
||||||
nbr2->link_metric, p2->rank);
|
|
||||||
|
|
||||||
|
/* Paths costs coarse-grained (multiple of min_hoprankinc), we operate without hysteresis */
|
||||||
r1 = DAG_RANK(p1->rank, p1->dag->instance) * RPL_MIN_HOPRANKINC +
|
if(p1_cost != p2_cost) {
|
||||||
nbr1->link_metric;
|
/* Pick parent with lowest path cost */
|
||||||
r2 = DAG_RANK(p2->rank, p1->dag->instance) * RPL_MIN_HOPRANKINC +
|
return p1_cost < p2_cost ? p1 : p2;
|
||||||
nbr2->link_metric;
|
|
||||||
/* Compare two parents by looking both and their rank and at the ETX
|
|
||||||
for that parent. We choose the parent that has the most
|
|
||||||
favourable combination. */
|
|
||||||
|
|
||||||
if(r1 < r2 + MIN_DIFFERENCE &&
|
|
||||||
r1 > r2 - MIN_DIFFERENCE) {
|
|
||||||
return dag->preferred_parent;
|
|
||||||
} else if(r1 < r2) {
|
|
||||||
return p1;
|
|
||||||
} else {
|
} else {
|
||||||
return p2;
|
/* We have a tie! */
|
||||||
|
/* Stik to current preferred parent if possible */
|
||||||
|
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
|
||||||
|
return dag->preferred_parent;
|
||||||
|
}
|
||||||
|
/* None of the nodes is the current preferred parent,
|
||||||
|
* choose parent with best link metric */
|
||||||
|
return parent_link_metric(p1) < parent_link_metric(p2) ? p1 : p2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
static rpl_dag_t *
|
||||||
|
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
||||||
|
{
|
||||||
|
if(d1->grounded != d2->grounded) {
|
||||||
|
return d1->grounded ? d1 : d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(d1->preference != d2->preference) {
|
||||||
|
return d1->preference > d2->preference ? d1 : d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d1->rank < d2->rank ? d1 : d2;
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
static void
|
static void
|
||||||
update_metric_container(rpl_instance_t *instance)
|
update_metric_container(rpl_instance_t *instance)
|
||||||
{
|
{
|
||||||
instance->mc.type = RPL_DAG_MC_NONE;
|
instance->mc.type = RPL_DAG_MC_NONE;
|
||||||
}
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
rpl_of_t rpl_of0 = {
|
||||||
|
reset,
|
||||||
|
#if RPL_WITH_DAO_ACK
|
||||||
|
dao_ack_callback,
|
||||||
|
#endif
|
||||||
|
parent_link_metric,
|
||||||
|
parent_has_usable_link,
|
||||||
|
parent_path_cost,
|
||||||
|
rank_via_parent,
|
||||||
|
best_parent,
|
||||||
|
best_dag,
|
||||||
|
update_metric_container,
|
||||||
|
RPL_OCP_OF0
|
||||||
|
};
|
||||||
|
|
||||||
/** @}*/
|
/** @}*/
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
#include "sys/clock.h"
|
#include "sys/clock.h"
|
||||||
#include "sys/ctimer.h"
|
#include "sys/ctimer.h"
|
||||||
#include "net/ipv6/uip-ds6.h"
|
#include "net/ipv6/uip-ds6.h"
|
||||||
|
#include "net/ipv6/uip-ds6-route.h"
|
||||||
|
#include "net/rpl/rpl-ns.h"
|
||||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
@ -90,10 +92,21 @@
|
||||||
|
|
||||||
#define RPL_DAO_K_FLAG 0x80 /* DAO ACK requested */
|
#define RPL_DAO_K_FLAG 0x80 /* DAO ACK requested */
|
||||||
#define RPL_DAO_D_FLAG 0x40 /* DODAG ID present */
|
#define RPL_DAO_D_FLAG 0x40 /* DODAG ID present */
|
||||||
|
|
||||||
|
#define RPL_DAO_ACK_UNCONDITIONAL_ACCEPT 0
|
||||||
|
#define RPL_DAO_ACK_ACCEPT 1 /* 1 - 127 is OK but not good */
|
||||||
|
#define RPL_DAO_ACK_UNABLE_TO_ACCEPT 128 /* >127 is fail */
|
||||||
|
#define RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT 255 /* root can not accept */
|
||||||
|
|
||||||
|
#define RPL_DAO_ACK_TIMEOUT -1
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* RPL IPv6 extension header option. */
|
/* RPL IPv6 extension header option. */
|
||||||
#define RPL_HDR_OPT_LEN 4
|
#define RPL_HDR_OPT_LEN 4
|
||||||
#define RPL_HOP_BY_HOP_LEN (RPL_HDR_OPT_LEN + 2 + 2)
|
#define RPL_HOP_BY_HOP_LEN (RPL_HDR_OPT_LEN + 2 + 2)
|
||||||
|
#define RPL_RH_LEN 4
|
||||||
|
#define RPL_SRH_LEN 4
|
||||||
|
#define RPL_RH_TYPE_SRH 3
|
||||||
#define RPL_HDR_OPT_DOWN 0x80
|
#define RPL_HDR_OPT_DOWN 0x80
|
||||||
#define RPL_HDR_OPT_DOWN_SHIFT 7
|
#define RPL_HDR_OPT_DOWN_SHIFT 7
|
||||||
#define RPL_HDR_OPT_RANK_ERR 0x40
|
#define RPL_HDR_OPT_RANK_ERR 0x40
|
||||||
|
@ -117,6 +130,18 @@
|
||||||
#define RPL_NOPATH_REMOVAL_DELAY 60
|
#define RPL_NOPATH_REMOVAL_DELAY 60
|
||||||
#endif /* RPL_CONF_NOPATH_REMOVAL_DELAY */
|
#endif /* RPL_CONF_NOPATH_REMOVAL_DELAY */
|
||||||
|
|
||||||
|
#ifdef RPL_CONF_DAO_MAX_RETRANSMISSIONS
|
||||||
|
#define RPL_DAO_MAX_RETRANSMISSIONS RPL_CONF_DAO_MAX_RETRANSMISSIONS
|
||||||
|
#else
|
||||||
|
#define RPL_DAO_MAX_RETRANSMISSIONS 5
|
||||||
|
#endif /* RPL_CONF_DAO_MAX_RETRANSMISSIONS */
|
||||||
|
|
||||||
|
#ifdef RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
|
||||||
|
#define RPL_DAO_RETRANSMISSION_TIMEOUT RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
|
||||||
|
#else
|
||||||
|
#define RPL_DAO_RETRANSMISSION_TIMEOUT (5 * CLOCK_SECOND)
|
||||||
|
#endif /* RPL_CONF_DAO_RETRANSMISSION_TIMEOUT */
|
||||||
|
|
||||||
/* Special value indicating immediate removal. */
|
/* Special value indicating immediate removal. */
|
||||||
#define RPL_ZERO_LIFETIME 0
|
#define RPL_ZERO_LIFETIME 0
|
||||||
|
|
||||||
|
@ -124,11 +149,28 @@
|
||||||
((unsigned long)(instance)->lifetime_unit * (lifetime))
|
((unsigned long)(instance)->lifetime_unit * (lifetime))
|
||||||
|
|
||||||
#ifndef RPL_CONF_MIN_HOPRANKINC
|
#ifndef RPL_CONF_MIN_HOPRANKINC
|
||||||
|
/* RFC6550 defines the default MIN_HOPRANKINC as 256.
|
||||||
|
* However, we use MRHOF as a default Objective Function (RFC6719),
|
||||||
|
* which recommends setting MIN_HOPRANKINC with care, in particular
|
||||||
|
* when used with ETX as a metric. ETX is computed as a fixed point
|
||||||
|
* real with a divisor of 128 (RFC6719, RFC6551). We choose to also
|
||||||
|
* use 128 for RPL_MIN_HOPRANKINC, resulting in a rank equal to the
|
||||||
|
* ETX path cost. Larger values may also be desirable, as discussed
|
||||||
|
* in section 6.1 of RFC6719. */
|
||||||
|
#if RPL_OF_OCP == RPL_OCP_MRHOF
|
||||||
|
#define RPL_MIN_HOPRANKINC 128
|
||||||
|
#else /* RPL_OF_OCP == RPL_OCP_MRHOF */
|
||||||
#define RPL_MIN_HOPRANKINC 256
|
#define RPL_MIN_HOPRANKINC 256
|
||||||
#else
|
#endif /* RPL_OF_OCP == RPL_OCP_MRHOF */
|
||||||
|
#else /* RPL_CONF_MIN_HOPRANKINC */
|
||||||
#define RPL_MIN_HOPRANKINC RPL_CONF_MIN_HOPRANKINC
|
#define RPL_MIN_HOPRANKINC RPL_CONF_MIN_HOPRANKINC
|
||||||
#endif
|
#endif /* RPL_CONF_MIN_HOPRANKINC */
|
||||||
|
|
||||||
|
#ifndef RPL_CONF_MAX_RANKINC
|
||||||
#define RPL_MAX_RANKINC (7 * RPL_MIN_HOPRANKINC)
|
#define RPL_MAX_RANKINC (7 * RPL_MIN_HOPRANKINC)
|
||||||
|
#else /* RPL_CONF_MAX_RANKINC */
|
||||||
|
#define RPL_MAX_RANKINC RPL_CONF_MAX_RANKINC
|
||||||
|
#endif /* RPL_CONF_MAX_RANKINC */
|
||||||
|
|
||||||
#define DAG_RANK(fixpt_rank, instance) \
|
#define DAG_RANK(fixpt_rank, instance) \
|
||||||
((fixpt_rank) / (instance)->min_hoprankinc)
|
((fixpt_rank) / (instance)->min_hoprankinc)
|
||||||
|
@ -157,6 +199,7 @@
|
||||||
#define RPL_MOP_STORING_NO_MULTICAST 2
|
#define RPL_MOP_STORING_NO_MULTICAST 2
|
||||||
#define RPL_MOP_STORING_MULTICAST 3
|
#define RPL_MOP_STORING_MULTICAST 3
|
||||||
|
|
||||||
|
/* RPL Mode of operation */
|
||||||
#ifdef RPL_CONF_MOP
|
#ifdef RPL_CONF_MOP
|
||||||
#define RPL_MOP_DEFAULT RPL_CONF_MOP
|
#define RPL_MOP_DEFAULT RPL_CONF_MOP
|
||||||
#else /* RPL_CONF_MOP */
|
#else /* RPL_CONF_MOP */
|
||||||
|
@ -167,6 +210,43 @@
|
||||||
#endif /* UIP_IPV6_MULTICAST_RPL */
|
#endif /* UIP_IPV6_MULTICAST_RPL */
|
||||||
#endif /* RPL_CONF_MOP */
|
#endif /* RPL_CONF_MOP */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Embed support for storing mode
|
||||||
|
*/
|
||||||
|
#ifdef RPL_CONF_WITH_STORING
|
||||||
|
#define RPL_WITH_STORING RPL_CONF_WITH_STORING
|
||||||
|
#else /* RPL_CONF_WITH_STORING */
|
||||||
|
/* By default: embed support for non-storing if and only if the configured MOP is not non-storing */
|
||||||
|
#define RPL_WITH_STORING (RPL_MOP_DEFAULT != RPL_MOP_NON_STORING)
|
||||||
|
#endif /* RPL_CONF_WITH_STORING */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Embed support for non-storing mode
|
||||||
|
*/
|
||||||
|
#ifdef RPL_CONF_WITH_NON_STORING
|
||||||
|
#define RPL_WITH_NON_STORING RPL_CONF_WITH_NON_STORING
|
||||||
|
#else /* RPL_CONF_WITH_NON_STORING */
|
||||||
|
/* By default: embed support for non-storing if and only if the configured MOP is non-storing */
|
||||||
|
#define RPL_WITH_NON_STORING (RPL_MOP_DEFAULT == RPL_MOP_NON_STORING)
|
||||||
|
#endif /* RPL_CONF_WITH_NON_STORING */
|
||||||
|
|
||||||
|
#if RPL_WITH_STORING && (UIP_DS6_ROUTE_NB == 0)
|
||||||
|
#error "RPL with storing mode included but #routes == 0. Set UIP_CONF_MAX_ROUTES accordingly."
|
||||||
|
#if !RPL_WITH_NON_STORING && (RPL_NS_LINK_NUM > 0)
|
||||||
|
#error "You might also want to set RPL_NS_CONF_LINK_NUM to 0."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if RPL_WITH_NON_STORING && (RPL_NS_LINK_NUM == 0)
|
||||||
|
#error "RPL with non-storing mode included but #links == 0. Set RPL_NS_CONF_LINK_NUM accordingly."
|
||||||
|
#if !RPL_WITH_STORING && (UIP_DS6_ROUTE_NB > 0)
|
||||||
|
#error "You might also want to set UIP_CONF_MAX_ROUTES to 0."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RPL_IS_STORING(instance) (RPL_WITH_STORING && ((instance) != NULL) && ((instance)->mop > RPL_MOP_NON_STORING))
|
||||||
|
#define RPL_IS_NON_STORING(instance) (RPL_WITH_NON_STORING && ((instance) != NULL) && ((instance)->mop == RPL_MOP_NON_STORING))
|
||||||
|
|
||||||
/* Emit a pre-processor error if the user configured multicast with bad MOP */
|
/* Emit a pre-processor error if the user configured multicast with bad MOP */
|
||||||
#if RPL_CONF_MULTICAST && (RPL_MOP_DEFAULT != RPL_MOP_STORING_MULTICAST)
|
#if RPL_CONF_MULTICAST && (RPL_MOP_DEFAULT != RPL_MOP_STORING_MULTICAST)
|
||||||
#error "RPL Multicast requires RPL_MOP_DEFAULT==3. Check contiki-conf.h"
|
#error "RPL Multicast requires RPL_MOP_DEFAULT==3. Check contiki-conf.h"
|
||||||
|
@ -179,13 +259,6 @@
|
||||||
#define RPL_MCAST_LIFETIME 3
|
#define RPL_MCAST_LIFETIME 3
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* The ETX in the metric container is expressed as a fixed-point value
|
|
||||||
* whose integer part can be obtained by dividing the value by
|
|
||||||
* RPL_DAG_MC_ETX_DIVISOR.
|
|
||||||
*/
|
|
||||||
#define RPL_DAG_MC_ETX_DIVISOR 256
|
|
||||||
|
|
||||||
/* DIS related */
|
/* DIS related */
|
||||||
#define RPL_DIS_SEND 1
|
#define RPL_DIS_SEND 1
|
||||||
|
|
||||||
|
@ -250,6 +323,8 @@ typedef struct rpl_stats rpl_stats_t;
|
||||||
|
|
||||||
extern rpl_stats_t rpl_stats;
|
extern rpl_stats_t rpl_stats;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* RPL macros. */
|
/* RPL macros. */
|
||||||
|
|
||||||
|
@ -268,8 +343,10 @@ void dis_output(uip_ipaddr_t *addr);
|
||||||
void dio_output(rpl_instance_t *, uip_ipaddr_t *uc_addr);
|
void dio_output(rpl_instance_t *, uip_ipaddr_t *uc_addr);
|
||||||
void dao_output(rpl_parent_t *, uint8_t lifetime);
|
void dao_output(rpl_parent_t *, uint8_t lifetime);
|
||||||
void dao_output_target(rpl_parent_t *, uip_ipaddr_t *, uint8_t lifetime);
|
void dao_output_target(rpl_parent_t *, uip_ipaddr_t *, uint8_t lifetime);
|
||||||
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t);
|
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t, uint8_t);
|
||||||
void rpl_icmp6_register_handlers(void);
|
void rpl_icmp6_register_handlers(void);
|
||||||
|
uip_ds6_nbr_t *rpl_icmp6_update_nbr_table(uip_ipaddr_t *from,
|
||||||
|
nbr_table_reason_t r, void *data);
|
||||||
|
|
||||||
/* RPL logic functions. */
|
/* RPL logic functions. */
|
||||||
void rpl_join_dag(uip_ipaddr_t *from, rpl_dio_t *dio);
|
void rpl_join_dag(uip_ipaddr_t *from, rpl_dio_t *dio);
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
|
|
||||||
#include "contiki-conf.h"
|
#include "contiki-conf.h"
|
||||||
#include "net/rpl/rpl-private.h"
|
#include "net/rpl/rpl-private.h"
|
||||||
|
#include "net/rpl/rpl-ns.h"
|
||||||
|
#include "net/link-stats.h"
|
||||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||||
#include "lib/random.h"
|
#include "lib/random.h"
|
||||||
#include "sys/ctimer.h"
|
#include "sys/ctimer.h"
|
||||||
|
@ -55,6 +57,14 @@
|
||||||
void RPL_CALLBACK_NEW_DIO_INTERVAL(uint8_t dio_interval);
|
void RPL_CALLBACK_NEW_DIO_INTERVAL(uint8_t dio_interval);
|
||||||
#endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
|
#endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
|
||||||
|
|
||||||
|
#ifdef RPL_PROBING_SELECT_FUNC
|
||||||
|
rpl_parent_t *RPL_PROBING_SELECT_FUNC(rpl_dag_t *dag);
|
||||||
|
#endif /* RPL_PROBING_SELECT_FUNC */
|
||||||
|
|
||||||
|
#ifdef RPL_PROBING_DELAY_FUNC
|
||||||
|
clock_time_t RPL_PROBING_DELAY_FUNC(rpl_dag_t *dag);
|
||||||
|
#endif /* RPL_PROBING_DELAY_FUNC */
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static struct ctimer periodic_timer;
|
static struct ctimer periodic_timer;
|
||||||
|
|
||||||
|
@ -71,14 +81,23 @@ static uint8_t dio_send_ok;
|
||||||
static void
|
static void
|
||||||
handle_periodic_timer(void *ptr)
|
handle_periodic_timer(void *ptr)
|
||||||
{
|
{
|
||||||
|
rpl_dag_t *dag = rpl_get_any_dag();
|
||||||
|
|
||||||
rpl_purge_dags();
|
rpl_purge_dags();
|
||||||
|
if(dag != NULL) {
|
||||||
|
if(RPL_IS_STORING(dag->instance)) {
|
||||||
rpl_purge_routes();
|
rpl_purge_routes();
|
||||||
|
}
|
||||||
|
if(RPL_IS_NON_STORING(dag->instance)) {
|
||||||
|
rpl_ns_periodic();
|
||||||
|
}
|
||||||
|
}
|
||||||
rpl_recalculate_ranks();
|
rpl_recalculate_ranks();
|
||||||
|
|
||||||
/* handle DIS */
|
/* handle DIS */
|
||||||
#if RPL_DIS_SEND
|
#if RPL_DIS_SEND
|
||||||
next_dis++;
|
next_dis++;
|
||||||
if(rpl_get_any_dag() == NULL && next_dis >= RPL_DIS_INTERVAL) {
|
if(dag == NULL && next_dis >= RPL_DIS_INTERVAL) {
|
||||||
next_dis = 0;
|
next_dis = 0;
|
||||||
dis_output(NULL);
|
dis_output(NULL);
|
||||||
}
|
}
|
||||||
|
@ -161,7 +180,7 @@ handle_dio_timer(void *ptr)
|
||||||
#endif /* RPL_CONF_STATS */
|
#endif /* RPL_CONF_STATS */
|
||||||
dio_output(instance, NULL);
|
dio_output(instance, NULL);
|
||||||
} else {
|
} else {
|
||||||
PRINTF("RPL: Supressing DIO transmission (%d >= %d)\n",
|
PRINTF("RPL: Suppressing DIO transmission (%d >= %d)\n",
|
||||||
instance->dio_counter, instance->dio_redundancy);
|
instance->dio_counter, instance->dio_redundancy);
|
||||||
}
|
}
|
||||||
instance->dio_send = 0;
|
instance->dio_send = 0;
|
||||||
|
@ -224,6 +243,8 @@ set_dao_lifetime_timer(rpl_instance_t *instance)
|
||||||
expiration_time = (clock_time_t)instance->default_lifetime *
|
expiration_time = (clock_time_t)instance->default_lifetime *
|
||||||
(clock_time_t)instance->lifetime_unit *
|
(clock_time_t)instance->lifetime_unit *
|
||||||
CLOCK_SECOND / 2;
|
CLOCK_SECOND / 2;
|
||||||
|
/* make the time for the re registration be betwen 1/2 - 3/4 of lifetime */
|
||||||
|
expiration_time = expiration_time + (random_rand() % (expiration_time / 2));
|
||||||
PRINTF("RPL: Scheduling DAO lifetime timer %u ticks in the future\n",
|
PRINTF("RPL: Scheduling DAO lifetime timer %u ticks in the future\n",
|
||||||
(unsigned)expiration_time);
|
(unsigned)expiration_time);
|
||||||
ctimer_set(&instance->dao_lifetime_timer, expiration_time,
|
ctimer_set(&instance->dao_lifetime_timer, expiration_time,
|
||||||
|
@ -356,42 +377,57 @@ rpl_schedule_unicast_dio_immediately(rpl_instance_t *instance)
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
#if RPL_WITH_PROBING
|
#if RPL_WITH_PROBING
|
||||||
static rpl_parent_t *
|
clock_time_t
|
||||||
|
get_probing_delay(rpl_dag_t *dag)
|
||||||
|
{
|
||||||
|
if(dag != NULL && dag->instance != NULL
|
||||||
|
&& dag->instance->urgent_probing_target != NULL) {
|
||||||
|
/* Urgent probing needed (to find out if a neighbor may become preferred parent) */
|
||||||
|
return random_rand() % (CLOCK_SECOND * 10);
|
||||||
|
} else {
|
||||||
|
/* Else, use normal probing interval */
|
||||||
|
return ((RPL_PROBING_INTERVAL) / 2) + random_rand() % (RPL_PROBING_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
rpl_parent_t *
|
||||||
get_probing_target(rpl_dag_t *dag)
|
get_probing_target(rpl_dag_t *dag)
|
||||||
{
|
{
|
||||||
/* Returns the next probing target. The current implementation probes the current
|
/* Returns the next probing target. The current implementation probes the urgent
|
||||||
* preferred parent if we have not updated its link for RPL_PROBING_EXPIRATION_TIME.
|
* probing target if any, or the preferred parent if its link statistics need refresh.
|
||||||
* Otherwise, it picks at random between:
|
* Otherwise, it picks at random between:
|
||||||
* (1) selecting the best parent not updated for RPL_PROBING_EXPIRATION_TIME
|
* (1) selecting the best parent with non-fresh link statistics
|
||||||
* (2) selecting the least recently updated parent
|
* (2) selecting the least recently updated parent
|
||||||
*/
|
*/
|
||||||
|
|
||||||
rpl_parent_t *p;
|
rpl_parent_t *p;
|
||||||
rpl_parent_t *probing_target = NULL;
|
rpl_parent_t *probing_target = NULL;
|
||||||
rpl_rank_t probing_target_rank = INFINITE_RANK;
|
rpl_rank_t probing_target_rank = INFINITE_RANK;
|
||||||
/* min_last_tx is the clock time RPL_PROBING_EXPIRATION_TIME in the past */
|
clock_time_t probing_target_age = 0;
|
||||||
clock_time_t min_last_tx = clock_time();
|
clock_time_t clock_now = clock_time();
|
||||||
min_last_tx = min_last_tx > 2 * RPL_PROBING_EXPIRATION_TIME
|
|
||||||
? min_last_tx - RPL_PROBING_EXPIRATION_TIME : 1;
|
|
||||||
|
|
||||||
if(dag == NULL ||
|
if(dag == NULL ||
|
||||||
dag->instance == NULL ||
|
dag->instance == NULL) {
|
||||||
dag->preferred_parent == NULL) {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Our preferred parent needs probing */
|
/* There is an urgent probing target */
|
||||||
if(dag->preferred_parent->last_tx_time < min_last_tx) {
|
if(dag->instance->urgent_probing_target != NULL) {
|
||||||
probing_target = dag->preferred_parent;
|
return dag->instance->urgent_probing_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* With 50% probability: probe best parent not updated for RPL_PROBING_EXPIRATION_TIME */
|
/* The preferred parent needs probing */
|
||||||
if(probing_target == NULL && (random_rand() % 2) == 0) {
|
if(dag->preferred_parent != NULL && !rpl_parent_is_fresh(dag->preferred_parent)) {
|
||||||
|
return dag->preferred_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* With 50% probability: probe best non-fresh parent */
|
||||||
|
if(random_rand() % 2 == 0) {
|
||||||
p = nbr_table_head(rpl_parents);
|
p = nbr_table_head(rpl_parents);
|
||||||
while(p != NULL) {
|
while(p != NULL) {
|
||||||
if(p->dag == dag && p->last_tx_time < min_last_tx) {
|
if(p->dag == dag && !rpl_parent_is_fresh(p)) {
|
||||||
/* p is in our dag and needs probing */
|
/* p is in our dag and needs probing */
|
||||||
rpl_rank_t p_rank = dag->instance->of->calculate_rank(p, 0);
|
rpl_rank_t p_rank = rpl_rank_via_parent(p);
|
||||||
if(probing_target == NULL
|
if(probing_target == NULL
|
||||||
|| p_rank < probing_target_rank) {
|
|| p_rank < probing_target_rank) {
|
||||||
probing_target = p;
|
probing_target = p;
|
||||||
|
@ -402,14 +438,16 @@ get_probing_target(rpl_dag_t *dag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The default probing target is the least recently updated parent */
|
/* If we still do not have a probing target: pick the least recently updated parent */
|
||||||
if(probing_target == NULL) {
|
if(probing_target == NULL) {
|
||||||
p = nbr_table_head(rpl_parents);
|
p = nbr_table_head(rpl_parents);
|
||||||
while(p != NULL) {
|
while(p != NULL) {
|
||||||
if(p->dag == dag) {
|
const struct link_stats *stats =rpl_get_parent_link_stats(p);
|
||||||
|
if(p->dag == dag && stats != NULL) {
|
||||||
if(probing_target == NULL
|
if(probing_target == NULL
|
||||||
|| p->last_tx_time < probing_target->last_tx_time) {
|
|| clock_now - stats->last_tx_time > probing_target_age) {
|
||||||
probing_target = p;
|
probing_target = p;
|
||||||
|
probing_target_age = clock_now - stats->last_tx_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p = nbr_table_next(rpl_parents, p);
|
p = nbr_table_next(rpl_parents, p);
|
||||||
|
@ -428,11 +466,17 @@ handle_probing_timer(void *ptr)
|
||||||
|
|
||||||
/* Perform probing */
|
/* Perform probing */
|
||||||
if(target_ipaddr != NULL) {
|
if(target_ipaddr != NULL) {
|
||||||
PRINTF("RPL: probing %u ((last tx %u min ago))\n",
|
const struct link_stats *stats = rpl_get_parent_link_stats(probing_target);
|
||||||
nbr_table_get_lladdr(rpl_parents, probing_target)->u8[7],
|
(void)stats;
|
||||||
(unsigned)((clock_time() - probing_target->last_tx_time) / (60 * CLOCK_SECOND)));
|
PRINTF("RPL: probing %u %s last tx %u min ago\n",
|
||||||
/* Send probe (unicast DIO or DIS) */
|
rpl_get_parent_lladdr(probing_target)->u8[7],
|
||||||
|
instance->urgent_probing_target != NULL ? "(urgent)" : "",
|
||||||
|
probing_target != NULL ?
|
||||||
|
(unsigned)((clock_time() - stats->last_tx_time) / (60 * CLOCK_SECOND)) : 0
|
||||||
|
);
|
||||||
|
/* Send probe, e.g. unicast DIO or DIS */
|
||||||
RPL_PROBING_SEND_FUNC(instance, target_ipaddr);
|
RPL_PROBING_SEND_FUNC(instance, target_ipaddr);
|
||||||
|
instance->urgent_probing_target = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Schedule next probing */
|
/* Schedule next probing */
|
||||||
|
@ -446,7 +490,7 @@ handle_probing_timer(void *ptr)
|
||||||
void
|
void
|
||||||
rpl_schedule_probing(rpl_instance_t *instance)
|
rpl_schedule_probing(rpl_instance_t *instance)
|
||||||
{
|
{
|
||||||
ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(),
|
ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(instance->current_dag),
|
||||||
handle_probing_timer, instance);
|
handle_probing_timer, instance);
|
||||||
}
|
}
|
||||||
#endif /* RPL_WITH_PROBING */
|
#endif /* RPL_WITH_PROBING */
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "net/ipv6/uip-ds6.h"
|
#include "net/ipv6/uip-ds6.h"
|
||||||
#include "net/ipv6/uip-icmp6.h"
|
#include "net/ipv6/uip-icmp6.h"
|
||||||
#include "net/rpl/rpl-private.h"
|
#include "net/rpl/rpl-private.h"
|
||||||
|
#include "net/rpl/rpl-ns.h"
|
||||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||||
|
|
||||||
#define DEBUG DEBUG_NONE
|
#define DEBUG DEBUG_NONE
|
||||||
|
@ -216,11 +217,9 @@ rpl_remove_routes_by_nexthop(uip_ipaddr_t *nexthop, rpl_dag_t *dag)
|
||||||
while(r != NULL) {
|
while(r != NULL) {
|
||||||
if(uip_ipaddr_cmp(uip_ds6_route_nexthop(r), nexthop) &&
|
if(uip_ipaddr_cmp(uip_ds6_route_nexthop(r), nexthop) &&
|
||||||
r->state.dag == dag) {
|
r->state.dag == dag) {
|
||||||
uip_ds6_route_rm(r);
|
r->state.lifetime = 0;
|
||||||
r = uip_ds6_route_head();
|
|
||||||
} else {
|
|
||||||
r = uip_ds6_route_next(r);
|
|
||||||
}
|
}
|
||||||
|
r = uip_ds6_route_next(r);
|
||||||
}
|
}
|
||||||
ANNOTATE("#L %u 0\n", nexthop->u8[sizeof(uip_ipaddr_t) - 1]);
|
ANNOTATE("#L %u 0\n", nexthop->u8[sizeof(uip_ipaddr_t) - 1]);
|
||||||
}
|
}
|
||||||
|
@ -238,7 +237,8 @@ rpl_add_route(rpl_dag_t *dag, uip_ipaddr_t *prefix, int prefix_len,
|
||||||
|
|
||||||
rep->state.dag = dag;
|
rep->state.dag = dag;
|
||||||
rep->state.lifetime = RPL_LIFETIME(dag->instance, dag->instance->default_lifetime);
|
rep->state.lifetime = RPL_LIFETIME(dag->instance, dag->instance->default_lifetime);
|
||||||
rep->state.learned_from = RPL_ROUTE_FROM_INTERNAL;
|
/* always clear state flags for the no-path received when adding/refreshing */
|
||||||
|
RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);
|
||||||
|
|
||||||
PRINTF("RPL: Added a route to ");
|
PRINTF("RPL: Added a route to ");
|
||||||
PRINT6ADDR(prefix);
|
PRINT6ADDR(prefix);
|
||||||
|
@ -267,10 +267,6 @@ rpl_link_neighbor_callback(const linkaddr_t *addr, int status, int numtx)
|
||||||
/* Trigger DAG rank recalculation. */
|
/* Trigger DAG rank recalculation. */
|
||||||
PRINTF("RPL: rpl_link_neighbor_callback triggering update\n");
|
PRINTF("RPL: rpl_link_neighbor_callback triggering update\n");
|
||||||
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||||
if(instance->of->neighbor_link_callback != NULL) {
|
|
||||||
instance->of->neighbor_link_callback(parent, status, numtx);
|
|
||||||
parent->last_tx_time = clock_time();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,7 +281,11 @@ rpl_ipv6_neighbor_callback(uip_ds6_nbr_t *nbr)
|
||||||
|
|
||||||
PRINTF("RPL: Neighbor state changed for ");
|
PRINTF("RPL: Neighbor state changed for ");
|
||||||
PRINT6ADDR(&nbr->ipaddr);
|
PRINT6ADDR(&nbr->ipaddr);
|
||||||
|
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
|
||||||
PRINTF(", nscount=%u, state=%u\n", nbr->nscount, nbr->state);
|
PRINTF(", nscount=%u, state=%u\n", nbr->nscount, nbr->state);
|
||||||
|
#else /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
|
||||||
|
PRINTF(", state=%u\n", nbr->state);
|
||||||
|
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
|
||||||
for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES; instance < end; ++instance) {
|
for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES; instance < end; ++instance) {
|
||||||
if(instance->used == 1 ) {
|
if(instance->used == 1 ) {
|
||||||
p = rpl_find_parent_any_dag(instance, &nbr->ipaddr);
|
p = rpl_find_parent_any_dag(instance, &nbr->ipaddr);
|
||||||
|
@ -346,7 +346,9 @@ rpl_init(void)
|
||||||
memset(&rpl_stats, 0, sizeof(rpl_stats));
|
memset(&rpl_stats, 0, sizeof(rpl_stats));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RPL_OF.reset(NULL);
|
#if RPL_WITH_NON_STORING
|
||||||
|
rpl_ns_init();
|
||||||
|
#endif /* RPL_WITH_NON_STORING */
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue