cleanup confilicrts
This commit is contained in:
commit
3c8e91d74e
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -80,7 +80,7 @@ contiki-cc2530dk.lib
|
|||
*.dsc
|
||||
|
||||
#cc65 build artifacts
|
||||
*.S
|
||||
*.s
|
||||
*.eth
|
||||
*.dsk
|
||||
*.po
|
||||
|
@ -125,3 +125,7 @@ platform/galileo/bsp/grub/bin/
|
|||
*.galileo.dll
|
||||
*.galileo.efi
|
||||
LOG_OPENOCD
|
||||
|
||||
# nRF52 build artifacts
|
||||
*.jlink
|
||||
*.nrf52dk
|
||||
|
|
34
.travis.yml
34
.travis.yml
|
@ -24,7 +24,11 @@ before_script:
|
|||
msp430-gcc --version
|
||||
|
||||
## Install avr toolchain
|
||||
- sudo apt-get -qq install gcc-avr avr-libc
|
||||
- $WGET http://atiselsts.github.io/resources/avr-gcc-4.9.2-compiled.tar.bz2 &&
|
||||
tar xjf avr-gcc*.tar.bz2 -C /tmp/ &&
|
||||
sudo cp -f -r /tmp/avr-gcc/* /usr/local/ &&
|
||||
rm -rf /tmp/avr-gcc avr-gcc*.tar.bz2 &&
|
||||
avr-gcc --version
|
||||
|
||||
## Install 32-bit compatibility libraries
|
||||
- sudo apt-get -qq install libc6:i386 libgcc1:i386 gcc-4.6-base:i386
|
||||
|
@ -40,13 +44,13 @@ before_script:
|
|||
arm-none-eabi-gcc --version ;
|
||||
fi
|
||||
|
||||
## Install mainline ARM toolchain. gcc-arm-none-eabi is available
|
||||
## in Ubuntu >= 14.04, but this external PPA is needed for 12.04.
|
||||
## Install srecord
|
||||
## Install mainline ARM toolchain and srecord.
|
||||
- if [ ${BUILD_ARCH:-0} = arm-aapcs ] ; then
|
||||
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa &&
|
||||
sudo apt-get -qq update &&
|
||||
sudo apt-get -qq install gcc-arm-embedded=5-2015q4-1~precise1 srecord &&
|
||||
sudo apt-get -qq install srecord &&
|
||||
$WGET https://launchpad.net/gcc-arm-embedded/5.0/5-2015-q4-major/+download/gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 &&
|
||||
tar xjf gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 -C /tmp/ &&
|
||||
sudo cp -f -r /tmp/gcc-arm-none-eabi-5_2-2015q4/* /usr/local/ &&
|
||||
rm -rf /tmp/gcc-arm-none-eabi-* gcc-arm-none-eabi-*-linux.tar.bz2 &&
|
||||
arm-none-eabi-gcc --version ;
|
||||
fi
|
||||
|
||||
|
@ -70,7 +74,6 @@ before_script:
|
|||
git clone https://github.com/cc65/cc65 /tmp/cc65 &&
|
||||
make -C /tmp/cc65 bin apple2enh atarixl c64 c128 &&
|
||||
sudo make -C /tmp/cc65 avail &&
|
||||
export CC65_HOME=/tmp/cc65/ &&
|
||||
cc65 --version ;
|
||||
fi
|
||||
|
||||
|
@ -90,6 +93,18 @@ before_script:
|
|||
ba-elf-gcc --version ;
|
||||
fi
|
||||
|
||||
## Install mainline ARM toolchain and download nRF52 SDK
|
||||
- if [ ${BUILD_ARCH:-0} = nrf52dk ] ; then
|
||||
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa &&
|
||||
sudo apt-get -qq update &&
|
||||
sudo apt-get -qq install gcc-arm-embedded srecord &&
|
||||
arm-none-eabi-gcc --version &&
|
||||
$WGET https://developer.nordicsemi.com/nRF5_IoT_SDK/nRF5_IoT_SDK_v0.9.x/nrf5_iot_sdk_3288530.zip &&
|
||||
mkdir /tmp/nrf52-sdk &&
|
||||
unzip nrf5_iot_sdk_3288530.zip -d /tmp/nrf52-sdk &&
|
||||
export NRF52_SDK_ROOT=/tmp/nrf52-sdk ;
|
||||
fi
|
||||
|
||||
## Compile cooja.jar only when it's going to be needed
|
||||
- if [ ${BUILD_CATEGORY:-sim} = sim ] ; then
|
||||
java -version &&
|
||||
|
@ -120,6 +135,7 @@ env:
|
|||
- BUILD_TYPE='collect'
|
||||
- BUILD_TYPE='collect-lossy'
|
||||
- BUILD_TYPE='rpl'
|
||||
- BUILD_TYPE='rpl-non-storing'
|
||||
- BUILD_TYPE='large-rpl'
|
||||
- BUILD_TYPE='rime'
|
||||
- BUILD_TYPE='ipv6'
|
||||
|
@ -138,5 +154,7 @@ env:
|
|||
- BUILD_TYPE='compile-6502-ports' BUILD_CATEGORY='compile' BUILD_ARCH='6502'
|
||||
- BUILD_TYPE='compile-arm-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-aapcs'
|
||||
- BUILD_TYPE='compile-nxp-ports' BUILD_CATEGORY='compile' BUILD_ARCH='jn516x'
|
||||
- BUILD_TYPE='compile-nrf52-ports' BUILD_CATEGORY='compile' BUILD_ARCH='nrf52dk'
|
||||
- BUILD_TYPE='slip-radio' MAKE_TARGETS='cooja'
|
||||
- BUILD_TYPE='llsec' MAKE_TARGETS='cooja'
|
||||
- BUILD_TYPE='compile-avr' BUILD_CATEGORY='compile' BUILD_ARCH='avr-rss2'
|
||||
|
|
|
@ -184,7 +184,7 @@ CONTIKI_CPU_DIRS_CONCAT = ${addprefix $(CONTIKI_CPU)/, \
|
|||
$(CONTIKI_CPU_DIRS)}
|
||||
|
||||
SOURCEDIRS = . $(PROJECTDIRS) $(CONTIKI_TARGET_DIRS_CONCAT) \
|
||||
$(CONTIKI_CPU_DIRS_CONCAT) $(CONTIKIDIRS) $(APPDS) ${dir $(target_makefile)}
|
||||
$(CONTIKI_CPU_DIRS_CONCAT) $(CONTIKIDIRS) $(APPDS) $(EXTERNALDIRS) ${dir $(target_makefile)}
|
||||
|
||||
vpath %.c $(SOURCEDIRS)
|
||||
vpath %.cpp $(SOURCEDIRS)
|
||||
|
|
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,
|
||||
void *response);
|
||||
void coap_separate_reject();
|
||||
void coap_separate_reject(void);
|
||||
void coap_separate_accept(void *request, coap_separate_t *separate_store);
|
||||
void coap_separate_resume(void *response, coap_separate_t *separate_store,
|
||||
uint8_t code);
|
||||
|
|
|
@ -67,7 +67,7 @@ typedef struct coap_transaction {
|
|||
* Use snprintf(buf, len+1, "", ...) to completely fill payload */
|
||||
} coap_transaction_t;
|
||||
|
||||
void coap_register_as_transaction_handler();
|
||||
void coap_register_as_transaction_handler(void);
|
||||
|
||||
coap_transaction_t *coap_new_transaction(uint16_t mid, uip_ipaddr_t *addr,
|
||||
uint16_t port);
|
||||
|
@ -75,6 +75,6 @@ void coap_send_transaction(coap_transaction_t *t);
|
|||
void coap_clear_transaction(coap_transaction_t *t);
|
||||
coap_transaction_t *coap_get_transaction_by_mid(uint16_t mid);
|
||||
|
||||
void coap_check_transactions();
|
||||
void coap_check_transactions(void);
|
||||
|
||||
#endif /* COAP_TRANSACTIONS_H_ */
|
||||
|
|
|
@ -79,7 +79,7 @@ LWM2M_RESOURCES(temperature_resources,
|
|||
/* Temperature (Current) */
|
||||
LWM2M_RESOURCE_CALLBACK(5700, { temp, NULL, NULL }),
|
||||
/* Units */
|
||||
LWM2M_RESOURCE_STRING(5701, "Celcius"),
|
||||
LWM2M_RESOURCE_STRING(5701, "Cel"),
|
||||
/* Min Range Value */
|
||||
LWM2M_RESOURCE_FLOATFIX(5603, IPSO_TEMPERATURE_MIN),
|
||||
/* Max Range Value */
|
||||
|
|
|
@ -71,6 +71,7 @@ enum {
|
|||
JSON_ERROR_UNEXPECTED_ARRAY,
|
||||
JSON_ERROR_UNEXPECTED_END_OF_ARRAY,
|
||||
JSON_ERROR_UNEXPECTED_OBJECT,
|
||||
JSON_ERROR_UNEXPECTED_END_OF_OBJECT,
|
||||
JSON_ERROR_UNEXPECTED_STRING
|
||||
};
|
||||
|
||||
|
|
|
@ -43,6 +43,14 @@ push(struct jsonparse_state *state, char c)
|
|||
return state->depth < JSONPARSE_MAX_DEPTH;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
static void
|
||||
modify(struct jsonparse_state *state, char c)
|
||||
{
|
||||
if(state->depth > 0) {
|
||||
state->stack[state->depth - 1] = c;
|
||||
}
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
static char
|
||||
pop(struct jsonparse_state *state)
|
||||
{
|
||||
|
@ -50,25 +58,31 @@ pop(struct jsonparse_state *state)
|
|||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
state->depth--;
|
||||
state->vtype = state->stack[state->depth];
|
||||
return state->stack[state->depth];
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
/* will pass by the value and store the start and length of the value for
|
||||
atomic types */
|
||||
/*--------------------------------------------------------------------*/
|
||||
static void
|
||||
static char
|
||||
atomic(struct jsonparse_state *state, char type)
|
||||
{
|
||||
char c;
|
||||
const char *str;
|
||||
int len;
|
||||
|
||||
state->vstart = state->pos;
|
||||
state->vtype = type;
|
||||
if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) {
|
||||
while((c = state->json[state->pos++]) && c != '"') {
|
||||
if(c == '\\') {
|
||||
state->pos++; /* skip current char */
|
||||
}
|
||||
}
|
||||
if (c != '"') {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
state->vlen = state->pos - state->vstart - 1;
|
||||
} else if(type == JSON_TYPE_NUMBER) {
|
||||
do {
|
||||
|
@ -82,8 +96,31 @@ atomic(struct jsonparse_state *state, char type)
|
|||
/* need to back one step since first char is already gone */
|
||||
state->vstart--;
|
||||
state->vlen = state->pos - state->vstart;
|
||||
} else if(type == JSON_TYPE_NULL || type == JSON_TYPE_TRUE || type == JSON_TYPE_FALSE) {
|
||||
state->vstart--;
|
||||
switch (type) {
|
||||
case JSON_TYPE_NULL: str = "null"; break;
|
||||
case JSON_TYPE_TRUE: str = "true"; break;
|
||||
case JSON_TYPE_FALSE: str = "false"; break;
|
||||
default: str = ""; break;
|
||||
}
|
||||
/* 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
|
||||
|
@ -97,6 +134,17 @@ skip_ws(struct jsonparse_state *state)
|
|||
}
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
static int
|
||||
is_atomic(struct jsonparse_state *state)
|
||||
{
|
||||
char v = state->vtype;
|
||||
if(v == 'N' || v == '"' || v == '0' || v == 'n' || v == 't' || v == 'f') {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
void
|
||||
jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
|
||||
{
|
||||
|
@ -105,6 +153,7 @@ jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
|
|||
state->pos = 0;
|
||||
state->depth = 0;
|
||||
state->error = 0;
|
||||
state->vtype = 0;
|
||||
state->stack[0] = 0;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
@ -113,31 +162,33 @@ jsonparse_next(struct jsonparse_state *state)
|
|||
{
|
||||
char c;
|
||||
char s;
|
||||
char v;
|
||||
|
||||
skip_ws(state);
|
||||
c = state->json[state->pos];
|
||||
s = jsonparse_get_type(state);
|
||||
v = state->vtype;
|
||||
state->pos++;
|
||||
|
||||
switch(c) {
|
||||
case '{':
|
||||
if((s == 0 && v == 0) || s == '[' || s == ':') {
|
||||
push(state, c);
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_OBJECT;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case '}':
|
||||
if(s == ':' && state->vtype != 0) {
|
||||
/* printf("Popping vtype: '%c'\n", state->vtype); */
|
||||
pop(state);
|
||||
s = jsonparse_get_type(state);
|
||||
}
|
||||
if(s == '{') {
|
||||
if((s == ':' && v != ',' && v != 0 ) || (s == '{' && v == 0)) {
|
||||
pop(state);
|
||||
} else {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
state->error = JSON_ERROR_UNEXPECTED_END_OF_OBJECT;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case ']':
|
||||
if(s == '[') {
|
||||
if(s == '[' && v != ',') {
|
||||
pop(state);
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY;
|
||||
|
@ -145,41 +196,67 @@ jsonparse_next(struct jsonparse_state *state)
|
|||
}
|
||||
return c;
|
||||
case ':':
|
||||
push(state, c);
|
||||
return c;
|
||||
if(s == '{' && v == 'N') {
|
||||
modify(state, ':');
|
||||
state->vtype = 0;
|
||||
} else {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return jsonparse_next(state);
|
||||
case ',':
|
||||
/* if x:y ... , */
|
||||
if(s == ':' && state->vtype != 0) {
|
||||
pop(state);
|
||||
if(s == ':' && v != 0) {
|
||||
modify(state, '{');
|
||||
state->vtype = c;
|
||||
} else if(s == '[') {
|
||||
/* ok! */
|
||||
state->vtype = c;
|
||||
} else {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case '"':
|
||||
if(s == '{' || s == '[' || s == ':') {
|
||||
atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
|
||||
if((s == 0 && v == 0) || s == '{' || s == '[' || s == ':') {
|
||||
return atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_STRING;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case '[':
|
||||
if(s == '{' || s == '[' || s == ':') {
|
||||
if((s == 0 && v == 0) || s == '[' || s == ':') {
|
||||
push(state, c);
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_ARRAY;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
default:
|
||||
if(s == ':' || s == '[') {
|
||||
if(c <= '9' && c >= '0') {
|
||||
atomic(state, JSON_TYPE_NUMBER);
|
||||
return JSON_TYPE_NUMBER;
|
||||
case 0:
|
||||
if(v == 0 || state->depth > 0) {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
}
|
||||
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;
|
||||
|
@ -192,16 +269,31 @@ jsonparse_next(struct jsonparse_state *state)
|
|||
int
|
||||
jsonparse_copy_value(struct jsonparse_state *state, char *str, int size)
|
||||
{
|
||||
int i;
|
||||
int i, o;
|
||||
char c;
|
||||
|
||||
if(state->vtype == 0) {
|
||||
if(!is_atomic(state)) {
|
||||
return 0;
|
||||
}
|
||||
size = size <= state->vlen ? (size - 1) : state->vlen;
|
||||
for(i = 0; i < size; i++) {
|
||||
str[i] = state->json[state->vstart + i];
|
||||
for(i = 0, o = 0; i < state->vlen && o < size - 1; i++) {
|
||||
c = state->json[state->vstart + i];
|
||||
if(c == '\\') {
|
||||
i++;
|
||||
switch(state->json[state->vstart + i]) {
|
||||
case '"': str[o++] = '"'; break;
|
||||
case '\\': str[o++] = '\\'; break;
|
||||
case '/': str[o++] = '/'; break;
|
||||
case 'b': str[o++] = '\b'; break;
|
||||
case 'f': str[o++] = '\f'; break;
|
||||
case 'n': str[o++] = '\n'; break;
|
||||
case 'r': str[o++] = '\r'; break;
|
||||
case 't': str[o++] = '\t'; break;
|
||||
}
|
||||
str[i] = 0;
|
||||
continue;
|
||||
}
|
||||
str[o++] = c;
|
||||
}
|
||||
str[o] = 0;
|
||||
return state->vtype;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
@ -228,7 +320,7 @@ jsonparse_get_value_as_long(struct jsonparse_state *state)
|
|||
int
|
||||
jsonparse_strcmp_value(struct jsonparse_state *state, const char *str)
|
||||
{
|
||||
if(state->vtype == 0) {
|
||||
if(!is_atomic(state)) {
|
||||
return -1;
|
||||
}
|
||||
return strncmp(str, &state->json[state->vstart], state->vlen);
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
oma-lwm2m_src = lwm2m-object.c lwm2m-engine.c \
|
||||
lwm2m-device.c lwm2m-server.c lwm2m-security.c \
|
||||
oma-tlv.c oma-tlv-reader.c oma-tlv-writer.c \
|
||||
lwm2m-plain-text.c
|
||||
oma-lwm2m_src = \
|
||||
lwm2m-object.c \
|
||||
lwm2m-engine.c \
|
||||
lwm2m-device.c \
|
||||
lwm2m-server.c \
|
||||
lwm2m-security.c \
|
||||
oma-tlv.c \
|
||||
oma-tlv-reader.c \
|
||||
oma-tlv-writer.c \
|
||||
lwm2m-plain-text.c \
|
||||
lwm2m-json.c \
|
||||
#
|
||||
CFLAGS += -DHAVE_OMA_LWM2M=1
|
||||
|
|
|
@ -46,10 +46,12 @@
|
|||
#include "lwm2m-object.h"
|
||||
#include "lwm2m-device.h"
|
||||
#include "lwm2m-plain-text.h"
|
||||
#include "lwm2m-json.h"
|
||||
#include "rest-engine.h"
|
||||
#include "er-coap-constants.h"
|
||||
#include "er-coap-engine.h"
|
||||
#include "oma-tlv.h"
|
||||
#include "oma-tlv-reader.h"
|
||||
#include "oma-tlv-writer.h"
|
||||
#include "net/ipv6/uip-ds6.h"
|
||||
#include <stdio.h>
|
||||
|
@ -639,10 +641,10 @@ write_rd_json_data(const lwm2m_context_t *context,
|
|||
value = lwm2m_object_get_resource_string(resource, context);
|
||||
slen = lwm2m_object_get_resource_strlen(resource, context);
|
||||
if(value != NULL) {
|
||||
PRINTF("%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s,
|
||||
PRINTF("%s{\"n\":\"%u\",\"sv\":\"%.*s\"}", s,
|
||||
resource->id, slen, value);
|
||||
len = snprintf(&buffer[rdlen], size - rdlen,
|
||||
"%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s,
|
||||
"%s{\"n\":\"%u\",\"sv\":\"%.*s\"}", s,
|
||||
resource->id, slen, value);
|
||||
}
|
||||
} else if(lwm2m_object_is_resource_int(resource)) {
|
||||
|
@ -682,10 +684,10 @@ write_rd_json_data(const lwm2m_context_t *context,
|
|||
} else if(lwm2m_object_is_resource_boolean(resource)) {
|
||||
int value;
|
||||
if(lwm2m_object_get_resource_boolean(resource, context, &value)) {
|
||||
PRINTF("%s{\"n\":\"%u\",\"v\":%s}", s, resource->id,
|
||||
PRINTF("%s{\"n\":\"%u\",\"bv\":%s}", s, resource->id,
|
||||
value ? "true" : "false");
|
||||
len = snprintf(&buffer[rdlen], size - rdlen,
|
||||
"%s{\"n\":\"%u\",\"v\":%s}", s, resource->id,
|
||||
"%s{\"n\":\"%u\",\"bv\":%s}", s, resource->id,
|
||||
value ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
@ -707,6 +709,63 @@ write_rd_json_data(const lwm2m_context_t *context,
|
|||
return rdlen;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Set the writer pointer to the proper writer based on the Accept: header
|
||||
*
|
||||
* @param[in] context LWM2M context to operate on
|
||||
* @param[in] accept Accept type number from CoAP headers
|
||||
*
|
||||
* @return The content type of the response if the selected writer is used
|
||||
*/
|
||||
static unsigned int
|
||||
lwm2m_engine_select_writer(lwm2m_context_t *context, unsigned int accept)
|
||||
{
|
||||
switch(accept) {
|
||||
case LWM2M_TLV:
|
||||
context->writer = &oma_tlv_writer;
|
||||
break;
|
||||
case LWM2M_TEXT_PLAIN:
|
||||
case TEXT_PLAIN:
|
||||
context->writer = &lwm2m_plain_text_writer;
|
||||
break;
|
||||
case LWM2M_JSON:
|
||||
case APPLICATION_JSON:
|
||||
context->writer = &lwm2m_json_writer;
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unknown Accept type %u, using LWM2M plain text\n", accept);
|
||||
context->writer = &lwm2m_plain_text_writer;
|
||||
/* Set the response type to plain text */
|
||||
accept = LWM2M_TEXT_PLAIN;
|
||||
break;
|
||||
}
|
||||
return accept;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Set the reader pointer to the proper reader based on the Content-format: header
|
||||
*
|
||||
* @param[in] context LWM2M context to operate on
|
||||
* @param[in] content_format Content-type type number from CoAP headers
|
||||
*/
|
||||
static void
|
||||
lwm2m_engine_select_reader(lwm2m_context_t *context, unsigned int content_format)
|
||||
{
|
||||
switch(content_format) {
|
||||
case LWM2M_TLV:
|
||||
context->reader = &oma_tlv_reader;
|
||||
break;
|
||||
case LWM2M_TEXT_PLAIN:
|
||||
case TEXT_PLAIN:
|
||||
context->reader = &lwm2m_plain_text_reader;
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unknown content type %u, using LWM2M plain text\n", accept);
|
||||
context->reader = &lwm2m_plain_text_reader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
lwm2m_engine_handler(const lwm2m_object_t *object,
|
||||
void *request, void *response,
|
||||
|
@ -716,6 +775,8 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
int len;
|
||||
const char *url;
|
||||
unsigned int format;
|
||||
unsigned int accept;
|
||||
unsigned int content_type;
|
||||
int depth;
|
||||
lwm2m_context_t context;
|
||||
rest_resource_flags_t method;
|
||||
|
@ -734,11 +795,19 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
/* CoAP content format text plain - assume LWM2M text plain */
|
||||
format = LWM2M_TEXT_PLAIN;
|
||||
}
|
||||
if(!REST.get_header_accept(request, &accept)) {
|
||||
PRINTF("No Accept header, using same as Content-format...\n");
|
||||
accept = format;
|
||||
}
|
||||
|
||||
depth = lwm2m_engine_parse_context(object, url, len, &context);
|
||||
PRINTF("Context: %u/%u/%u found: %d\n", context.object_id,
|
||||
context.object_instance_id, context.resource_id, depth);
|
||||
|
||||
/* Select reader and writer based on provided Content type and Accept headers */
|
||||
lwm2m_engine_select_reader(&context, format);
|
||||
content_type = lwm2m_engine_select_writer(&context, accept);
|
||||
|
||||
#if (DEBUG) & DEBUG_PRINT
|
||||
/* for debugging */
|
||||
if(method == METHOD_GET) {
|
||||
|
@ -861,7 +930,7 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
|
||||
if(depth == 3) {
|
||||
const lwm2m_resource_t *resource = get_resource(instance, &context);
|
||||
size_t tlvlen = 0;
|
||||
size_t content_len = 0;
|
||||
if(resource == NULL) {
|
||||
PRINTF("Error - do not have resource %d\n", context.resource_id);
|
||||
REST.set_response_status(response, NOT_FOUND_4_04);
|
||||
|
@ -879,9 +948,9 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
context.reader = &lwm2m_plain_text_reader;
|
||||
PRINTF("PUT Callback with data: '%.*s'\n", plen, data);
|
||||
/* no specific reader for plain text */
|
||||
tlvlen = resource->value.callback.write(&context, data, plen,
|
||||
content_len = resource->value.callback.write(&context, data, plen,
|
||||
buffer, preferred_size);
|
||||
PRINTF("tlvlen:%u\n", (unsigned int)tlvlen);
|
||||
PRINTF("content_len:%u\n", (unsigned int)content_len);
|
||||
REST.set_response_status(response, CHANGED_2_04);
|
||||
} else {
|
||||
PRINTF("PUT callback with format %d\n", format);
|
||||
|
@ -899,48 +968,39 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
} else if(method == METHOD_GET) {
|
||||
if(lwm2m_object_is_resource_string(resource)) {
|
||||
const uint8_t *value;
|
||||
uint16_t len;
|
||||
value = lwm2m_object_get_resource_string(resource, &context);
|
||||
len = lwm2m_object_get_resource_strlen(resource, &context);
|
||||
if(value != NULL) {
|
||||
uint16_t len = lwm2m_object_get_resource_strlen(resource, &context);
|
||||
PRINTF("Get string value: %.*s\n", (int)len, (char *)value);
|
||||
/* TODO check format */
|
||||
REST.set_response_payload(response, value, len);
|
||||
REST.set_header_content_type(response, LWM2M_TEXT_PLAIN);
|
||||
/* Done */
|
||||
return;
|
||||
content_len = context.writer->write_string(&context, buffer,
|
||||
preferred_size, (const char *)value, len);
|
||||
}
|
||||
} else if(lwm2m_object_is_resource_int(resource)) {
|
||||
int32_t value;
|
||||
if(lwm2m_object_get_resource_int(resource, &context, &value)) {
|
||||
/* export INT as TLV */
|
||||
tlvlen = oma_tlv_write_int32(resource->id, value, buffer, preferred_size);
|
||||
PRINTF("Exporting int as TLV: %" PRId32 ", len: %u\n",
|
||||
value, (unsigned int)tlvlen);
|
||||
content_len = context.writer->write_int(&context, buffer, preferred_size, value);
|
||||
}
|
||||
} else if(lwm2m_object_is_resource_floatfix(resource)) {
|
||||
int32_t value;
|
||||
if(lwm2m_object_get_resource_floatfix(resource, &context, &value)) {
|
||||
/* export FLOATFIX as TLV */
|
||||
/* export FLOATFIX */
|
||||
PRINTF("Exporting %d-bit fix as float: %" PRId32 "\n",
|
||||
LWM2M_FLOAT32_BITS, value);
|
||||
tlvlen = oma_tlv_write_float32(resource->id,
|
||||
value, LWM2M_FLOAT32_BITS,
|
||||
buffer, preferred_size);
|
||||
PRINTF("Exporting as TLV: len:%u\n", (unsigned int)tlvlen);
|
||||
content_len = context.writer->write_float32fix(&context, buffer,
|
||||
preferred_size, value, LWM2M_FLOAT32_BITS);
|
||||
}
|
||||
} else if(lwm2m_object_is_resource_callback(resource)) {
|
||||
if(resource->value.callback.read != NULL) {
|
||||
tlvlen = resource->value.callback.read(&context,
|
||||
content_len = resource->value.callback.read(&context,
|
||||
buffer, preferred_size);
|
||||
} else {
|
||||
REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(tlvlen > 0) {
|
||||
REST.set_response_payload(response, buffer, tlvlen);
|
||||
REST.set_header_content_type(response, LWM2M_TLV);
|
||||
if(content_len > 0) {
|
||||
REST.set_response_payload(response, buffer, content_len);
|
||||
REST.set_header_content_type(response, content_type);
|
||||
} else {
|
||||
/* failed to produce output - it is an internal error */
|
||||
REST.set_response_status(response, INTERNAL_SERVER_ERROR_5_00);
|
||||
|
@ -952,7 +1012,7 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
const uint8_t *data;
|
||||
int plen = REST.get_request_payload(request, &data);
|
||||
PRINTF("Execute Callback with data: '%.*s'\n", plen, data);
|
||||
tlvlen = resource->value.callback.exec(&context,
|
||||
content_len = resource->value.callback.exec(&context,
|
||||
data, plen,
|
||||
buffer, preferred_size);
|
||||
REST.set_response_status(response, CHANGED_2_04);
|
||||
|
@ -973,7 +1033,7 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
REST.set_response_status(response, NOT_FOUND_4_04);
|
||||
} else {
|
||||
int rdlen;
|
||||
if(format == APPLICATION_LINK_FORMAT) {
|
||||
if(accept == APPLICATION_LINK_FORMAT) {
|
||||
rdlen = write_rd_link_data(object, instance,
|
||||
(char *)buffer, preferred_size);
|
||||
} else {
|
||||
|
@ -986,10 +1046,10 @@ lwm2m_engine_handler(const lwm2m_object_t *object,
|
|||
return;
|
||||
}
|
||||
REST.set_response_payload(response, buffer, rdlen);
|
||||
if(format == APPLICATION_LINK_FORMAT) {
|
||||
if(accept == APPLICATION_LINK_FORMAT) {
|
||||
REST.set_header_content_type(response, REST.type.APPLICATION_LINK_FORMAT);
|
||||
} else {
|
||||
REST.set_header_content_type(response, REST.type.APPLICATION_JSON);
|
||||
REST.set_header_content_type(response, LWM2M_JSON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
162
apps/oma-lwm2m/lwm2m-json.c
Normal file
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;
|
||||
}
|
||||
}
|
||||
if(dot == 0) {
|
||||
integerpart = counter;
|
||||
counter = 0;
|
||||
frac = 1;
|
||||
}
|
||||
*value = integerpart << bits;
|
||||
if(frac > 1) {
|
||||
*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 common shared slotframe for any other traffic (mostly broadcast)
|
||||
* */
|
||||
#define ORCHESTRA_RULES { &eb_per_time_source, \
|
||||
&unicast_per_neighbor, \
|
||||
&default_common, \
|
||||
}
|
||||
#define ORCHESTRA_RULES { &eb_per_time_source, &unicast_per_neighbor_rpl_storing, &default_common }
|
||||
/* Example configuration for RPL non-storing mode: */
|
||||
/* #define ORCHESTRA_RULES { &eb_per_time_source, &unicast_per_neighbor_rpl_ns, &default_common } */
|
||||
|
||||
#endif /* ORCHESTRA_CONF_RULES */
|
||||
|
||||
/* Length of the various slotframes. Tune to balance network capacity,
|
||||
|
|
|
@ -74,8 +74,8 @@ select_packet(uint16_t *slotframe, uint16_t *timeslot)
|
|||
static void
|
||||
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
|
||||
{
|
||||
uint16_t old_ts = get_node_timeslot(&old->addr);
|
||||
uint16_t new_ts = get_node_timeslot(&new->addr);
|
||||
uint16_t old_ts = old != NULL ? get_node_timeslot(&old->addr) : 0xffff;
|
||||
uint16_t new_ts = new != NULL ? get_node_timeslot(&new->addr) : 0xffff;
|
||||
|
||||
if(new_ts == old_ts) {
|
||||
return;
|
||||
|
@ -83,14 +83,24 @@ new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new
|
|||
|
||||
if(old_ts != 0xffff) {
|
||||
/* Stop listening to the old time source's EBs */
|
||||
if(old_ts == get_node_timeslot(&linkaddr_node_addr)) {
|
||||
/* This was the same timeslot as slot. Reset original link options */
|
||||
tsch_schedule_add_link(sf_eb, LINK_OPTION_TX, LINK_TYPE_ADVERTISING_ONLY,
|
||||
&tsch_broadcast_address, old_ts, 0);
|
||||
} else {
|
||||
/* Remove slot */
|
||||
tsch_schedule_remove_link_by_timeslot(sf_eb, old_ts);
|
||||
}
|
||||
}
|
||||
if(new_ts != 0xffff) {
|
||||
uint8_t link_options = LINK_OPTION_RX;
|
||||
if(new_ts == get_node_timeslot(&linkaddr_node_addr)) {
|
||||
/* This is also our timeslot, add necessary flags */
|
||||
link_options |= LINK_OPTION_TX;
|
||||
}
|
||||
/* Listen to the time source's EBs */
|
||||
tsch_schedule_add_link(sf_eb,
|
||||
LINK_OPTION_RX,
|
||||
LINK_TYPE_ADVERTISING_ONLY, NULL,
|
||||
new_ts, 0);
|
||||
tsch_schedule_add_link(sf_eb, link_options, LINK_TYPE_ADVERTISING_ONLY,
|
||||
&tsch_broadcast_address, new_ts, 0);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
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
|
||||
* Orchestra: a slotframe dedicated to unicast data transmission.
|
||||
* If sender-based:
|
||||
* Orchestra: a slotframe dedicated to unicast data transmission. Designed for
|
||||
* RPL storing mode only, as this is based on the knowledge of the children (and parent).
|
||||
* If receiver-based:
|
||||
* Nodes listen at a timeslot defined as hash(MAC) % ORCHESTRA_SB_UNICAST_PERIOD
|
||||
* Nodes transmit at: for each nbr in RPL children and RPL preferred parent,
|
||||
* hash(nbr.MAC) % ORCHESTRA_SB_UNICAST_PERIOD
|
||||
* If receiver-based: the opposite
|
||||
* If sender-based: the opposite
|
||||
*
|
||||
* \author Simon Duquennoy <simonduq@sics.se>
|
||||
*/
|
||||
|
@ -43,6 +44,7 @@
|
|||
#include "orchestra.h"
|
||||
#include "net/ipv6/uip-ds6-route.h"
|
||||
#include "net/packetbuf.h"
|
||||
#include "net/rpl/rpl-conf.h"
|
||||
|
||||
#if ORCHESTRA_UNICAST_SENDER_BASED && ORCHESTRA_COLLISION_FREE_HASH
|
||||
#define UNICAST_SLOT_SHARED_FLAG ((ORCHESTRA_UNICAST_PERIOD < (ORCHESTRA_MAX_HASH + 1)) ? LINK_OPTION_SHARED : 0)
|
||||
|
@ -85,9 +87,15 @@ add_uc_link(const linkaddr_t *linkaddr)
|
|||
{
|
||||
if(linkaddr != NULL) {
|
||||
uint16_t timeslot = get_node_timeslot(linkaddr);
|
||||
tsch_schedule_add_link(sf_unicast,
|
||||
ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG,
|
||||
LINK_TYPE_NORMAL, &tsch_broadcast_address,
|
||||
uint8_t link_options = ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG;
|
||||
|
||||
if(timeslot == get_node_timeslot(&linkaddr_node_addr)) {
|
||||
/* This is also our timeslot, add necessary flags */
|
||||
link_options |= ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG: LINK_OPTION_RX;
|
||||
}
|
||||
|
||||
/* Add/update link */
|
||||
tsch_schedule_add_link(sf_unicast, link_options, LINK_TYPE_NORMAL, &tsch_broadcast_address,
|
||||
timeslot, channel_offset);
|
||||
}
|
||||
}
|
||||
|
@ -123,8 +131,18 @@ remove_uc_link(const linkaddr_t *linkaddr)
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
child_added(const linkaddr_t *linkaddr)
|
||||
|
@ -160,13 +178,14 @@ static void
|
|||
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
|
||||
{
|
||||
if(new != old) {
|
||||
const linkaddr_t *old_addr = old != NULL ? &old->addr : NULL;
|
||||
const linkaddr_t *new_addr = new != NULL ? &new->addr : NULL;
|
||||
if(new_addr != NULL) {
|
||||
linkaddr_copy(&orchestra_parent_linkaddr, new_addr);
|
||||
} else {
|
||||
linkaddr_copy(&orchestra_parent_linkaddr, &linkaddr_null);
|
||||
}
|
||||
remove_uc_link(new_addr);
|
||||
remove_uc_link(old_addr);
|
||||
add_uc_link(new_addr);
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +204,7 @@ init(uint16_t sf_handle)
|
|||
timeslot, channel_offset);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
struct orchestra_rule unicast_per_neighbor = {
|
||||
struct orchestra_rule unicast_per_neighbor_rpl_storing = {
|
||||
init,
|
||||
new_time_source,
|
||||
select_packet,
|
|
@ -53,7 +53,8 @@ struct orchestra_rule {
|
|||
};
|
||||
|
||||
struct orchestra_rule eb_per_time_source;
|
||||
struct orchestra_rule unicast_per_neighbor;
|
||||
struct orchestra_rule unicast_per_neighbor_rpl_storing;
|
||||
struct orchestra_rule unicast_per_neighbor_rpl_ns;
|
||||
struct orchestra_rule default_common;
|
||||
|
||||
extern linkaddr_t orchestra_parent_linkaddr;
|
||||
|
|
|
@ -116,11 +116,13 @@ PROCESS_THREAD(shell_exec_process, ev, data)
|
|||
shell_output_str(&exec_command, print, symbol);
|
||||
|
||||
if(ret == ELFLOADER_OK) {
|
||||
#if !PROCESS_CONF_NO_PROCESS_NAMES
|
||||
int i;
|
||||
for(i = 0; elfloader_autostart_processes[i] != NULL; ++i) {
|
||||
shell_output_str(&exec_command, "exec: starting process ",
|
||||
elfloader_autostart_processes[i]->name);
|
||||
}
|
||||
#endif
|
||||
autostart_start(elfloader_autostart_processes);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,12 +75,6 @@
|
|||
#error "Cannot have COFFEE_APPEND_ONLY set when COFFEE_MICRO_LOGS is set."
|
||||
#endif
|
||||
|
||||
/* I/O semantics can be set on file descriptors in order to optimize
|
||||
file access on certain storage types. */
|
||||
#ifndef COFFEE_IO_SEMANTICS
|
||||
#define COFFEE_IO_SEMANTICS 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Prevent sectors from being erased directly after file removal.
|
||||
* This will level the wear across sectors better, but may lead
|
||||
|
@ -94,16 +88,21 @@
|
|||
#error COFFEE_START must point to the first byte in a sector.
|
||||
#endif
|
||||
|
||||
/* File descriptor flags. */
|
||||
#define COFFEE_FD_FREE 0x0
|
||||
#define COFFEE_FD_READ 0x1
|
||||
#define COFFEE_FD_WRITE 0x2
|
||||
#define COFFEE_FD_APPEND 0x4
|
||||
|
||||
/* File object flags. */
|
||||
#define COFFEE_FILE_MODIFIED 0x1
|
||||
|
||||
/* Internal Coffee markers. */
|
||||
#define INVALID_PAGE ((coffee_page_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 CLOSE_FDS 1
|
||||
#define ALLOW_GC 1
|
||||
|
@ -114,8 +113,7 @@
|
|||
#define GC_RELUCTANT 1
|
||||
|
||||
/* File descriptor macros. */
|
||||
#define FD_VALID(fd) \
|
||||
((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
|
||||
#define FD_VALID(fd) ((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
|
||||
coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
|
||||
#define FD_READABLE(fd) (coffee_fd_set[(fd)].flags & CFS_READ)
|
||||
#define FD_WRITABLE(fd) (coffee_fd_set[(fd)].flags & CFS_WRITE)
|
||||
|
@ -127,10 +125,10 @@
|
|||
#define FILE_UNREFERENCED(file) ((file)->references == 0)
|
||||
|
||||
/* File header flags. */
|
||||
#define HDR_FLAG_VALID 0x1 /* Completely written header. */
|
||||
#define HDR_FLAG_ALLOCATED 0x2 /* Allocated file. */
|
||||
#define HDR_FLAG_OBSOLETE 0x4 /* File marked for GC. */
|
||||
#define HDR_FLAG_MODIFIED 0x8 /* Modified file, log exists. */
|
||||
#define HDR_FLAG_VALID 0x01 /* Completely written header. */
|
||||
#define HDR_FLAG_ALLOCATED 0x02 /* Allocated file. */
|
||||
#define HDR_FLAG_OBSOLETE 0x04 /* File marked for GC. */
|
||||
#define HDR_FLAG_MODIFIED 0x08 /* Modified file, log exists. */
|
||||
#define HDR_FLAG_LOG 0x10 /* Log file. */
|
||||
#define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */
|
||||
|
||||
|
@ -148,7 +146,8 @@
|
|||
!HDR_ISOLATED(hdr))
|
||||
|
||||
/* Shortcuts derived from the hardware-dependent configuration of Coffee. */
|
||||
#define COFFEE_SECTOR_COUNT (unsigned)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
|
||||
#define COFFEE_SECTOR_COUNT \
|
||||
(coffee_page_t)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
|
||||
#define COFFEE_PAGE_COUNT \
|
||||
((coffee_page_t)(COFFEE_SIZE / COFFEE_PAGE_SIZE))
|
||||
#define COFFEE_PAGES_PER_SECTOR \
|
||||
|
@ -176,9 +175,7 @@ struct file_desc {
|
|||
cfs_offset_t offset;
|
||||
struct file *file;
|
||||
uint8_t flags;
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
uint8_t io_flags;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* The file header structure mimics the representation of file headers
|
||||
|
@ -196,26 +193,18 @@ struct file_header {
|
|||
/* This is needed because of a buggy compiler. */
|
||||
struct log_param {
|
||||
cfs_offset_t offset;
|
||||
const char *buf;
|
||||
char *buf;
|
||||
uint16_t size;
|
||||
};
|
||||
|
||||
/*
|
||||
* The protected memory consists of structures that should not be
|
||||
* overwritten during system checkpointing because they may be used by
|
||||
* the checkpointing implementation. These structures need not be
|
||||
* protected if checkpointing is not used.
|
||||
* Variables that keep track of opened files and internal
|
||||
* optimization information for Coffee.
|
||||
*/
|
||||
static struct protected_mem_t {
|
||||
struct file coffee_files[COFFEE_MAX_OPEN_FILES];
|
||||
struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
|
||||
coffee_page_t next_free;
|
||||
char gc_wait;
|
||||
} protected_mem;
|
||||
static struct file *const coffee_files = protected_mem.coffee_files;
|
||||
static struct file_desc *const coffee_fd_set = protected_mem.coffee_fd_set;
|
||||
static coffee_page_t *const next_free = &protected_mem.next_free;
|
||||
static char *const gc_wait = &protected_mem.gc_wait;
|
||||
static struct file coffee_files[COFFEE_MAX_OPEN_FILES];
|
||||
static struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
|
||||
static coffee_page_t next_free;
|
||||
static char gc_wait;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
|
@ -229,11 +218,9 @@ static void
|
|||
read_header(struct file_header *hdr, coffee_page_t page)
|
||||
{
|
||||
COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
|
||||
#if DEBUG
|
||||
if(HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
|
||||
PRINTF("Invalid header at page %u!\n", (unsigned)page);
|
||||
if(DEBUG && HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
|
||||
PRINTF("Coffee: Invalid header at page %u!\n", (unsigned)page);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static cfs_offset_t
|
||||
|
@ -243,7 +230,7 @@ absolute_offset(coffee_page_t page, cfs_offset_t offset)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static coffee_page_t
|
||||
get_sector_status(uint16_t sector, struct sector_status *stats)
|
||||
get_sector_status(coffee_page_t sector, struct sector_status *stats)
|
||||
{
|
||||
static coffee_page_t skip_pages;
|
||||
static char last_pages_are_active;
|
||||
|
@ -313,7 +300,7 @@ get_sector_status(uint16_t sector, struct sector_status *stats)
|
|||
/*
|
||||
* Determine the amount of pages in the following sectors that
|
||||
* should be remembered for the next iteration. This is necessary
|
||||
* because no page except the first of a file contains information
|
||||
* because no file page except the first contains information
|
||||
* about what type of page it is. A side effect of remembering this
|
||||
* amount is that there is no need to read in the headers of each
|
||||
* of these pages from the storage.
|
||||
|
@ -364,11 +351,11 @@ isolate_pages(coffee_page_t start, coffee_page_t skip_pages)
|
|||
static void
|
||||
collect_garbage(int mode)
|
||||
{
|
||||
uint16_t sector;
|
||||
coffee_page_t sector;
|
||||
struct sector_status stats;
|
||||
coffee_page_t first_page, isolation_count;
|
||||
|
||||
PRINTF("Coffee: Running the file system garbage collector in %s mode\n",
|
||||
PRINTF("Coffee: Running the garbage collector in %s mode\n",
|
||||
mode == GC_RELUCTANT ? "reluctant" : "greedy");
|
||||
/*
|
||||
* The garbage collector erases as many sectors as possible. A sector is
|
||||
|
@ -377,7 +364,7 @@ collect_garbage(int mode)
|
|||
for(sector = 0; sector < COFFEE_SECTOR_COUNT; sector++) {
|
||||
isolation_count = get_sector_status(sector, &stats);
|
||||
PRINTF("Coffee: Sector %u has %u active, %u obsolete, and %u free pages.\n",
|
||||
sector, (unsigned)stats.active,
|
||||
(unsigned)sector, (unsigned)stats.active,
|
||||
(unsigned)stats.obsolete, (unsigned)stats.free);
|
||||
|
||||
if(stats.active > 0) {
|
||||
|
@ -387,8 +374,8 @@ collect_garbage(int mode)
|
|||
if((mode == GC_RELUCTANT && stats.free == 0) ||
|
||||
(mode == GC_GREEDY && stats.obsolete > 0)) {
|
||||
first_page = sector * COFFEE_PAGES_PER_SECTOR;
|
||||
if(first_page < *next_free) {
|
||||
*next_free = first_page;
|
||||
if(first_page < next_free) {
|
||||
next_free = first_page;
|
||||
}
|
||||
|
||||
if(isolation_count > 0) {
|
||||
|
@ -410,7 +397,7 @@ next_file(coffee_page_t page, struct file_header *hdr)
|
|||
{
|
||||
/*
|
||||
* The quick-skip algorithm for finding file extents is the most
|
||||
* essential part of Coffee. The file allocation rules enables this
|
||||
* essential part of Coffee. The file allocation rules enable this
|
||||
* algorithm to quickly jump over free areas and allocated extents
|
||||
* after reading single headers and determining their status.
|
||||
*
|
||||
|
@ -458,10 +445,7 @@ load_file(coffee_page_t start, struct file_header *hdr)
|
|||
file->page = start;
|
||||
file->end = UNKNOWN_OFFSET;
|
||||
file->max_pages = hdr->max_pages;
|
||||
file->flags = 0;
|
||||
if(HDR_MODIFIED(*hdr)) {
|
||||
file->flags |= COFFEE_FILE_MODIFIED;
|
||||
}
|
||||
file->flags = HDR_MODIFIED(*hdr) ? COFFEE_FILE_MODIFIED : 0;
|
||||
/* We don't know the amount of records yet. */
|
||||
file->record_count = -1;
|
||||
|
||||
|
@ -539,7 +523,7 @@ find_contiguous_pages(coffee_page_t amount)
|
|||
struct file_header hdr;
|
||||
|
||||
start = INVALID_PAGE;
|
||||
for(page = *next_free; page < COFFEE_PAGE_COUNT;) {
|
||||
for(page = next_free; page < COFFEE_PAGE_COUNT;) {
|
||||
read_header(&hdr, page);
|
||||
if(HDR_FREE(hdr)) {
|
||||
if(start == INVALID_PAGE) {
|
||||
|
@ -555,8 +539,8 @@ find_contiguous_pages(coffee_page_t amount)
|
|||
page = next_file(page, &hdr);
|
||||
|
||||
if(start + amount <= page) {
|
||||
if(start == *next_free) {
|
||||
*next_free = start + amount;
|
||||
if(start == next_free) {
|
||||
next_free = start + amount;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
@ -569,8 +553,8 @@ find_contiguous_pages(coffee_page_t amount)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
remove_by_page(coffee_page_t page, int remove_log, int close_fds,
|
||||
int gc_allowed)
|
||||
remove_by_page(coffee_page_t page, int remove_log,
|
||||
int close_fds, int gc_allowed)
|
||||
{
|
||||
struct file_header hdr;
|
||||
int i;
|
||||
|
@ -589,7 +573,7 @@ remove_by_page(coffee_page_t page, int remove_log, int close_fds,
|
|||
hdr.flags |= HDR_FLAG_OBSOLETE;
|
||||
write_header(&hdr, page);
|
||||
|
||||
*gc_wait = 0;
|
||||
gc_wait = 0;
|
||||
|
||||
/* Close all file descriptors that reference the removed file. */
|
||||
if(close_fds) {
|
||||
|
@ -608,11 +592,9 @@ remove_by_page(coffee_page_t page, int remove_log, int close_fds,
|
|||
}
|
||||
}
|
||||
|
||||
#if !COFFEE_EXTENDED_WEAR_LEVELLING
|
||||
if(gc_allowed) {
|
||||
if(!COFFEE_EXTENDED_WEAR_LEVELLING && gc_allowed) {
|
||||
collect_garbage(GC_RELUCTANT);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -638,13 +620,13 @@ reserve(const char *name, coffee_page_t pages,
|
|||
|
||||
page = find_contiguous_pages(pages);
|
||||
if(page == INVALID_PAGE) {
|
||||
if(*gc_wait) {
|
||||
if(gc_wait) {
|
||||
return NULL;
|
||||
}
|
||||
collect_garbage(GC_GREEDY);
|
||||
page = find_contiguous_pages(pages);
|
||||
if(page == INVALID_PAGE) {
|
||||
*gc_wait = 1;
|
||||
gc_wait = 1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -656,7 +638,7 @@ reserve(const char *name, coffee_page_t pages,
|
|||
write_header(&hdr, page);
|
||||
|
||||
PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n",
|
||||
pages, page, name);
|
||||
(unsigned)pages, (unsigned)page, name);
|
||||
|
||||
file = load_file(page, &hdr);
|
||||
if(file != NULL) {
|
||||
|
@ -851,7 +833,7 @@ merge_log(coffee_page_t file_page, int extend)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Copy the log configuration and the EOF hint. */
|
||||
/* Copy the log configuration. */
|
||||
read_header(&hdr2, new_file->page);
|
||||
hdr2.log_record_size = hdr.log_record_size;
|
||||
hdr2.log_records = hdr.log_records;
|
||||
|
@ -1005,6 +987,7 @@ cfs_open(const char *name, int flags)
|
|||
|
||||
fdp = &coffee_fd_set[fd];
|
||||
fdp->flags = 0;
|
||||
fdp->io_flags = 0;
|
||||
|
||||
fdp->file = find_file(name);
|
||||
if(fdp->file == NULL) {
|
||||
|
@ -1063,7 +1046,12 @@ cfs_seek(int fd, cfs_offset_t offset, int whence)
|
|||
}
|
||||
|
||||
if(fdp->file->end < new_offset) {
|
||||
if(FD_WRITABLE(fd)) {
|
||||
fdp->file->end = new_offset;
|
||||
} else {
|
||||
/* Disallow seeking past the end of the file for read only FDs */
|
||||
return (cfs_offset_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
return fdp->offset = new_offset;
|
||||
|
@ -1106,11 +1094,16 @@ cfs_read(int fd, void *buf, unsigned size)
|
|||
|
||||
fdp = &coffee_fd_set[fd];
|
||||
file = fdp->file;
|
||||
if(fdp->offset + size > file->end) {
|
||||
|
||||
if(fdp->io_flags & CFS_COFFEE_IO_ENSURE_READ_LENGTH) {
|
||||
while(fdp->offset + size > file->end) {
|
||||
((char *)buf)[--size] = '\0';
|
||||
}
|
||||
} else if(fdp->offset + size > file->end) {
|
||||
size = file->end - fdp->offset;
|
||||
}
|
||||
|
||||
/* If the file is allocated, read directly in the file. */
|
||||
/* If the file is not modified, read directly from the file extent. */
|
||||
if(!FILE_MODIFIED(file)) {
|
||||
COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset));
|
||||
fdp->offset += size;
|
||||
|
@ -1121,8 +1114,9 @@ cfs_read(int fd, void *buf, unsigned size)
|
|||
read_header(&hdr, file->page);
|
||||
|
||||
/*
|
||||
* Fill the buffer by copying from the log in first hand, or the
|
||||
* ordinary file if the page has no log record.
|
||||
* Copy the contents of the most recent log record. If there is
|
||||
* no log record for the file area to read from, we simply read
|
||||
* from the original file extent.
|
||||
*/
|
||||
for(bytes_left = size; bytes_left > 0; bytes_left -= r) {
|
||||
lp.offset = fdp->offset;
|
||||
|
@ -1164,9 +1158,7 @@ cfs_write(int fd, const void *buf, unsigned size)
|
|||
file = fdp->file;
|
||||
|
||||
/* Attempt to extend the file if we try to write past the end. */
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) {
|
||||
#endif
|
||||
while(size + fdp->offset + sizeof(struct file_header) >
|
||||
(file->max_pages * COFFEE_PAGE_SIZE)) {
|
||||
if(merge_log(file->page, 1) < 0) {
|
||||
|
@ -1175,21 +1167,15 @@ cfs_write(int fd, const void *buf, unsigned size)
|
|||
file = fdp->file;
|
||||
PRINTF("Extended the file at page %u\n", (unsigned)file->page);
|
||||
}
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
}
|
||||
#endif
|
||||
|
||||
#if COFFEE_MICRO_LOGS
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) &&
|
||||
(FILE_MODIFIED(file) || fdp->offset < file->end)) {
|
||||
#else
|
||||
if(FILE_MODIFIED(file) || fdp->offset < file->end) {
|
||||
#endif
|
||||
need_dummy_write = 0;
|
||||
for(bytes_left = size; bytes_left > 0;) {
|
||||
lp.offset = fdp->offset;
|
||||
lp.buf = buf;
|
||||
lp.buf = (void *)buf;
|
||||
lp.size = bytes_left;
|
||||
i = write_log_page(file, &lp);
|
||||
if(i < 0) {
|
||||
|
@ -1227,11 +1213,9 @@ cfs_write(int fd, const void *buf, unsigned size)
|
|||
}
|
||||
} else {
|
||||
#endif /* COFFEE_MICRO_LOGS */
|
||||
#if COFFEE_APPEND_ONLY
|
||||
if(fdp->offset < file->end) {
|
||||
if(COFFEE_APPEND_ONLY && fdp->offset < file->end) {
|
||||
return -1;
|
||||
}
|
||||
#endif /* COFFEE_APPEND_ONLY */
|
||||
|
||||
COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
|
||||
fdp->offset += size;
|
||||
|
@ -1250,10 +1234,10 @@ int
|
|||
cfs_opendir(struct cfs_dir *dir, const char *name)
|
||||
{
|
||||
/*
|
||||
* Coffee is only guaranteed to support "/" and ".", but it does not
|
||||
* currently enforce this.
|
||||
* Coffee is only guaranteed to support the directory names "/" and ".",
|
||||
* but it does not enforce this currently.
|
||||
*/
|
||||
memset(dir->dummy_space, 0, sizeof(coffee_page_t));
|
||||
memset(dir->state, 0, sizeof(coffee_page_t));
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -1262,19 +1246,19 @@ cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
|
|||
{
|
||||
struct file_header hdr;
|
||||
coffee_page_t page;
|
||||
coffee_page_t next_page;
|
||||
|
||||
memcpy(&page, dir->dummy_space, sizeof(coffee_page_t));
|
||||
memcpy(&page, dir->state, sizeof(coffee_page_t));
|
||||
|
||||
while(page < COFFEE_PAGE_COUNT) {
|
||||
read_header(&hdr, page);
|
||||
if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) {
|
||||
coffee_page_t next_page;
|
||||
memcpy(record->name, hdr.name, sizeof(record->name));
|
||||
record->name[sizeof(record->name) - 1] = '\0';
|
||||
record->size = file_end(page);
|
||||
|
||||
next_page = next_file(page, &hdr);
|
||||
memcpy(dir->dummy_space, &next_page, sizeof(coffee_page_t));
|
||||
memcpy(dir->state, &next_page, sizeof(coffee_page_t));
|
||||
return 0;
|
||||
}
|
||||
page = next_file(page, &hdr);
|
||||
|
@ -1342,11 +1326,9 @@ cfs_coffee_set_io_semantics(int fd, unsigned flags)
|
|||
int
|
||||
cfs_coffee_format(void)
|
||||
{
|
||||
unsigned i;
|
||||
coffee_page_t i;
|
||||
|
||||
PRINTF("Coffee: Formatting %u sectors", COFFEE_SECTOR_COUNT);
|
||||
|
||||
*next_free = 0;
|
||||
PRINTF("Coffee: Formatting %u sectors", (unsigned)COFFEE_SECTOR_COUNT);
|
||||
|
||||
for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
|
||||
COFFEE_ERASE(i);
|
||||
|
@ -1354,16 +1336,13 @@ cfs_coffee_format(void)
|
|||
}
|
||||
|
||||
/* Formatting invalidates the file information. */
|
||||
memset(&protected_mem, 0, sizeof(protected_mem));
|
||||
memset(&coffee_files, 0, sizeof(coffee_files));
|
||||
memset(&coffee_fd_set, 0, sizeof(coffee_fd_set));
|
||||
next_free = 0;
|
||||
gc_wait = 1;
|
||||
|
||||
PRINTF(" done!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void *
|
||||
cfs_coffee_get_protected_mem(unsigned *size)
|
||||
{
|
||||
*size = sizeof(protected_mem);
|
||||
return &protected_mem;
|
||||
}
|
||||
|
|
|
@ -46,15 +46,15 @@
|
|||
* invoke its own micro logs when file modifications occur.
|
||||
*
|
||||
* This semantical I/O setting is useful when implementing flash storage
|
||||
* algorithms on top of Coffee.
|
||||
* algorithms such as database indices on top of Coffee.
|
||||
*
|
||||
* \sa cfs_coffee_set_io_semantics()
|
||||
*/
|
||||
#define CFS_COFFEE_IO_FLASH_AWARE 0x1
|
||||
|
||||
/**
|
||||
* Instruct Coffee not to attempt to extend the file when there is
|
||||
* an attempt to write past the reserved file size.
|
||||
* Instruct Coffee not to attempt to extend the file upon a request
|
||||
* to write past the reserved file size.
|
||||
*
|
||||
* A case when this is necessary is when the file has a firm size limit,
|
||||
* and a safeguard is needed to protect against writes beyond this limit.
|
||||
|
@ -63,6 +63,15 @@
|
|||
*/
|
||||
#define CFS_COFFEE_IO_FIRM_SIZE 0x2
|
||||
|
||||
/**
|
||||
* Instruct Coffee to set unused bytes in the destination buffer to zero.
|
||||
* Trailing zeros may cause a wrong file size, this option ensures that
|
||||
* the corresponding bytes get set, so Coffee does not read unexpected data.
|
||||
*
|
||||
* \sa cfs_coffee_set_io_semantics()
|
||||
*/
|
||||
#define CFS_COFFEE_IO_ENSURE_READ_LENGTH 0x4
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Header for the Coffee file system.
|
||||
|
@ -76,7 +85,7 @@
|
|||
/**
|
||||
* \brief Reserve space for a file.
|
||||
* \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.
|
||||
*
|
||||
* Coffee uses sequential page structures for files. The sequential
|
||||
|
@ -89,14 +98,14 @@ int cfs_coffee_reserve(const char *name, cfs_offset_t size);
|
|||
/**
|
||||
* \brief Configure the on-demand log file.
|
||||
* \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.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*
|
||||
* When file data is first modified, Coffee creates a micro log for the
|
||||
* file. The micro log stores a table of modifications whose
|
||||
* parameters--the log size and the log entry size--can be modified
|
||||
* through the cfs_coffee_configure_log function.
|
||||
* file. The micro log stores a table of modifications whose parameters --
|
||||
* the log size and the log entry size -- can be modified through the
|
||||
* cfs_coffee_configure_log function.
|
||||
*/
|
||||
int cfs_coffee_configure_log(const char *file, unsigned log_size,
|
||||
unsigned log_entry_size);
|
||||
|
@ -109,8 +118,8 @@ int cfs_coffee_configure_log(const char *file, unsigned log_size,
|
|||
*
|
||||
* Coffee is used on a wide range of storage types, and the default
|
||||
* I/O file semantics may not be optimal for the access pattern
|
||||
* of a certain file. Hence, this functions allows programmers to
|
||||
* switch the /O semantics on a file that is accessed through a
|
||||
* of a certain file. Hence, this function allows programmers to
|
||||
* switch the I/O semantics on a file that is accessed through a
|
||||
* particular file descriptor.
|
||||
*
|
||||
*/
|
||||
|
@ -123,21 +132,14 @@ int cfs_coffee_set_io_semantics(int fd, unsigned flags);
|
|||
* Coffee formats the underlying storage by setting all bits to zero.
|
||||
* Formatting must be done before using Coffee for the first time in
|
||||
* a mote.
|
||||
*
|
||||
* Notice that the erased bits may be set to 1 on the physical storage
|
||||
* when using flash memory. In this case, Coffee requires that the
|
||||
* COFFEE_READ and COFFEE_WRITE functions used to access the flash memory
|
||||
* invert all bits.
|
||||
*/
|
||||
int cfs_coffee_format(void);
|
||||
|
||||
/**
|
||||
* \brief Points out a memory region that may not be altered during
|
||||
* checkpointing operations that use the file system.
|
||||
* \param size
|
||||
* \return A pointer to the protected memory.
|
||||
*
|
||||
* This function returns the protected memory pointer and writes its size
|
||||
* to the given parameter. Mainly used by sensornet checkpointing to protect
|
||||
* the coffee state during CFS-based checkpointing operations.
|
||||
*/
|
||||
void *cfs_coffee_get_protected_mem(unsigned *size);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
|
|
|
@ -68,7 +68,9 @@ typedef CFS_CONF_OFFSET_TYPE cfs_offset_t;
|
|||
#endif
|
||||
|
||||
struct cfs_dir {
|
||||
char dummy_space[32];
|
||||
/* Iteration state, which is implementation-defined and should not be
|
||||
accessed externally. */
|
||||
char state[32];
|
||||
};
|
||||
|
||||
struct cfs_dirent {
|
||||
|
|
|
@ -78,13 +78,6 @@
|
|||
#define NETSTACK_CONF_LLSEC nullsec_driver
|
||||
#endif /* NETSTACK_CONF_LLSEC */
|
||||
|
||||
/* To avoid unnecessary complexity, we assume the common case of
|
||||
a constant LoWPAN-wide IEEE 802.15.4 security level, which
|
||||
can be specified by defining LLSEC802154_CONF_SECURITY_LEVEL. */
|
||||
#ifndef LLSEC802154_CONF_SECURITY_LEVEL
|
||||
#define LLSEC802154_CONF_SECURITY_LEVEL 0
|
||||
#endif /* LLSEC802154_CONF_SECURITY_LEVEL */
|
||||
|
||||
/* NETSTACK_CONF_NETWORK specifies the network layer and can be either
|
||||
sicslowpan_driver, for IPv6 networking, or rime_driver, for the
|
||||
custom Rime network stack. */
|
||||
|
@ -148,12 +141,32 @@
|
|||
#define UIP_CONF_IPV6_RPL 1
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
/* If RPL is enabled also enable the RPL NBR Policy */
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
#ifndef NBR_TABLE_FIND_REMOVABLE
|
||||
#define NBR_TABLE_FIND_REMOVABLE rpl_nbr_policy_find_removable
|
||||
#endif /* NBR_TABLE_FIND_REMOVABLE */
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
/* RPL_CONF_MOP specifies the RPL mode of operation that will be
|
||||
* advertised by the RPL root. Possible values: RPL_MOP_NO_DOWNWARD_ROUTES,
|
||||
* RPL_MOP_NON_STORING, RPL_MOP_STORING_NO_MULTICAST, RPL_MOP_STORING_MULTICAST */
|
||||
#ifndef RPL_CONF_MOP
|
||||
#define RPL_CONF_MOP RPL_MOP_STORING_NO_MULTICAST
|
||||
#endif /* RPL_CONF_MOP */
|
||||
|
||||
/* UIP_CONF_MAX_ROUTES specifies the maximum number of routes that each
|
||||
node will be able to handle. */
|
||||
#ifndef UIP_CONF_MAX_ROUTES
|
||||
#define UIP_CONF_MAX_ROUTES 20
|
||||
#endif /* UIP_CONF_MAX_ROUTES */
|
||||
|
||||
/* RPL_NS_CONF_LINK_NUM specifies the maximum number of links a RPL root
|
||||
* will maintain in non-storing mode. */
|
||||
#ifndef RPL_NS_CONF_LINK_NUM
|
||||
#define RPL_NS_CONF_LINK_NUM 20
|
||||
#endif /* RPL_NS_CONF_LINK_NUM */
|
||||
|
||||
/* UIP_CONF_UDP specifies if UDP support should be included or
|
||||
not. Disabling UDP saves memory but breaks a lot of stuff. */
|
||||
#ifndef UIP_CONF_UDP
|
||||
|
@ -220,14 +233,6 @@
|
|||
* on the target platform, and are therefore platform-specific.
|
||||
*/
|
||||
|
||||
/* SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS specifies how many times the
|
||||
MAC layer should resend packets if no link-layer ACK was
|
||||
received. This only makes sense with the csma_driver
|
||||
NETSTACK_CONF_MAC. */
|
||||
#ifndef SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS
|
||||
#define SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS 4
|
||||
#endif /* SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS */
|
||||
|
||||
/* SICSLOWPAN_CONF_FRAG specifies if 6lowpan fragmentation should be
|
||||
used or not. Fragmentation is on by default. */
|
||||
#ifndef SICSLOWPAN_CONF_FRAG
|
||||
|
|
|
@ -64,13 +64,13 @@ static unsigned char gcr_bits = 0;
|
|||
static unsigned short gcr_val = 0;
|
||||
|
||||
/* Call before starting encoding or decoding */
|
||||
void gcr_init() {
|
||||
void gcr_init(void) {
|
||||
gcr_val = 0;
|
||||
gcr_bits = 0;
|
||||
}
|
||||
|
||||
/* Use this to check if encoding / decoding is complete for now */
|
||||
unsigned char gcr_finished() {
|
||||
unsigned char gcr_finished(void) {
|
||||
return gcr_bits == 0;
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ void gcr_decode(unsigned char gcr_data) {
|
|||
}
|
||||
|
||||
/* check if the current decoded stream is correct */
|
||||
unsigned char gcr_valid() {
|
||||
unsigned char gcr_valid(void) {
|
||||
if (gcr_bits >= 10) {
|
||||
unsigned short val = gcr_val & 0x3ff;
|
||||
if ((GCR_decode[val >> 5u] << 4u) == 0xff ||
|
||||
|
|
|
@ -186,7 +186,7 @@ extern settings_status_t settings_delete(settings_key_t key, uint8_t index);
|
|||
typedef eeprom_addr_t settings_iter_t;
|
||||
|
||||
/** Will return \ref SETTINGS_INVALID_ITER if the settings store is empty. */
|
||||
extern settings_iter_t settings_iter_begin();
|
||||
extern settings_iter_t settings_iter_begin(void);
|
||||
|
||||
/** Will return \ref SETTINGS_INVALID_ITER if at the end of settings list. */
|
||||
extern settings_iter_t settings_iter_next(settings_iter_t iter);
|
||||
|
|
|
@ -79,7 +79,7 @@ static void double_interval(void *ptr);
|
|||
#if TRICKLE_TIMER_WIDE_RAND
|
||||
/* Returns a 4-byte wide, unsigned random number */
|
||||
static uint32_t
|
||||
wide_rand()
|
||||
wide_rand(void)
|
||||
{
|
||||
return ((uint32_t)random_rand() << 16 | random_rand());
|
||||
}
|
||||
|
|
|
@ -324,6 +324,7 @@ parse_url(const char *url, char *host, uint16_t *portptr, char *path)
|
|||
if(host != NULL) {
|
||||
host[i] = 0;
|
||||
}
|
||||
urlptr++;
|
||||
break;
|
||||
}
|
||||
if(host != NULL) {
|
||||
|
@ -349,6 +350,11 @@ parse_url(const char *url, char *host, uint16_t *portptr, char *path)
|
|||
}
|
||||
}
|
||||
|
||||
/* check if host is null terminated */
|
||||
if(!memchr(host, 0, MAX_HOSTLEN)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the port. Default is 80. */
|
||||
port = 80;
|
||||
if(*urlptr == ':') {
|
||||
|
@ -412,7 +418,15 @@ event(struct tcp_socket *tcps, void *ptr,
|
|||
tcp_socket_send_str(tcps, " HTTP/1.1\r\n");
|
||||
tcp_socket_send_str(tcps, "Connection: close\r\n");
|
||||
tcp_socket_send_str(tcps, "Host: ");
|
||||
/* If we have IPv6 host, add the '[' and the ']' characters
|
||||
to the host. As in rfc2732. */
|
||||
if(memchr(host, ':', MAX_HOSTLEN)) {
|
||||
tcp_socket_send_str(tcps, "[");
|
||||
}
|
||||
tcp_socket_send_str(tcps, host);
|
||||
if(memchr(host, ':', MAX_HOSTLEN)) {
|
||||
tcp_socket_send_str(tcps, "]");
|
||||
}
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
if(s->postdata != NULL) {
|
||||
if(s->content_type) {
|
||||
|
|
|
@ -111,6 +111,9 @@ strcasecmp(const char *s1, const char *s2)
|
|||
/* TODO: Add case support! */
|
||||
return strcmp(s1, s2);
|
||||
}
|
||||
#else
|
||||
int strcasecmp(const char *s1, const char *s2);
|
||||
int strncasecmp(const char *s1, const char *s2, size_t n);
|
||||
#endif /* __SDCC */
|
||||
|
||||
#define UIP_UDP_BUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])
|
||||
|
@ -783,9 +786,9 @@ check_entries(void)
|
|||
static void
|
||||
newdata(void)
|
||||
{
|
||||
static uint8_t nquestions, nanswers;
|
||||
uint8_t nquestions, nanswers;
|
||||
|
||||
static int8_t i;
|
||||
int8_t i;
|
||||
|
||||
register struct namemap *namemapptr = NULL;
|
||||
|
||||
|
@ -872,7 +875,7 @@ newdata(void)
|
|||
}
|
||||
return;
|
||||
} else {
|
||||
static uint8_t nauthrr;
|
||||
uint8_t nauthrr;
|
||||
PRINTF("resolver: But we are still probing. Waiting...\n");
|
||||
/* We are still probing. We need to do the mDNS
|
||||
* probe race condition check here and make sure
|
||||
|
@ -960,7 +963,7 @@ newdata(void)
|
|||
#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */
|
||||
|
||||
#if VERBOSE_DEBUG
|
||||
static char debug_name[40];
|
||||
char debug_name[40];
|
||||
decode_name(queryptr, debug_name, uip_appdata);
|
||||
DEBUG_PRINTF("resolver: Answer %d: \"%s\", type %d, class %d, ttl %d, length %d\n",
|
||||
++i, debug_name, uip_ntohs(ans->type),
|
||||
|
@ -1266,9 +1269,9 @@ remove_trailing_dots(const char *name) {
|
|||
void
|
||||
resolv_query(const char *name)
|
||||
{
|
||||
static uint8_t i;
|
||||
uint8_t i;
|
||||
|
||||
static uint8_t lseq, lseqi;
|
||||
uint8_t lseq, lseqi;
|
||||
|
||||
register struct namemap *nameptr = 0;
|
||||
|
||||
|
@ -1315,7 +1318,7 @@ resolv_query(const char *name)
|
|||
{
|
||||
size_t name_len = strlen(name);
|
||||
|
||||
static const char local_suffix[] = "local";
|
||||
const char local_suffix[] = "local";
|
||||
|
||||
if((name_len > (sizeof(local_suffix) - 1)) &&
|
||||
(0 == strcasecmp(name + name_len - (sizeof(local_suffix) - 1), local_suffix))) {
|
||||
|
@ -1347,7 +1350,7 @@ resolv_lookup(const char *name, uip_ipaddr_t ** ipaddr)
|
|||
{
|
||||
resolv_status_t ret = RESOLV_STATUS_UNCACHED;
|
||||
|
||||
static uint8_t i;
|
||||
uint8_t i;
|
||||
|
||||
struct namemap *nameptr;
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ simple_udp_register(struct simple_udp_connection *c,
|
|||
|
||||
PROCESS_CONTEXT_BEGIN(&simple_udp_process);
|
||||
c->udp_conn = udp_new(remote_addr, UIP_HTONS(remote_port), c);
|
||||
if(c->udp_conn != NULL) {
|
||||
if(c->udp_conn != NULL && local_port) {
|
||||
udp_bind(c->udp_conn, UIP_HTONS(local_port));
|
||||
}
|
||||
PROCESS_CONTEXT_END();
|
||||
|
|
|
@ -47,6 +47,11 @@
|
|||
#include "net/ipv6/uip-ds6.h"
|
||||
#endif
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
#include "net/rpl/rpl.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
|
@ -237,7 +242,7 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
|
|||
void
|
||||
tcp_unlisten(uint16_t port)
|
||||
{
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
struct listenport *l;
|
||||
|
||||
l = s.listenports;
|
||||
|
@ -255,7 +260,7 @@ tcp_unlisten(uint16_t port)
|
|||
void
|
||||
tcp_listen(uint16_t port)
|
||||
{
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
struct listenport *l;
|
||||
|
||||
l = s.listenports;
|
||||
|
@ -359,7 +364,7 @@ static void
|
|||
eventhandler(process_event_t ev, process_data_t data)
|
||||
{
|
||||
#if UIP_TCP
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
register struct listenport *l;
|
||||
#endif /*UIP_TCP*/
|
||||
struct process *p;
|
||||
|
@ -525,7 +530,7 @@ void
|
|||
tcpip_ipv6_output(void)
|
||||
{
|
||||
uip_ds6_nbr_t *nbr = NULL;
|
||||
uip_ipaddr_t *nexthop;
|
||||
uip_ipaddr_t *nexthop = NULL;
|
||||
|
||||
if(uip_len == 0) {
|
||||
return;
|
||||
|
@ -545,14 +550,25 @@ tcpip_ipv6_output(void)
|
|||
|
||||
if(!uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
|
||||
/* Next hop determination */
|
||||
|
||||
#if UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING
|
||||
uip_ipaddr_t ipaddr;
|
||||
/* Look for a RPL Source Route */
|
||||
if(rpl_srh_get_next_hop(&ipaddr)) {
|
||||
nexthop = &ipaddr;
|
||||
}
|
||||
#endif /* UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING */
|
||||
|
||||
nbr = NULL;
|
||||
|
||||
/* We first check if the destination address is on our immediate
|
||||
link. If so, we simply use the destination address as our
|
||||
nexthop address. */
|
||||
if(uip_ds6_is_addr_onlink(&UIP_IP_BUF->destipaddr)){
|
||||
if(nexthop == NULL && uip_ds6_is_addr_onlink(&UIP_IP_BUF->destipaddr)){
|
||||
nexthop = &UIP_IP_BUF->destipaddr;
|
||||
} else {
|
||||
}
|
||||
|
||||
if(nexthop == NULL) {
|
||||
uip_ds6_route_t *route;
|
||||
/* Check if we have a route to the destination address. */
|
||||
route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
|
||||
|
@ -636,7 +652,7 @@ tcpip_ipv6_output(void)
|
|||
/* End of next hop determination */
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
if(rpl_update_header_final(nexthop)) {
|
||||
if(!rpl_finalize_header(nexthop)) {
|
||||
uip_clear_buf();
|
||||
return;
|
||||
}
|
||||
|
@ -644,8 +660,9 @@ tcpip_ipv6_output(void)
|
|||
nbr = uip_ds6_nbr_lookup(nexthop);
|
||||
if(nbr == NULL) {
|
||||
#if UIP_ND6_SEND_NA
|
||||
if((nbr = uip_ds6_nbr_add(nexthop, NULL, 0, NBR_INCOMPLETE)) == NULL) {
|
||||
if((nbr = uip_ds6_nbr_add(nexthop, NULL, 0, NBR_INCOMPLETE, NBR_TABLE_REASON_IPV6_ND, NULL)) == NULL) {
|
||||
uip_clear_buf();
|
||||
PRINTF("tcpip_ipv6_output: failed to add neighbor to cache\n");
|
||||
return;
|
||||
} else {
|
||||
#if UIP_CONF_IPV6_QUEUE_PKT
|
||||
|
@ -672,6 +689,7 @@ tcpip_ipv6_output(void)
|
|||
/* Send the first NS try from here (multicast destination IP address). */
|
||||
}
|
||||
#else /* UIP_ND6_SEND_NA */
|
||||
PRINTF("tcpip_ipv6_output: neighbor not in cache\n");
|
||||
uip_len = 0;
|
||||
return;
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
|
@ -760,7 +778,7 @@ tcpip_uipcall(void)
|
|||
|
||||
#if UIP_TCP
|
||||
{
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
struct listenport *l;
|
||||
|
||||
/* 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
|
||||
{
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
|
||||
for(i = 0; i < UIP_LISTENPORTS; ++i) {
|
||||
s.listenports[i].port = 0;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
/**
|
||||
* \file
|
||||
* A set of debugging tools
|
||||
* A set of debugging tools for the IP stack
|
||||
* \author
|
||||
* Nicolas Tsiftes <nvt@sics.se>
|
||||
* Niclas Finne <nfi@sics.se>
|
||||
|
@ -92,19 +92,3 @@ uip_debug_ipaddr_print(const uip_ipaddr_t *addr)
|
|||
#endif /* NETSTACK_CONF_WITH_IPV6 */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
uip_debug_lladdr_print(const uip_lladdr_t *addr)
|
||||
{
|
||||
unsigned int i;
|
||||
if(addr == NULL) {
|
||||
PRINTA("(NULL LL addr)");
|
||||
return;
|
||||
}
|
||||
for(i = 0; i < sizeof(uip_lladdr_t); i++) {
|
||||
if(i > 0) {
|
||||
PRINTA(":");
|
||||
}
|
||||
PRINTA("%02x", addr->addr[i]);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
|
@ -31,57 +31,27 @@
|
|||
*/
|
||||
/**
|
||||
* \file
|
||||
* A set of debugging macros.
|
||||
* A set of debugging macros for the IP stack
|
||||
*
|
||||
* \author Nicolas Tsiftes <nvt@sics.se>
|
||||
* Niclas Finne <nfi@sics.se>
|
||||
* Joakim Eriksson <joakime@sics.se>
|
||||
* Simon Duquennoy <simon.duquennoy@inria.fr>
|
||||
*/
|
||||
|
||||
#ifndef UIP_DEBUG_H
|
||||
#define UIP_DEBUG_H
|
||||
|
||||
#include "net/net-debug.h"
|
||||
#include "net/ip/uip.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void uip_debug_ipaddr_print(const uip_ipaddr_t *addr);
|
||||
void uip_debug_lladdr_print(const uip_lladdr_t *addr);
|
||||
|
||||
#define DEBUG_NONE 0
|
||||
#define DEBUG_PRINT 1
|
||||
#define DEBUG_ANNOTATE 2
|
||||
#define DEBUG_FULL DEBUG_ANNOTATE | DEBUG_PRINT
|
||||
|
||||
/* PRINTA will always print if the debug routines are called directly */
|
||||
#ifdef __AVR__
|
||||
#include <avr/pgmspace.h>
|
||||
#define PRINTA(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
|
||||
#else
|
||||
#define PRINTA(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#if (DEBUG) & DEBUG_ANNOTATE
|
||||
#ifdef __AVR__
|
||||
#define ANNOTATE(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
|
||||
#else
|
||||
#define ANNOTATE(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define ANNOTATE(...)
|
||||
#endif /* (DEBUG) & DEBUG_ANNOTATE */
|
||||
|
||||
#if (DEBUG) & DEBUG_PRINT
|
||||
#ifdef __AVR__
|
||||
#define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
|
||||
#else
|
||||
#define PRINTF(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
#define PRINT6ADDR(addr) uip_debug_ipaddr_print(addr)
|
||||
#define PRINTLLADDR(lladdr) uip_debug_lladdr_print(lladdr)
|
||||
#else
|
||||
#define PRINTF(...)
|
||||
#define PRINT6ADDR(addr)
|
||||
#define PRINTLLADDR(lladdr)
|
||||
#endif /* (DEBUG) & DEBUG_PRINT */
|
||||
|
||||
#endif
|
||||
#endif /* UIP_DEBUG_H */
|
||||
|
|
|
@ -51,12 +51,10 @@ void
|
|||
uip_udp_packet_send(struct uip_udp_conn *c, const void *data, int len)
|
||||
{
|
||||
#if UIP_UDP
|
||||
if(data != NULL) {
|
||||
if(data != NULL && len <= (UIP_BUFSIZE - (UIP_LLH_LEN + UIP_IPUDPH_LEN))) {
|
||||
uip_udp_conn = c;
|
||||
uip_slen = len;
|
||||
memmove(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data,
|
||||
len > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?
|
||||
UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: len);
|
||||
memmove(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data, len);
|
||||
uip_process(UIP_UDP_SEND_CONN);
|
||||
|
||||
#if UIP_CONF_IPV6_MULTICAST
|
||||
|
|
|
@ -1801,6 +1801,13 @@ typedef struct uip_routing_hdr {
|
|||
uint8_t seg_left;
|
||||
} uip_routing_hdr;
|
||||
|
||||
/* RPL Source Routing Header */
|
||||
typedef struct uip_rpl_srh_hdr {
|
||||
uint8_t cmpr; /* CmprI and CmprE */
|
||||
uint8_t pad;
|
||||
uint8_t reserved[2];
|
||||
} uip_rpl_srh_hdr;
|
||||
|
||||
/* fragmentation header */
|
||||
typedef struct uip_frag_hdr {
|
||||
uint8_t next;
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
|
||||
#include "contiki.h"
|
||||
#include "dev/watchdog.h"
|
||||
#include "net/link-stats.h"
|
||||
#include "net/ip/tcpip.h"
|
||||
#include "net/ip/uip.h"
|
||||
#include "net/ipv6/uip-ds6.h"
|
||||
|
@ -92,12 +93,6 @@ void uip_log(char *msg);
|
|||
#define UIP_LOG(m)
|
||||
#endif /* UIP_LOGGING == 1 */
|
||||
|
||||
#ifdef SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS
|
||||
#define SICSLOWPAN_MAX_MAC_TRANSMISSIONS SICSLOWPAN_CONF_MAX_MAC_TRANSMISSIONS
|
||||
#else
|
||||
#define SICSLOWPAN_MAX_MAC_TRANSMISSIONS 4
|
||||
#endif
|
||||
|
||||
#ifndef SICSLOWPAN_COMPRESSION
|
||||
#ifdef SICSLOWPAN_CONF_COMPRESSION
|
||||
#define SICSLOWPAN_COMPRESSION SICSLOWPAN_CONF_COMPRESSION
|
||||
|
@ -169,6 +164,14 @@ void uip_log(char *msg);
|
|||
#define COMPRESSION_THRESHOLD 0
|
||||
#endif
|
||||
|
||||
/** \brief Fixed size of a frame header. This value is
|
||||
* used in case framer returns an error or if SICSLOWPAN_USE_FIXED_HDRLEN
|
||||
* is defined.
|
||||
*/
|
||||
#ifndef SICSLOWPAN_FIXED_HDRLEN
|
||||
#define SICSLOWPAN_FIXED_HDRLEN 21
|
||||
#endif
|
||||
|
||||
/** \name General variables
|
||||
* @{
|
||||
*/
|
||||
|
@ -452,7 +455,7 @@ rime_sniffer_remove(struct rime_sniffer *s)
|
|||
}
|
||||
|
||||
static void
|
||||
set_packet_attrs()
|
||||
set_packet_attrs(void)
|
||||
{
|
||||
int c = 0;
|
||||
/* set protocol in NETWORK_ID */
|
||||
|
@ -1277,11 +1280,6 @@ output(const uip_lladdr_t *localdest)
|
|||
/* The MAC address of the destination of the packet */
|
||||
linkaddr_t dest;
|
||||
|
||||
#if SICSLOWPAN_CONF_FRAG
|
||||
/* Number of bytes processed. */
|
||||
uint16_t processed_ip_out_len;
|
||||
#endif /* SICSLOWPAN_CONF_FRAG */
|
||||
|
||||
/* init */
|
||||
uncomp_hdr_len = 0;
|
||||
packetbuf_hdr_len = 0;
|
||||
|
@ -1290,9 +1288,6 @@ output(const uip_lladdr_t *localdest)
|
|||
packetbuf_clear();
|
||||
packetbuf_ptr = packetbuf_dataptr();
|
||||
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS,
|
||||
SICSLOWPAN_MAX_MAC_TRANSMISSIONS);
|
||||
|
||||
if(callback) {
|
||||
/* call the attribution when the callback comes, but set attributes
|
||||
here ! */
|
||||
|
@ -1345,21 +1340,23 @@ output(const uip_lladdr_t *localdest)
|
|||
/* Calculate NETSTACK_FRAMER's header length, that will be added in the NETSTACK_RDC.
|
||||
* We calculate it here only to make a better decision of whether the outgoing packet
|
||||
* needs to be fragmented or not. */
|
||||
#define USE_FRAMER_HDRLEN 1
|
||||
#if USE_FRAMER_HDRLEN
|
||||
#ifndef SICSLOWPAN_USE_FIXED_HDRLEN
|
||||
packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &dest);
|
||||
framer_hdrlen = NETSTACK_FRAMER.length();
|
||||
if(framer_hdrlen < 0) {
|
||||
/* Framing failed, we assume the maximum header length */
|
||||
framer_hdrlen = 21;
|
||||
framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN;
|
||||
}
|
||||
#else /* USE_FRAMER_HDRLEN */
|
||||
framer_hdrlen = 21;
|
||||
framer_hdrlen = SICSLOWPAN_FIXED_HDRLEN;
|
||||
#endif /* USE_FRAMER_HDRLEN */
|
||||
|
||||
max_payload = MAC_MAX_PAYLOAD - framer_hdrlen;
|
||||
if((int)uip_len - (int)uncomp_hdr_len > max_payload - (int)packetbuf_hdr_len) {
|
||||
#if SICSLOWPAN_CONF_FRAG
|
||||
/* Number of bytes processed. */
|
||||
uint16_t processed_ip_out_len;
|
||||
|
||||
struct queuebuf *q;
|
||||
uint16_t frag_tag;
|
||||
|
||||
|
@ -1520,6 +1517,9 @@ input(void)
|
|||
uint8_t first_fragment = 0, last_fragment = 0;
|
||||
#endif /*SICSLOWPAN_CONF_FRAG*/
|
||||
|
||||
/* Update link statistics */
|
||||
link_stats_input_callback(packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
|
||||
/* init */
|
||||
uncomp_hdr_len = 0;
|
||||
packetbuf_hdr_len = 0;
|
||||
|
@ -1757,8 +1757,8 @@ sicslowpan_init(void)
|
|||
#ifdef SICSLOWPAN_CONF_ADDR_CONTEXT_0
|
||||
SICSLOWPAN_CONF_ADDR_CONTEXT_0;
|
||||
#else
|
||||
addr_contexts[0].prefix[0] = 0xaa;
|
||||
addr_contexts[0].prefix[1] = 0xaa;
|
||||
addr_contexts[0].prefix[0] = UIP_DS6_DEFAULT_PREFIX_0;
|
||||
addr_contexts[0].prefix[1] = UIP_DS6_DEFAULT_PREFIX_1;
|
||||
#endif
|
||||
#endif /* SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS > 0 */
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include "lib/list.h"
|
||||
#include "net/link-stats.h"
|
||||
#include "net/linkaddr.h"
|
||||
#include "net/packetbuf.h"
|
||||
#include "net/ipv6/uip-ds6-nbr.h"
|
||||
|
@ -74,25 +75,32 @@ NBR_TABLE_GLOBAL(uip_ds6_nbr_t, ds6_neighbors);
|
|||
void
|
||||
uip_ds6_neighbors_init(void)
|
||||
{
|
||||
link_stats_init();
|
||||
nbr_table_register(ds6_neighbors, (nbr_table_callback *)uip_ds6_nbr_rm);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ds6_nbr_t *
|
||||
uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
|
||||
uint8_t isrouter, uint8_t state)
|
||||
uint8_t isrouter, uint8_t state, nbr_table_reason_t reason,
|
||||
void *data)
|
||||
{
|
||||
uip_ds6_nbr_t *nbr = nbr_table_add_lladdr(ds6_neighbors, (linkaddr_t*)lladdr);
|
||||
uip_ds6_nbr_t *nbr = nbr_table_add_lladdr(ds6_neighbors, (linkaddr_t*)lladdr
|
||||
, reason, data);
|
||||
if(nbr) {
|
||||
uip_ipaddr_copy(&nbr->ipaddr, ipaddr);
|
||||
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
|
||||
nbr->isrouter = isrouter;
|
||||
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
|
||||
nbr->state = state;
|
||||
#if UIP_CONF_IPV6_QUEUE_PKT
|
||||
uip_packetqueue_new(&nbr->packethandle);
|
||||
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
|
||||
#if UIP_ND6_SEND_NA
|
||||
/* timers are set separately, for now we put them in expired state */
|
||||
stimer_set(&nbr->reachable, 0);
|
||||
stimer_set(&nbr->sendns, 0);
|
||||
nbr->nscount = 0;
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
PRINTF("Adding neighbor with ip addr ");
|
||||
PRINT6ADDR(ipaddr);
|
||||
PRINTF(" link addr ");
|
||||
|
@ -111,7 +119,7 @@ uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
|
|||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
int
|
||||
uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
|
||||
{
|
||||
if(nbr != NULL) {
|
||||
|
@ -119,9 +127,9 @@ uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
|
|||
uip_packetqueue_free(&nbr->packethandle);
|
||||
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
|
||||
NEIGHBOR_STATE_CHANGED(nbr);
|
||||
nbr_table_remove(ds6_neighbors, nbr);
|
||||
return nbr_table_remove(ds6_neighbors, nbr);
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -198,6 +206,9 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Update neighbor link statistics */
|
||||
link_stats_packet_sent(dest, status, numtx);
|
||||
/* Call upper-layer callback (e.g. RPL) */
|
||||
LINK_NEIGHBOR_CALLBACK(dest, status, numtx);
|
||||
|
||||
#if UIP_DS6_LL_NUD
|
||||
|
@ -230,6 +241,7 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
|
|||
#endif /* UIP_DS6_LL_NUD */
|
||||
|
||||
}
|
||||
#if UIP_ND6_SEND_NA
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** Periodic processing on neighbors */
|
||||
void
|
||||
|
@ -241,7 +253,7 @@ uip_ds6_neighbor_periodic(void)
|
|||
case NBR_REACHABLE:
|
||||
if(stimer_expired(&nbr->reachable)) {
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
/* when a neighbor leave it's REACHABLE state and is a default router,
|
||||
/* when a neighbor leave its REACHABLE state and is a default router,
|
||||
instead of going to STALE state it enters DELAY state in order to
|
||||
force a NUD on it. Otherwise, if there is no upward traffic, the
|
||||
node never knows if the default router is still reachable. This
|
||||
|
@ -268,7 +280,6 @@ uip_ds6_neighbor_periodic(void)
|
|||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
}
|
||||
break;
|
||||
#if UIP_ND6_SEND_NA
|
||||
case NBR_INCOMPLETE:
|
||||
if(nbr->nscount >= UIP_ND6_MAX_MULTICAST_SOLICIT) {
|
||||
uip_ds6_nbr_rm(nbr);
|
||||
|
@ -304,7 +315,6 @@ uip_ds6_neighbor_periodic(void)
|
|||
stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000);
|
||||
}
|
||||
break;
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -330,5 +340,6 @@ uip_ds6_get_least_lifetime_neighbor(void)
|
|||
}
|
||||
return nbr_expiring;
|
||||
}
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @} */
|
||||
|
|
|
@ -69,12 +69,13 @@ NBR_TABLE_DECLARE(ds6_neighbors);
|
|||
/** \brief An entry in the nbr cache */
|
||||
typedef struct uip_ds6_nbr {
|
||||
uip_ipaddr_t ipaddr;
|
||||
uint8_t isrouter;
|
||||
uint8_t state;
|
||||
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
|
||||
struct stimer reachable;
|
||||
struct stimer sendns;
|
||||
uint8_t nscount;
|
||||
uint8_t isrouter;
|
||||
uint8_t state;
|
||||
uint16_t link_metric;
|
||||
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
|
||||
#if UIP_CONF_IPV6_QUEUE_PKT
|
||||
struct uip_packetqueue_handle packethandle;
|
||||
#define UIP_DS6_NBR_PACKET_LIFETIME CLOCK_SECOND * 4
|
||||
|
@ -84,9 +85,11 @@ typedef struct uip_ds6_nbr {
|
|||
void uip_ds6_neighbors_init(void);
|
||||
|
||||
/** \brief Neighbor Cache basic routines */
|
||||
uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
|
||||
uint8_t isrouter, uint8_t state);
|
||||
void uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr);
|
||||
uip_ds6_nbr_t *uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr,
|
||||
const uip_lladdr_t *lladdr,
|
||||
uint8_t isrouter, uint8_t state,
|
||||
nbr_table_reason_t reason, void *data);
|
||||
int uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr);
|
||||
const uip_lladdr_t *uip_ds6_nbr_get_ll(const uip_ds6_nbr_t *nbr);
|
||||
const uip_ipaddr_t *uip_ds6_nbr_get_ipaddr(const uip_ds6_nbr_t *nbr);
|
||||
uip_ds6_nbr_t *uip_ds6_nbr_lookup(const uip_ipaddr_t *ipaddr);
|
||||
|
|
|
@ -57,6 +57,7 @@ void NETSTACK_CONF_ROUTING_NEIGHBOR_ADDED_CALLBACK(const linkaddr_t *addr);
|
|||
void NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK(const linkaddr_t *addr);
|
||||
#endif /* NETSTACK_CONF_ROUTING_NEIGHBOR_REMOVED_CALLBACK */
|
||||
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
/* The nbr_routes holds a neighbor table to be able to maintain
|
||||
information about what routes go through what neighbor. This
|
||||
neighbor table is registered with the central nbr-table repository
|
||||
|
@ -71,6 +72,11 @@ MEMB(neighborroutememb, struct uip_ds6_route_neighbor_route, UIP_DS6_ROUTE_NB);
|
|||
LIST(routelist);
|
||||
MEMB(routememb, uip_ds6_route_t, UIP_DS6_ROUTE_NB);
|
||||
|
||||
static int num_routes = 0;
|
||||
static void rm_routelist_callback(nbr_table_item_t *ptr);
|
||||
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
|
||||
/* Default routes are held on the defaultrouterlist and their
|
||||
structures are allocated from the defaultroutermemb memory block.*/
|
||||
LIST(defaultrouterlist);
|
||||
|
@ -80,13 +86,10 @@ MEMB(defaultroutermemb, uip_ds6_defrt_t, UIP_DS6_DEFRT_NB);
|
|||
LIST(notificationlist);
|
||||
#endif
|
||||
|
||||
static int num_routes = 0;
|
||||
|
||||
#undef DEBUG
|
||||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
static void rm_routelist_callback(nbr_table_item_t *ptr);
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if DEBUG != DEBUG_NONE
|
||||
static void
|
||||
|
@ -156,10 +159,12 @@ uip_ds6_notification_rm(struct uip_ds6_notification *n)
|
|||
void
|
||||
uip_ds6_route_init(void)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
memb_init(&routememb);
|
||||
list_init(routelist);
|
||||
nbr_table_register(nbr_routes,
|
||||
(nbr_table_callback *)rm_routelist_callback);
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
|
||||
memb_init(&defaultroutermemb);
|
||||
list_init(defaultrouterlist);
|
||||
|
@ -168,6 +173,7 @@ uip_ds6_route_init(void)
|
|||
list_init(notificationlist);
|
||||
#endif
|
||||
}
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static uip_lladdr_t *
|
||||
uip_ds6_route_nexthop_lladdr(uip_ds6_route_t *route)
|
||||
|
@ -179,42 +185,75 @@ uip_ds6_route_nexthop_lladdr(uip_ds6_route_t *route)
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ipaddr_t *
|
||||
uip_ds6_route_nexthop(uip_ds6_route_t *route)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
if(route != NULL) {
|
||||
return uip_ds6_nbr_ipaddr_from_lladdr(uip_ds6_route_nexthop_lladdr(route));
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
#else /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return NULL;
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ds6_route_t *
|
||||
uip_ds6_route_head(void)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
return list_head(routelist);
|
||||
#else /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return NULL;
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ds6_route_t *
|
||||
uip_ds6_route_next(uip_ds6_route_t *r)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
if(r != NULL) {
|
||||
uip_ds6_route_t *n = list_item_next(r);
|
||||
return n;
|
||||
}
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return NULL;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
uip_ds6_route_is_nexthop(const uip_ipaddr_t *ipaddr)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
const uip_lladdr_t *lladdr;
|
||||
lladdr = uip_ds6_nbr_lladdr_from_ipaddr(ipaddr);
|
||||
|
||||
if(lladdr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nbr_table_get_from_lladdr(nbr_routes, (linkaddr_t *)lladdr) != NULL;
|
||||
#else /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return 0;
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
uip_ds6_route_num_routes(void)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
return num_routes;
|
||||
#else /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return 0;
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ds6_route_t *
|
||||
uip_ds6_route_lookup(uip_ipaddr_t *addr)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
uip_ds6_route_t *r;
|
||||
uip_ds6_route_t *found_route;
|
||||
uint8_t longestmatch;
|
||||
|
@ -261,12 +300,16 @@ uip_ds6_route_lookup(uip_ipaddr_t *addr)
|
|||
}
|
||||
|
||||
return found_route;
|
||||
#else /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return NULL;
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ds6_route_t *
|
||||
uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
|
||||
uip_ipaddr_t *nexthop)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
uip_ds6_route_t *r;
|
||||
struct uip_ds6_route_neighbor_route *nbrr;
|
||||
|
||||
|
@ -307,11 +350,16 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
|
|||
least recently used one we have. */
|
||||
|
||||
if(uip_ds6_route_num_routes() == UIP_DS6_ROUTE_NB) {
|
||||
uip_ds6_route_t *oldest;
|
||||
oldest = NULL;
|
||||
#if UIP_DS6_ROUTE_REMOVE_LEAST_RECENTLY_USED
|
||||
/* Removing the oldest route entry from the route table. The
|
||||
least recently used route is the first route on the list. */
|
||||
uip_ds6_route_t *oldest;
|
||||
|
||||
oldest = list_tail(routelist); /* uip_ds6_route_head(); */
|
||||
oldest = list_tail(routelist);
|
||||
#endif
|
||||
if(oldest == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PRINTF("uip_ds6_route_add: dropping route to ");
|
||||
PRINT6ADDR(&oldest->ipaddr);
|
||||
PRINTF("\n");
|
||||
|
@ -337,7 +385,8 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
|
|||
initialize this pointer with the list of routing entries that
|
||||
are attached to this neighbor. */
|
||||
routes = nbr_table_add_lladdr(nbr_routes,
|
||||
(linkaddr_t *)nexthop_lladdr);
|
||||
(linkaddr_t *)nexthop_lladdr,
|
||||
NBR_TABLE_REASON_ROUTE, NULL);
|
||||
if(routes == NULL) {
|
||||
/* This should not happen, as we explicitly deallocated one
|
||||
route table entry above. */
|
||||
|
@ -407,12 +456,17 @@ uip_ds6_route_add(uip_ipaddr_t *ipaddr, uint8_t length,
|
|||
assert_nbr_routes_list_sane();
|
||||
#endif /* DEBUG != DEBUG_NONE */
|
||||
return r;
|
||||
|
||||
#else /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return NULL;
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
uip_ds6_route_rm(uip_ds6_route_t *route)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
struct uip_ds6_route_neighbor_route *neighbor_route;
|
||||
#if DEBUG != DEBUG_NONE
|
||||
assert_nbr_routes_list_sane();
|
||||
|
@ -469,8 +523,11 @@ uip_ds6_route_rm(uip_ds6_route_t *route)
|
|||
#if DEBUG != DEBUG_NONE
|
||||
assert_nbr_routes_list_sane();
|
||||
#endif /* DEBUG != DEBUG_NONE */
|
||||
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
return;
|
||||
}
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
rm_routelist(struct uip_ds6_route_neighbor_routes *routes)
|
||||
|
@ -498,10 +555,12 @@ rm_routelist_callback(nbr_table_item_t *ptr)
|
|||
{
|
||||
rm_routelist((struct uip_ds6_route_neighbor_routes *)ptr);
|
||||
}
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop)
|
||||
{
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
/* Get routing entry list of this neighbor */
|
||||
const uip_lladdr_t *nexthop_lladdr;
|
||||
struct uip_ds6_route_neighbor_routes *routes;
|
||||
|
@ -510,6 +569,7 @@ uip_ds6_route_rm_by_nexthop(uip_ipaddr_t *nexthop)
|
|||
routes = nbr_table_get_from_lladdr(nbr_routes,
|
||||
(linkaddr_t *)nexthop_lladdr);
|
||||
rm_routelist(routes);
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ds6_defrt_t *
|
||||
|
|
|
@ -50,7 +50,7 @@ NBR_TABLE_DECLARE(nbr_routes);
|
|||
void uip_ds6_route_init(void);
|
||||
|
||||
#ifndef UIP_CONF_UIP_DS6_NOTIFICATIONS
|
||||
#define UIP_DS6_NOTIFICATIONS 1
|
||||
#define UIP_DS6_NOTIFICATIONS (UIP_CONF_MAX_ROUTES != 0)
|
||||
#else
|
||||
#define UIP_DS6_NOTIFICATIONS UIP_CONF_UIP_DS6_NOTIFICATIONS
|
||||
#endif
|
||||
|
@ -97,11 +97,48 @@ void uip_ds6_notification_rm(struct uip_ds6_notification *n);
|
|||
#ifndef UIP_DS6_ROUTE_STATE_TYPE
|
||||
#define UIP_DS6_ROUTE_STATE_TYPE rpl_route_entry_t
|
||||
/* Needed for the extended route entry state when using ContikiRPL */
|
||||
#define RPL_ROUTE_ENTRY_NOPATH_RECEIVED 0x01
|
||||
#define RPL_ROUTE_ENTRY_DAO_PENDING 0x02
|
||||
#define RPL_ROUTE_ENTRY_DAO_NACK 0x04
|
||||
|
||||
#define RPL_ROUTE_IS_NOPATH_RECEIVED(route) \
|
||||
(((route)->state.state_flags & RPL_ROUTE_ENTRY_NOPATH_RECEIVED) != 0)
|
||||
#define RPL_ROUTE_SET_NOPATH_RECEIVED(route) do { \
|
||||
(route)->state.state_flags |= RPL_ROUTE_ENTRY_NOPATH_RECEIVED; \
|
||||
} while(0)
|
||||
#define RPL_ROUTE_CLEAR_NOPATH_RECEIVED(route) do { \
|
||||
(route)->state.state_flags &= ~RPL_ROUTE_ENTRY_NOPATH_RECEIVED; \
|
||||
} while(0)
|
||||
|
||||
#define RPL_ROUTE_IS_DAO_PENDING(route) \
|
||||
((route->state.state_flags & RPL_ROUTE_ENTRY_DAO_PENDING) != 0)
|
||||
#define RPL_ROUTE_SET_DAO_PENDING(route) do { \
|
||||
(route)->state.state_flags |= RPL_ROUTE_ENTRY_DAO_PENDING; \
|
||||
} while(0)
|
||||
#define RPL_ROUTE_CLEAR_DAO_PENDING(route) do { \
|
||||
(route)->state.state_flags &= ~RPL_ROUTE_ENTRY_DAO_PENDING; \
|
||||
} while(0)
|
||||
|
||||
#define RPL_ROUTE_IS_DAO_NACKED(route) \
|
||||
((route->state.state_flags & RPL_ROUTE_ENTRY_DAO_NACK) != 0)
|
||||
#define RPL_ROUTE_SET_DAO_NACKED(route) do { \
|
||||
(route)->state.state_flags |= RPL_ROUTE_ENTRY_DAO_NACK; \
|
||||
} while(0)
|
||||
#define RPL_ROUTE_CLEAR_DAO_NACKED(route) do { \
|
||||
(route)->state.state_flags &= ~RPL_ROUTE_ENTRY_DAO_NACK; \
|
||||
} while(0)
|
||||
|
||||
#define RPL_ROUTE_CLEAR_DAO(route) do { \
|
||||
(route)->state.state_flags &= ~(RPL_ROUTE_ENTRY_DAO_NACK|RPL_ROUTE_ENTRY_DAO_PENDING); \
|
||||
} while(0)
|
||||
|
||||
struct rpl_dag;
|
||||
typedef struct rpl_route_entry {
|
||||
uint32_t lifetime;
|
||||
void *dag;
|
||||
uint8_t learned_from;
|
||||
uint8_t nopath_received;
|
||||
struct rpl_dag *dag;
|
||||
uint8_t dao_seqno_out;
|
||||
uint8_t dao_seqno_in;
|
||||
uint8_t state_flags;
|
||||
} rpl_route_entry_t;
|
||||
#endif /* UIP_DS6_ROUTE_STATE_TYPE */
|
||||
|
||||
|
@ -166,7 +203,7 @@ uip_ipaddr_t *uip_ds6_route_nexthop(uip_ds6_route_t *);
|
|||
int uip_ds6_route_num_routes(void);
|
||||
uip_ds6_route_t *uip_ds6_route_head(void);
|
||||
uip_ds6_route_t *uip_ds6_route_next(uip_ds6_route_t *);
|
||||
|
||||
int uip_ds6_route_is_nexthop(const uip_ipaddr_t *ipaddr);
|
||||
/** @} */
|
||||
|
||||
#endif /* UIP_DS6_ROUTE_H */
|
||||
|
|
|
@ -186,7 +186,9 @@ uip_ds6_periodic(void)
|
|||
}
|
||||
#endif /* !UIP_CONF_ROUTER */
|
||||
|
||||
#if UIP_ND6_SEND_NA
|
||||
uip_ds6_neighbor_periodic();
|
||||
#endif /* UIP_ND6_SEND_RA */
|
||||
|
||||
#if UIP_CONF_ROUTER && UIP_ND6_SEND_RA
|
||||
/* Periodic RA sending */
|
||||
|
|
|
@ -67,6 +67,36 @@
|
|||
#endif
|
||||
#define UIP_DS6_DEFRT_NB UIP_DS6_DEFRT_NBS + UIP_DS6_DEFRT_NBU
|
||||
|
||||
/* Default prefix */
|
||||
#ifdef UIP_CONF_DS6_DEFAULT_PREFIX
|
||||
#define UIP_DS6_DEFAULT_PREFIX UIP_CONF_DS6_DEFAULT_PREFIX
|
||||
#else
|
||||
/* From RFC4193, section 3.1:
|
||||
* | 7 bits |1| 40 bits | 16 bits | 64 bits |
|
||||
* +--------+-+------------+-----------+----------------------------+
|
||||
* | Prefix |L| Global ID | Subnet ID | Interface ID |
|
||||
* +--------+-+------------+-----------+----------------------------+
|
||||
* Prefix FC00::/7 prefix to identify Local IPv6 unicast
|
||||
* addresses.
|
||||
* L Set to 1 if the prefix is locally assigned.
|
||||
* Set to 0 may be defined in the future. See
|
||||
* Section 3.2 for additional information.
|
||||
* Global ID 40-bit global identifier used to create a
|
||||
* globally unique prefix. See Section 3.2 for
|
||||
* additional information.
|
||||
*
|
||||
* We set prefix to 0xfc00 and set the local bit, resulting in 0xfd00.
|
||||
* For high probability of network uniqueness, Global ID must be generated
|
||||
* pseudo-randomly. As this is a hard-coded default prefix, we simply use
|
||||
* a Global ID of 0. For real deployments, make sure to install a pseudo-random
|
||||
* Global ID, e.g. in a RPL network, by configuring it at the root.
|
||||
*/
|
||||
#define UIP_DS6_DEFAULT_PREFIX 0xfd00
|
||||
#endif /* UIP_CONF_DS6_DEFAULT_PREFIX */
|
||||
|
||||
#define UIP_DS6_DEFAULT_PREFIX_0 ((UIP_DS6_DEFAULT_PREFIX >> 8) & 0xff)
|
||||
#define UIP_DS6_DEFAULT_PREFIX_1 (UIP_DS6_DEFAULT_PREFIX & 0xff)
|
||||
|
||||
/* Prefix list */
|
||||
#define UIP_DS6_PREFIX_NBS 1
|
||||
#ifndef UIP_CONF_DS6_PREFIX_NBU
|
||||
|
|
|
@ -120,9 +120,6 @@ uip_icmp6_register_input_handler(uip_icmp6_input_handler_t *handler)
|
|||
static void
|
||||
echo_request_input(void)
|
||||
{
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
uint8_t temp_ext_len;
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/*
|
||||
* we send an echo reply. It is trivial if there was no extension
|
||||
* headers in the request otherwise we need to remove the extension
|
||||
|
@ -147,27 +144,7 @@ echo_request_input(void)
|
|||
}
|
||||
|
||||
if(uip_ext_len > 0) {
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
if((temp_ext_len = rpl_invert_header())) {
|
||||
/* If there were other extension headers*/
|
||||
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
|
||||
if (uip_ext_len != temp_ext_len) {
|
||||
uip_len -= (uip_ext_len - temp_ext_len);
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo request payload (starting after the icmp header)
|
||||
* to the new location in the reply.
|
||||
* The shift is equal to the length of the remaining extension headers present
|
||||
* Note: UIP_ICMP_BUF still points to the echo request at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
|
||||
}
|
||||
uip_ext_len = temp_ext_len;
|
||||
} else {
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/* If there were extension headers*/
|
||||
/* Remove extension headers if any */
|
||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||
uip_len -= uip_ext_len;
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
|
@ -181,10 +158,13 @@ echo_request_input(void)
|
|||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||
uip_ext_len = 0;
|
||||
}
|
||||
|
||||
/* Insert RPL extension headers */
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
}
|
||||
rpl_insert_header();
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
}
|
||||
|
||||
/* Below is important for the correctness of UIP_ICMP_BUF and the
|
||||
* checksum
|
||||
*/
|
||||
|
@ -206,7 +186,6 @@ echo_request_input(void)
|
|||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
|
||||
|
||||
/* check if originating packet is not an ICMP error */
|
||||
if(uip_ext_len) {
|
||||
if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
|
||||
|
@ -221,8 +200,8 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
|
|||
}
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
uip_ext_len = rpl_invert_header();
|
||||
#else /* UIP_CONF_IPV6_RPL */
|
||||
rpl_remove_header();
|
||||
#else
|
||||
uip_ext_len = 0;
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
|
@ -231,8 +210,9 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
|
|||
|
||||
uip_len += UIP_IPICMPH_LEN + UIP_ICMP6_ERROR_LEN;
|
||||
|
||||
if(uip_len > UIP_LINK_MTU)
|
||||
if(uip_len > UIP_LINK_MTU) {
|
||||
uip_len = UIP_LINK_MTU;
|
||||
}
|
||||
|
||||
memmove((uint8_t *)UIP_ICMP6_ERROR_BUF + uip_ext_len + UIP_ICMP6_ERROR_LEN,
|
||||
(void *)UIP_IP_BUF, uip_len - UIP_IPICMPH_LEN - uip_ext_len - UIP_ICMP6_ERROR_LEN);
|
||||
|
@ -280,6 +260,10 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
|
|||
UIP_ICMP_BUF->icmpchksum = 0;
|
||||
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
rpl_insert_header();
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
UIP_STAT(++uip_stat.icmp.sent);
|
||||
|
||||
PRINTF("Sending ICMPv6 ERROR message type %d code %d to ", type, code);
|
||||
|
@ -313,6 +297,13 @@ uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
|
|||
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
|
||||
|
||||
uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
|
||||
|
||||
UIP_STAT(++uip_stat.icmp.sent);
|
||||
UIP_STAT(++uip_stat.ip.sent);
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
rpl_insert_header();
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -321,37 +312,18 @@ echo_reply_input(void)
|
|||
{
|
||||
int ttl;
|
||||
uip_ipaddr_t sender;
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
uint8_t temp_ext_len;
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
PRINTF("Received Echo Reply from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
PRINTF(" to ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
|
||||
PRINTF("\n");
|
||||
|
||||
uip_ipaddr_copy(&sender, &UIP_IP_BUF->srcipaddr);
|
||||
ttl = UIP_IP_BUF->ttl;
|
||||
|
||||
if(uip_ext_len > 0) {
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
if((temp_ext_len = rpl_invert_header())) {
|
||||
/* If there were other extension headers*/
|
||||
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
|
||||
if (uip_ext_len != temp_ext_len) {
|
||||
uip_len -= (uip_ext_len - temp_ext_len);
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo reply payload (starting after the icmp
|
||||
* header) to the new location in the reply. The shift is
|
||||
* equal to the length of the remaining extension headers
|
||||
* present Note: UIP_ICMP_BUF still points to the echo reply
|
||||
* at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
|
||||
}
|
||||
uip_ext_len = temp_ext_len;
|
||||
uip_len -= uip_ext_len;
|
||||
} else {
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/* If there were extension headers*/
|
||||
/* Remove extension headers if any */
|
||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||
uip_len -= uip_ext_len;
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
|
@ -365,9 +337,6 @@ echo_reply_input(void)
|
|||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||
uip_ext_len = 0;
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
}
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
}
|
||||
|
||||
/* Call all registered applications to let them know an echo reply
|
||||
|
|
|
@ -135,11 +135,13 @@ static uip_ds6_prefix_t *prefix; /** Pointer to a prefix list entry */
|
|||
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Copy link-layer address from LLAO option to a word-aligned uip_lladdr_t */
|
||||
static void
|
||||
extract_lladdr_aligned(uip_lladdr_t *dest) {
|
||||
static int
|
||||
extract_lladdr_from_llao_aligned(uip_lladdr_t *dest) {
|
||||
if(dest != NULL && nd6_opt_llao != NULL) {
|
||||
memcpy(dest, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
|
||||
/*------------------------------------------------------------------*/
|
||||
|
@ -199,16 +201,23 @@ ns_input(void)
|
|||
goto discard;
|
||||
} else {
|
||||
#endif /*UIP_CONF_IPV6_CHECKS */
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_from_llao_aligned(&lladdr_aligned);
|
||||
nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
|
||||
if(nbr == NULL) {
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_aligned(&lladdr_aligned);
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
} else {
|
||||
uip_lladdr_t *lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
|
||||
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
lladdr, UIP_LLADDR_LEN) != 0) {
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN);
|
||||
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
nbr->state = NBR_STALE;
|
||||
} else {
|
||||
if(nbr->state == NBR_INCOMPLETE) {
|
||||
|
@ -427,6 +436,7 @@ na_input(void)
|
|||
uint8_t is_router;
|
||||
uint8_t is_solicited;
|
||||
uint8_t is_override;
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
|
||||
PRINTF("Received NA from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
|
@ -489,23 +499,29 @@ na_input(void)
|
|||
PRINTF("NA received is bad\n");
|
||||
goto discard;
|
||||
} else {
|
||||
uip_lladdr_t *lladdr;
|
||||
const uip_lladdr_t *lladdr;
|
||||
nbr = uip_ds6_nbr_lookup(&UIP_ND6_NA_BUF->tgtipaddr);
|
||||
lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
|
||||
if(nbr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(nd6_opt_llao != 0) {
|
||||
lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(nd6_opt_llao != NULL) {
|
||||
is_llchange =
|
||||
memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], (void *)lladdr,
|
||||
memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], lladdr,
|
||||
UIP_LLADDR_LEN);
|
||||
}
|
||||
if(nbr->state == NBR_INCOMPLETE) {
|
||||
if(nd6_opt_llao == NULL) {
|
||||
if(nd6_opt_llao == NULL || !extract_lladdr_from_llao_aligned(&lladdr_aligned)) {
|
||||
goto discard;
|
||||
}
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
UIP_LLADDR_LEN);
|
||||
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
|
||||
if(is_solicited) {
|
||||
nbr->state = NBR_REACHABLE;
|
||||
nbr->nscount = 0;
|
||||
|
@ -517,27 +533,29 @@ na_input(void)
|
|||
nbr->state = NBR_STALE;
|
||||
}
|
||||
nbr->isrouter = is_router;
|
||||
} else {
|
||||
} else { /* NBR is not INCOMPLETE */
|
||||
if(!is_override && is_llchange) {
|
||||
if(nbr->state == NBR_REACHABLE) {
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
goto discard;
|
||||
} else {
|
||||
if(is_override || (!is_override && nd6_opt_llao != 0 && !is_llchange)
|
||||
|| nd6_opt_llao == 0) {
|
||||
if(nd6_opt_llao != 0) {
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
UIP_LLADDR_LEN);
|
||||
/**
|
||||
* If this is an cache override, or same lladdr, or no llao -
|
||||
* do updates of nbr states.
|
||||
*/
|
||||
if(is_override || !is_llchange || nd6_opt_llao == NULL) {
|
||||
if(nd6_opt_llao != NULL && is_llchange) {
|
||||
if(!extract_lladdr_from_llao_aligned(&lladdr_aligned) ||
|
||||
nbr_table_update_lladdr((const linkaddr_t *) lladdr, (const linkaddr_t *) &lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
}
|
||||
if(is_solicited) {
|
||||
nbr->state = NBR_REACHABLE;
|
||||
/* reachable time is stored in ms */
|
||||
stimer_set(&(nbr->reachable), uip_ds6_if.reachable_time / 1000);
|
||||
} else {
|
||||
if(nd6_opt_llao != 0 && is_llchange) {
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -631,17 +649,23 @@ rs_input(void)
|
|||
} else {
|
||||
#endif /*UIP_CONF_IPV6_CHECKS */
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_aligned(&lladdr_aligned);
|
||||
extract_lladdr_from_llao_aligned(&lladdr_aligned);
|
||||
if((nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr)) == NULL) {
|
||||
/* we need to add the neighbor */
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
} else {
|
||||
/* If LL address changed, set neighbor state to stale */
|
||||
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
uip_ds6_nbr_get_ll(nbr), UIP_LLADDR_LEN) != 0) {
|
||||
lladdr, UIP_LLADDR_LEN) != 0) {
|
||||
uip_ds6_nbr_t nbr_data = *nbr;
|
||||
uip_ds6_nbr_rm(nbr);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
nbr->reachable = nbr_data.reachable;
|
||||
nbr->sendns = nbr_data.sendns;
|
||||
nbr->nscount = nbr_data.nscount;
|
||||
|
@ -823,6 +847,8 @@ uip_nd6_rs_output(void)
|
|||
void
|
||||
ra_input(void)
|
||||
{
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
|
||||
PRINTF("Received RA from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
PRINTF(" to ");
|
||||
|
@ -867,19 +893,28 @@ ra_input(void)
|
|||
PRINTF("Processing SLLAO option in RA\n");
|
||||
nd6_opt_llao = (uint8_t *) UIP_ND6_OPT_HDR_BUF;
|
||||
nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
|
||||
if(!extract_lladdr_from_llao_aligned(&lladdr_aligned)) {
|
||||
/* failed to extract llao - discard packet */
|
||||
goto discard;
|
||||
}
|
||||
if(nbr == NULL) {
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_aligned(&lladdr_aligned);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 1, NBR_STALE);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
1, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
} else {
|
||||
uip_lladdr_t *lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
|
||||
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(nbr->state == NBR_INCOMPLETE) {
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
lladdr, UIP_LLADDR_LEN) != 0) {
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
UIP_LLADDR_LEN);
|
||||
/* change of link layer address */
|
||||
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
nbr->isrouter = 1;
|
||||
|
|
|
@ -79,6 +79,11 @@
|
|||
#include "net/ipv6/uip-ds6.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>
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -88,10 +93,6 @@
|
|||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
#include "rpl/rpl.h"
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
#if UIP_LOGGING == 1
|
||||
#include <stdio.h>
|
||||
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). */
|
||||
struct uip_conn *uip_conn;
|
||||
|
||||
/* Temporary variables. */
|
||||
#if (UIP_TCP || UIP_UDP)
|
||||
static uint8_t c;
|
||||
#endif
|
||||
|
||||
#if UIP_ACTIVE_OPEN || UIP_UDP
|
||||
/* Keeps track of the last port used for a new connection. */
|
||||
static uint16_t lastport;
|
||||
|
@ -256,8 +252,6 @@ static uint8_t iss[4];
|
|||
|
||||
/* Temporary variables. */
|
||||
uint8_t uip_acc32[4];
|
||||
static uint8_t opt;
|
||||
static uint16_t tmp16;
|
||||
#endif /* UIP_TCP */
|
||||
/** @} */
|
||||
|
||||
|
@ -432,6 +426,7 @@ uip_udpchksum(void)
|
|||
void
|
||||
uip_init(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
uip_ds6_init();
|
||||
uip_icmp6_init();
|
||||
|
@ -466,6 +461,7 @@ struct uip_conn *
|
|||
uip_connect(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
||||
{
|
||||
register struct uip_conn *conn, *cconn;
|
||||
int c;
|
||||
|
||||
/* Find an unused local port. */
|
||||
again:
|
||||
|
@ -560,6 +556,7 @@ remove_ext_hdr(void)
|
|||
struct uip_udp_conn *
|
||||
uip_udp_new(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
||||
{
|
||||
int c;
|
||||
register struct uip_udp_conn *conn;
|
||||
|
||||
/* Find an unused local port. */
|
||||
|
@ -605,6 +602,7 @@ uip_udp_new(const uip_ipaddr_t *ripaddr, uint16_t rport)
|
|||
void
|
||||
uip_unlisten(uint16_t port)
|
||||
{
|
||||
int c;
|
||||
for(c = 0; c < UIP_LISTENPORTS; ++c) {
|
||||
if(uip_listenports[c] == port) {
|
||||
uip_listenports[c] = 0;
|
||||
|
@ -616,6 +614,7 @@ uip_unlisten(uint16_t port)
|
|||
void
|
||||
uip_listen(uint16_t port)
|
||||
{
|
||||
int c;
|
||||
for(c = 0; c < UIP_LISTENPORTS; ++c) {
|
||||
if(uip_listenports[c] == 0) {
|
||||
uip_listenports[c] = port;
|
||||
|
@ -891,7 +890,7 @@ ext_hdr_options_process(void)
|
|||
*/
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
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");
|
||||
return 1;
|
||||
}
|
||||
|
@ -941,6 +940,9 @@ void
|
|||
uip_process(uint8_t flag)
|
||||
{
|
||||
#if UIP_TCP
|
||||
int c;
|
||||
uint16_t tmp16;
|
||||
uint8_t opt;
|
||||
register struct uip_conn *uip_connr = uip_conn;
|
||||
#endif /* UIP_TCP */
|
||||
#if UIP_UDP
|
||||
|
@ -1227,9 +1229,9 @@ uip_process(uint8_t flag)
|
|||
}
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
if(rpl_update_header_empty()) {
|
||||
if(!rpl_update_header()) {
|
||||
/* Packet can not be forwarded */
|
||||
PRINTF("RPL Forward Option Error\n");
|
||||
PRINTF("RPL header update error\n");
|
||||
goto drop;
|
||||
}
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
@ -1367,6 +1369,11 @@ uip_process(uint8_t flag)
|
|||
|
||||
PRINTF("Processing Routing header\n");
|
||||
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_STAT(++uip_stat.ip.drop);
|
||||
UIP_LOG("ip6: unrecognized routing type");
|
||||
|
@ -1473,6 +1480,7 @@ uip_process(uint8_t flag)
|
|||
udp_input:
|
||||
|
||||
remove_ext_hdr();
|
||||
UIP_IP_BUF->proto = UIP_PROTO_UDP;
|
||||
|
||||
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
|
||||
send. */
|
||||
#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
|
||||
checksum 0. Here, we explicitly receive UDP packets with checksum
|
||||
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());
|
||||
goto drop;
|
||||
}
|
||||
#else /* UIP_UDP_CHECKSUMS */
|
||||
uip_len = uip_len - UIP_IPUDPH_LEN;
|
||||
#endif /* UIP_UDP_CHECKSUMS */
|
||||
|
||||
/* 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");
|
||||
UIP_STAT(++uip_stat.udp.drop);
|
||||
|
||||
#if UIP_UDP_SEND_UNREACH_NOPORT
|
||||
uip_icmp6_error_output(ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0);
|
||||
goto send;
|
||||
#else
|
||||
goto drop;
|
||||
#endif
|
||||
|
||||
udp_found:
|
||||
PRINTF("In udp_found\n");
|
||||
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_flags = UIP_NEWDATA;
|
||||
uip_sappdata = uip_appdata = &uip_buf[UIP_IPUDPH_LEN + UIP_LLH_LEN];
|
||||
|
@ -1593,6 +1595,7 @@ uip_process(uint8_t flag)
|
|||
tcp_input:
|
||||
|
||||
remove_ext_hdr();
|
||||
UIP_IP_BUF->proto = UIP_PROTO_TCP;
|
||||
|
||||
UIP_STAT(++uip_stat.tcp.recv);
|
||||
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/packetbuf.h"
|
||||
#include "net/llsec/llsec802154.h"
|
||||
|
||||
#if LLSEC802154_USES_FRAME_COUNTER
|
||||
|
||||
/* This node's current frame counter value */
|
||||
static uint32_t counter;
|
||||
|
@ -107,5 +110,6 @@ anti_replay_was_replayed(struct anti_replay_info *info)
|
|||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#endif /* LLSEC802154_USES_FRAME_COUNTER */
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -41,8 +41,11 @@
|
|||
#include "llsec/ccm-star-packetbuf.h"
|
||||
#include "net/linkaddr.h"
|
||||
#include "net/packetbuf.h"
|
||||
#include "net/llsec/llsec802154.h"
|
||||
#include <string.h>
|
||||
|
||||
#if LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_FRAME_COUNTER
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static const uint8_t *
|
||||
get_extended_address(const linkaddr_t *addr)
|
||||
|
@ -76,3 +79,4 @@ ccm_star_packetbuf_set_nonce(uint8_t *nonce, int forward)
|
|||
nonce[12] = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_FRAME_COUNTER */
|
||||
|
|
|
@ -56,28 +56,13 @@
|
|||
#include "net/mac/frame802154.h"
|
||||
#include "net/ip/uip.h"
|
||||
|
||||
#ifdef LLSEC802154_CONF_SECURITY_LEVEL
|
||||
#define LLSEC802154_SECURITY_LEVEL LLSEC802154_CONF_SECURITY_LEVEL
|
||||
#else /* LLSEC802154_CONF_SECURITY_LEVEL */
|
||||
#define LLSEC802154_SECURITY_LEVEL FRAME802154_SECURITY_LEVEL_NONE
|
||||
#endif /* LLSEC802154_CONF_SECURITY_LEVEL */
|
||||
#ifdef LLSEC802154_CONF_ENABLED
|
||||
#define LLSEC802154_ENABLED LLSEC802154_CONF_ENABLED
|
||||
#else /* LLSEC802154_CONF_ENABLED */
|
||||
#define LLSEC802154_ENABLED 0
|
||||
#endif /* LLSEC802154_CONF_ENABLED */
|
||||
|
||||
#if ((LLSEC802154_SECURITY_LEVEL < 0) || (LLSEC802154_SECURITY_LEVEL > 7))
|
||||
#error "unsupported security level"
|
||||
#endif
|
||||
|
||||
#define LLSEC802154_SECURITY_LEVEL_MIC (LLSEC802154_SECURITY_LEVEL & 3)
|
||||
#if LLSEC802154_SECURITY_LEVEL_MIC
|
||||
#define LLSEC802154_MIC_LENGTH (2 << LLSEC802154_SECURITY_LEVEL_MIC)
|
||||
#else
|
||||
#define LLSEC802154_MIC_LENGTH 0
|
||||
#endif
|
||||
|
||||
#ifdef LLSEC802154_CONF_USES_ENCRYPTION
|
||||
#define LLSEC802154_USES_ENCRYPTION LLSEC802154_CONF_USES_ENCRYPTION
|
||||
#else /* LLSEC802154_CONF_USES_ENCRYPTION */
|
||||
#define LLSEC802154_USES_ENCRYPTION (LLSEC802154_SECURITY_LEVEL & (1 << 2))
|
||||
#endif /* LLSEC802154_CONF_USES_ENCRYPTION */
|
||||
#define LLSEC802154_MIC_LEN(sec_lvl) (2 << (sec_lvl & 3))
|
||||
|
||||
#ifdef LLSEC802154_CONF_USES_EXPLICIT_KEYS
|
||||
#define LLSEC802154_USES_EXPLICIT_KEYS LLSEC802154_CONF_USES_EXPLICIT_KEYS
|
||||
|
@ -88,9 +73,15 @@
|
|||
#ifdef LLSEC802154_CONF_USES_FRAME_COUNTER
|
||||
#define LLSEC802154_USES_FRAME_COUNTER LLSEC802154_CONF_USES_FRAME_COUNTER
|
||||
#else /* LLSEC802154_CONF_USES_FRAME_COUNTER */
|
||||
#define LLSEC802154_USES_FRAME_COUNTER (LLSEC802154_SECURITY_LEVEL != FRAME802154_SECURITY_LEVEL_NONE)
|
||||
#define LLSEC802154_USES_FRAME_COUNTER LLSEC802154_ENABLED
|
||||
#endif /* LLSEC802154_CONF_USES_FRAME_COUNTER */
|
||||
|
||||
#ifdef LLSEC802154_CONF_USES_AUX_HEADER
|
||||
#define LLSEC802154_USES_AUX_HEADER LLSEC802154_CONF_USES_AUX_HEADER
|
||||
#else /* LLSEC802154_CONF_USES_AUX_HEADER */
|
||||
#define LLSEC802154_USES_AUX_HEADER LLSEC802154_ENABLED
|
||||
#endif /* LLSEC802154_CONF_USES_AUX_HEADER */
|
||||
|
||||
#if UIP_BYTE_ORDER == UIP_LITTLE_ENDIAN
|
||||
#define LLSEC802154_HTONS(n) (n)
|
||||
#define LLSEC802154_HTONL(n) (n)
|
||||
|
|
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/ccm-star-packetbuf.h"
|
||||
#include "net/mac/frame802154.h"
|
||||
#include "net/mac/framer-802154.h"
|
||||
#include "net/netstack.h"
|
||||
#include "net/packetbuf.h"
|
||||
#include "net/nbr-table.h"
|
||||
|
@ -55,7 +54,22 @@
|
|||
#include "lib/ccm-star.h"
|
||||
#include <string.h>
|
||||
|
||||
#define WITH_ENCRYPTION (LLSEC802154_SECURITY_LEVEL & (1 << 2))
|
||||
#ifdef NONCORESEC_CONF_DECORATED_FRAMER
|
||||
#define DECORATED_FRAMER NONCORESEC_CONF_DECORATED_FRAMER
|
||||
#else /* NONCORESEC_CONF_DECORATED_FRAMER */
|
||||
#define DECORATED_FRAMER framer_802154
|
||||
#endif /* NONCORESEC_CONF_DECORATED_FRAMER */
|
||||
|
||||
extern const struct framer DECORATED_FRAMER;
|
||||
|
||||
#ifdef NONCORESEC_CONF_SEC_LVL
|
||||
#define SEC_LVL NONCORESEC_CONF_SEC_LVL
|
||||
#else /* NONCORESEC_CONF_SEC_LVL */
|
||||
#define SEC_LVL 2
|
||||
#endif /* NONCORESEC_CONF_SEC_LVL */
|
||||
|
||||
#define WITH_ENCRYPTION (SEC_LVL & (1 << 2))
|
||||
#define MIC_LEN LLSEC802154_MIC_LEN(SEC_LVL)
|
||||
|
||||
#ifdef NONCORESEC_CONF_KEY
|
||||
#define NONCORESEC_KEY NONCORESEC_CONF_KEY
|
||||
|
@ -76,6 +90,8 @@
|
|||
#define PRINTF(...)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#if LLSEC802154_USES_AUX_HEADER && SEC_LVL && LLSEC802154_USES_FRAME_COUNTER
|
||||
|
||||
/* network-wide CCM* key */
|
||||
static uint8_t key[16] = NONCORESEC_KEY;
|
||||
NBR_TABLE(struct anti_replay_info, anti_replay_table);
|
||||
|
@ -91,7 +107,7 @@ aead(uint8_t hdrlen, int forward)
|
|||
uint8_t *a;
|
||||
uint8_t a_len;
|
||||
uint8_t *result;
|
||||
uint8_t generated_mic[LLSEC802154_MIC_LENGTH];
|
||||
uint8_t generated_mic[MIC_LEN];
|
||||
uint8_t *mic;
|
||||
|
||||
ccm_star_packetbuf_set_nonce(nonce, forward);
|
||||
|
@ -113,30 +129,29 @@ aead(uint8_t hdrlen, int forward)
|
|||
CCM_STAR.aead(nonce,
|
||||
m, m_len,
|
||||
a, a_len,
|
||||
result, LLSEC802154_MIC_LENGTH,
|
||||
result, MIC_LEN,
|
||||
forward);
|
||||
|
||||
if(forward) {
|
||||
packetbuf_set_datalen(packetbuf_datalen() + LLSEC802154_MIC_LENGTH);
|
||||
packetbuf_set_datalen(packetbuf_datalen() + MIC_LEN);
|
||||
return 1;
|
||||
} else {
|
||||
return (memcmp(generated_mic, mic, LLSEC802154_MIC_LENGTH) == 0);
|
||||
return (memcmp(generated_mic, mic, MIC_LEN) == 0);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
add_security_header(void)
|
||||
{
|
||||
if(!packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) {
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_DATAFRAME);
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, LLSEC802154_SECURITY_LEVEL);
|
||||
anti_replay_set_counter();
|
||||
}
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, SEC_LVL);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
send(mac_callback_t sent, void *ptr)
|
||||
{
|
||||
add_security_header();
|
||||
anti_replay_set_counter();
|
||||
NETSTACK_MAC.send(sent, ptr);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -145,8 +160,7 @@ create(void)
|
|||
{
|
||||
int result;
|
||||
|
||||
add_security_header();
|
||||
result = framer_802154.create();
|
||||
result = DECORATED_FRAMER.create();
|
||||
if(result == FRAMER_FAILED) {
|
||||
return result;
|
||||
}
|
||||
|
@ -163,12 +177,12 @@ parse(void)
|
|||
const linkaddr_t *sender;
|
||||
struct anti_replay_info* info;
|
||||
|
||||
result = framer_802154.parse();
|
||||
result = DECORATED_FRAMER.parse();
|
||||
if(result == FRAMER_FAILED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) != LLSEC802154_SECURITY_LEVEL) {
|
||||
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) != SEC_LVL) {
|
||||
PRINTF("noncoresec: received frame with wrong security level\n");
|
||||
return FRAMER_FAILED;
|
||||
}
|
||||
|
@ -178,17 +192,17 @@ parse(void)
|
|||
return FRAMER_FAILED;
|
||||
}
|
||||
|
||||
packetbuf_set_datalen(packetbuf_datalen() - LLSEC802154_MIC_LENGTH);
|
||||
packetbuf_set_datalen(packetbuf_datalen() - MIC_LEN);
|
||||
|
||||
if(!aead(result, 0)) {
|
||||
PRINTF("noncoresec: received unauthentic frame %"PRIu32"\n",
|
||||
PRINTF("noncoresec: received unauthentic frame %lu\n",
|
||||
anti_replay_get_counter());
|
||||
return FRAMER_FAILED;
|
||||
}
|
||||
|
||||
info = nbr_table_get_from_lladdr(anti_replay_table, sender);
|
||||
if(!info) {
|
||||
info = nbr_table_add_lladdr(anti_replay_table, sender);
|
||||
info = nbr_table_add_lladdr(anti_replay_table, sender, NBR_TABLE_REASON_LLSEC, NULL);
|
||||
if(!info) {
|
||||
PRINTF("noncoresec: could not get nbr_table_item\n");
|
||||
return FRAMER_FAILED;
|
||||
|
@ -214,7 +228,7 @@ parse(void)
|
|||
anti_replay_init_info(info);
|
||||
} else {
|
||||
if(anti_replay_was_replayed(info)) {
|
||||
PRINTF("noncoresec: received replayed frame %"PRIu32"\n",
|
||||
PRINTF("noncoresec: received replayed frame %lu\n",
|
||||
anti_replay_get_counter());
|
||||
return FRAMER_FAILED;
|
||||
}
|
||||
|
@ -233,7 +247,7 @@ static int
|
|||
length(void)
|
||||
{
|
||||
add_security_header();
|
||||
return framer_802154.length() + LLSEC802154_MIC_LENGTH;
|
||||
return DECORATED_FRAMER.length() + MIC_LEN;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
|
@ -256,5 +270,6 @@ const struct framer noncoresec_framer = {
|
|||
parse
|
||||
};
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER && SEC_LVL && LLSEC802154_USES_FRAME_COUNTER */
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -63,26 +63,35 @@
|
|||
#define PRINTF(...)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef CSMA_MAX_BACKOFF_EXPONENT
|
||||
#ifdef CSMA_CONF_MAX_BACKOFF_EXPONENT
|
||||
#define CSMA_MAX_BACKOFF_EXPONENT CSMA_CONF_MAX_BACKOFF_EXPONENT
|
||||
#else
|
||||
#define CSMA_MAX_BACKOFF_EXPONENT 3
|
||||
#endif /* CSMA_CONF_MAX_BACKOFF_EXPONENT */
|
||||
#endif /* CSMA_MAX_BACKOFF_EXPONENT */
|
||||
/* Constants of the IEEE 802.15.4 standard */
|
||||
|
||||
#ifndef CSMA_MAX_MAC_TRANSMISSIONS
|
||||
#ifdef CSMA_CONF_MAX_MAC_TRANSMISSIONS
|
||||
#define CSMA_MAX_MAC_TRANSMISSIONS CSMA_CONF_MAX_MAC_TRANSMISSIONS
|
||||
/* macMinBE: Initial backoff exponent. Range 0--CSMA_MAX_BE */
|
||||
#ifdef CSMA_CONF_MIN_BE
|
||||
#define CSMA_MIN_BE CSMA_CONF_MIN_BE
|
||||
#else
|
||||
#define CSMA_MAX_MAC_TRANSMISSIONS 3
|
||||
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS */
|
||||
#endif /* CSMA_MAX_MAC_TRANSMISSIONS */
|
||||
#define CSMA_MIN_BE 0
|
||||
#endif
|
||||
|
||||
#if CSMA_MAX_MAC_TRANSMISSIONS < 1
|
||||
#error CSMA_CONF_MAX_MAC_TRANSMISSIONS must be at least 1.
|
||||
#error Change CSMA_CONF_MAX_MAC_TRANSMISSIONS in contiki-conf.h or in your Makefile.
|
||||
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS < 1 */
|
||||
/* macMaxBE: Maximum backoff exponent. Range 3--8 */
|
||||
#ifdef CSMA_CONF_MAX_BE
|
||||
#define CSMA_MAX_BE CSMA_CONF_MAX_BE
|
||||
#else
|
||||
#define CSMA_MAX_BE 4
|
||||
#endif
|
||||
|
||||
/* macMaxCSMABackoffs: Maximum number of backoffs in case of channel busy/collision. Range 0--5 */
|
||||
#ifdef CSMA_CONF_MAX_BACKOFF
|
||||
#define CSMA_MAX_BACKOFF CSMA_CONF_MAX_BACKOFF
|
||||
#else
|
||||
#define CSMA_MAX_BACKOFF 5
|
||||
#endif
|
||||
|
||||
/* macMaxFrameRetries: Maximum number of re-transmissions attampts. Range 0--7 */
|
||||
#ifdef CSMA_CONF_MAX_FRAME_RETRIES
|
||||
#define CSMA_MAX_MAX_FRAME_RETRIES CSMA_CONF_MAX_FRAME_RETRIES
|
||||
#else
|
||||
#define CSMA_MAX_MAX_FRAME_RETRIES 7
|
||||
#endif
|
||||
|
||||
/* Packet metadata */
|
||||
struct qbuf_metadata {
|
||||
|
@ -97,7 +106,7 @@ struct neighbor_queue {
|
|||
linkaddr_t addr;
|
||||
struct ctimer transmit_timer;
|
||||
uint8_t transmissions;
|
||||
uint8_t collisions, deferrals;
|
||||
uint8_t collisions;
|
||||
LIST_STRUCT(queued_packet_list);
|
||||
};
|
||||
|
||||
|
@ -123,7 +132,6 @@ LIST(neighbor_list);
|
|||
|
||||
static void packet_sent(void *ptr, int status, int num_transmissions);
|
||||
static void transmit_packet_list(void *ptr);
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static struct neighbor_queue *
|
||||
neighbor_queue_from_addr(const linkaddr_t *addr)
|
||||
|
@ -139,18 +147,18 @@ neighbor_queue_from_addr(const linkaddr_t *addr)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static clock_time_t
|
||||
default_timebase(void)
|
||||
backoff_period(void)
|
||||
{
|
||||
clock_time_t time;
|
||||
/* The retransmission time must be proportional to the channel
|
||||
check interval of the underlying radio duty cycling layer. */
|
||||
time = NETSTACK_RDC.channel_check_interval();
|
||||
|
||||
/* If the radio duty cycle has no channel check interval (i.e., it
|
||||
does not turn the radio off), we make the retransmission time
|
||||
proportional to the configured MAC channel check rate. */
|
||||
/* If the radio duty cycle has no channel check interval, we use
|
||||
* the default in IEEE 802.15.4: aUnitBackoffPeriod which is
|
||||
* 20 symbols i.e. 320 usec. That is, 1/3125 second. */
|
||||
if(time == 0) {
|
||||
time = CLOCK_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE;
|
||||
time = MAX(CLOCK_SECOND / 3125, 1);
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
@ -171,10 +179,28 @@ transmit_packet_list(void *ptr)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
schedule_transmission(struct neighbor_queue *n)
|
||||
{
|
||||
clock_time_t delay;
|
||||
int backoff_exponent; /* BE in IEEE 802.15.4 */
|
||||
|
||||
backoff_exponent = MIN(n->collisions, CSMA_MAX_BE);
|
||||
|
||||
/* Compute max delay as per IEEE 802.15.4: 2^BE-1 backoff periods */
|
||||
delay = ((1 << backoff_exponent) - 1) * backoff_period();
|
||||
if(delay > 0) {
|
||||
/* Pick a time for next transmission */
|
||||
delay = random_rand() % delay;
|
||||
}
|
||||
|
||||
PRINTF("csma: scheduling transmission in %u ticks, NB=%u, BE=%u\n",
|
||||
(unsigned)delay, n->collisions, backoff_exponent);
|
||||
ctimer_set(&n->transmit_timer, delay, transmit_packet_list, n);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
|
||||
{
|
||||
clock_time_t tx_delay;
|
||||
|
||||
if(p != NULL) {
|
||||
/* Remove packet from list and deallocate */
|
||||
list_remove(n->queued_packet_list, p);
|
||||
|
@ -187,11 +213,9 @@ free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
|
|||
if(list_head(n->queued_packet_list) != NULL) {
|
||||
/* There is a next packet. We reset current tx information */
|
||||
n->transmissions = 0;
|
||||
n->collisions = 0;
|
||||
n->deferrals = 0;
|
||||
/* Set a timer for next transmissions */
|
||||
tx_delay = (status == MAC_TX_OK) ? 0 : default_timebase();
|
||||
ctimer_set(&n->transmit_timer, tx_delay, transmit_packet_list, n);
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
/* Schedule next transmissions */
|
||||
schedule_transmission(n);
|
||||
} else {
|
||||
/* This was the last packet in the queue, we free the neighbor */
|
||||
ctimer_stop(&n->transmit_timer);
|
||||
|
@ -202,34 +226,103 @@ free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
tx_done(int status, struct rdc_buf_list *q, struct neighbor_queue *n)
|
||||
{
|
||||
mac_callback_t sent;
|
||||
struct qbuf_metadata *metadata;
|
||||
void *cptr;
|
||||
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
sent = metadata->sent;
|
||||
cptr = metadata->cptr;
|
||||
|
||||
switch(status) {
|
||||
case MAC_TX_OK:
|
||||
PRINTF("csma: rexmit ok %d\n", n->transmissions);
|
||||
break;
|
||||
case MAC_TX_COLLISION:
|
||||
case MAC_TX_NOACK:
|
||||
PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n",
|
||||
status, n->transmissions, n->collisions);
|
||||
break;
|
||||
default:
|
||||
PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status);
|
||||
break;
|
||||
}
|
||||
|
||||
free_packet(n, q, status);
|
||||
mac_call_sent_callback(sent, cptr, status, n->transmissions);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
rexmit(struct rdc_buf_list *q, struct neighbor_queue *n)
|
||||
{
|
||||
schedule_transmission(n);
|
||||
/* This is needed to correctly attribute energy that we spent
|
||||
transmitting this packet. */
|
||||
queuebuf_update_attr_from_packetbuf(q->buf);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
collision(struct rdc_buf_list *q, struct neighbor_queue *n,
|
||||
int num_transmissions)
|
||||
{
|
||||
struct qbuf_metadata *metadata;
|
||||
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
|
||||
n->collisions += num_transmissions;
|
||||
|
||||
if(n->collisions > CSMA_MAX_BACKOFF) {
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
/* Increment to indicate a next retry */
|
||||
n->transmissions++;
|
||||
}
|
||||
|
||||
if(n->transmissions >= metadata->max_transmissions) {
|
||||
tx_done(MAC_TX_COLLISION, q, n);
|
||||
} else {
|
||||
PRINTF("csma: rexmit collision %d\n", n->transmissions);
|
||||
rexmit(q, n);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
noack(struct rdc_buf_list *q, struct neighbor_queue *n, int num_transmissions)
|
||||
{
|
||||
struct qbuf_metadata *metadata;
|
||||
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
n->transmissions += num_transmissions;
|
||||
|
||||
if(n->transmissions >= metadata->max_transmissions) {
|
||||
tx_done(MAC_TX_NOACK, q, n);
|
||||
} else {
|
||||
PRINTF("csma: rexmit noack %d\n", n->transmissions);
|
||||
rexmit(q, n);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
tx_ok(struct rdc_buf_list *q, struct neighbor_queue *n, int num_transmissions)
|
||||
{
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
n->transmissions += num_transmissions;
|
||||
tx_done(MAC_TX_OK, q, n);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
packet_sent(void *ptr, int status, int num_transmissions)
|
||||
{
|
||||
struct neighbor_queue *n;
|
||||
struct rdc_buf_list *q;
|
||||
struct qbuf_metadata *metadata;
|
||||
clock_time_t time = 0;
|
||||
mac_callback_t sent;
|
||||
void *cptr;
|
||||
int num_tx;
|
||||
int backoff_exponent;
|
||||
int backoff_transmissions;
|
||||
|
||||
n = ptr;
|
||||
if(n == NULL) {
|
||||
return;
|
||||
}
|
||||
switch(status) {
|
||||
case MAC_TX_OK:
|
||||
case MAC_TX_NOACK:
|
||||
n->transmissions += num_transmissions;
|
||||
break;
|
||||
case MAC_TX_COLLISION:
|
||||
n->collisions += num_transmissions;
|
||||
break;
|
||||
case MAC_TX_DEFERRED:
|
||||
n->deferrals += num_transmissions;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find out what packet this callback refers to */
|
||||
for(q = list_head(n->queued_packet_list);
|
||||
|
@ -240,78 +333,30 @@ packet_sent(void *ptr, int status, int num_transmissions)
|
|||
}
|
||||
}
|
||||
|
||||
if(q != NULL) {
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
|
||||
if(metadata != NULL) {
|
||||
sent = metadata->sent;
|
||||
cptr = metadata->cptr;
|
||||
num_tx = n->transmissions;
|
||||
if(status == MAC_TX_COLLISION ||
|
||||
status == MAC_TX_NOACK) {
|
||||
|
||||
/* If the transmission was not performed because of a
|
||||
collision or noack, we must retransmit the packet. */
|
||||
if(q == NULL) {
|
||||
PRINTF("csma: seqno %d not found\n",
|
||||
packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
|
||||
return;
|
||||
} else if(q->ptr == NULL) {
|
||||
PRINTF("csma: no metadata\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch(status) {
|
||||
case MAC_TX_COLLISION:
|
||||
PRINTF("csma: rexmit collision %d\n", n->transmissions);
|
||||
case MAC_TX_OK:
|
||||
tx_ok(q, n, num_transmissions);
|
||||
break;
|
||||
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;
|
||||
default:
|
||||
PRINTF("csma: rexmit err %d, %d\n", status, n->transmissions);
|
||||
}
|
||||
|
||||
/* The retransmission time must be proportional to the channel
|
||||
check interval of the underlying radio duty cycling layer. */
|
||||
time = default_timebase();
|
||||
|
||||
/* The retransmission time uses a truncated exponential backoff
|
||||
* so that the interval between the transmissions increase with
|
||||
* each retransmit. */
|
||||
backoff_exponent = num_tx;
|
||||
|
||||
/* Truncate the exponent if needed. */
|
||||
if(backoff_exponent > CSMA_MAX_BACKOFF_EXPONENT) {
|
||||
backoff_exponent = CSMA_MAX_BACKOFF_EXPONENT;
|
||||
}
|
||||
|
||||
/* Proceed to exponentiation. */
|
||||
backoff_transmissions = 1 << backoff_exponent;
|
||||
|
||||
/* Pick a time for next transmission, within the interval:
|
||||
* [time, time + 2^backoff_exponent * time[ */
|
||||
time = time + (random_rand() % (backoff_transmissions * time));
|
||||
|
||||
if(n->transmissions < metadata->max_transmissions) {
|
||||
PRINTF("csma: retransmitting with time %lu %p\n", time, q);
|
||||
ctimer_set(&n->transmit_timer, time,
|
||||
transmit_packet_list, n);
|
||||
/* This is needed to correctly attribute energy that we spent
|
||||
transmitting this packet. */
|
||||
queuebuf_update_attr_from_packetbuf(q->buf);
|
||||
} else {
|
||||
PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n",
|
||||
status, n->transmissions, n->collisions);
|
||||
free_packet(n, q, status);
|
||||
mac_call_sent_callback(sent, cptr, status, num_tx);
|
||||
}
|
||||
} else {
|
||||
if(status == MAC_TX_OK) {
|
||||
PRINTF("csma: rexmit ok %d\n", n->transmissions);
|
||||
} else {
|
||||
PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status);
|
||||
}
|
||||
free_packet(n, q, status);
|
||||
mac_call_sent_callback(sent, cptr, status, num_tx);
|
||||
}
|
||||
} else {
|
||||
PRINTF("csma: no metadata\n");
|
||||
}
|
||||
} else {
|
||||
PRINTF("csma: seqno %d not found\n", packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
|
||||
tx_done(status, q, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -346,8 +391,7 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
/* Init neighbor entry */
|
||||
linkaddr_copy(&n->addr, addr);
|
||||
n->transmissions = 0;
|
||||
n->collisions = 0;
|
||||
n->deferrals = 0;
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
/* Init packet list for this neighbor */
|
||||
LIST_STRUCT_INIT(n, queued_packet_list);
|
||||
/* Add neighbor to the list */
|
||||
|
@ -368,7 +412,7 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
/* Neighbor and packet successfully allocated */
|
||||
if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) {
|
||||
/* Use default configuration for max transmissions */
|
||||
metadata->max_transmissions = CSMA_MAX_MAC_TRANSMISSIONS;
|
||||
metadata->max_transmissions = CSMA_MAX_MAX_FRAME_RETRIES + 1;
|
||||
} else {
|
||||
metadata->max_transmissions =
|
||||
packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS);
|
||||
|
@ -389,7 +433,7 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
list_length(n->queued_packet_list), memb_numfree(&packet_memb));
|
||||
/* If q is the first packet in the neighbor's queue, send asap */
|
||||
if(list_head(n->queued_packet_list) == q) {
|
||||
ctimer_set(&n->transmit_timer, 0, transmit_packet_list, n);
|
||||
schedule_transmission(n);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ addr_len(uint8_t mode)
|
|||
}
|
||||
}
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#if LLSEC802154_USES_EXPLICIT_KEYS
|
||||
#if LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_EXPLICIT_KEYS
|
||||
static uint8_t
|
||||
get_key_id_len(uint8_t key_id_mode)
|
||||
{
|
||||
|
@ -117,7 +117,7 @@ get_key_id_len(uint8_t key_id_mode)
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER && LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Get current PAN ID */
|
||||
uint16_t
|
||||
|
@ -317,7 +317,7 @@ field_len(frame802154_t *p, field_length_t *flen)
|
|||
flen->dest_addr_len = addr_len(p->fcf.dest_addr_mode & 3);
|
||||
flen->src_addr_len = addr_len(p->fcf.src_addr_mode & 3);
|
||||
|
||||
#if LLSEC802154_SECURITY_LEVEL
|
||||
#if LLSEC802154_USES_AUX_HEADER
|
||||
/* Aux security header */
|
||||
if(p->fcf.security_enabled & 1) {
|
||||
flen->aux_sec_len = 1; /* FCF + possibly frame counter and key ID */
|
||||
|
@ -333,7 +333,7 @@ field_len(frame802154_t *p, field_length_t *flen)
|
|||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
;
|
||||
}
|
||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER */
|
||||
}
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/**
|
||||
|
@ -418,7 +418,7 @@ frame802154_create(frame802154_t *p, uint8_t *buf)
|
|||
for(c = flen.src_addr_len; c > 0; c--) {
|
||||
buf[pos++] = p->src_addr[c - 1];
|
||||
}
|
||||
#if LLSEC802154_SECURITY_LEVEL
|
||||
#if LLSEC802154_USES_AUX_HEADER
|
||||
/* Aux header */
|
||||
if(flen.aux_sec_len) {
|
||||
buf[pos++] = p->aux_hdr.security_control.security_level
|
||||
|
@ -447,7 +447,7 @@ frame802154_create(frame802154_t *p, uint8_t *buf)
|
|||
}
|
||||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
}
|
||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER */
|
||||
|
||||
return (int)pos;
|
||||
}
|
||||
|
@ -570,7 +570,7 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
|
|||
pf->src_pid = 0;
|
||||
}
|
||||
|
||||
#if LLSEC802154_SECURITY_LEVEL
|
||||
#if LLSEC802154_USES_AUX_HEADER
|
||||
if(fcf.security_enabled) {
|
||||
pf->aux_hdr.security_control.security_level = p[0] & 7;
|
||||
#if LLSEC802154_USES_EXPLICIT_KEYS
|
||||
|
@ -599,7 +599,7 @@ frame802154_parse(uint8_t *data, int len, frame802154_t *pf)
|
|||
}
|
||||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
}
|
||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER */
|
||||
|
||||
/* header length */
|
||||
c = p - data;
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#include "net/mac/frame802154e-ie.h"
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/* c.f. IEEE 802.15.4e Table 4b */
|
||||
enum ieee802154e_header_ie_id {
|
||||
|
|
|
@ -98,7 +98,7 @@ create_frame(int type, int do_create)
|
|||
/* Insert IEEE 802.15.4 version bits. */
|
||||
params.fcf.frame_version = FRAME802154_VERSION;
|
||||
|
||||
#if LLSEC802154_SECURITY_LEVEL
|
||||
#if LLSEC802154_USES_AUX_HEADER
|
||||
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL)) {
|
||||
params.fcf.security_enabled = 1;
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ create_frame(int type, int do_create)
|
|||
params.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
|
||||
params.aux_hdr.key_source.u16[0] = packetbuf_attr(PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1);
|
||||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER */
|
||||
|
||||
/* Increment and set the data sequence number. */
|
||||
if(!do_create) {
|
||||
|
@ -238,7 +238,7 @@ parse(void)
|
|||
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, frame.seq);
|
||||
#endif
|
||||
|
||||
#if LLSEC802154_SECURITY_LEVEL
|
||||
#if LLSEC802154_USES_AUX_HEADER
|
||||
if(frame.fcf.security_enabled) {
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, frame.aux_hdr.security_control.security_level);
|
||||
#if LLSEC802154_USES_FRAME_COUNTER
|
||||
|
@ -251,7 +251,7 @@ parse(void)
|
|||
packetbuf_set_attr(PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1, frame.aux_hdr.key_source.u16[0]);
|
||||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
}
|
||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER */
|
||||
|
||||
PRINTF("15.4-IN: %2X", frame.fcf.frame_type);
|
||||
PRINTADDR(packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
|
|
|
@ -122,7 +122,7 @@ phase_update(const linkaddr_t *neighbor, rtimer_clock_t time,
|
|||
} else {
|
||||
/* No matching phase was found, so we allocate a new one. */
|
||||
if(mac_status == MAC_TX_OK && e == NULL) {
|
||||
e = nbr_table_add_lladdr(nbr_phase, neighbor);
|
||||
e = nbr_table_add_lladdr(nbr_phase, neighbor, NBR_TABLE_REASON_MAC, NULL);
|
||||
if(e) {
|
||||
e->time = time;
|
||||
#if PHASE_DRIFT_CORRECT
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
#include "contiki-conf.h"
|
||||
#include "net/mac/mac.h"
|
||||
#include "net/llsec/llsec802154.h"
|
||||
|
||||
#ifdef RDC_CONF_WITH_DUPLICATE_DETECTION
|
||||
#define RDC_WITH_DUPLICATE_DETECTION RDC_CONF_WITH_DUPLICATE_DETECTION
|
||||
|
@ -51,7 +52,7 @@
|
|||
frame because it has seen its sequence number already. Replay
|
||||
protection should be implemented at the LLSEC layer where the
|
||||
authenticity of frames is verified. */
|
||||
#define RDC_WITH_DUPLICATE_DETECTION !LLSEC802154_CONF_SECURITY_LEVEL
|
||||
#define RDC_WITH_DUPLICATE_DETECTION !LLSEC802154_ENABLED
|
||||
#endif /* RDC_CONF_WITH_DUPLICATE_DETECTION */
|
||||
|
||||
/* List of packets to be sent by RDC layer */
|
||||
|
|
|
@ -36,6 +36,9 @@ It has been tested on the following platforms:
|
|||
* NXP JN516x (`jn516x`, tested on hardware)
|
||||
* Tmote Sky (`sky`, tested on hardware and in cooja)
|
||||
* Zolertia Z1 (`z1`, tested in cooja only)
|
||||
* CC2538DK (`cc2538dk`, tested on hardware)
|
||||
* Zolertia Zoul (`zoul`, tested on hardware)
|
||||
* CC2650 (`srf06-cc26xx`, tested on hardware)
|
||||
|
||||
This implementation was present at the ETSI Plugtest
|
||||
event in Prague in July 2015, and did successfully inter-operate with all
|
||||
|
@ -76,7 +79,7 @@ Orchestra is implemented in:
|
|||
|
||||
A simple TSCH+RPL example is included under `examples/ipv6/rpl-tsch`.
|
||||
To use TSCH, first make sure your platform supports it.
|
||||
Currently, `jn516x`, `sky` and `z1` are the supported platforms.
|
||||
Currently, `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul` and `srf06-cc26xx` are the supported platforms.
|
||||
To add your own, we refer the reader to the next section.
|
||||
|
||||
To add TSCH to your application, first include the TSCH module from your makefile with:
|
||||
|
@ -128,8 +131,8 @@ To configure TSCH, see the macros in `.h` files under `core/net/mac/tsch/` and r
|
|||
To include TSCH standard-compliant security, set the following:
|
||||
```
|
||||
/* Enable security */
|
||||
#undef LLSEC802154_CONF_SECURITY_LEVEL
|
||||
#define LLSEC802154_CONF_SECURITY_LEVEL 1
|
||||
#undef LLSEC802154_CONF_ENABLED
|
||||
#define LLSEC802154_CONF_ENABLED 1
|
||||
/* TSCH uses explicit keys to identify k1 and k2 */
|
||||
#undef LLSEC802154_CONF_USES_EXPLICIT_KEYS
|
||||
#define LLSEC802154_CONF_USES_EXPLICIT_KEYS 1
|
||||
|
@ -162,7 +165,7 @@ Finally, one can also implement his own scheduler, centralized or distributed, b
|
|||
## Porting TSCH to a new platform
|
||||
|
||||
Porting TSCH to a new platform requires a few new features in the radio driver, a number of timing-related configuration paramters.
|
||||
The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`.
|
||||
The easiest is probably to start from one of the existing port: `jn516x`, `sky`, `z1`, `cc2538dk`, `zoul`, `srf06-cc26xx`.
|
||||
|
||||
### Radio features required for TSCH
|
||||
|
||||
|
|
|
@ -86,12 +86,16 @@ timesync_entry_add(int32_t val, uint32_t time_delta)
|
|||
static void
|
||||
timesync_learn_drift_ticks(uint32_t time_delta_asn, int32_t drift_ticks)
|
||||
{
|
||||
/* should fit in 32-bit unsigned integer */
|
||||
uint32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length];
|
||||
/* should fit in a 32-bit integer */
|
||||
int32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length];
|
||||
int32_t real_drift_ticks = drift_ticks + compensated_ticks;
|
||||
int32_t last_drift_ppm = (int32_t)((int64_t)real_drift_ticks * TSCH_DRIFT_UNIT / time_delta_ticks);
|
||||
|
||||
drift_ppm = timesync_entry_add(last_drift_ppm, time_delta_ticks);
|
||||
|
||||
TSCH_LOG_ADD(tsch_log_message,
|
||||
snprintf(log->message, sizeof(log->message),
|
||||
"drift %ld", drift_ppm / 256));
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Either reset or update the neighbor's drift */
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
#define TSCH_HOPPING_SEQUENCE_4_16 (uint8_t[]){ 20, 26, 25, 26, 15, 15, 25, 20, 26, 15, 26, 25, 20, 15, 20, 25 }
|
||||
/* 4 channels, sequence length 4 */
|
||||
#define TSCH_HOPPING_SEQUENCE_4_4 (uint8_t[]){ 15, 25, 26, 20 }
|
||||
/* 2 channels, sequence length 2 */
|
||||
#define TSCH_HOPPING_SEQUENCE_2_2 (uint8_t[]){ 20, 25 }
|
||||
/* 1 channel, sequence length 1 */
|
||||
#define TSCH_HOPPING_SEQUENCE_1_1 (uint8_t[]){ 20 }
|
||||
|
||||
|
@ -120,7 +122,7 @@
|
|||
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 10000
|
||||
|
||||
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 15000
|
||||
/* Default timeslot timing for platfroms requiring 15ms slots */
|
||||
/* Default timeslot timing for platforms requiring 15ms slots */
|
||||
|
||||
#define TSCH_DEFAULT_TS_CCA_OFFSET 1800
|
||||
#define TSCH_DEFAULT_TS_CCA 128
|
||||
|
@ -135,7 +137,7 @@
|
|||
#define TSCH_DEFAULT_TS_MAX_TX 4256
|
||||
#define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 15000
|
||||
|
||||
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 65000
|
||||
#elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 65000U
|
||||
/* 65ms timeslot, i.e. nearly the max length allowed by standard (16-bit unsigned in micro-seconds).
|
||||
* Useful for running link-layer security on sky or z1 in Cooja, where only S/W security is supported.
|
||||
* Note: this slot timing would require a total of 120ms. If a slot overlaps with the next active slot,
|
||||
|
@ -175,4 +177,25 @@
|
|||
#define TSCH_ADAPTIVE_TIMESYNC 0
|
||||
#endif
|
||||
|
||||
/* HW frame filtering enabled */
|
||||
#ifdef TSCH_CONF_HW_FRAME_FILTERING
|
||||
#define TSCH_HW_FRAME_FILTERING TSCH_CONF_HW_FRAME_FILTERING
|
||||
#else /* TSCH_CONF_HW_FRAME_FILTERING */
|
||||
#define TSCH_HW_FRAME_FILTERING 1
|
||||
#endif /* TSCH_CONF_HW_FRAME_FILTERING */
|
||||
|
||||
/* Keep radio always on within TSCH timeslot (1) or turn it off between packet and ACK? (0) */
|
||||
#ifdef TSCH_CONF_RADIO_ON_DURING_TIMESLOT
|
||||
#define TSCH_RADIO_ON_DURING_TIMESLOT TSCH_CONF_RADIO_ON_DURING_TIMESLOT
|
||||
#else
|
||||
#define TSCH_RADIO_ON_DURING_TIMESLOT 0
|
||||
#endif
|
||||
|
||||
/* How long to scan each channel in the scanning phase */
|
||||
#ifdef TSCH_CONF_CHANNEL_SCAN_DURATION
|
||||
#define TSCH_CHANNEL_SCAN_DURATION TSCH_CONF_CHANNEL_SCAN_DURATION
|
||||
#else
|
||||
#define TSCH_CHANNEL_SCAN_DURATION CLOCK_SECOND
|
||||
#endif
|
||||
|
||||
#endif /* __TSCH_CONF_H__ */
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
#if TSCH_LOG_LEVEL >= 2 /* Skip this file for log levels 0 or 1 */
|
||||
|
||||
|
@ -84,11 +84,15 @@ tsch_log_process_pending(void)
|
|||
}
|
||||
while((log_index = ringbufindex_peek_get(&log_ringbuf)) != -1) {
|
||||
struct tsch_log_t *log = &log_array[log_index];
|
||||
if(log->link == NULL) {
|
||||
printf("TSCH: {asn-%x.%lx link-NULL} ", log->asn.ms1b, log->asn.ls4b);
|
||||
} else {
|
||||
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
|
||||
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
|
||||
log->asn.ms1b, log->asn.ls4b,
|
||||
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
|
||||
tsch_calculate_channel(&log->asn, log->link->channel_offset));
|
||||
}
|
||||
switch(log->type) {
|
||||
case tsch_log_tx:
|
||||
printf("%s-%u-%u %u tx %d, st %d-%d",
|
||||
|
@ -103,7 +107,7 @@ tsch_log_process_pending(void)
|
|||
break;
|
||||
case tsch_log_rx:
|
||||
printf("%s-%u-%u %u rx %d",
|
||||
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->tx.sec_level,
|
||||
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->rx.sec_level,
|
||||
log->rx.datalen,
|
||||
log->rx.src);
|
||||
if(log->rx.drift_used) {
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Construct enhanced ACK packet and return ACK length */
|
||||
|
@ -94,7 +94,7 @@ tsch_packet_create_eack(uint8_t *buf, int buf_size,
|
|||
p.src_pid = IEEE802154_PANID;
|
||||
linkaddr_copy((linkaddr_t *)&p.src_addr, &linkaddr_node_addr);
|
||||
#endif
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(tsch_is_pan_secured) {
|
||||
p.fcf.security_enabled = 1;
|
||||
p.aux_hdr.security_control.security_level = TSCH_SECURITY_KEY_SEC_LEVEL_ACK;
|
||||
|
@ -103,7 +103,7 @@ tsch_packet_create_eack(uint8_t *buf, int buf_size,
|
|||
p.aux_hdr.security_control.frame_counter_size = 1;
|
||||
p.aux_hdr.key_index = TSCH_SECURITY_KEY_INDEX_ACK;
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
if((curr_len = frame802154_create(&p, buf)) == 0) {
|
||||
return 0;
|
||||
|
@ -166,13 +166,13 @@ tsch_packet_parse_eack(const uint8_t *buf, int buf_size,
|
|||
|
||||
if(frame->fcf.ie_list_present) {
|
||||
int mic_len = 0;
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
/* Check if there is space for the security MIC (if any) */
|
||||
mic_len = tsch_security_mic_len(frame);
|
||||
if(buf_size < curr_len + mic_len) {
|
||||
return 0;
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
|
||||
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
|
||||
return 0;
|
||||
|
@ -222,7 +222,7 @@ tsch_packet_create_eb(uint8_t *buf, int buf_size, uint8_t seqno,
|
|||
p.dest_addr[0] = 0xff;
|
||||
p.dest_addr[1] = 0xff;
|
||||
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(tsch_is_pan_secured) {
|
||||
p.fcf.security_enabled = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) > 0;
|
||||
p.aux_hdr.security_control.security_level = packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL);
|
||||
|
@ -231,7 +231,7 @@ tsch_packet_create_eb(uint8_t *buf, int buf_size, uint8_t seqno,
|
|||
p.aux_hdr.security_control.frame_counter_size = 1;
|
||||
p.aux_hdr.key_index = packetbuf_attr(PACKETBUF_ATTR_KEY_INDEX);
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
if((curr_len = frame802154_create(&p, buf)) == 0) {
|
||||
return 0;
|
||||
|
@ -387,14 +387,14 @@ tsch_packet_parse_eb(const uint8_t *buf, int buf_size,
|
|||
if(frame->fcf.ie_list_present) {
|
||||
/* Calculate space needed for the security MIC, if any, before attempting to parse IEs */
|
||||
int mic_len = 0;
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(!frame_without_mic) {
|
||||
mic_len = tsch_security_mic_len(frame);
|
||||
if(buf_size < curr_len + mic_len) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
/* Parse information elements. We need to substract the MIC length, as the exact payload len is needed while parsing */
|
||||
if((ret = frame802154e_parse_information_elements(buf + curr_len, buf_size - curr_len - mic_len, ies)) == -1) {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
/********** Includes **********/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "net/packetbuf.h"
|
||||
#include "net/mac/tsch/tsch-private.h"
|
||||
#include "net/mac/frame802154.h"
|
||||
#include "net/mac/frame802154e-ie.h"
|
||||
|
@ -81,7 +82,7 @@ by default, useful in case of duplicate seqno */
|
|||
/********** Constants *********/
|
||||
|
||||
/* Max TSCH packet lenght */
|
||||
#define TSCH_PACKET_MAX_LEN 127
|
||||
#define TSCH_PACKET_MAX_LEN MIN(127,PACKETBUF_SIZE)
|
||||
|
||||
/********** Functions *********/
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/* Check if TSCH_QUEUE_NUM_PER_NEIGHBOR is power of two */
|
||||
#if (TSCH_QUEUE_NUM_PER_NEIGHBOR & (TSCH_QUEUE_NUM_PER_NEIGHBOR - 1)) != 0
|
||||
|
@ -302,14 +302,17 @@ tsch_queue_free_packet(struct tsch_packet *p)
|
|||
/*---------------------------------------------------------------------------*/
|
||||
/* Flush all neighbor queues */
|
||||
void
|
||||
tsch_queue_flush_all(void)
|
||||
tsch_queue_reset(void)
|
||||
{
|
||||
/* Deallocate unneeded neighbors */
|
||||
if(!tsch_is_locked()) {
|
||||
struct tsch_neighbor *n = list_head(neighbor_list);
|
||||
while(n != NULL) {
|
||||
struct tsch_neighbor *next_n = list_item_next(n);
|
||||
/* Flush queue */
|
||||
tsch_queue_flush_nbr_queue(n);
|
||||
/* Reset backoff exponent */
|
||||
tsch_queue_backoff_reset(n);
|
||||
n = next_n;
|
||||
}
|
||||
}
|
||||
|
@ -379,6 +382,7 @@ tsch_queue_get_packet_for_dest_addr(const linkaddr_t *addr, struct tsch_link *li
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Returns the head packet of any neighbor queue with zero backoff counter.
|
||||
* Writes pointer to the neighbor in *n */
|
||||
struct tsch_packet *
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "lib/ringbufindex.h"
|
||||
#include "net/linkaddr.h"
|
||||
#include "net/mac/tsch/tsch-schedule.h"
|
||||
#include "net/mac/mac.h"
|
||||
|
||||
/******** Configuration *******/
|
||||
|
||||
|
@ -166,8 +167,8 @@ int tsch_queue_packet_count(const linkaddr_t *addr);
|
|||
struct tsch_packet *tsch_queue_remove_packet_from_queue(struct tsch_neighbor *n);
|
||||
/* Free a packet */
|
||||
void tsch_queue_free_packet(struct tsch_packet *p);
|
||||
/* Flush all neighbor queues */
|
||||
void tsch_queue_flush_all(void);
|
||||
/* Reset neighbor queues */
|
||||
void tsch_queue_reset(void);
|
||||
/* Deallocate neighbors with empty queue */
|
||||
void tsch_queue_free_unused_neighbors(void);
|
||||
/* Is the neighbor queue empty? */
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* To use, set #define TSCH_CALLBACK_JOINING_NETWORK tsch_rpl_callback_joining_network */
|
||||
|
@ -73,7 +73,7 @@ tsch_rpl_callback_leaving_network(void)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Set TSCH EB period based on current RPL DIO period.
|
||||
* To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_new_dio_interval */
|
||||
* To use, set #define RPL_CALLBACK_NEW_DIO_INTERVAL tsch_rpl_callback_new_dio_interval */
|
||||
void
|
||||
tsch_rpl_callback_new_dio_interval(uint8_t dio_interval)
|
||||
{
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/* Pre-allocated space for links */
|
||||
MEMB(link_memb, struct tsch_link, TSCH_SCHEDULE_MAX_LINKS);
|
||||
|
@ -191,6 +191,7 @@ tsch_schedule_add_link(struct tsch_slotframe *slotframe,
|
|||
l = memb_alloc(&link_memb);
|
||||
if(l == NULL) {
|
||||
PRINTF("TSCH-schedule:! add_link memb_alloc failed\n");
|
||||
tsch_release_lock();
|
||||
} else {
|
||||
static int current_link_handle = 0;
|
||||
struct tsch_neighbor *n;
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/* The two keys K1 and K2 from 6TiSCH minimal configuration
|
||||
* K1: well-known, used for EBs
|
||||
|
|
|
@ -39,22 +39,23 @@
|
|||
#include "net/mac/tsch/tsch-asn.h"
|
||||
#include "net/mac/tsch/tsch-private.h"
|
||||
#include "net/mac/frame802154.h"
|
||||
#include "net/llsec/llsec802154.h"
|
||||
#include "net/mac/frame802154e-ie.h"
|
||||
|
||||
/******** Configuration *******/
|
||||
|
||||
/* To enable TSCH security:
|
||||
* - set LLSEC802154_CONF_SECURITY_LEVEL
|
||||
* - set LLSEC802154_CONF_ENABLED
|
||||
* - set LLSEC802154_CONF_USES_EXPLICIT_KEYS
|
||||
* - unset LLSEC802154_CONF_USES_FRAME_COUNTER
|
||||
* */
|
||||
#define TSCH_SECURITY_ENABLED (LLSEC802154_CONF_SECURITY_LEVEL != 0)
|
||||
#if TSCH_SECURITY_ENABLED && !LLSEC802154_CONF_USES_EXPLICIT_KEYS
|
||||
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_EXPLICIT_KEYS unset
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#if TSCH_SECURITY_ENABLED && LLSEC802154_CONF_USES_FRAME_COUNTER
|
||||
#error TSCH_SECURITY_ENABLED set but LLSEC802154_CONF_USES_FRAME_COUNTER set
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
|
||||
#if LLSEC802154_ENABLED && !LLSEC802154_USES_EXPLICIT_KEYS
|
||||
#error LLSEC802154_ENABLED set but LLSEC802154_USES_EXPLICIT_KEYS unset
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
#if LLSEC802154_ENABLED && LLSEC802154_USES_FRAME_COUNTER
|
||||
#error LLSEC802154_ENABLED set but LLSEC802154_USES_FRAME_COUNTER set
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
/* K1, defined in 6TiSCH minimal, is well-known (offers no security) and used for EBs only */
|
||||
#ifdef TSCH_SECURITY_CONF_K1
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
* \author
|
||||
* Simon Duquennoy <simonduq@sics.se>
|
||||
* Beshr Al Nahas <beshr@sics.se>
|
||||
* Atis Elsts <atis.elsts@bristol.ac.uk>
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -59,7 +60,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/* TSCH debug macros, i.e. to set LEDs or GPIOs on various TSCH
|
||||
* timeslot events */
|
||||
|
@ -109,6 +110,18 @@
|
|||
#define RTIMER_GUARD 2u
|
||||
#endif
|
||||
|
||||
enum tsch_radio_state_on_cmd {
|
||||
TSCH_RADIO_CMD_ON_START_OF_TIMESLOT,
|
||||
TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT,
|
||||
TSCH_RADIO_CMD_ON_FORCE,
|
||||
};
|
||||
|
||||
enum tsch_radio_state_off_cmd {
|
||||
TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT,
|
||||
TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT,
|
||||
TSCH_RADIO_CMD_OFF_FORCE,
|
||||
};
|
||||
|
||||
/* A ringbuf storing outgoing packets after they were dequeued.
|
||||
* Will be processed layer by tsch_tx_process_pending */
|
||||
struct ringbufindex dequeued_ringbuf;
|
||||
|
@ -370,6 +383,68 @@ update_neighbor_state(struct tsch_neighbor *n, struct tsch_packet *p,
|
|||
return in_queue;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* This function turns on the radio. Its semantics is dependent on
|
||||
* the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
|
||||
* - if enabled, the radio is turned on at the start of the slot
|
||||
* - if disabled, the radio is turned on within the slot,
|
||||
* directly before the packet Rx guard time and ACK Rx guard time.
|
||||
*/
|
||||
static void
|
||||
tsch_radio_on(enum tsch_radio_state_on_cmd command)
|
||||
{
|
||||
int do_it = 0;
|
||||
switch(command) {
|
||||
case TSCH_RADIO_CMD_ON_START_OF_TIMESLOT:
|
||||
if(TSCH_RADIO_ON_DURING_TIMESLOT) {
|
||||
do_it = 1;
|
||||
}
|
||||
break;
|
||||
case TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT:
|
||||
if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
|
||||
do_it = 1;
|
||||
}
|
||||
break;
|
||||
case TSCH_RADIO_CMD_ON_FORCE:
|
||||
do_it = 1;
|
||||
break;
|
||||
}
|
||||
if(do_it) {
|
||||
NETSTACK_RADIO.on();
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**
|
||||
* This function turns off the radio. In the same way as for tsch_radio_on(),
|
||||
* it depends on the value of TSCH_RADIO_ON_DURING_TIMESLOT constant:
|
||||
* - if enabled, the radio is turned off at the end of the slot
|
||||
* - if disabled, the radio is turned off within the slot,
|
||||
* directly after Tx'ing or Rx'ing a packet or Tx'ing an ACK.
|
||||
*/
|
||||
static void
|
||||
tsch_radio_off(enum tsch_radio_state_off_cmd command)
|
||||
{
|
||||
int do_it = 0;
|
||||
switch(command) {
|
||||
case TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT:
|
||||
if(TSCH_RADIO_ON_DURING_TIMESLOT) {
|
||||
do_it = 1;
|
||||
}
|
||||
break;
|
||||
case TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT:
|
||||
if(!TSCH_RADIO_ON_DURING_TIMESLOT) {
|
||||
do_it = 1;
|
||||
}
|
||||
break;
|
||||
case TSCH_RADIO_CMD_OFF_FORCE:
|
||||
do_it = 1;
|
||||
break;
|
||||
}
|
||||
if(do_it) {
|
||||
NETSTACK_RADIO.off();
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static
|
||||
PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
||||
{
|
||||
|
@ -404,10 +479,10 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
} else {
|
||||
/* packet payload */
|
||||
static void *packet;
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
/* encrypted payload */
|
||||
static uint8_t encrypted_packet[TSCH_PACKET_MAX_LEN];
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
/* packet payload length */
|
||||
static uint8_t packet_len;
|
||||
/* packet seqno */
|
||||
|
@ -434,7 +509,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
packet_ready = 1;
|
||||
}
|
||||
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(tsch_is_pan_secured) {
|
||||
/* If we are going to encrypt, we need to generate the output in a separate buffer and keep
|
||||
* the original untouched. This is to allow for future retransmissions. */
|
||||
|
@ -445,7 +520,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
packet = encrypted_packet;
|
||||
}
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
/* prepare packet to send: copy to radio buffer */
|
||||
if(packet_ready && NETSTACK_RADIO.prepare(packet, packet_len) == 0) { /* 0 means success */
|
||||
|
@ -456,7 +531,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
/* delay before CCA */
|
||||
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca");
|
||||
TSCH_DEBUG_TX_EVENT();
|
||||
NETSTACK_RADIO.on();
|
||||
tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
|
||||
/* CCA */
|
||||
BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()),
|
||||
current_slot_start, TS_CCA_OFFSET + TS_CCA);
|
||||
|
@ -480,7 +555,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
/* limit tx_time to its max value */
|
||||
tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]);
|
||||
/* turn tadio off -- will turn on again to wait for ACK if needed */
|
||||
NETSTACK_RADIO.off();
|
||||
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
|
||||
|
||||
if(mac_tx_status == RADIO_TX_OK) {
|
||||
if(!is_broadcast) {
|
||||
|
@ -488,35 +563,39 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
int ack_len;
|
||||
rtimer_clock_t ack_start_time;
|
||||
int is_time_source;
|
||||
radio_value_t radio_rx_mode;
|
||||
struct ieee802154_ies ack_ies;
|
||||
uint8_t ack_hdrlen;
|
||||
frame802154_t frame;
|
||||
|
||||
#if TSCH_HW_FRAME_FILTERING
|
||||
radio_value_t radio_rx_mode;
|
||||
/* Entering promiscuous mode so that the radio accepts the enhanced ACK */
|
||||
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
|
||||
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER));
|
||||
#endif /* TSCH_HW_FRAME_FILTERING */
|
||||
/* Unicast: wait for ack after tx: sleep until ack time */
|
||||
TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start,
|
||||
tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck");
|
||||
TSCH_DEBUG_TX_EVENT();
|
||||
NETSTACK_RADIO.on();
|
||||
tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
|
||||
/* Wait for ACK to come */
|
||||
BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(),
|
||||
tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]);
|
||||
TSCH_DEBUG_TX_EVENT();
|
||||
|
||||
ack_start_time = RTIMER_NOW();
|
||||
ack_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
|
||||
|
||||
/* Wait for ACK to finish */
|
||||
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
|
||||
ack_start_time, tsch_timing[tsch_ts_max_ack]);
|
||||
TSCH_DEBUG_TX_EVENT();
|
||||
NETSTACK_RADIO.off();
|
||||
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
|
||||
|
||||
#if TSCH_HW_FRAME_FILTERING
|
||||
/* Leaving promiscuous mode */
|
||||
NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode);
|
||||
NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode | RADIO_RX_MODE_ADDRESS_FILTER);
|
||||
#endif /* TSCH_HW_FRAME_FILTERING */
|
||||
|
||||
/* Read ack frame */
|
||||
ack_len = NETSTACK_RADIO.read((void *)ackbuf, sizeof(ackbuf));
|
||||
|
@ -530,7 +609,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
ack_len = 0;
|
||||
}
|
||||
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(ack_len != 0) {
|
||||
if(!tsch_security_parse_frame(ackbuf, ack_hdrlen, ack_len - ack_hdrlen - tsch_security_mic_len(&frame),
|
||||
&frame, ¤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),
|
||||
"!failed to parse ACK"));
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
}
|
||||
|
||||
if(ack_len != 0) {
|
||||
|
@ -584,6 +663,8 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
}
|
||||
}
|
||||
|
||||
tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
|
||||
|
||||
current_packet->transmissions++;
|
||||
current_packet->ret = mac_tx_status;
|
||||
|
||||
|
@ -604,7 +685,11 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t))
|
|||
log->tx.drift = drift_correction;
|
||||
log->tx.drift_used = is_drift_correction_used;
|
||||
log->tx.is_data = ((((uint8_t *)(queuebuf_dataptr(current_packet->qb)))[0]) & 7) == FRAME802154_DATAFRAME;
|
||||
#if LLSEC802154_ENABLED
|
||||
log->tx.sec_level = queuebuf_attr(current_packet->qb, PACKETBUF_ATTR_SECURITY_LEVEL);
|
||||
#else /* LLSEC802154_ENABLED */
|
||||
log->tx.sec_level = 0;
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
log->tx.dest = TSCH_LOG_ID_FROM_LINKADDR(queuebuf_addr(current_packet->qb, PACKETBUF_ADDR_RECEIVER));
|
||||
);
|
||||
|
||||
|
@ -663,32 +748,26 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
|
|||
TSCH_DEBUG_RX_EVENT();
|
||||
|
||||
/* Start radio for at least guard time */
|
||||
NETSTACK_RADIO.on();
|
||||
packet_seen = NETSTACK_RADIO.receiving_packet();
|
||||
tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT);
|
||||
packet_seen = NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet();
|
||||
if(!packet_seen) {
|
||||
/* Check if receiving within guard time */
|
||||
BUSYWAIT_UNTIL_ABS((packet_seen = NETSTACK_RADIO.receiving_packet()),
|
||||
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait]);
|
||||
}
|
||||
if(packet_seen) {
|
||||
if(!packet_seen) {
|
||||
/* no packets on air */
|
||||
tsch_radio_off(TSCH_RADIO_CMD_OFF_FORCE);
|
||||
} else {
|
||||
TSCH_DEBUG_RX_EVENT();
|
||||
/* Save packet timestamp */
|
||||
rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT;
|
||||
}
|
||||
if(!NETSTACK_RADIO.receiving_packet() && !NETSTACK_RADIO.pending_packet()) {
|
||||
NETSTACK_RADIO.off();
|
||||
/* no packets on air */
|
||||
} else {
|
||||
|
||||
/* Wait until packet is received, turn radio off */
|
||||
BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(),
|
||||
current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]);
|
||||
TSCH_DEBUG_RX_EVENT();
|
||||
NETSTACK_RADIO.off();
|
||||
|
||||
#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
|
||||
/* At the end of the reception, get an more accurate estimate of SFD arrival time */
|
||||
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
|
||||
#endif
|
||||
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
|
||||
|
||||
if(NETSTACK_RADIO.pending_packet()) {
|
||||
static int frame_valid;
|
||||
|
@ -696,9 +775,9 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
|
|||
static frame802154_t frame;
|
||||
radio_value_t radio_last_rssi;
|
||||
|
||||
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
|
||||
/* Read packet */
|
||||
current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN);
|
||||
NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi);
|
||||
current_input->rx_asn = current_asn;
|
||||
current_input->rssi = (signed)radio_last_rssi;
|
||||
header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame);
|
||||
|
@ -706,9 +785,14 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
|
|||
frame802154_check_dest_panid(&frame) &&
|
||||
frame802154_extract_linkaddr(&frame, &source_address, &destination_address);
|
||||
|
||||
#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS
|
||||
/* At the end of the reception, get an more accurate estimate of SFD arrival time */
|
||||
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t));
|
||||
#endif
|
||||
|
||||
packet_duration = TSCH_PACKET_DURATION(current_input->len);
|
||||
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
/* Decrypt and verify incoming frame */
|
||||
if(frame_valid) {
|
||||
if(tsch_security_parse_frame(
|
||||
|
@ -727,17 +811,17 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
|
|||
"!failed to parse frame %u %u", header_len, current_input->len));
|
||||
frame_valid = 0;
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
if(frame_valid) {
|
||||
if(linkaddr_cmp(&destination_address, &linkaddr_node_addr)
|
||||
|| linkaddr_cmp(&destination_address, &linkaddr_null)) {
|
||||
int do_nack = 0;
|
||||
estimated_drift = ((int32_t)expected_rx_time - (int32_t)rx_start_time);
|
||||
estimated_drift = RTIMER_CLOCK_DIFF(expected_rx_time, rx_start_time);
|
||||
|
||||
#if TSCH_TIMESYNC_REMOVE_JITTER
|
||||
/* remove jitter due to measurement errors */
|
||||
if(abs(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) {
|
||||
if(ABS(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) {
|
||||
estimated_drift = 0;
|
||||
} else if(estimated_drift > 0) {
|
||||
estimated_drift -= TSCH_TIMESYNC_MEASUREMENT_ERROR;
|
||||
|
@ -761,12 +845,12 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
|
|||
ack_len = tsch_packet_create_eack(ack_buf, sizeof(ack_buf),
|
||||
&source_address, frame.seq, (int16_t)RTIMERTICKS_TO_US(estimated_drift), do_nack);
|
||||
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(tsch_is_pan_secured) {
|
||||
/* Secure ACK frame. There is only header and header IEs, therefore data len == 0. */
|
||||
ack_len += tsch_security_secure_frame(ack_buf, ack_buf, ack_len, 0, ¤t_asn);
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
/* Copy to radio buffer */
|
||||
NETSTACK_RADIO.prepare((const void *)ack_buf, ack_len);
|
||||
|
@ -776,6 +860,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
|
|||
packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck");
|
||||
TSCH_DEBUG_RX_EVENT();
|
||||
NETSTACK_RADIO.transmit(ack_len);
|
||||
tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT);
|
||||
}
|
||||
|
||||
/* If the sender is a time source, proceed to clock drift compensation */
|
||||
|
@ -805,18 +890,14 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t))
|
|||
log->rx.sec_level = frame.aux_hdr.security_control.security_level;
|
||||
log->rx.estimated_drift = estimated_drift;
|
||||
);
|
||||
} else {
|
||||
TSCH_LOG_ADD(tsch_log_message,
|
||||
snprintf(log->message, sizeof(log->message),
|
||||
"!not for us %x:%x",
|
||||
destination_address.u8[LINKADDR_SIZE - 2], destination_address.u8[LINKADDR_SIZE - 1]);
|
||||
);
|
||||
}
|
||||
|
||||
/* Poll process for processing of pending input and logs */
|
||||
process_poll(&tsch_pending_events_process);
|
||||
}
|
||||
}
|
||||
|
||||
tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT);
|
||||
}
|
||||
|
||||
if(input_queue_drop != 0) {
|
||||
|
@ -857,8 +938,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
|
|||
|
||||
} else {
|
||||
uint8_t current_channel;
|
||||
int is_active_slot;
|
||||
TSCH_DEBUG_SLOT_START();
|
||||
tsch_in_slot_operation = 1;
|
||||
/* Reset drift correction */
|
||||
drift_correction = 0;
|
||||
is_drift_correction_used = 0;
|
||||
/* Get a packet ready to be sent */
|
||||
current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor);
|
||||
/* 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_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 */
|
||||
current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset);
|
||||
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
|
||||
/* Reset drift correction */
|
||||
drift_correction = 0;
|
||||
is_drift_correction_used = 0;
|
||||
/* Turn the radio on already here if configured so; necessary for radios with slow startup */
|
||||
tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
|
||||
/* Decide whether it is a TX/RX/IDLE or OFF slot */
|
||||
/* Actual slot operation */
|
||||
if(current_packet != NULL) {
|
||||
|
@ -883,11 +969,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
|
|||
**/
|
||||
static struct pt slot_tx_pt;
|
||||
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
|
||||
} else if((current_link->link_options & LINK_OPTION_RX)) {
|
||||
} else {
|
||||
/* Listen */
|
||||
static struct pt slot_rx_pt;
|
||||
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
|
||||
}
|
||||
}
|
||||
TSCH_DEBUG_SLOT_END();
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
#else /* TSCH_LOG_LEVEL */
|
||||
#define DEBUG DEBUG_NONE
|
||||
#endif /* TSCH_LOG_LEVEL */
|
||||
#include "net/ip/uip-debug.h"
|
||||
#include "net/net-debug.h"
|
||||
|
||||
/* Use to collect link statistics even on Keep-Alive, even though they were
|
||||
* not sent from an upper layer and don't have a valid packet_sent callback */
|
||||
|
@ -141,7 +141,7 @@ int tsch_is_coordinator = 0;
|
|||
/* Are we associated to a TSCH network? */
|
||||
int tsch_is_associated = 0;
|
||||
/* Is the PAN running link-layer security? */
|
||||
int tsch_is_pan_secured = TSCH_SECURITY_ENABLED;
|
||||
int tsch_is_pan_secured = LLSEC802154_ENABLED;
|
||||
/* The current Absolute Slot Number (ASN) */
|
||||
struct asn_t current_asn;
|
||||
/* Device rank or join priority:
|
||||
|
@ -177,7 +177,7 @@ tsch_set_coordinator(int enable)
|
|||
void
|
||||
tsch_set_pan_secured(int enable)
|
||||
{
|
||||
tsch_is_pan_secured = TSCH_SECURITY_ENABLED && enable;
|
||||
tsch_is_pan_secured = LLSEC802154_ENABLED && enable;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
|
@ -199,8 +199,8 @@ tsch_reset(void)
|
|||
frame802154_set_pan_id(0xffff);
|
||||
/* First make sure pending packet callbacks are sent etc */
|
||||
process_post_synch(&tsch_pending_events_process, PROCESS_EVENT_POLL, NULL);
|
||||
/* Empty all neighbor queues */
|
||||
/* tsch_queue_flush_all(); */
|
||||
/* Reset neighbor queues */
|
||||
tsch_queue_reset();
|
||||
/* Remove unused neighbors */
|
||||
tsch_queue_free_unused_neighbors();
|
||||
tsch_queue_update_time_source(NULL);
|
||||
|
@ -455,21 +455,21 @@ tsch_associate(const struct input_packet *input_eb, rtimer_clock_t timestamp)
|
|||
}
|
||||
#endif /* TSCH_JOIN_SECURED_ONLY */
|
||||
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(!tsch_security_parse_frame(input_eb->payload, hdrlen,
|
||||
input_eb->len - hdrlen - tsch_security_mic_len(&frame),
|
||||
&frame, (linkaddr_t*)&frame.src_addr, ¤t_asn)) {
|
||||
PRINTF("TSCH:! parse_eb: failed to authenticate\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
#if !TSCH_SECURITY_ENABLED
|
||||
#if !LLSEC802154_ENABLED
|
||||
if(frame.fcf.security_enabled == 1) {
|
||||
PRINTF("TSCH:! parse_eb: we do not support security, but EB is secured\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* !TSCH_SECURITY_ENABLED */
|
||||
#endif /* !LLSEC802154_ENABLED */
|
||||
|
||||
#if TSCH_JOIN_MY_PANID_ONLY
|
||||
/* Check if the EB comes from the PAN ID we expect */
|
||||
|
@ -609,24 +609,25 @@ PT_THREAD(tsch_scan(struct pt *pt))
|
|||
|
||||
static struct input_packet input_eb;
|
||||
static struct etimer scan_timer;
|
||||
/* Time when we started scanning on current_channel */
|
||||
static clock_time_t current_channel_since;
|
||||
|
||||
ASN_INIT(current_asn, 0, 0);
|
||||
|
||||
etimer_set(&scan_timer, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY);
|
||||
current_channel_since = clock_time();
|
||||
|
||||
while(!tsch_is_associated && !tsch_is_coordinator) {
|
||||
/* Hop to any channel offset */
|
||||
static int current_channel = 0;
|
||||
/* Time when we started scanning on current_channel */
|
||||
static clock_time_t current_channel_since = 0;
|
||||
static uint8_t current_channel = 0;
|
||||
|
||||
/* We are not coordinator, try to associate */
|
||||
rtimer_clock_t t0;
|
||||
int is_packet_pending = 0;
|
||||
clock_time_t now_seconds = clock_seconds();
|
||||
clock_time_t now_time = clock_time();
|
||||
|
||||
/* Switch to a (new) channel for scanning */
|
||||
if(current_channel == 0 || now_seconds != current_channel_since) {
|
||||
if(current_channel == 0 || now_time - current_channel_since > TSCH_CHANNEL_SCAN_DURATION) {
|
||||
/* Pick a channel at random in TSCH_JOIN_HOPPING_SEQUENCE */
|
||||
uint8_t scan_channel = TSCH_JOIN_HOPPING_SEQUENCE[
|
||||
random_rand() % sizeof(TSCH_JOIN_HOPPING_SEQUENCE)];
|
||||
|
@ -635,7 +636,7 @@ PT_THREAD(tsch_scan(struct pt *pt))
|
|||
current_channel = scan_channel;
|
||||
PRINTF("TSCH: scanning on channel %u\n", scan_channel);
|
||||
}
|
||||
current_channel_since = now_seconds;
|
||||
current_channel_since = now_time;
|
||||
}
|
||||
|
||||
/* Turn radio on and wait for EB */
|
||||
|
@ -649,12 +650,12 @@ PT_THREAD(tsch_scan(struct pt *pt))
|
|||
}
|
||||
|
||||
if(is_packet_pending) {
|
||||
/* Save packet timestamp */
|
||||
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t));
|
||||
|
||||
/* Read packet */
|
||||
input_eb.len = NETSTACK_RADIO.read(input_eb.payload, TSCH_PACKET_MAX_LEN);
|
||||
|
||||
/* Save packet timestamp */
|
||||
NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t));
|
||||
|
||||
/* Parse EB and attempt to associate */
|
||||
PRINTF("TSCH: association: received packet (%u bytes) on channel %u\n", input_eb.len, current_channel);
|
||||
|
||||
|
@ -746,14 +747,14 @@ PROCESS_THREAD(tsch_send_eb_process, ev, data)
|
|||
}
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_BEACONFRAME);
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno);
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(tsch_is_pan_secured) {
|
||||
/* Set security level, key id and index */
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, TSCH_SECURITY_KEY_SEC_LEVEL_EB);
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, FRAME802154_1_BYTE_KEY_ID_MODE); /* Use 1-byte key index */
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, TSCH_SECURITY_KEY_INDEX_EB);
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
eb_len = tsch_packet_create_eb(packetbuf_dataptr(), PACKETBUF_SIZE,
|
||||
tsch_packet_seqno, &hdr_len, &tsch_sync_ie_offset);
|
||||
if(eb_len != 0) {
|
||||
|
@ -907,14 +908,14 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
packetbuf_set_attr(PACKETBUF_ATTR_FRAME_TYPE, FRAME802154_DATAFRAME);
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, tsch_packet_seqno);
|
||||
|
||||
#if TSCH_SECURITY_ENABLED
|
||||
#if LLSEC802154_ENABLED
|
||||
if(tsch_is_pan_secured) {
|
||||
/* Set security level, key id and index */
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, TSCH_SECURITY_KEY_SEC_LEVEL_OTHER);
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_KEY_ID_MODE, FRAME802154_1_BYTE_KEY_ID_MODE); /* Use 1-byte key index */
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_KEY_INDEX, TSCH_SECURITY_KEY_INDEX_OTHER);
|
||||
}
|
||||
#endif /* TSCH_SECURITY_ENABLED */
|
||||
#endif /* LLSEC802154_ENABLED */
|
||||
|
||||
packet_count_before = tsch_queue_packet_count(addr);
|
||||
|
||||
|
@ -936,6 +937,7 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
tsch_queue_packet_count(addr),
|
||||
p->header_len,
|
||||
queuebuf_datalen(p->qb));
|
||||
(void)packet_count_before; /* Discard "variable set but unused" warning in case of TSCH_LOG_LEVEL of 0 */
|
||||
}
|
||||
}
|
||||
if(ret != MAC_TX_DEFERRED) {
|
||||
|
|
|
@ -84,8 +84,8 @@
|
|||
#ifdef TSCH_CONF_JOIN_SECURED_ONLY
|
||||
#define TSCH_JOIN_SECURED_ONLY TSCH_CONF_JOIN_SECURED_ONLY
|
||||
#else
|
||||
/* By default, set if TSCH_SECURITY_ENABLED is also non-zero */
|
||||
#define TSCH_JOIN_SECURED_ONLY TSCH_SECURITY_ENABLED
|
||||
/* By default, set if LLSEC802154_ENABLED is also non-zero */
|
||||
#define TSCH_JOIN_SECURED_ONLY LLSEC802154_ENABLED
|
||||
#endif
|
||||
|
||||
/* By default, join any PAN ID. Otherwise, wait for an EB from IEEE802154_PANID */
|
||||
|
|
|
@ -47,11 +47,20 @@
|
|||
static void handle_periodic_timer(void *ptr);
|
||||
static struct ctimer periodic_timer;
|
||||
static uint8_t initialized = 0;
|
||||
static void print_table();
|
||||
#define PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define PRINTF(...)
|
||||
#endif
|
||||
|
||||
/* This is the callback function that will be called when there is a
|
||||
* nbr-policy active
|
||||
**/
|
||||
#ifdef NBR_TABLE_FIND_REMOVABLE
|
||||
const linkaddr_t *NBR_TABLE_FIND_REMOVABLE(nbr_table_reason_t reason, void *data);
|
||||
#endif /* NBR_TABLE_FIND_REMOVABLE */
|
||||
|
||||
|
||||
/* List of link-layer addresses of the neighbors, used as key in the tables */
|
||||
typedef struct nbr_table_key {
|
||||
struct nbr_table_key *next;
|
||||
|
@ -169,8 +178,27 @@ nbr_set_bit(uint8_t *bitmap, nbr_table_t *table, nbr_table_item_t *item, int val
|
|||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
remove_key(nbr_table_key_t *least_used_key)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < MAX_NUM_TABLES; i++) {
|
||||
if(all_tables[i] != NULL && all_tables[i]->callback != NULL) {
|
||||
/* Call table callback for each table that uses this item */
|
||||
nbr_table_item_t *removed_item = item_from_key(all_tables[i], least_used_key);
|
||||
if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) {
|
||||
all_tables[i]->callback(removed_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Empty used map */
|
||||
used_map[index_from_key(least_used_key)] = 0;
|
||||
/* Remove neighbor from list */
|
||||
list_remove(nbr_table_keys, least_used_key);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static nbr_table_key_t *
|
||||
nbr_table_allocate(void)
|
||||
nbr_table_allocate(nbr_table_reason_t reason, void *data)
|
||||
{
|
||||
nbr_table_key_t *key;
|
||||
int least_used_count = 0;
|
||||
|
@ -179,7 +207,32 @@ nbr_table_allocate(void)
|
|||
key = memb_alloc(&neighbor_addr_mem);
|
||||
if(key != NULL) {
|
||||
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:
|
||||
* (1) not locked
|
||||
* (2) used by fewest tables
|
||||
|
@ -212,26 +265,14 @@ nbr_table_allocate(void)
|
|||
}
|
||||
key = list_item_next(key);
|
||||
}
|
||||
}
|
||||
|
||||
if(least_used_key == NULL) {
|
||||
/* We haven't found any unlocked item, allocation fails */
|
||||
return NULL;
|
||||
} else {
|
||||
/* Reuse least used item */
|
||||
int i;
|
||||
for(i = 0; i<MAX_NUM_TABLES; i++) {
|
||||
if(all_tables[i] != NULL && all_tables[i]->callback != NULL) {
|
||||
/* Call table callback for each table that uses this item */
|
||||
nbr_table_item_t *removed_item = item_from_key(all_tables[i], least_used_key);
|
||||
if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) {
|
||||
all_tables[i]->callback(removed_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Empty used map */
|
||||
used_map[index_from_key(least_used_key)] = 0;
|
||||
/* Remove neighbor from list */
|
||||
list_remove(nbr_table_keys, least_used_key);
|
||||
/* Return associated key */
|
||||
remove_key(least_used_key);
|
||||
return least_used_key;
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +330,7 @@ nbr_table_next(nbr_table_t *table, nbr_table_item_t *item)
|
|||
/*---------------------------------------------------------------------------*/
|
||||
/* Add a neighbor indexed with its link-layer address */
|
||||
nbr_table_item_t *
|
||||
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
|
||||
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data)
|
||||
{
|
||||
int index;
|
||||
nbr_table_item_t *item;
|
||||
|
@ -303,7 +344,7 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
|
|||
|
||||
if((index = index_from_lladdr(lladdr)) == -1) {
|
||||
/* Neighbor not yet in table, let's try to allocate one */
|
||||
key = nbr_table_allocate();
|
||||
key = nbr_table_allocate(reason, data);
|
||||
|
||||
/* No space available for new entry */
|
||||
if(key == NULL) {
|
||||
|
@ -327,6 +368,9 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
|
|||
memset(item, 0, table->item_size);
|
||||
nbr_set_bit(used_map, table, item, 1);
|
||||
|
||||
#if DEBUG
|
||||
print_table();
|
||||
#endif
|
||||
return item;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -377,9 +421,42 @@ nbr_table_get_lladdr(nbr_table_t *table, const void *item)
|
|||
return key != NULL ? &key->lladdr : NULL;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Update link-layer address of an item */
|
||||
int
|
||||
nbr_table_update_lladdr(const linkaddr_t *old_addr, const linkaddr_t *new_addr,
|
||||
int remove_if_duplicate)
|
||||
{
|
||||
int index;
|
||||
int new_index;
|
||||
nbr_table_key_t *key;
|
||||
index = index_from_lladdr(old_addr);
|
||||
if(index == -1) {
|
||||
/* Failure to change since there is nothing to change. */
|
||||
return 0;
|
||||
}
|
||||
if((new_index = index_from_lladdr(new_addr)) != -1) {
|
||||
/* check if it is a change or not - do not remove / fail if same */
|
||||
if(new_index == index) {
|
||||
return 1;
|
||||
}
|
||||
/* This new entry already exists - failure! - remove if requested. */
|
||||
if(remove_if_duplicate) {
|
||||
remove_key(key_from_index(index));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
key = key_from_index(index);
|
||||
/**
|
||||
* Copy the new lladdr into the key - since we know that there is no
|
||||
* conflicting entry.
|
||||
*/
|
||||
memcpy(&key->lladdr, new_addr, sizeof(linkaddr_t));
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if DEBUG
|
||||
static void
|
||||
handle_periodic_timer(void *ptr)
|
||||
print_table()
|
||||
{
|
||||
int i, j;
|
||||
/* Printout all neighbors and which tables they are used in */
|
||||
|
@ -394,6 +471,12 @@ handle_periodic_timer(void *ptr)
|
|||
PRINTF("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
handle_periodic_timer(void *ptr)
|
||||
{
|
||||
print_table();
|
||||
ctimer_reset(&periodic_timer);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -75,6 +75,18 @@ typedef struct nbr_table {
|
|||
/** \brief Declaration of non-static neighbor tables */
|
||||
#define NBR_TABLE_DECLARE(name) extern nbr_table_t *name
|
||||
|
||||
typedef enum {
|
||||
NBR_TABLE_REASON_UNDEFINED,
|
||||
NBR_TABLE_REASON_RPL_DIO,
|
||||
NBR_TABLE_REASON_RPL_DAO,
|
||||
NBR_TABLE_REASON_RPL_DIS,
|
||||
NBR_TABLE_REASON_ROUTE,
|
||||
NBR_TABLE_REASON_IPV6_ND,
|
||||
NBR_TABLE_REASON_MAC,
|
||||
NBR_TABLE_REASON_LLSEC,
|
||||
NBR_TABLE_REASON_LINK_STATS,
|
||||
} nbr_table_reason_t;
|
||||
|
||||
/** \name Neighbor tables: register and loop through table elements */
|
||||
/** @{ */
|
||||
int nbr_table_register(nbr_table_t *table, nbr_table_callback *callback);
|
||||
|
@ -84,7 +96,7 @@ nbr_table_item_t *nbr_table_next(nbr_table_t *table, nbr_table_item_t *item);
|
|||
|
||||
/** \name Neighbor tables: add and get data */
|
||||
/** @{ */
|
||||
nbr_table_item_t *nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr);
|
||||
nbr_table_item_t *nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data);
|
||||
nbr_table_item_t *nbr_table_get_from_lladdr(nbr_table_t *table, const linkaddr_t *lladdr);
|
||||
/** @} */
|
||||
|
||||
|
@ -98,6 +110,7 @@ int nbr_table_unlock(nbr_table_t *table, nbr_table_item_t *item);
|
|||
/** \name Neighbor tables: address manipulation */
|
||||
/** @{ */
|
||||
linkaddr_t *nbr_table_get_lladdr(nbr_table_t *table, const nbr_table_item_t *item);
|
||||
int nbr_table_update_lladdr(const linkaddr_t *old_addr, const linkaddr_t *new_addr, int remove_if_duplicate);
|
||||
/** @} */
|
||||
|
||||
#endif /* NBR_TABLE_H_ */
|
||||
|
|
65
core/net/net-debug.c
Normal file
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 "net/packetbuf.h"
|
||||
#include "net/rime/rime.h"
|
||||
#include "sys/cc.h"
|
||||
|
||||
struct packetbuf_attr packetbuf_attrs[PACKETBUF_NUM_ATTRS];
|
||||
struct packetbuf_addr packetbuf_addrs[PACKETBUF_NUM_ADDRS];
|
||||
|
||||
|
||||
static uint16_t buflen, bufptr;
|
||||
static uint8_t hdrptr;
|
||||
static uint8_t hdrlen;
|
||||
|
||||
/* The declarations below ensure that the packet buffer is aligned on
|
||||
an even 32-bit boundary. On some platforms (most notably the
|
||||
msp430 or OpenRISC), having a potentially misaligned packet buffer may lead to
|
||||
problems when accessing words. */
|
||||
static uint32_t packetbuf_aligned[(PACKETBUF_SIZE + PACKETBUF_HDR_SIZE + 3) / 4];
|
||||
static uint32_t packetbuf_aligned[(PACKETBUF_SIZE + 3) / 4];
|
||||
static uint8_t *packetbuf = (uint8_t *)packetbuf_aligned;
|
||||
|
||||
static uint8_t *packetbufptr;
|
||||
|
||||
#define DEBUG 0
|
||||
#if DEBUG
|
||||
#include <stdio.h>
|
||||
|
@ -77,26 +76,19 @@ void
|
|||
packetbuf_clear(void)
|
||||
{
|
||||
buflen = bufptr = 0;
|
||||
hdrptr = PACKETBUF_HDR_SIZE;
|
||||
hdrlen = 0;
|
||||
|
||||
packetbufptr = &packetbuf[PACKETBUF_HDR_SIZE];
|
||||
packetbuf_attr_clear();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
packetbuf_clear_hdr(void)
|
||||
{
|
||||
hdrptr = PACKETBUF_HDR_SIZE;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
packetbuf_copyfrom(const void *from, uint16_t len)
|
||||
{
|
||||
uint16_t l;
|
||||
|
||||
packetbuf_clear();
|
||||
l = len > PACKETBUF_SIZE? PACKETBUF_SIZE: len;
|
||||
memcpy(packetbufptr, from, l);
|
||||
l = MIN(PACKETBUF_SIZE, len);
|
||||
memcpy(packetbuf, from, l);
|
||||
buflen = l;
|
||||
return l;
|
||||
}
|
||||
|
@ -104,82 +96,43 @@ packetbuf_copyfrom(const void *from, uint16_t len)
|
|||
void
|
||||
packetbuf_compact(void)
|
||||
{
|
||||
int i, len;
|
||||
int16_t i;
|
||||
|
||||
if(bufptr > 0) {
|
||||
len = packetbuf_datalen() + PACKETBUF_HDR_SIZE;
|
||||
for(i = PACKETBUF_HDR_SIZE; i < len; i++) {
|
||||
packetbuf[i] = packetbuf[bufptr + i];
|
||||
if(bufptr) {
|
||||
/* shift data to the left */
|
||||
for(i = 0; i < buflen; i++) {
|
||||
packetbuf[hdrlen + i] = packetbuf[packetbuf_hdrlen() + i];
|
||||
}
|
||||
|
||||
bufptr = 0;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
packetbuf_copyto_hdr(uint8_t *to)
|
||||
{
|
||||
#if DEBUG_LEVEL > 0
|
||||
{
|
||||
int i;
|
||||
PRINTF("packetbuf_write_hdr: header:\n");
|
||||
for(i = hdrptr; i < PACKETBUF_HDR_SIZE; ++i) {
|
||||
PRINTF("0x%02x, ", packetbuf[i]);
|
||||
}
|
||||
PRINTF("\n");
|
||||
}
|
||||
#endif /* DEBUG_LEVEL */
|
||||
memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);
|
||||
return PACKETBUF_HDR_SIZE - hdrptr;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
packetbuf_copyto(void *to)
|
||||
{
|
||||
#if DEBUG_LEVEL > 0
|
||||
{
|
||||
int i;
|
||||
char buffer[1000];
|
||||
char *bufferptr = buffer;
|
||||
int bufferlen = 0;
|
||||
|
||||
bufferptr[0] = 0;
|
||||
for(i = hdrptr; i < PACKETBUF_HDR_SIZE; ++i) {
|
||||
bufferptr += sprintf(bufferptr, "0x%02x, ", packetbuf[i]);
|
||||
}
|
||||
PRINTF("packetbuf_write: header: %s\n", buffer);
|
||||
bufferptr = buffer;
|
||||
bufferptr[0] = 0;
|
||||
for(i = bufptr; ((i < buflen + bufptr) && (bufferlen < (sizeof(buffer) - 10))); ++i) {
|
||||
bufferlen += sprintf(bufferptr + bufferlen, "0x%02x, ", packetbufptr[i]);
|
||||
}
|
||||
PRINTF("packetbuf_write: data: %s\n", buffer);
|
||||
}
|
||||
#endif /* DEBUG_LEVEL */
|
||||
if(PACKETBUF_HDR_SIZE - hdrptr + buflen > PACKETBUF_SIZE) {
|
||||
/* Too large packet */
|
||||
if(hdrlen + buflen > PACKETBUF_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(to, packetbuf + hdrptr, PACKETBUF_HDR_SIZE - hdrptr);
|
||||
memcpy((uint8_t *)to + PACKETBUF_HDR_SIZE - hdrptr, packetbufptr + bufptr,
|
||||
buflen);
|
||||
return PACKETBUF_HDR_SIZE - hdrptr + buflen;
|
||||
memcpy(to, packetbuf_hdrptr(), hdrlen);
|
||||
memcpy((uint8_t *)to + hdrlen, packetbuf_dataptr(), buflen);
|
||||
return hdrlen + buflen;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
packetbuf_hdralloc(int size)
|
||||
{
|
||||
if(hdrptr >= size && packetbuf_totlen() + size <= PACKETBUF_SIZE) {
|
||||
hdrptr -= size;
|
||||
return 1;
|
||||
}
|
||||
int16_t i;
|
||||
|
||||
if(size + packetbuf_totlen() > PACKETBUF_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
packetbuf_hdr_remove(int size)
|
||||
{
|
||||
hdrptr += size;
|
||||
|
||||
/* shift data to the right */
|
||||
for(i = packetbuf_totlen() - 1; i >= 0; i--) {
|
||||
packetbuf[i + size] = packetbuf[i];
|
||||
}
|
||||
hdrlen += size;
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
|
@ -204,13 +157,13 @@ packetbuf_set_datalen(uint16_t len)
|
|||
void *
|
||||
packetbuf_dataptr(void)
|
||||
{
|
||||
return (void *)(&packetbuf[bufptr + PACKETBUF_HDR_SIZE]);
|
||||
return packetbuf + packetbuf_hdrlen();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void *
|
||||
packetbuf_hdrptr(void)
|
||||
{
|
||||
return (void *)(&packetbuf[hdrptr]);
|
||||
return packetbuf;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uint16_t
|
||||
|
@ -222,16 +175,7 @@ packetbuf_datalen(void)
|
|||
uint8_t
|
||||
packetbuf_hdrlen(void)
|
||||
{
|
||||
uint8_t hdrlen;
|
||||
|
||||
hdrlen = PACKETBUF_HDR_SIZE - hdrptr;
|
||||
if(hdrlen) {
|
||||
/* outbound packet */
|
||||
return hdrlen;
|
||||
} else {
|
||||
/* inbound packet */
|
||||
return bufptr;
|
||||
}
|
||||
return bufptr + hdrlen;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uint16_t
|
||||
|
@ -244,9 +188,7 @@ void
|
|||
packetbuf_attr_clear(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < PACKETBUF_NUM_ATTRS; ++i) {
|
||||
packetbuf_attrs[i].val = 0;
|
||||
}
|
||||
memset(packetbuf_attrs, 0, sizeof(packetbuf_attrs));
|
||||
for(i = 0; i < PACKETBUF_NUM_ADDRS; ++i) {
|
||||
linkaddr_copy(&packetbuf_addrs[i].addr, &linkaddr_null);
|
||||
}
|
||||
|
@ -272,7 +214,6 @@ packetbuf_attr_copyfrom(struct packetbuf_attr *attrs,
|
|||
int
|
||||
packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
|
||||
{
|
||||
/* packetbuf_attrs[type].type = type; */
|
||||
packetbuf_attrs[type].val = val;
|
||||
return 1;
|
||||
}
|
||||
|
@ -286,7 +227,6 @@ packetbuf_attr(uint8_t type)
|
|||
int
|
||||
packetbuf_set_addr(uint8_t type, const linkaddr_t *addr)
|
||||
{
|
||||
/* packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].type = type; */
|
||||
linkaddr_copy(&packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr, addr);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -66,15 +66,6 @@
|
|||
#define PACKETBUF_SIZE 128
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief The size of the packetbuf header, in bytes
|
||||
*/
|
||||
#ifdef PACKETBUF_CONF_HDR_SIZE
|
||||
#define PACKETBUF_HDR_SIZE PACKETBUF_CONF_HDR_SIZE
|
||||
#else
|
||||
#define PACKETBUF_HDR_SIZE 48
|
||||
#endif
|
||||
|
||||
#ifdef PACKETBUF_CONF_WITH_PACKET_TYPE
|
||||
#define PACKETBUF_WITH_PACKET_TYPE PACKETBUF_CONF_WITH_PACKET_TYPE
|
||||
#else
|
||||
|
@ -92,21 +83,6 @@
|
|||
*/
|
||||
void packetbuf_clear(void);
|
||||
|
||||
/**
|
||||
* \brief Clear and reset the header of the packetbuf
|
||||
*
|
||||
* This function clears the header of the packetbuf and
|
||||
* resets all the internal state pointers pertaining to
|
||||
* the header (header size, header pointer, but not
|
||||
* external data pointer). It is used before after sending
|
||||
* a packet in the packetbuf, to be able to reuse the
|
||||
* packet buffer for a later retransmission.
|
||||
*
|
||||
*/
|
||||
void packetbuf_clear_hdr(void);
|
||||
|
||||
void packetbuf_hdr_remove(int bytes);
|
||||
|
||||
/**
|
||||
* \brief Get a pointer to the data in the packetbuf
|
||||
* \return Pointer to the packetbuf data
|
||||
|
@ -115,15 +91,6 @@ void packetbuf_hdr_remove(int bytes);
|
|||
* the packetbuf. The data is either stored in the packetbuf,
|
||||
* or referenced to an external location.
|
||||
*
|
||||
* For outbound packets, the packetbuf consists of two
|
||||
* parts: header and data. The header is accessed with the
|
||||
* packetbuf_hdrptr() function.
|
||||
*
|
||||
* For incoming packets, both the packet header and the
|
||||
* packet data is stored in the data portion of the
|
||||
* packetbuf. Thus this function is used to get a pointer to
|
||||
* the header for incoming packets.
|
||||
*
|
||||
*/
|
||||
void *packetbuf_dataptr(void);
|
||||
|
||||
|
@ -131,11 +98,6 @@ void *packetbuf_dataptr(void);
|
|||
* \brief Get a pointer to the header in the packetbuf, for outbound packets
|
||||
* \return Pointer to the packetbuf header
|
||||
*
|
||||
* For outbound packets, the packetbuf consists of two
|
||||
* parts: header and data. This function is used to get a
|
||||
* pointer to the header in the packetbuf. The header is
|
||||
* stored in the packetbuf.
|
||||
*
|
||||
*/
|
||||
void *packetbuf_hdrptr(void);
|
||||
|
||||
|
@ -143,12 +105,6 @@ void *packetbuf_hdrptr(void);
|
|||
* \brief Get the length of the header in the packetbuf
|
||||
* \return Length of the header in the packetbuf
|
||||
*
|
||||
* For outbound packets, the packetbuf consists of two
|
||||
* parts: header and data. This function is used to get
|
||||
* the length of the header in the packetbuf. The header is
|
||||
* stored in the packetbuf and accessed via the
|
||||
* packetbuf_hdrptr() function.
|
||||
*
|
||||
*/
|
||||
uint8_t packetbuf_hdrlen(void);
|
||||
|
||||
|
@ -157,17 +113,6 @@ uint8_t packetbuf_hdrlen(void);
|
|||
* \brief Get the length of the data in the packetbuf
|
||||
* \return Length of the data in the packetbuf
|
||||
*
|
||||
* For outbound packets, the packetbuf consists of two
|
||||
* parts: header and data. This function is used to get
|
||||
* the length of the data in the packetbuf. The data is
|
||||
* stored in the packetbuf and accessed via the
|
||||
* packetbuf_dataptr() function.
|
||||
*
|
||||
* For incoming packets, both the packet header and the
|
||||
* packet data is stored in the data portion of the
|
||||
* packetbuf. This function is then used to get the total
|
||||
* length of the packet - both header and data.
|
||||
*
|
||||
*/
|
||||
uint16_t packetbuf_datalen(void);
|
||||
|
||||
|
@ -181,10 +126,6 @@ uint16_t packetbuf_totlen(void);
|
|||
/**
|
||||
* \brief Set the length of the data in the packetbuf
|
||||
* \param len The length of the data
|
||||
*
|
||||
* For outbound packets, the packetbuf consists of two
|
||||
* parts: header and data. This function is used to set
|
||||
* the length of the data in the packetbuf.
|
||||
*/
|
||||
void packetbuf_set_datalen(uint16_t len);
|
||||
|
||||
|
@ -228,29 +169,13 @@ int packetbuf_copyfrom(const void *from, uint16_t len);
|
|||
*
|
||||
* The external buffer to which the packetbuf is to be
|
||||
* copied must be able to accomodate at least
|
||||
* (PACKETBUF_SIZE + PACKETBUF_HDR_SIZE) bytes. The number of
|
||||
* PACKETBUF_SIZE bytes. The number of
|
||||
* bytes that was copied to the external buffer is
|
||||
* returned.
|
||||
*
|
||||
*/
|
||||
int packetbuf_copyto(void *to);
|
||||
|
||||
/**
|
||||
* \brief Copy the header portion of the packetbuf to an external buffer
|
||||
* \param to A pointer to the buffer to which the data is to be copied
|
||||
* \retval The number of bytes that was copied to the external buffer
|
||||
*
|
||||
* This function copies the header portion of the packetbuf
|
||||
* to an external buffer.
|
||||
*
|
||||
* The external buffer to which the packetbuf is to be
|
||||
* copied must be able to accomodate at least
|
||||
* PACKETBUF_HDR_SIZE bytes. The number of bytes that was
|
||||
* copied to the external buffer is returned.
|
||||
*
|
||||
*/
|
||||
int packetbuf_copyto_hdr(uint8_t *to);
|
||||
|
||||
/**
|
||||
* \brief Extend the header of the packetbuf, for outbound packets
|
||||
* \param size The number of bytes the header should be extended
|
||||
|
@ -284,11 +209,9 @@ int packetbuf_hdrreduce(int size);
|
|||
typedef uint16_t packetbuf_attr_t;
|
||||
|
||||
struct packetbuf_attr {
|
||||
/* uint8_t type; */
|
||||
packetbuf_attr_t val;
|
||||
};
|
||||
struct packetbuf_addr {
|
||||
/* uint8_t type; */
|
||||
linkaddr_t addr;
|
||||
};
|
||||
|
||||
|
@ -332,9 +255,9 @@ enum {
|
|||
#endif /* NETSTACK_CONF_WITH_RIME */
|
||||
PACKETBUF_ATTR_PENDING,
|
||||
PACKETBUF_ATTR_FRAME_TYPE,
|
||||
#if LLSEC802154_SECURITY_LEVEL
|
||||
#if LLSEC802154_USES_AUX_HEADER
|
||||
PACKETBUF_ATTR_SECURITY_LEVEL,
|
||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER */
|
||||
#if LLSEC802154_USES_FRAME_COUNTER
|
||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
|
||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3,
|
||||
|
@ -365,29 +288,6 @@ enum {
|
|||
PACKETBUF_ATTR_MAX
|
||||
};
|
||||
|
||||
/* Define surrogates when 802.15.4 security is off */
|
||||
#if !LLSEC802154_SECURITY_LEVEL
|
||||
enum {
|
||||
PACKETBUF_ATTR_SECURITY_LEVEL,
|
||||
};
|
||||
#endif /* LLSEC802154_SECURITY_LEVEL */
|
||||
|
||||
#if !LLSEC802154_USES_FRAME_COUNTER
|
||||
enum {
|
||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_0_1,
|
||||
PACKETBUF_ATTR_FRAME_COUNTER_BYTES_2_3
|
||||
};
|
||||
#endif /* LLSEC802154_USES_FRAME_COUNTER */
|
||||
|
||||
/* Define surrogates when not using explicit keys */
|
||||
#if !LLSEC802154_USES_EXPLICIT_KEYS
|
||||
enum {
|
||||
PACKETBUF_ATTR_KEY_ID_MODE,
|
||||
PACKETBUF_ATTR_KEY_INDEX,
|
||||
PACKETBUF_ATTR_KEY_SOURCE_BYTES_0_1
|
||||
};
|
||||
#endif /* LLSEC802154_USES_EXPLICIT_KEYS */
|
||||
|
||||
#if NETSTACK_CONF_WITH_RIME
|
||||
#define PACKETBUF_NUM_ADDRS 4
|
||||
#else /* NETSTACK_CONF_WITH_RIME */
|
||||
|
@ -403,15 +303,9 @@ enum {
|
|||
extern struct packetbuf_attr packetbuf_attrs[];
|
||||
extern struct packetbuf_addr packetbuf_addrs[];
|
||||
|
||||
static int packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val);
|
||||
static packetbuf_attr_t packetbuf_attr(uint8_t type);
|
||||
static int packetbuf_set_addr(uint8_t type, const linkaddr_t *addr);
|
||||
static const linkaddr_t *packetbuf_addr(uint8_t type);
|
||||
|
||||
static inline int
|
||||
packetbuf_set_attr(uint8_t type, const packetbuf_attr_t val)
|
||||
{
|
||||
/* packetbuf_attrs[type].type = type; */
|
||||
packetbuf_attrs[type].val = val;
|
||||
return 1;
|
||||
}
|
||||
|
@ -424,7 +318,6 @@ packetbuf_attr(uint8_t type)
|
|||
static inline int
|
||||
packetbuf_set_addr(uint8_t type, const linkaddr_t *addr)
|
||||
{
|
||||
/* packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].type = type; */
|
||||
linkaddr_copy(&packetbuf_addrs[type - PACKETBUF_ADDR_FIRST].addr, addr);
|
||||
return 1;
|
||||
}
|
||||
|
|
5
core/net/rime/chameleon-raw.c
Normal file → Executable file
5
core/net/rime/chameleon-raw.c
Normal file → Executable file
|
@ -205,10 +205,7 @@ hdrsize(const struct packetbuf_attrlist *a)
|
|||
continue;
|
||||
}
|
||||
#endif /* CHAMELEON_WITH_MAC_LINK_ADDRESSES */
|
||||
len = a->len;
|
||||
if(len < 8) {
|
||||
len = 8;
|
||||
}
|
||||
len = (a->len & 0xf8) + ((a->len & 7) ? 8: 0);
|
||||
size += len;
|
||||
}
|
||||
return size / 8;
|
||||
|
|
|
@ -46,30 +46,51 @@
|
|||
#endif /* RPL_CONF_STATS */
|
||||
|
||||
/*
|
||||
* Select routing metric supported at runtime. This must be a valid
|
||||
* DAG Metric Container Object Type (see below). Currently, we only
|
||||
* support RPL_DAG_MC_ETX and RPL_DAG_MC_ENERGY.
|
||||
* When MRHOF (RFC6719) is used with ETX, no metric container must
|
||||
* be used; instead the rank carries ETX directly.
|
||||
* The objective function (OF) used by a RPL root is configurable through
|
||||
* the RPL_CONF_OF_OCP parameter. This is defined as the objective code
|
||||
* point (OCP) of the OF, RPL_OCP_OF0 or RPL_OCP_MRHOF. This flag is of
|
||||
* no relevance to non-root nodes, which run the OF advertised in the
|
||||
* instance they join.
|
||||
* Make sure the selected of is inRPL_SUPPORTED_OFS.
|
||||
*/
|
||||
#ifdef RPL_CONF_OF_OCP
|
||||
#define RPL_OF_OCP RPL_CONF_OF_OCP
|
||||
#else /* RPL_CONF_OF_OCP */
|
||||
#define RPL_OF_OCP RPL_OCP_MRHOF
|
||||
#endif /* RPL_CONF_OF_OCP */
|
||||
|
||||
/*
|
||||
* The set of objective functions supported at runtime. Nodes are only
|
||||
* able to join instances that advertise an OF in this set. To include
|
||||
* both OF0 and MRHOF, use {&rpl_of0, &rpl_mrhof}.
|
||||
*/
|
||||
#ifdef RPL_CONF_SUPPORTED_OFS
|
||||
#define RPL_SUPPORTED_OFS RPL_CONF_SUPPORTED_OFS
|
||||
#else /* RPL_CONF_SUPPORTED_OFS */
|
||||
#define RPL_SUPPORTED_OFS {&rpl_mrhof}
|
||||
#endif /* RPL_CONF_SUPPORTED_OFS */
|
||||
|
||||
/*
|
||||
* Enable/disable RPL Metric Containers (MC). The actual MC in use
|
||||
* for a given DODAG is decided at runtime, when joining. Note that
|
||||
* OF0 (RFC6552) operates without MC, and so does MRHOF (RFC6719) when
|
||||
* used with ETX as a metric (the rank is the metric). We disable MC
|
||||
* by default, but note it must be enabled to support joining a DODAG
|
||||
* that requires MC (e.g., MRHOF with a metric other than ETX).
|
||||
*/
|
||||
#ifdef RPL_CONF_WITH_MC
|
||||
#define RPL_WITH_MC RPL_CONF_WITH_MC
|
||||
#else /* RPL_CONF_WITH_MC */
|
||||
#define RPL_WITH_MC 0
|
||||
#endif /* RPL_CONF_WITH_MC */
|
||||
|
||||
/* The MC advertised in DIOs and propagating from the root */
|
||||
#ifdef RPL_CONF_DAG_MC
|
||||
#define RPL_DAG_MC RPL_CONF_DAG_MC
|
||||
#else
|
||||
#define RPL_DAG_MC RPL_DAG_MC_NONE
|
||||
#endif /* RPL_CONF_DAG_MC */
|
||||
|
||||
/*
|
||||
* The objective function used by RPL is configurable through the
|
||||
* RPL_CONF_OF parameter. This should be defined to be the name of an
|
||||
* rpl_of object linked into the system image, e.g., rpl_of0.
|
||||
*/
|
||||
#ifdef RPL_CONF_OF
|
||||
#define RPL_OF RPL_CONF_OF
|
||||
#else
|
||||
/* ETX is the default objective function. */
|
||||
#define RPL_OF rpl_mrhof
|
||||
#endif /* RPL_CONF_OF */
|
||||
|
||||
/* This value decides which DAG instance we should participate in by default. */
|
||||
#ifdef RPL_CONF_DEFAULT_INSTANCE
|
||||
#define RPL_DEFAULT_INSTANCE RPL_CONF_DEFAULT_INSTANCE
|
||||
|
@ -118,7 +139,7 @@
|
|||
#ifdef RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
|
||||
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
|
||||
#else
|
||||
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 0
|
||||
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 1
|
||||
#endif /* RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME */
|
||||
|
||||
/*
|
||||
|
@ -186,21 +207,12 @@
|
|||
#define RPL_DIO_REDUNDANCY 10
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initial metric attributed to a link when the ETX is unknown
|
||||
*/
|
||||
#ifndef RPL_CONF_INIT_LINK_METRIC
|
||||
#define RPL_INIT_LINK_METRIC 2
|
||||
#else
|
||||
#define RPL_INIT_LINK_METRIC RPL_CONF_INIT_LINK_METRIC
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default route lifetime unit. This is the granularity of time
|
||||
* used in RPL lifetime values, in seconds.
|
||||
*/
|
||||
#ifndef RPL_CONF_DEFAULT_LIFETIME_UNIT
|
||||
#define RPL_DEFAULT_LIFETIME_UNIT 0xffff
|
||||
#define RPL_DEFAULT_LIFETIME_UNIT 60
|
||||
#else
|
||||
#define RPL_DEFAULT_LIFETIME_UNIT RPL_CONF_DEFAULT_LIFETIME_UNIT
|
||||
#endif
|
||||
|
@ -209,7 +221,7 @@
|
|||
* Default route lifetime as a multiple of the lifetime unit.
|
||||
*/
|
||||
#ifndef RPL_CONF_DEFAULT_LIFETIME
|
||||
#define RPL_DEFAULT_LIFETIME 0xff
|
||||
#define RPL_DEFAULT_LIFETIME 30
|
||||
#else
|
||||
#define RPL_DEFAULT_LIFETIME RPL_CONF_DEFAULT_LIFETIME
|
||||
#endif
|
||||
|
@ -224,16 +236,38 @@
|
|||
#endif
|
||||
|
||||
/*
|
||||
* Hop-by-hop option
|
||||
* This option control the insertion of the RPL Hop-by-Hop extension header
|
||||
* into packets originating from this node. Incoming Hop-by-hop extension
|
||||
* header are still processed and forwarded.
|
||||
*/
|
||||
#ifdef RPL_CONF_INSERT_HBH_OPTION
|
||||
#define RPL_INSERT_HBH_OPTION RPL_CONF_INSERT_HBH_OPTION
|
||||
* RPL DAO ACK support. When enabled, DAO ACK will be sent and requested.
|
||||
* This will also enable retransmission of DAO when no ack is received.
|
||||
* */
|
||||
#ifdef RPL_CONF_WITH_DAO_ACK
|
||||
#define RPL_WITH_DAO_ACK RPL_CONF_WITH_DAO_ACK
|
||||
#else
|
||||
#define RPL_INSERT_HBH_OPTION 1
|
||||
#endif
|
||||
#define RPL_WITH_DAO_ACK 0
|
||||
#endif /* RPL_CONF_WITH_DAO_ACK */
|
||||
|
||||
/*
|
||||
* RPL REPAIR ON DAO NACK. When enabled, DAO NACK will trigger a local
|
||||
* repair in order to quickly find a new parent to send DAO's to.
|
||||
* NOTE: this is too agressive in some cases so use with care.
|
||||
* */
|
||||
#ifdef RPL_CONF_RPL_REPAIR_ON_DAO_NACK
|
||||
#define RPL_REPAIR_ON_DAO_NACK RPL_CONF_RPL_REPAIR_ON_DAO_NACK
|
||||
#else
|
||||
#define RPL_REPAIR_ON_DAO_NACK 0
|
||||
#endif /* RPL_CONF_RPL_REPAIR_ON_DAO_NACK */
|
||||
|
||||
/*
|
||||
* Setting the DIO_REFRESH_DAO_ROUTES will make the RPL root always
|
||||
* increase the DTSN (Destination Advertisement Trigger Sequence Number)
|
||||
* when sending multicast DIO. This is to get all children to re-register
|
||||
* their DAO route. This is needed when DAO-ACK is not enabled to add
|
||||
* reliability to route maintenance.
|
||||
* */
|
||||
#ifdef RPL_CONF_DIO_REFRESH_DAO_ROUTES
|
||||
#define RPL_DIO_REFRESH_DAO_ROUTES RPL_CONF_DIO_REFRESH_DAO_ROUTES
|
||||
#else
|
||||
#define RPL_DIO_REFRESH_DAO_ROUTES 1
|
||||
#endif /* RPL_CONF_DIO_REFRESH_DAO_ROUTES */
|
||||
|
||||
/*
|
||||
* RPL probing. When enabled, probes will be sent periodically to keep
|
||||
|
@ -253,23 +287,12 @@
|
|||
#else
|
||||
#define RPL_PROBING_INTERVAL (120 * CLOCK_SECOND)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* RPL probing expiration time.
|
||||
*/
|
||||
#ifdef RPL_CONF_PROBING_EXPIRATION_TIME
|
||||
#define RPL_PROBING_EXPIRATION_TIME RPL_CONF_PROBING_EXPIRATION_TIME
|
||||
#else
|
||||
#define RPL_PROBING_EXPIRATION_TIME (10L * 60 * CLOCK_SECOND)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function used to select the next parent to be probed.
|
||||
*/
|
||||
#ifdef RPL_CONF_PROBING_SELECT_FUNC
|
||||
#define RPL_PROBING_SELECT_FUNC RPL_CONF_PROBING_SELECT_FUNC
|
||||
#else
|
||||
#define RPL_PROBING_SELECT_FUNC(dag) get_probing_target((dag))
|
||||
#define RPL_PROBING_SELECT_FUNC get_probing_target
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -292,8 +315,7 @@
|
|||
#ifdef RPL_CONF_PROBING_DELAY_FUNC
|
||||
#define RPL_PROBING_DELAY_FUNC RPL_CONF_PROBING_DELAY_FUNC
|
||||
#else
|
||||
#define RPL_PROBING_DELAY_FUNC() ((RPL_PROBING_INTERVAL / 2) \
|
||||
+ random_rand() % (RPL_PROBING_INTERVAL))
|
||||
#define RPL_PROBING_DELAY_FUNC get_probing_delay
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
@ -43,7 +43,9 @@
|
|||
|
||||
#define RPL_DAG_GRACE_PERIOD (CLOCK_SECOND * 20 * 1)
|
||||
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
static struct uip_ds6_notification n;
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
static uint8_t to_become_root;
|
||||
static struct ctimer c;
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -121,6 +123,7 @@ create_dag_callback(void *ptr)
|
|||
ctimer_set(&c, RPL_DAG_GRACE_PERIOD, create_dag_callback, NULL);
|
||||
}
|
||||
}
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *ipaddr,
|
||||
|
@ -136,6 +139,7 @@ route_callback(int event, uip_ipaddr_t *route, uip_ipaddr_t *ipaddr,
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static uip_ipaddr_t *
|
||||
set_global_address(void)
|
||||
|
@ -146,7 +150,7 @@ set_global_address(void)
|
|||
|
||||
/* Assign a unique local address (RFC4193,
|
||||
http://tools.ietf.org/html/rfc4193). */
|
||||
uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0);
|
||||
uip_ip6addr(&ipaddr, UIP_DS6_DEFAULT_PREFIX, 0, 0, 0, 0, 0, 0, 0);
|
||||
uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr);
|
||||
uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF);
|
||||
|
||||
|
@ -171,7 +175,9 @@ rpl_dag_root_init(void)
|
|||
if(!initialized) {
|
||||
to_become_root = 0;
|
||||
set_global_address();
|
||||
#if (UIP_CONF_MAX_ROUTES != 0)
|
||||
uip_ds6_notification_add(&n, route_callback);
|
||||
#endif /* (UIP_CONF_MAX_ROUTES != 0) */
|
||||
initialized = 1;
|
||||
}
|
||||
}
|
||||
|
@ -206,14 +212,16 @@ rpl_dag_root_init_dag_immediately(void)
|
|||
|
||||
/* If there are routes in this dag, we remove them all as we are
|
||||
from now on the new dag root and the old routes are wrong */
|
||||
if(RPL_IS_STORING(dag->instance)) {
|
||||
rpl_remove_routes(dag);
|
||||
}
|
||||
if(dag->instance != NULL &&
|
||||
dag->instance->def_route != NULL) {
|
||||
uip_ds6_defrt_rm(dag->instance->def_route);
|
||||
dag->instance->def_route = NULL;
|
||||
}
|
||||
|
||||
uip_ip6addr(&prefix, 0xaaaa, 0, 0, 0, 0, 0, 0, 0);
|
||||
uip_ip6addr(&prefix, UIP_DS6_DEFAULT_PREFIX, 0, 0, 0, 0, 0, 0, 0);
|
||||
rpl_set_prefix(dag, &prefix, 64);
|
||||
PRINTF("rpl_dag_root_init_dag: created a new RPL dag\n");
|
||||
return 0;
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "net/link-stats.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/ip/uip.h"
|
||||
#include "net/ipv6/uip-nd6.h"
|
||||
|
@ -66,8 +67,8 @@ void RPL_CALLBACK_PARENT_SWITCH(rpl_parent_t *old, rpl_parent_t *new);
|
|||
#endif /* RPL_CALLBACK_PARENT_SWITCH */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
extern rpl_of_t RPL_OF;
|
||||
static rpl_of_t * const objective_functions[] = {&RPL_OF};
|
||||
extern rpl_of_t rpl_of0, rpl_mrhof;
|
||||
static rpl_of_t * const objective_functions[] = RPL_SUPPORTED_OFS;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* RPL definitions. */
|
||||
|
@ -88,25 +89,29 @@ rpl_instance_t *default_instance;
|
|||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_print_neighbor_list()
|
||||
rpl_print_neighbor_list(void)
|
||||
{
|
||||
if(default_instance != NULL && default_instance->current_dag != NULL &&
|
||||
default_instance->of != NULL && default_instance->of->calculate_rank != NULL) {
|
||||
default_instance->of != NULL) {
|
||||
int curr_dio_interval = default_instance->dio_intcurrent;
|
||||
int curr_rank = default_instance->current_dag->rank;
|
||||
rpl_parent_t *p = nbr_table_head(rpl_parents);
|
||||
clock_time_t now = clock_time();
|
||||
clock_time_t clock_now = clock_time();
|
||||
|
||||
printf("RPL: rank %u dioint %u, %u nbr(s)\n", curr_rank, curr_dio_interval, uip_ds6_nbr_num());
|
||||
printf("RPL: MOP %u OCP %u rank %u dioint %u, nbr count %u\n",
|
||||
default_instance->mop, default_instance->of->ocp, curr_rank, curr_dio_interval, uip_ds6_nbr_num());
|
||||
while(p != NULL) {
|
||||
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
|
||||
printf("RPL: nbr %3u %5u, %5u => %5u %c%c (last tx %u min ago)\n",
|
||||
nbr_table_get_lladdr(rpl_parents, p)->u8[7],
|
||||
p->rank, nbr ? nbr->link_metric : 0,
|
||||
default_instance->of->calculate_rank(p, 0),
|
||||
default_instance->current_dag == p->dag ? 'd' : ' ',
|
||||
p == default_instance->current_dag->preferred_parent ? '*' : ' ',
|
||||
(unsigned)((now - p->last_tx_time) / (60 * CLOCK_SECOND)));
|
||||
const struct link_stats *stats = rpl_get_parent_link_stats(p);
|
||||
printf("RPL: nbr %3u %5u, %5u => %5u -- %2u %c%c (last tx %u min ago)\n",
|
||||
rpl_get_parent_ipaddr(p)->u8[15],
|
||||
p->rank,
|
||||
rpl_get_parent_link_metric(p),
|
||||
rpl_rank_via_parent(p),
|
||||
stats != NULL ? stats->freshness : 0,
|
||||
link_stats_is_fresh(stats) ? 'f' : ' ',
|
||||
p == default_instance->current_dag->preferred_parent ? 'p' : ' ',
|
||||
(unsigned)((clock_now - stats->last_tx_time) / (60 * CLOCK_SECOND))
|
||||
);
|
||||
p = nbr_table_next(rpl_parents, p);
|
||||
}
|
||||
printf("RPL: end of list\n");
|
||||
|
@ -116,8 +121,7 @@ rpl_print_neighbor_list()
|
|||
uip_ds6_nbr_t *
|
||||
rpl_get_nbr(rpl_parent_t *parent)
|
||||
{
|
||||
linkaddr_t *lladdr = NULL;
|
||||
lladdr = nbr_table_get_lladdr(rpl_parents, parent);
|
||||
const linkaddr_t *lladdr = rpl_get_parent_lladdr(parent);
|
||||
if(lladdr != NULL) {
|
||||
return nbr_table_get_from_lladdr(ds6_neighbors, lladdr);
|
||||
} else {
|
||||
|
@ -151,30 +155,78 @@ rpl_get_parent_rank(uip_lladdr_t *addr)
|
|||
if(p != NULL) {
|
||||
return p->rank;
|
||||
} else {
|
||||
return 0;
|
||||
return INFINITE_RANK;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uint16_t
|
||||
rpl_get_parent_link_metric(const uip_lladdr_t *addr)
|
||||
rpl_get_parent_link_metric(rpl_parent_t *p)
|
||||
{
|
||||
uip_ds6_nbr_t *nbr;
|
||||
nbr = nbr_table_get_from_lladdr(ds6_neighbors, (const linkaddr_t *)addr);
|
||||
|
||||
if(nbr != NULL) {
|
||||
return nbr->link_metric;
|
||||
} else {
|
||||
return 0;
|
||||
if(p != NULL && p->dag != NULL) {
|
||||
rpl_instance_t *instance = p->dag->instance;
|
||||
if(instance != NULL && instance->of != NULL && instance->of->parent_link_metric != NULL) {
|
||||
return instance->of->parent_link_metric(p);
|
||||
}
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_rank_t
|
||||
rpl_rank_via_parent(rpl_parent_t *p)
|
||||
{
|
||||
if(p != NULL && p->dag != NULL) {
|
||||
rpl_instance_t *instance = p->dag->instance;
|
||||
if(instance != NULL && instance->of != NULL && instance->of->rank_via_parent != NULL) {
|
||||
return instance->of->rank_via_parent(p);
|
||||
}
|
||||
}
|
||||
return INFINITE_RANK;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
const linkaddr_t *
|
||||
rpl_get_parent_lladdr(rpl_parent_t *p)
|
||||
{
|
||||
return nbr_table_get_lladdr(rpl_parents, p);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ipaddr_t *
|
||||
rpl_get_parent_ipaddr(rpl_parent_t *p)
|
||||
{
|
||||
linkaddr_t *lladdr = nbr_table_get_lladdr(rpl_parents, p);
|
||||
const linkaddr_t *lladdr = rpl_get_parent_lladdr(p);
|
||||
return uip_ds6_nbr_ipaddr_from_lladdr((uip_lladdr_t *)lladdr);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
const struct link_stats *
|
||||
rpl_get_parent_link_stats(rpl_parent_t *p)
|
||||
{
|
||||
const linkaddr_t *lladdr = rpl_get_parent_lladdr(p);
|
||||
return link_stats_from_lladdr(lladdr);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_parent_is_fresh(rpl_parent_t *p)
|
||||
{
|
||||
const struct link_stats *stats = rpl_get_parent_link_stats(p);
|
||||
return link_stats_is_fresh(stats);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_parent_is_reachable(rpl_parent_t *p) {
|
||||
if(p == NULL || p->dag == NULL || p->dag->instance == NULL || p->dag->instance->of == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
#ifndef UIP_CONF_ND6_SEND_NA
|
||||
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
|
||||
/* Exclude links to a neighbor that is not reachable at a NUD level */
|
||||
if(nbr == NULL || nbr->state != NBR_REACHABLE) {
|
||||
return 0;
|
||||
}
|
||||
#endif /* UIP_CONF_ND6_SEND_NA */
|
||||
/* If we don't have fresh link information, assume the parent is reachable. */
|
||||
return !rpl_parent_is_fresh(p) || p->dag->instance->of->parent_has_usable_link(p);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
rpl_set_preferred_parent(rpl_dag_t *dag, rpl_parent_t *p)
|
||||
{
|
||||
|
@ -340,7 +392,12 @@ rpl_set_root(uint8_t instance_id, uip_ipaddr_t *dag_id)
|
|||
dag->grounded = RPL_GROUNDED;
|
||||
dag->preference = RPL_PREFERENCE;
|
||||
instance->mop = RPL_MOP_DEFAULT;
|
||||
instance->of = &RPL_OF;
|
||||
instance->of = rpl_find_of(RPL_OF_OCP);
|
||||
if(instance->of == NULL) {
|
||||
PRINTF("RPL: OF with OCP %u not supported\n", RPL_OF_OCP);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rpl_set_preferred_parent(dag, NULL);
|
||||
|
||||
memcpy(&dag->dag_id, dag_id, sizeof(dag->dag_id));
|
||||
|
@ -361,7 +418,9 @@ rpl_set_root(uint8_t instance_id, uip_ipaddr_t *dag_id)
|
|||
|
||||
if(instance->current_dag != dag && instance->current_dag != NULL) {
|
||||
/* Remove routes installed by DAOs. */
|
||||
if(RPL_IS_STORING(instance)) {
|
||||
rpl_remove_routes(instance->current_dag);
|
||||
}
|
||||
|
||||
instance->current_dag->joined = 0;
|
||||
}
|
||||
|
@ -610,7 +669,9 @@ rpl_free_dag(rpl_dag_t *dag)
|
|||
dag->joined = 0;
|
||||
|
||||
/* Remove routes installed by DAOs. */
|
||||
if(RPL_IS_STORING(dag->instance)) {
|
||||
rpl_remove_routes(dag);
|
||||
}
|
||||
|
||||
/* Remove autoconfigured address */
|
||||
if((dag->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) {
|
||||
|
@ -634,25 +695,18 @@ rpl_add_parent(rpl_dag_t *dag, rpl_dio_t *dio, uip_ipaddr_t *addr)
|
|||
PRINT6ADDR(addr);
|
||||
PRINTF("\n");
|
||||
if(lladdr != NULL) {
|
||||
/* Add parent in rpl_parents */
|
||||
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr);
|
||||
/* Add parent in rpl_parents - again this is due to DIO */
|
||||
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr,
|
||||
NBR_TABLE_REASON_RPL_DIO, dio);
|
||||
if(p == NULL) {
|
||||
PRINTF("RPL: rpl_add_parent p NULL\n");
|
||||
} else {
|
||||
uip_ds6_nbr_t *nbr;
|
||||
nbr = rpl_get_nbr(p);
|
||||
|
||||
p->dag = dag;
|
||||
p->rank = dio->rank;
|
||||
p->dtsn = dio->dtsn;
|
||||
|
||||
/* Check whether we have a neighbor that has not gotten a link metric yet */
|
||||
if(nbr != NULL && nbr->link_metric == 0) {
|
||||
nbr->link_metric = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
|
||||
}
|
||||
#if RPL_DAG_MC != RPL_DAG_MC_NONE
|
||||
#if RPL_WITH_MC
|
||||
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
|
||||
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
|
||||
#endif /* RPL_WITH_MC */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -737,7 +791,9 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
|
||||
if(instance->current_dag != best_dag) {
|
||||
/* Remove routes installed by DAOs. */
|
||||
if(RPL_IS_STORING(instance)) {
|
||||
rpl_remove_routes(instance->current_dag);
|
||||
}
|
||||
|
||||
PRINTF("RPL: New preferred DAG: ");
|
||||
PRINT6ADDR(&best_dag->dag_id);
|
||||
|
@ -756,13 +812,17 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
|
||||
instance->of->update_metric_container(instance);
|
||||
/* Update the DAG rank. */
|
||||
best_dag->rank = instance->of->calculate_rank(best_dag->preferred_parent, 0);
|
||||
best_dag->rank = rpl_rank_via_parent(best_dag->preferred_parent);
|
||||
if(last_parent == NULL || best_dag->rank < best_dag->min_rank) {
|
||||
/* This is a slight departure from RFC6550: if we had no preferred parent before,
|
||||
* reset min_rank. This helps recovering from temporary bad link conditions. */
|
||||
best_dag->min_rank = best_dag->rank;
|
||||
} else if(!acceptable_rank(best_dag, best_dag->rank)) {
|
||||
}
|
||||
|
||||
if(!acceptable_rank(best_dag, best_dag->rank)) {
|
||||
PRINTF("RPL: New rank unacceptable!\n");
|
||||
rpl_set_preferred_parent(instance->current_dag, NULL);
|
||||
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES && last_parent != NULL) {
|
||||
if(RPL_IS_STORING(instance) && last_parent != NULL) {
|
||||
/* Send a No-Path DAO to the removed preferred parent. */
|
||||
dao_output(last_parent, RPL_ZERO_LIFETIME);
|
||||
}
|
||||
|
@ -774,15 +834,13 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
PRINTF("RPL: Changed preferred parent, rank changed from %u to %u\n",
|
||||
(unsigned)old_rank, best_dag->rank);
|
||||
RPL_STAT(rpl_stats.parent_switch++);
|
||||
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
|
||||
if(last_parent != NULL) {
|
||||
if(RPL_IS_STORING(instance) && last_parent != NULL) {
|
||||
/* Send a No-Path DAO to the removed preferred parent. */
|
||||
dao_output(last_parent, RPL_ZERO_LIFETIME);
|
||||
}
|
||||
/* The DAO parent set changed - schedule a DAO transmission. */
|
||||
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
|
||||
rpl_schedule_dao(instance);
|
||||
}
|
||||
rpl_reset_dio_timer(instance);
|
||||
#if DEBUG
|
||||
rpl_print_neighbor_list();
|
||||
|
@ -795,22 +853,42 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static rpl_parent_t *
|
||||
best_parent(rpl_dag_t *dag)
|
||||
best_parent(rpl_dag_t *dag, int fresh_only)
|
||||
{
|
||||
rpl_parent_t *p, *best;
|
||||
rpl_parent_t *p;
|
||||
rpl_of_t *of;
|
||||
rpl_parent_t *best = NULL;
|
||||
|
||||
best = NULL;
|
||||
|
||||
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);
|
||||
if(dag == NULL || dag->instance == NULL || dag->instance->of == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
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;
|
||||
|
@ -819,16 +897,37 @@ best_parent(rpl_dag_t *dag)
|
|||
rpl_parent_t *
|
||||
rpl_select_parent(rpl_dag_t *dag)
|
||||
{
|
||||
rpl_parent_t *best = best_parent(dag);
|
||||
/* Look for best parent (regardless of freshness) */
|
||||
rpl_parent_t *best = best_parent(dag, 0);
|
||||
|
||||
if(best != NULL) {
|
||||
#if RPL_WITH_PROBING
|
||||
if(rpl_parent_is_fresh(best)) {
|
||||
rpl_set_preferred_parent(dag, best);
|
||||
dag->rank = dag->instance->of->calculate_rank(dag->preferred_parent, 0);
|
||||
} 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
|
||||
|
@ -859,9 +958,11 @@ rpl_nullify_parent(rpl_parent_t *parent)
|
|||
uip_ds6_defrt_rm(dag->instance->def_route);
|
||||
dag->instance->def_route = NULL;
|
||||
}
|
||||
/* Send No-Path DAO only to preferred parent, if any */
|
||||
/* Send No-Path DAO only when nullifying preferred parent */
|
||||
if(parent == dag->preferred_parent) {
|
||||
if(RPL_IS_STORING(dag->instance)) {
|
||||
dao_output(parent, RPL_ZERO_LIFETIME);
|
||||
}
|
||||
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;
|
||||
}
|
||||
} else if(dag_src->joined) {
|
||||
if(RPL_IS_STORING(dag_src->instance)) {
|
||||
/* Remove uIPv6 routes that have this parent as the next hop. */
|
||||
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
|
||||
}
|
||||
}
|
||||
|
||||
PRINTF("RPL: Moving parent ");
|
||||
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;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_has_downward_route(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
|
||||
if(instance_table[i].used && instance_table[i].has_downward_route) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_dag_t *
|
||||
rpl_get_dag(const uip_ipaddr_t *addr)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
|
||||
if(instance_table[i].used) {
|
||||
for(j = 0; j < RPL_MAX_DAG_PER_INSTANCE; ++j) {
|
||||
if(instance_table[i].dag_table[j].joined
|
||||
&& uip_ipaddr_prefixcmp(&instance_table[i].dag_table[j].dag_id, addr,
|
||||
instance_table[i].dag_table[j].prefix_info.length)) {
|
||||
return &instance_table[i].dag_table[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_dag_t *
|
||||
rpl_get_any_dag(void)
|
||||
{
|
||||
|
@ -948,6 +1082,12 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
rpl_parent_t *p;
|
||||
rpl_of_t *of;
|
||||
|
||||
if((!RPL_WITH_NON_STORING && dio->mop == RPL_MOP_NON_STORING)
|
||||
|| (!RPL_WITH_STORING && (dio->mop == RPL_MOP_STORING_NO_MULTICAST
|
||||
|| dio->mop == RPL_MOP_STORING_MULTICAST))) {
|
||||
PRINTF("RPL: DIO advertising a non-supported MOP %u\n", dio->mop);
|
||||
}
|
||||
|
||||
/* Determine the objective function by using the
|
||||
objective code point of the DIO. */
|
||||
of = rpl_find_of(dio->ocp);
|
||||
|
@ -990,6 +1130,10 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
|
||||
instance->of = of;
|
||||
instance->mop = dio->mop;
|
||||
instance->mc.type = dio->mc.type;
|
||||
instance->mc.flags = dio->mc.flags;
|
||||
instance->mc.aggr = dio->mc.aggr;
|
||||
instance->mc.prec = dio->mc.prec;
|
||||
instance->current_dag = dag;
|
||||
instance->dtsn_out = RPL_LOLLIPOP_INIT;
|
||||
|
||||
|
@ -1009,7 +1153,7 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
|
||||
rpl_set_preferred_parent(dag, p);
|
||||
instance->of->update_metric_container(instance);
|
||||
dag->rank = instance->of->calculate_rank(p, 0);
|
||||
dag->rank = rpl_rank_via_parent(p);
|
||||
/* So far this is the lowest rank we are aware of. */
|
||||
dag->min_rank = dag->rank;
|
||||
|
||||
|
@ -1032,6 +1176,8 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
} else {
|
||||
PRINTF("RPL: The DIO does not meet the prerequisites for sending a DAO\n");
|
||||
}
|
||||
|
||||
instance->of->reset(dag);
|
||||
}
|
||||
|
||||
#if RPL_MAX_DAG_PER_INSTANCE > 1
|
||||
|
@ -1102,7 +1248,7 @@ rpl_add_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
memcpy(&dag->prefix_info, &dio->prefix_info, sizeof(rpl_prefix_t));
|
||||
|
||||
rpl_set_preferred_parent(dag, p);
|
||||
dag->rank = instance->of->calculate_rank(p, 0);
|
||||
dag->rank = rpl_rank_via_parent(p);
|
||||
dag->min_rank = dag->rank; /* So far this is the lowest rank we know of. */
|
||||
|
||||
PRINTF("RPL: Joined DAG with instance ID %u, rank %hu, DAG ID ",
|
||||
|
@ -1144,7 +1290,7 @@ global_repair(uip_ipaddr_t *from, rpl_dag_t *dag, rpl_dio_t *dio)
|
|||
PRINTF("RPL: Failed to add a parent during the global repair\n");
|
||||
dag->rank = INFINITE_RANK;
|
||||
} else {
|
||||
dag->rank = dag->instance->of->calculate_rank(p, 0);
|
||||
dag->rank = rpl_rank_via_parent(p);
|
||||
dag->min_rank = dag->rank;
|
||||
PRINTF("RPL: rpl_process_parent_event global repair\n");
|
||||
rpl_process_parent_event(dag->instance, p);
|
||||
|
@ -1155,6 +1301,7 @@ global_repair(uip_ipaddr_t *from, rpl_dag_t *dag, rpl_dio_t *dio)
|
|||
|
||||
RPL_STAT(rpl_stats.global_repairs++);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_local_repair(rpl_instance_t *instance)
|
||||
|
@ -1173,7 +1320,12 @@ rpl_local_repair(rpl_instance_t *instance)
|
|||
}
|
||||
}
|
||||
|
||||
/* no downward route anymore */
|
||||
instance->has_downward_route = 0;
|
||||
|
||||
rpl_reset_dio_timer(instance);
|
||||
/* Request refresh of DAO registrations next DIO */
|
||||
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
|
||||
|
||||
RPL_STAT(rpl_stats.local_repairs++);
|
||||
}
|
||||
|
@ -1205,6 +1357,7 @@ int
|
|||
rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
|
||||
{
|
||||
int return_value;
|
||||
rpl_parent_t *last_parent = instance->current_dag->preferred_parent;
|
||||
|
||||
#if DEBUG
|
||||
rpl_rank_t old_rank;
|
||||
|
@ -1213,10 +1366,20 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
|
||||
return_value = 1;
|
||||
|
||||
if(RPL_IS_STORING(instance)
|
||||
&& uip_ds6_route_is_nexthop(rpl_get_parent_ipaddr(p))
|
||||
&& !rpl_parent_is_reachable(p) && instance->mop > RPL_MOP_NON_STORING) {
|
||||
PRINTF("RPL: Unacceptable link %u, removing routes via: ", rpl_get_parent_link_metric(p));
|
||||
PRINT6ADDR(rpl_get_parent_ipaddr(p));
|
||||
PRINTF("\n");
|
||||
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(p), p->dag);
|
||||
}
|
||||
|
||||
if(!acceptable_rank(p->dag, p->rank)) {
|
||||
/* The candidate parent is no longer valid: the rank increase resulting
|
||||
from the choice of it as a parent would be too high. */
|
||||
PRINTF("RPL: Unacceptable rank %u\n", (unsigned)p->rank);
|
||||
PRINTF("RPL: Unacceptable rank %u (Current min %u, MaxRankInc %u)\n", (unsigned)p->rank,
|
||||
p->dag->min_rank, p->dag->instance->max_rankinc);
|
||||
rpl_nullify_parent(p);
|
||||
if(p != instance->current_dag->preferred_parent) {
|
||||
return 0;
|
||||
|
@ -1226,11 +1389,13 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
}
|
||||
|
||||
if(rpl_select_dag(instance, p) == NULL) {
|
||||
/* No suitable parent; trigger a local repair. */
|
||||
if(last_parent != NULL) {
|
||||
/* No suitable parent anymore; trigger a local repair. */
|
||||
PRINTF("RPL: No parents found in any DAG\n");
|
||||
rpl_local_repair(instance);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
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;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
add_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
||||
{
|
||||
/* add this to the neighbor cache if not already there */
|
||||
if(rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIO, dio) == NULL) {
|
||||
PRINTF("RPL: Out of memory, dropping DIO from ");
|
||||
PRINT6ADDR(from);
|
||||
PRINTF("\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
||||
{
|
||||
|
@ -1303,7 +1481,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
|
||||
if(instance == NULL) {
|
||||
PRINTF("RPL: New instance detected (ID=%u): Joining...\n", dio->instance_id);
|
||||
if(add_nbr_from_dio(from, dio)) {
|
||||
rpl_join_instance(from, dio);
|
||||
} else {
|
||||
PRINTF("RPL: Not joining since could not add parent\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1315,6 +1497,10 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
if(dag == NULL) {
|
||||
#if RPL_MAX_DAG_PER_INSTANCE > 1
|
||||
PRINTF("RPL: Adding new DAG to known instance.\n");
|
||||
if(!add_nbr_from_dio(from, dio)) {
|
||||
PRINTF("RPL: Could not add new DAG, could not add parent\n");
|
||||
return;
|
||||
}
|
||||
dag = rpl_add_dag(from, dio);
|
||||
if(dag == NULL) {
|
||||
PRINTF("RPL: Failed to add DAG.\n");
|
||||
|
@ -1331,8 +1517,6 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
PRINTF("RPL: Ignoring DIO with too low rank: %u\n",
|
||||
(unsigned)dio->rank);
|
||||
return;
|
||||
} else if(dio->rank == INFINITE_RANK && dag->joined) {
|
||||
rpl_reset_dio_timer(instance);
|
||||
}
|
||||
|
||||
/* Prefix Information Option treated to add new prefix */
|
||||
|
@ -1343,6 +1527,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
}
|
||||
}
|
||||
|
||||
if(!add_nbr_from_dio(from, dio)) {
|
||||
PRINTF("RPL: Could not add parent based on DIO\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(dag->rank == ROOT_RANK(instance)) {
|
||||
if(dio->rank != INFINITE_RANK) {
|
||||
instance->dio_counter++;
|
||||
|
@ -1394,6 +1583,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
}
|
||||
p->rank = dio->rank;
|
||||
|
||||
if(dio->rank == INFINITE_RANK && p == dag->preferred_parent) {
|
||||
/* Our preferred parent advertised an infinite rank, reset DIO timer */
|
||||
rpl_reset_dio_timer(instance);
|
||||
}
|
||||
|
||||
/* Parent info has been updated, trigger rank recalculation */
|
||||
p->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||
|
||||
|
@ -1401,14 +1595,14 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
PRINT6ADDR(&instance->current_dag->dag_id);
|
||||
PRINTF(", rank %u, min_rank %u, ",
|
||||
instance->current_dag->rank, instance->current_dag->min_rank);
|
||||
PRINTF("parent rank %u, parent etx %u, link metric %u, instance etx %u\n",
|
||||
p->rank, -1/*p->mc.obj.etx*/, rpl_get_nbr(p)->link_metric, instance->mc.obj.etx);
|
||||
PRINTF("parent rank %u, link metric %u\n",
|
||||
p->rank, rpl_get_parent_link_metric(p));
|
||||
|
||||
/* We have allocated a candidate parent; process the DIO further. */
|
||||
|
||||
#if RPL_DAG_MC != RPL_DAG_MC_NONE
|
||||
#if RPL_WITH_MC
|
||||
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
|
||||
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
|
||||
#endif /* RPL_WITH_MC */
|
||||
if(rpl_process_parent_event(instance, p) == 0) {
|
||||
PRINTF("RPL: The candidate parent is rejected\n");
|
||||
return;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "net/ip/tcpip.h"
|
||||
#include "net/ipv6/uip-ds6.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/rpl/rpl-ns.h"
|
||||
#include "net/packetbuf.h"
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
|
@ -61,12 +62,14 @@
|
|||
#define UIP_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
||||
#define UIP_HBHO_BUF ((struct uip_hbho_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
||||
#define UIP_HBHO_NEXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_HOP_BY_HOP_LEN])
|
||||
#define UIP_RH_BUF ((struct uip_routing_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
||||
#define UIP_RPL_SRH_BUF ((struct uip_rpl_srh_hdr *)&uip_buf[uip_l2_l3_hdr_len + RPL_RH_LEN])
|
||||
#define UIP_EXT_HDR_OPT_BUF ((struct uip_ext_hdr_opt *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
||||
#define UIP_EXT_HDR_OPT_PADN_BUF ((struct uip_ext_hdr_opt_padn *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
||||
#define UIP_EXT_HDR_OPT_RPL_BUF ((struct uip_ext_hdr_opt_rpl *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset])
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_verify_header(int uip_ext_opt_offset)
|
||||
rpl_verify_hbh_header(int uip_ext_opt_offset)
|
||||
{
|
||||
rpl_instance_t *instance;
|
||||
int down;
|
||||
|
@ -75,7 +78,7 @@ rpl_verify_header(int uip_ext_opt_offset)
|
|||
uip_ds6_route_t *route;
|
||||
rpl_parent_t *sender = NULL;
|
||||
|
||||
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
|
||||
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
|
||||
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -103,10 +106,12 @@ rpl_verify_header(int uip_ext_opt_offset)
|
|||
the packet to be forwareded in the first place. We drop any
|
||||
routes that go through the neighbor that sent the packet to
|
||||
us. */
|
||||
if(RPL_IS_STORING(instance)) {
|
||||
route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
|
||||
if(route != NULL) {
|
||||
uip_ds6_route_rm(route);
|
||||
}
|
||||
}
|
||||
RPL_STAT(rpl_stats.forward_errors++);
|
||||
/* Trigger DAO retransmission */
|
||||
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 = 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;
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
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)) {
|
||||
PRINTF("RPL: Loop detected - senderrank: %d my-rank: %d sender_closer: %d\n",
|
||||
sender_rank, instance->current_dag->rank,
|
||||
|
@ -166,35 +176,308 @@ rpl_verify_header(int uip_ext_opt_offset)
|
|||
}
|
||||
|
||||
PRINTF("RPL: Rank OK\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
set_rpl_opt(unsigned uip_ext_opt_offset)
|
||||
#if RPL_WITH_NON_STORING
|
||||
int
|
||||
rpl_srh_get_next_hop(uip_ipaddr_t *ipaddr)
|
||||
{
|
||||
uint8_t temp_len;
|
||||
uint8_t *uip_next_hdr;
|
||||
int last_uip_ext_len = uip_ext_len;
|
||||
rpl_dag_t *dag;
|
||||
rpl_ns_node_t *dest_node;
|
||||
rpl_ns_node_t *root_node;
|
||||
|
||||
memmove(UIP_HBHO_NEXT_BUF, UIP_EXT_BUF, uip_len - UIP_IPH_LEN);
|
||||
memset(UIP_HBHO_BUF, 0, RPL_HOP_BY_HOP_LEN);
|
||||
UIP_HBHO_BUF->next = UIP_IP_BUF->proto;
|
||||
UIP_IP_BUF->proto = UIP_PROTO_HBHO;
|
||||
UIP_HBHO_BUF->len = RPL_HOP_BY_HOP_LEN - 8;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->opt_type = UIP_EXT_HDR_OPT_RPL;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->opt_len = RPL_HDR_OPT_LEN;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->flags = 0;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->instance = 0;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = 0;
|
||||
uip_len += RPL_HOP_BY_HOP_LEN;
|
||||
temp_len = UIP_IP_BUF->len[1];
|
||||
UIP_IP_BUF->len[1] += UIP_HBHO_BUF->len + 8;
|
||||
if(UIP_IP_BUF->len[1] < temp_len) {
|
||||
UIP_IP_BUF->len[0]++;
|
||||
uip_ext_len = 0;
|
||||
uip_next_hdr = &UIP_IP_BUF->proto;
|
||||
|
||||
/* Look for routing header */
|
||||
while(uip_next_hdr != NULL && *uip_next_hdr != UIP_PROTO_ROUTING) {
|
||||
switch(*uip_next_hdr) {
|
||||
case UIP_PROTO_TCP:
|
||||
case UIP_PROTO_UDP:
|
||||
case UIP_PROTO_ICMP6:
|
||||
case UIP_PROTO_NONE:
|
||||
uip_next_hdr = NULL;
|
||||
break;
|
||||
case UIP_PROTO_HBHO:
|
||||
case UIP_PROTO_DESTO:
|
||||
case UIP_PROTO_FRAG:
|
||||
/* Move to next header */
|
||||
if(uip_next_hdr != &UIP_IP_BUF->proto) {
|
||||
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
|
||||
}
|
||||
uip_next_hdr = &UIP_EXT_BUF->next;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dag = rpl_get_dag(&UIP_IP_BUF->destipaddr);
|
||||
root_node = rpl_ns_get_node(dag, &dag->dag_id);
|
||||
dest_node = rpl_ns_get_node(dag, &UIP_IP_BUF->destipaddr);
|
||||
|
||||
if((uip_next_hdr != NULL && *uip_next_hdr == UIP_PROTO_ROUTING
|
||||
&& UIP_RH_BUF->routing_type == RPL_RH_TYPE_SRH) ||
|
||||
(dest_node != NULL && root_node != NULL &&
|
||||
dest_node->parent == root_node)) {
|
||||
/* Routing header found or the packet destined for a direct child of the root.
|
||||
* The next hop should be already copied as the IPv6 destination
|
||||
* address, via rpl_process_srh_header. We turn this address into a link-local to enable
|
||||
* forwarding to next hop */
|
||||
uip_ipaddr_copy(ipaddr, &UIP_IP_BUF->destipaddr);
|
||||
uip_create_linklocal_prefix(ipaddr);
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_update_header_empty(void)
|
||||
rpl_process_srh_header(void)
|
||||
{
|
||||
uint8_t *uip_next_hdr;
|
||||
int last_uip_ext_len = uip_ext_len;
|
||||
|
||||
uip_ext_len = 0;
|
||||
uip_next_hdr = &UIP_IP_BUF->proto;
|
||||
|
||||
/* Look for routing header */
|
||||
while(uip_next_hdr != NULL && *uip_next_hdr != UIP_PROTO_ROUTING) {
|
||||
switch(*uip_next_hdr) {
|
||||
case UIP_PROTO_TCP:
|
||||
case UIP_PROTO_UDP:
|
||||
case UIP_PROTO_ICMP6:
|
||||
case UIP_PROTO_NONE:
|
||||
uip_next_hdr = NULL;
|
||||
break;
|
||||
case UIP_PROTO_HBHO:
|
||||
case UIP_PROTO_DESTO:
|
||||
case UIP_PROTO_FRAG:
|
||||
/* Move to next header */
|
||||
if(uip_next_hdr != &UIP_IP_BUF->proto) {
|
||||
uip_ext_len += (UIP_EXT_BUF->len << 3) + 8;
|
||||
}
|
||||
uip_next_hdr = &UIP_EXT_BUF->next;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(uip_next_hdr != NULL && *uip_next_hdr == UIP_PROTO_ROUTING
|
||||
&& UIP_RH_BUF->routing_type == RPL_RH_TYPE_SRH) {
|
||||
/* SRH found, now look for next hop */
|
||||
uint8_t cmpri, cmpre;
|
||||
uint8_t ext_len;
|
||||
uint8_t padding;
|
||||
uint8_t path_len;
|
||||
uint8_t segments_left;
|
||||
uip_ipaddr_t current_dest_addr;
|
||||
|
||||
segments_left = UIP_RH_BUF->seg_left;
|
||||
ext_len = (UIP_RH_BUF->len * 8) + 8;
|
||||
cmpri = UIP_RPL_SRH_BUF->cmpr >> 4;
|
||||
cmpre = UIP_RPL_SRH_BUF->cmpr & 0x0f;
|
||||
padding = UIP_RPL_SRH_BUF->pad >> 4;
|
||||
path_len = ((ext_len - padding - RPL_RH_LEN - RPL_SRH_LEN - (16 - cmpre)) / (16 - cmpri)) + 1;
|
||||
(void)path_len;
|
||||
|
||||
PRINTF("RPL: read SRH, path len %u, segments left %u, Cmpri %u, Cmpre %u, ext len %u (padding %u)\n",
|
||||
path_len, segments_left, cmpri, cmpre, ext_len, padding);
|
||||
|
||||
if(segments_left == 0) {
|
||||
/* We are the final destination, do nothing */
|
||||
} else {
|
||||
uint8_t i = path_len - segments_left; /* The index of the next address to be visited */
|
||||
uint8_t *addr_ptr = ((uint8_t *)UIP_RH_BUF) + RPL_RH_LEN + RPL_SRH_LEN + (i * (16 - cmpri));
|
||||
uint8_t cmpr = segments_left == 1 ? cmpre : cmpri;
|
||||
|
||||
/* As per RFC6554: swap the IPv6 destination address and address[i] */
|
||||
|
||||
/* First, copy the current IPv6 destination address */
|
||||
uip_ipaddr_copy(¤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;
|
||||
int uip_ext_opt_offset;
|
||||
|
@ -209,39 +492,30 @@ rpl_update_header_empty(void)
|
|||
|
||||
switch(UIP_IP_BUF->proto) {
|
||||
case UIP_PROTO_HBHO:
|
||||
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
|
||||
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
|
||||
PRINTF("RPL: Hop-by-hop extension header has wrong size\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_type != UIP_EXT_HDR_OPT_RPL) {
|
||||
PRINTF("RPL: Non RPL Hop-by-hop option support not implemented\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
if(UIP_EXT_HDR_OPT_RPL_BUF->opt_len != RPL_HDR_OPT_LEN) {
|
||||
PRINTF("RPL: RPL Hop-by-hop option has wrong length\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
instance = rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance);
|
||||
if(instance == NULL || !instance->used || !instance->current_dag->joined) {
|
||||
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect instance\n");
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#if RPL_INSERT_HBH_OPTION
|
||||
PRINTF("RPL: No hop-by-hop option found, creating it\n");
|
||||
if(uip_len + RPL_HOP_BY_HOP_LEN > UIP_BUFSIZE) {
|
||||
PRINTF("RPL: Packet too long: impossible to add hop-by-hop option\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
}
|
||||
set_rpl_opt(uip_ext_opt_offset);
|
||||
uip_ext_len = last_uip_ext_len + RPL_HOP_BY_HOP_LEN;
|
||||
#endif
|
||||
return 0;
|
||||
PRINTF("RPL: No hop-by-hop option found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch(UIP_EXT_HDR_OPT_BUF->type) {
|
||||
|
@ -249,6 +523,7 @@ rpl_update_header_empty(void)
|
|||
PRINTF("RPL: Updating RPL option\n");
|
||||
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,
|
||||
which states that if a packet is going down it should in
|
||||
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);
|
||||
}
|
||||
/* Drop packet */
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
return 1;
|
||||
default:
|
||||
PRINTF("RPL: Multi Hop-by-hop options not implemented\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
insert_hbh_header(void)
|
||||
{
|
||||
int uip_ext_opt_offset;
|
||||
int last_uip_ext_len;
|
||||
uint8_t temp_len;
|
||||
|
||||
last_uip_ext_len = uip_ext_len;
|
||||
uip_ext_len = 0;
|
||||
uip_ext_opt_offset = 2;
|
||||
|
||||
/* Insert hop-by-hop header */
|
||||
PRINTF("RPL: Creating hop-by-hop option\n");
|
||||
if(uip_len + RPL_HOP_BY_HOP_LEN > UIP_BUFSIZE) {
|
||||
PRINTF("RPL: Packet too long: impossible to add hop-by-hop option\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Move existing ext headers and payload UIP_EXT_BUF further */
|
||||
memmove(UIP_HBHO_NEXT_BUF, UIP_EXT_BUF, uip_len - UIP_IPH_LEN);
|
||||
memset(UIP_HBHO_BUF, 0, RPL_HOP_BY_HOP_LEN);
|
||||
|
||||
/* Update IP and HBH protocol and fields */
|
||||
UIP_HBHO_BUF->next = UIP_IP_BUF->proto;
|
||||
UIP_IP_BUF->proto = UIP_PROTO_HBHO;
|
||||
|
||||
/* Initialize HBH option */
|
||||
UIP_HBHO_BUF->len = (RPL_HOP_BY_HOP_LEN - 8) / 8;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->opt_type = UIP_EXT_HDR_OPT_RPL;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->opt_len = RPL_HDR_OPT_LEN;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->flags = 0;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->instance = 0;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = 0;
|
||||
uip_len += RPL_HOP_BY_HOP_LEN;
|
||||
temp_len = UIP_IP_BUF->len[1];
|
||||
UIP_IP_BUF->len[1] += RPL_HOP_BY_HOP_LEN;
|
||||
if(UIP_IP_BUF->len[1] < temp_len) {
|
||||
UIP_IP_BUF->len[0]++;
|
||||
}
|
||||
|
||||
uip_ext_len = last_uip_ext_len + RPL_HOP_BY_HOP_LEN;
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_update_header_final(uip_ipaddr_t *addr)
|
||||
rpl_finalize_header(uip_ipaddr_t *addr)
|
||||
{
|
||||
rpl_parent_t *parent;
|
||||
int uip_ext_opt_offset;
|
||||
|
@ -304,10 +625,10 @@ rpl_update_header_final(uip_ipaddr_t *addr)
|
|||
uip_ext_opt_offset = 2;
|
||||
|
||||
if(UIP_IP_BUF->proto == UIP_PROTO_HBHO) {
|
||||
if(UIP_HBHO_BUF->len != RPL_HOP_BY_HOP_LEN - 8) {
|
||||
if(UIP_HBHO_BUF->len != ((RPL_HOP_BY_HOP_LEN - 8) / 8)) {
|
||||
PRINTF("RPL: Non RPL Hop-by-hop options support not implemented\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(UIP_EXT_HDR_OPT_BUF->type == UIP_EXT_HDR_OPT_RPL) {
|
||||
|
@ -315,7 +636,7 @@ rpl_update_header_final(uip_ipaddr_t *addr)
|
|||
PRINTF("RPL: Updating RPL option\n");
|
||||
if(default_instance == NULL || !default_instance->used || !default_instance->current_dag->joined) {
|
||||
PRINTF("RPL: Unable to add hop-by-hop extension header: incorrect default instance\n");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
parent = rpl_find_parent(default_instance->current_dag, addr);
|
||||
if(parent == NULL || parent != parent->dag->preferred_parent) {
|
||||
|
@ -326,78 +647,100 @@ rpl_update_header_final(uip_ipaddr_t *addr)
|
|||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_remove_header(void)
|
||||
{
|
||||
uint8_t temp_len;
|
||||
uint8_t rpl_ext_hdr_len;
|
||||
uint8_t *uip_next_hdr;
|
||||
|
||||
uip_ext_len = 0;
|
||||
uip_next_hdr = &UIP_IP_BUF->proto;
|
||||
|
||||
PRINTF("RPL: Verifying the presence of the RPL header option\n");
|
||||
switch(UIP_IP_BUF->proto){
|
||||
PRINTF("RPL: Verifying the presence of RPL extension headers\n");
|
||||
|
||||
/* Look for hop-by-hop and routing headers */
|
||||
while(uip_next_hdr != NULL) {
|
||||
switch(*uip_next_hdr) {
|
||||
case UIP_PROTO_TCP:
|
||||
case UIP_PROTO_UDP:
|
||||
case UIP_PROTO_ICMP6:
|
||||
case UIP_PROTO_NONE:
|
||||
return;
|
||||
case UIP_PROTO_HBHO:
|
||||
PRINTF("RPL: Removing the RPL header option\n");
|
||||
UIP_IP_BUF->proto = UIP_HBHO_BUF->next;
|
||||
case UIP_PROTO_ROUTING:
|
||||
/* Remove hop-by-hop and routing headers */
|
||||
*uip_next_hdr = UIP_EXT_BUF->next;
|
||||
rpl_ext_hdr_len = (UIP_EXT_BUF->len * 8) + 8;
|
||||
temp_len = UIP_IP_BUF->len[1];
|
||||
uip_len -= UIP_HBHO_BUF->len + 8;
|
||||
UIP_IP_BUF->len[1] -= UIP_HBHO_BUF->len + 8;
|
||||
uip_len -= rpl_ext_hdr_len;
|
||||
UIP_IP_BUF->len[1] -= rpl_ext_hdr_len;
|
||||
if(UIP_IP_BUF->len[1] > temp_len) {
|
||||
UIP_IP_BUF->len[0]--;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
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:
|
||||
uip_next_hdr = &UIP_EXT_BUF->next;
|
||||
break;
|
||||
default:
|
||||
PRINTF("RPL: No hop-by-hop Option found\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (UIP_EXT_HDR_OPT_BUF->type) {
|
||||
case UIP_EXT_HDR_OPT_RPL:
|
||||
PRINTF("RPL: Updating RPL option (switching direction)\n");
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->flags &= RPL_HDR_OPT_DOWN;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->flags ^= RPL_HDR_OPT_DOWN;
|
||||
UIP_EXT_HDR_OPT_RPL_BUF->senderrank = UIP_HTONS(rpl_get_instance(UIP_EXT_HDR_OPT_RPL_BUF->instance)->current_dag->rank);
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return RPL_HOP_BY_HOP_LEN;
|
||||
default:
|
||||
PRINTF("RPL: Multi Hop-by-hop options not implemented\n");
|
||||
uip_ext_len = last_uip_ext_len;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_insert_header(void)
|
||||
{
|
||||
#if RPL_INSERT_HBH_OPTION
|
||||
if(default_instance != NULL && !uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
|
||||
rpl_update_header_empty();
|
||||
if(default_instance == NULL || default_instance->current_dag == NULL
|
||||
|| uip_is_addr_linklocal(&UIP_IP_BUF->destipaddr) || uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(RPL_IS_STORING(default_instance)) {
|
||||
insert_hbh_header();
|
||||
}
|
||||
|
||||
if(RPL_IS_NON_STORING(default_instance)) {
|
||||
if(default_instance->current_dag != NULL) {
|
||||
if(default_instance->current_dag->rank == ROOT_RANK(default_instance)) {
|
||||
insert_srh_header();
|
||||
} else {
|
||||
insert_hbh_header();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_update_header(void)
|
||||
{
|
||||
if(default_instance == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(default_instance->current_dag != NULL) {
|
||||
if(default_instance->current_dag->rank == ROOT_RANK(default_instance)) {
|
||||
/* At the root, remove headers if any, and insert SRH or HBH
|
||||
* (SRH is inserted only if the destination is in the DODAG) */
|
||||
rpl_remove_header();
|
||||
if(RPL_IS_NON_STORING(default_instance)) {
|
||||
return insert_srh_header();
|
||||
} else {
|
||||
return insert_hbh_header();
|
||||
}
|
||||
} else {
|
||||
return update_hbh_header();
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** @}*/
|
||||
|
|
|
@ -51,8 +51,10 @@
|
|||
#include "net/ipv6/uip-nd6.h"
|
||||
#include "net/ipv6/uip-icmp6.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/rpl/rpl-ns.h"
|
||||
#include "net/packetbuf.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||
#include "random.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
@ -76,6 +78,9 @@ static void dio_input(void);
|
|||
static void dao_input(void);
|
||||
static void dao_ack_input(void);
|
||||
|
||||
static void dao_output_target_seq(rpl_parent_t *parent, uip_ipaddr_t *prefix,
|
||||
uint8_t lifetime, uint8_t seq_no);
|
||||
|
||||
/* some debug callbacks useful when debugging RPL networks */
|
||||
#ifdef RPL_DEBUG_DIO_INPUT
|
||||
void RPL_DEBUG_DIO_INPUT(uip_ipaddr_t *, rpl_dio_t *);
|
||||
|
@ -87,8 +92,6 @@ void RPL_DEBUG_DAO_OUTPUT(rpl_parent_t *);
|
|||
|
||||
static uint8_t dao_sequence = RPL_LOLLIPOP_INIT;
|
||||
|
||||
extern rpl_of_t RPL_OF;
|
||||
|
||||
#if RPL_CONF_MULTICAST
|
||||
static uip_mcast6_route_t *mcast_group;
|
||||
#endif
|
||||
|
@ -99,6 +102,40 @@ UIP_ICMP6_HANDLER(dio_handler, ICMP6_RPL, RPL_CODE_DIO, dio_input);
|
|||
UIP_ICMP6_HANDLER(dao_handler, ICMP6_RPL, RPL_CODE_DAO, dao_input);
|
||||
UIP_ICMP6_HANDLER(dao_ack_handler, ICMP6_RPL, RPL_CODE_DAO_ACK, dao_ack_input);
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#if RPL_WITH_DAO_ACK
|
||||
static uip_ds6_route_t *
|
||||
find_route_entry_by_dao_ack(uint8_t seq)
|
||||
{
|
||||
uip_ds6_route_t *re;
|
||||
re = uip_ds6_route_head();
|
||||
while(re != NULL) {
|
||||
if(re->state.dao_seqno_out == seq && RPL_ROUTE_IS_DAO_PENDING(re)) {
|
||||
/* found it! */
|
||||
return re;
|
||||
}
|
||||
re = uip_ds6_route_next(re);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif /* RPL_WITH_DAO_ACK */
|
||||
|
||||
#if RPL_WITH_STORING
|
||||
/* prepare for forwarding of DAO */
|
||||
static uint8_t
|
||||
prepare_for_dao_fwd(uint8_t sequence, uip_ds6_route_t *rep)
|
||||
{
|
||||
/* not pending - or pending but not a retransmission */
|
||||
RPL_LOLLIPOP_INCREMENT(dao_sequence);
|
||||
|
||||
/* set DAO pending and sequence numbers */
|
||||
rep->state.dao_seqno_in = sequence;
|
||||
rep->state.dao_seqno_out = dao_sequence;
|
||||
RPL_ROUTE_SET_DAO_PENDING(rep);
|
||||
return dao_sequence;
|
||||
}
|
||||
#endif /* RPL_WITH_STORING */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
get_global_addr(uip_ipaddr_t *addr)
|
||||
{
|
||||
|
@ -147,6 +184,34 @@ set16(uint8_t *buffer, int pos, uint16_t value)
|
|||
buffer[pos++] = value & 0xff;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ds6_nbr_t *
|
||||
rpl_icmp6_update_nbr_table(uip_ipaddr_t *from, nbr_table_reason_t reason, void *data)
|
||||
{
|
||||
uip_ds6_nbr_t *nbr;
|
||||
|
||||
if((nbr = uip_ds6_nbr_lookup(from)) == NULL) {
|
||||
if((nbr = uip_ds6_nbr_add(from, (uip_lladdr_t *)
|
||||
packetbuf_addr(PACKETBUF_ADDR_SENDER),
|
||||
0, NBR_REACHABLE, reason, data)) != NULL) {
|
||||
PRINTF("RPL: Neighbor added to neighbor cache ");
|
||||
PRINT6ADDR(from);
|
||||
PRINTF(", ");
|
||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
PRINTF("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(nbr != NULL) {
|
||||
#if UIP_ND6_SEND_NA
|
||||
/* set reachable timer if we added or found the nbr entry - and update
|
||||
neighbor entry to reachable to avoid sending NS/NA, etc. */
|
||||
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
|
||||
nbr->state = NBR_REACHABLE;
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
}
|
||||
return nbr;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
dis_input(void)
|
||||
{
|
||||
|
@ -170,9 +235,20 @@ dis_input(void)
|
|||
rpl_reset_dio_timer(instance);
|
||||
} else {
|
||||
#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");
|
||||
dio_output(instance, &UIP_IP_BUF->srcipaddr);
|
||||
}
|
||||
/* } */
|
||||
}
|
||||
}
|
||||
}
|
||||
uip_clear_buf();
|
||||
|
@ -218,7 +294,6 @@ dio_input(void)
|
|||
int i;
|
||||
int len;
|
||||
uip_ipaddr_t from;
|
||||
uip_ds6_nbr_t *nbr;
|
||||
|
||||
memset(&dio, 0, sizeof(dio));
|
||||
|
||||
|
@ -228,7 +303,7 @@ dio_input(void)
|
|||
dio.dag_redund = RPL_DIO_REDUNDANCY;
|
||||
dio.dag_min_hoprankinc = RPL_MIN_HOPRANKINC;
|
||||
dio.dag_max_rankinc = RPL_MAX_RANKINC;
|
||||
dio.ocp = RPL_OF.ocp;
|
||||
dio.ocp = RPL_OF_OCP;
|
||||
dio.default_lifetime = RPL_DEFAULT_LIFETIME;
|
||||
dio.lifetime_unit = RPL_DEFAULT_LIFETIME_UNIT;
|
||||
|
||||
|
@ -239,30 +314,6 @@ dio_input(void)
|
|||
PRINT6ADDR(&from);
|
||||
PRINTF("\n");
|
||||
|
||||
if((nbr = uip_ds6_nbr_lookup(&from)) == NULL) {
|
||||
if((nbr = uip_ds6_nbr_add(&from, (uip_lladdr_t *)
|
||||
packetbuf_addr(PACKETBUF_ADDR_SENDER),
|
||||
0, NBR_REACHABLE)) != NULL) {
|
||||
/* set reachable timer */
|
||||
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
|
||||
PRINTF("RPL: Neighbor added to neighbor cache ");
|
||||
PRINT6ADDR(&from);
|
||||
PRINTF(", ");
|
||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
PRINTF("\n");
|
||||
} else {
|
||||
PRINTF("RPL: Out of memory, dropping DIO from ");
|
||||
PRINT6ADDR(&from);
|
||||
PRINTF(", ");
|
||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
PRINTF("\n");
|
||||
|
||||
goto discard;
|
||||
}
|
||||
} else {
|
||||
PRINTF("RPL: Neighbor already in neighbor cache\n");
|
||||
}
|
||||
|
||||
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
||||
|
||||
/* Process the DIO base option. */
|
||||
|
@ -428,6 +479,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
|||
{
|
||||
unsigned char *buffer;
|
||||
int pos;
|
||||
int is_root;
|
||||
rpl_dag_t *dag = instance->current_dag;
|
||||
#if !RPL_LEAF_ONLY
|
||||
uip_ipaddr_t addr;
|
||||
|
@ -448,6 +500,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
|||
buffer = UIP_ICMP_PAYLOAD;
|
||||
buffer[pos++] = instance->instance_id;
|
||||
buffer[pos++] = dag->version;
|
||||
is_root = (dag->rank == ROOT_RANK(instance));
|
||||
|
||||
#if RPL_LEAF_ONLY
|
||||
PRINTF("RPL: LEAF ONLY DIO rank set to INFINITE_RANK\n");
|
||||
|
@ -468,7 +521,7 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
|||
|
||||
buffer[pos++] = instance->dtsn_out;
|
||||
|
||||
if(uc_addr == NULL) {
|
||||
if(RPL_DIO_REFRESH_DAO_ROUTES && is_root && uc_addr == NULL) {
|
||||
/* Request new DAO to refresh route. We do not do this for unicast DIO
|
||||
* in order to avoid DAO messages after a DIS-DIO update,
|
||||
* or upon unicast DIO probing. */
|
||||
|
@ -578,8 +631,9 @@ dio_output(rpl_instance_t *instance, uip_ipaddr_t *uc_addr)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
dao_input(void)
|
||||
dao_input_storing(void)
|
||||
{
|
||||
#if RPL_WITH_STORING
|
||||
uip_ipaddr_t dao_sender_addr;
|
||||
rpl_dag_t *dag;
|
||||
rpl_instance_t *instance;
|
||||
|
@ -603,17 +657,13 @@ dao_input(void)
|
|||
int learned_from;
|
||||
rpl_parent_t *parent;
|
||||
uip_ds6_nbr_t *nbr;
|
||||
int is_root;
|
||||
|
||||
prefixlen = 0;
|
||||
parent = NULL;
|
||||
|
||||
uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);
|
||||
|
||||
/* Destination Advertisement Object */
|
||||
PRINTF("RPL: Received a DAO from ");
|
||||
PRINT6ADDR(&dao_sender_addr);
|
||||
PRINTF("\n");
|
||||
|
||||
buffer = UIP_ICMP_PAYLOAD;
|
||||
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
||||
|
||||
|
@ -621,11 +671,6 @@ dao_input(void)
|
|||
instance_id = buffer[pos++];
|
||||
|
||||
instance = rpl_get_instance(instance_id);
|
||||
if(instance == NULL) {
|
||||
PRINTF("RPL: Ignoring a DAO for an unknown RPL instance(%u)\n",
|
||||
instance_id);
|
||||
goto discard;
|
||||
}
|
||||
|
||||
lifetime = instance->default_lifetime;
|
||||
|
||||
|
@ -635,11 +680,13 @@ dao_input(void)
|
|||
sequence = buffer[pos++];
|
||||
|
||||
dag = instance->current_dag;
|
||||
is_root = (dag->rank == ROOT_RANK(instance));
|
||||
|
||||
/* Is the DAG ID present? */
|
||||
if(flags & RPL_DAO_D_FLAG) {
|
||||
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
|
||||
PRINTF("RPL: Ignoring a DAO for a DAG different from ours\n");
|
||||
goto discard;
|
||||
return;
|
||||
}
|
||||
pos += 16;
|
||||
}
|
||||
|
@ -647,8 +694,12 @@ dao_input(void)
|
|||
learned_from = uip_is_addr_mcast(&dao_sender_addr) ?
|
||||
RPL_ROUTE_FROM_MULTICAST_DAO : RPL_ROUTE_FROM_UNICAST_DAO;
|
||||
|
||||
PRINTF("RPL: DAO from %s\n",
|
||||
learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast");
|
||||
/* Destination Advertisement Object */
|
||||
PRINTF("RPL: Received a (%s) DAO with sequence number %u from ",
|
||||
learned_from == RPL_ROUTE_FROM_UNICAST_DAO? "unicast": "multicast", sequence);
|
||||
PRINT6ADDR(&dao_sender_addr);
|
||||
PRINTF("\n");
|
||||
|
||||
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
|
||||
/* Check whether this is a DAO forwarding loop. */
|
||||
parent = rpl_find_parent(dag, &dao_sender_addr);
|
||||
|
@ -660,7 +711,7 @@ dao_input(void)
|
|||
DAG_RANK(parent->rank, instance), DAG_RANK(dag->rank, instance));
|
||||
parent->rank = INFINITE_RANK;
|
||||
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||
goto discard;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we get the DAO from our parent, we also have a loop. */
|
||||
|
@ -668,7 +719,7 @@ dao_input(void)
|
|||
PRINTF("RPL: Loop detected when receiving a unicast DAO from our parent\n");
|
||||
parent->rank = INFINITE_RANK;
|
||||
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||
goto discard;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -721,91 +772,319 @@ dao_input(void)
|
|||
PRINTF("RPL: No-Path DAO received\n");
|
||||
/* No-Path DAO received; invoke the route purging routine. */
|
||||
if(rep != NULL &&
|
||||
rep->state.nopath_received == 0 &&
|
||||
!RPL_ROUTE_IS_NOPATH_RECEIVED(rep) &&
|
||||
rep->length == prefixlen &&
|
||||
uip_ds6_route_nexthop(rep) != NULL &&
|
||||
uip_ipaddr_cmp(uip_ds6_route_nexthop(rep), &dao_sender_addr)) {
|
||||
PRINTF("RPL: Setting expiration timer for prefix ");
|
||||
PRINT6ADDR(&prefix);
|
||||
PRINTF("\n");
|
||||
rep->state.nopath_received = 1;
|
||||
RPL_ROUTE_SET_NOPATH_RECEIVED(rep);
|
||||
rep->state.lifetime = RPL_NOPATH_REMOVAL_DELAY;
|
||||
|
||||
/* We forward the incoming No-Path DAO to our parent, if we have
|
||||
one. */
|
||||
if(dag->preferred_parent != NULL &&
|
||||
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
|
||||
PRINTF("RPL: Forwarding No-Path DAO to parent ");
|
||||
uint8_t out_seq;
|
||||
out_seq = prepare_for_dao_fwd(sequence, rep);
|
||||
|
||||
PRINTF("RPL: Forwarding No-path DAO to parent - out_seq:%d",
|
||||
out_seq);
|
||||
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
|
||||
PRINTF("\n");
|
||||
|
||||
buffer = UIP_ICMP_PAYLOAD;
|
||||
buffer[3] = out_seq; /* add an outgoing seq no before fwd */
|
||||
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
|
||||
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
|
||||
}
|
||||
}
|
||||
/* independent if we remove or not - ACK the request */
|
||||
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);
|
||||
}
|
||||
}
|
||||
goto discard;
|
||||
return;
|
||||
}
|
||||
|
||||
PRINTF("RPL: adding DAO route\n");
|
||||
PRINTF("RPL: Adding DAO route\n");
|
||||
|
||||
if((nbr = uip_ds6_nbr_lookup(&dao_sender_addr)) == NULL) {
|
||||
if((nbr = uip_ds6_nbr_add(&dao_sender_addr,
|
||||
(uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER),
|
||||
0, NBR_REACHABLE)) != NULL) {
|
||||
/* set reachable timer */
|
||||
stimer_set(&nbr->reachable, UIP_ND6_REACHABLE_TIME / 1000);
|
||||
PRINTF("RPL: Neighbor added to neighbor cache ");
|
||||
PRINT6ADDR(&dao_sender_addr);
|
||||
PRINTF(", ");
|
||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
PRINTF("\n");
|
||||
} else {
|
||||
/* Update and add neighbor - if no room - fail. */
|
||||
if((nbr = rpl_icmp6_update_nbr_table(&dao_sender_addr, NBR_TABLE_REASON_RPL_DAO, instance)) == NULL) {
|
||||
PRINTF("RPL: Out of Memory, dropping DAO from ");
|
||||
PRINT6ADDR(&dao_sender_addr);
|
||||
PRINTF(", ");
|
||||
PRINTLLADDR((uip_lladdr_t *)packetbuf_addr(PACKETBUF_ADDR_SENDER));
|
||||
PRINTF("\n");
|
||||
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 {
|
||||
PRINTF("RPL: Neighbor already in neighbor cache\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rep = rpl_add_route(dag, &prefix, prefixlen, &dao_sender_addr);
|
||||
if(rep == NULL) {
|
||||
RPL_STAT(rpl_stats.mem_overflows++);
|
||||
PRINTF("RPL: Could not add a route after receiving a DAO\n");
|
||||
goto discard;
|
||||
if(flags & RPL_DAO_K_FLAG) {
|
||||
/* signal the failure to add the node */
|
||||
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||
is_root ? RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT :
|
||||
RPL_DAO_ACK_UNABLE_TO_ACCEPT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* set lifetime and clear NOPATH bit */
|
||||
rep->state.lifetime = RPL_LIFETIME(instance, lifetime);
|
||||
rep->state.learned_from = learned_from;
|
||||
rep->state.nopath_received = 0;
|
||||
RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);
|
||||
|
||||
#if RPL_CONF_MULTICAST
|
||||
fwd_dao:
|
||||
#endif
|
||||
|
||||
if(learned_from == RPL_ROUTE_FROM_UNICAST_DAO) {
|
||||
int should_ack = 0;
|
||||
|
||||
if(flags & RPL_DAO_K_FLAG) {
|
||||
/*
|
||||
* check if this route is already installed and we can ack now!
|
||||
* not pending - and same seq-no means that we can ack.
|
||||
* (e.g. the route is installed already so it will not take any
|
||||
* more room that it already takes - so should be ok!)
|
||||
*/
|
||||
if((!RPL_ROUTE_IS_DAO_PENDING(rep) &&
|
||||
rep->state.dao_seqno_in == sequence) ||
|
||||
dag->rank == ROOT_RANK(instance)) {
|
||||
should_ack = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(dag->preferred_parent != NULL &&
|
||||
rpl_get_parent_ipaddr(dag->preferred_parent) != NULL) {
|
||||
uint8_t out_seq = 0;
|
||||
/* if this is pending and we get the same seq no it is a retrans */
|
||||
if(RPL_ROUTE_IS_DAO_PENDING(rep) &&
|
||||
rep->state.dao_seqno_in == sequence) {
|
||||
/* keep the same seq-no as before for parent also */
|
||||
out_seq = rep->state.dao_seqno_out;
|
||||
} else {
|
||||
out_seq = prepare_for_dao_fwd(sequence, rep);
|
||||
}
|
||||
|
||||
PRINTF("RPL: Forwarding DAO to parent ");
|
||||
PRINT6ADDR(rpl_get_parent_ipaddr(dag->preferred_parent));
|
||||
PRINTF("\n");
|
||||
PRINTF(" in seq: %d out seq: %d\n", sequence, out_seq);
|
||||
|
||||
buffer = UIP_ICMP_PAYLOAD;
|
||||
buffer[3] = out_seq; /* add an outgoing seq no before fwd */
|
||||
uip_icmp6_send(rpl_get_parent_ipaddr(dag->preferred_parent),
|
||||
ICMP6_RPL, RPL_CODE_DAO, buffer_length);
|
||||
}
|
||||
if(flags & RPL_DAO_K_FLAG) {
|
||||
dao_ack_output(instance, &dao_sender_addr, sequence);
|
||||
if(should_ack) {
|
||||
PRINTF("RPL: Sending DAO ACK\n");
|
||||
uip_clear_buf();
|
||||
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
|
||||
}
|
||||
}
|
||||
#endif /* RPL_WITH_STORING */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
dao_input_nonstoring(void)
|
||||
{
|
||||
#if RPL_WITH_NON_STORING
|
||||
uip_ipaddr_t dao_sender_addr;
|
||||
uip_ipaddr_t dao_parent_addr;
|
||||
rpl_dag_t *dag;
|
||||
rpl_instance_t *instance;
|
||||
unsigned char *buffer;
|
||||
uint16_t sequence;
|
||||
uint8_t instance_id;
|
||||
uint8_t lifetime;
|
||||
uint8_t prefixlen;
|
||||
uint8_t flags;
|
||||
uint8_t subopt_type;
|
||||
uip_ipaddr_t prefix;
|
||||
uint8_t buffer_length;
|
||||
int pos;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
prefixlen = 0;
|
||||
|
||||
uip_ipaddr_copy(&dao_sender_addr, &UIP_IP_BUF->srcipaddr);
|
||||
memset(&dao_parent_addr, 0, 16);
|
||||
|
||||
buffer = UIP_ICMP_PAYLOAD;
|
||||
buffer_length = uip_len - uip_l3_icmp_hdr_len;
|
||||
|
||||
pos = 0;
|
||||
instance_id = buffer[pos++];
|
||||
instance = rpl_get_instance(instance_id);
|
||||
lifetime = instance->default_lifetime;
|
||||
|
||||
flags = buffer[pos++];
|
||||
/* reserved */
|
||||
pos++;
|
||||
sequence = buffer[pos++];
|
||||
|
||||
dag = instance->current_dag;
|
||||
/* Is the DAG ID present? */
|
||||
if(flags & RPL_DAO_D_FLAG) {
|
||||
if(memcmp(&dag->dag_id, &buffer[pos], sizeof(dag->dag_id))) {
|
||||
PRINTF("RPL: Ignoring a DAO for a DAG different from ours\n");
|
||||
return;
|
||||
}
|
||||
pos += 16;
|
||||
}
|
||||
|
||||
/* Check if there are any RPL options present. */
|
||||
for(i = pos; i < buffer_length; i += len) {
|
||||
subopt_type = buffer[i];
|
||||
if(subopt_type == RPL_OPTION_PAD1) {
|
||||
len = 1;
|
||||
} else {
|
||||
/* The option consists of a two-byte header and a payload. */
|
||||
len = 2 + buffer[i + 1];
|
||||
}
|
||||
|
||||
switch(subopt_type) {
|
||||
case RPL_OPTION_TARGET:
|
||||
/* Handle the target option. */
|
||||
prefixlen = buffer[i + 3];
|
||||
memset(&prefix, 0, sizeof(prefix));
|
||||
memcpy(&prefix, buffer + i + 4, (prefixlen + 7) / CHAR_BIT);
|
||||
break;
|
||||
case RPL_OPTION_TRANSIT:
|
||||
/* The path sequence and control are ignored. */
|
||||
/* pathcontrol = buffer[i + 3];
|
||||
pathsequence = buffer[i + 4];*/
|
||||
lifetime = buffer[i + 5];
|
||||
if(len >= 20) {
|
||||
memcpy(&dao_parent_addr, buffer + i + 6, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PRINTF("RPL: DAO lifetime: %u, prefix length: %u prefix: ",
|
||||
(unsigned)lifetime, (unsigned)prefixlen);
|
||||
PRINT6ADDR(&prefix);
|
||||
PRINTF(", parent: ");
|
||||
PRINT6ADDR(&dao_parent_addr);
|
||||
PRINTF(" \n");
|
||||
|
||||
if(lifetime == RPL_ZERO_LIFETIME) {
|
||||
PRINTF("RPL: No-Path DAO received\n");
|
||||
rpl_ns_expire_parent(dag, &prefix, &dao_parent_addr);
|
||||
} else {
|
||||
if(rpl_ns_update_node(dag, &prefix, &dao_parent_addr, RPL_LIFETIME(instance, lifetime)) == NULL) {
|
||||
PRINTF("RPL: failed to add link\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(flags & RPL_DAO_K_FLAG) {
|
||||
PRINTF("RPL: Sending DAO ACK\n");
|
||||
uip_clear_buf();
|
||||
dao_ack_output(instance, &dao_sender_addr, sequence,
|
||||
RPL_DAO_ACK_UNCONDITIONAL_ACCEPT);
|
||||
}
|
||||
#endif /* RPL_WITH_NON_STORING */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
dao_input(void)
|
||||
{
|
||||
rpl_instance_t *instance;
|
||||
uint8_t instance_id;
|
||||
|
||||
/* Destination Advertisement Object */
|
||||
PRINTF("RPL: Received a DAO from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
PRINTF("\n");
|
||||
|
||||
instance_id = UIP_ICMP_PAYLOAD[0];
|
||||
instance = rpl_get_instance(instance_id);
|
||||
if(instance == NULL) {
|
||||
PRINTF("RPL: Ignoring a DAO for an unknown RPL instance(%u)\n",
|
||||
instance_id);
|
||||
goto discard;
|
||||
}
|
||||
|
||||
if(RPL_IS_STORING(instance)) {
|
||||
dao_input_storing();
|
||||
} else if(RPL_IS_NON_STORING(instance)) {
|
||||
dao_input_nonstoring();
|
||||
}
|
||||
|
||||
discard:
|
||||
uip_clear_buf();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if RPL_WITH_DAO_ACK
|
||||
static void
|
||||
handle_dao_retransmission(void *ptr)
|
||||
{
|
||||
rpl_parent_t *parent;
|
||||
uip_ipaddr_t prefix;
|
||||
rpl_instance_t *instance;
|
||||
|
||||
parent = ptr;
|
||||
if(parent == NULL || parent->dag == NULL || parent->dag->instance == NULL) {
|
||||
return;
|
||||
}
|
||||
instance = parent->dag->instance;
|
||||
|
||||
if(instance->my_dao_transmissions >= RPL_DAO_MAX_RETRANSMISSIONS) {
|
||||
/* No more retransmissions - give up. */
|
||||
if(instance->lifetime_unit == 0xffff && instance->default_lifetime == 0xff) {
|
||||
/*
|
||||
* ContikiRPL was previously using infinite lifetime for routes
|
||||
* and no DAO_ACK configured. This probably means that the root
|
||||
* and possibly other nodes might be running an old version that
|
||||
* does not support DAO ack. Assume that everything is ok for
|
||||
* now and let the normal repair mechanisms detect any problems.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if(RPL_IS_STORING(instance) && instance->of->dao_ack_callback) {
|
||||
/* Inform the objective function about the timeout. */
|
||||
instance->of->dao_ack_callback(parent, RPL_DAO_ACK_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Perform local repair and hope to find another parent. */
|
||||
rpl_local_repair(instance);
|
||||
return;
|
||||
}
|
||||
|
||||
PRINTF("RPL: will retransmit DAO - seq:%d trans:%d\n", instance->my_dao_seqno,
|
||||
instance->my_dao_transmissions);
|
||||
|
||||
if(get_global_addr(&prefix) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctimer_set(&instance->dao_retransmit_timer,
|
||||
RPL_DAO_RETRANSMISSION_TIMEOUT / 2 +
|
||||
(random_rand() % (RPL_DAO_RETRANSMISSION_TIMEOUT / 2)),
|
||||
handle_dao_retransmission, parent);
|
||||
|
||||
instance->my_dao_transmissions++;
|
||||
dao_output_target_seq(parent, &prefix,
|
||||
instance->default_lifetime, instance->my_dao_seqno);
|
||||
}
|
||||
#endif /* RPL_WITH_DAO_ACK */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
dao_output(rpl_parent_t *parent, uint8_t lifetime)
|
||||
{
|
||||
|
@ -817,18 +1096,51 @@ dao_output(rpl_parent_t *parent, uint8_t lifetime)
|
|||
return;
|
||||
}
|
||||
|
||||
if(parent == NULL || parent->dag == NULL || parent->dag->instance == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
RPL_LOLLIPOP_INCREMENT(dao_sequence);
|
||||
#if RPL_WITH_DAO_ACK
|
||||
/* set up the state since this will be the first transmission of DAO */
|
||||
/* retransmissions will call directly to dao_output_target_seq */
|
||||
/* keep track of my own sending of DAO for handling ack and loss of ack */
|
||||
if(lifetime != RPL_ZERO_LIFETIME) {
|
||||
rpl_instance_t *instance;
|
||||
instance = parent->dag->instance;
|
||||
|
||||
instance->my_dao_seqno = dao_sequence;
|
||||
instance->my_dao_transmissions = 1;
|
||||
ctimer_set(&instance->dao_retransmit_timer, RPL_DAO_RETRANSMISSION_TIMEOUT,
|
||||
handle_dao_retransmission, parent);
|
||||
}
|
||||
#else
|
||||
/* We know that we have tried to register so now we are assuming
|
||||
that we have a down-link - unless this is a zero lifetime one */
|
||||
parent->dag->instance->has_downward_route = lifetime != RPL_ZERO_LIFETIME;
|
||||
#endif /* RPL_WITH_DAO_ACK */
|
||||
|
||||
/* Sending a DAO with own prefix as target */
|
||||
dao_output_target(parent, &prefix, lifetime);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
||||
{
|
||||
dao_output_target_seq(parent, prefix, lifetime, dao_sequence);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
dao_output_target_seq(rpl_parent_t *parent, uip_ipaddr_t *prefix,
|
||||
uint8_t lifetime, uint8_t seq_no)
|
||||
{
|
||||
rpl_dag_t *dag;
|
||||
rpl_instance_t *instance;
|
||||
unsigned char *buffer;
|
||||
uint8_t prefixlen;
|
||||
int pos;
|
||||
uip_ipaddr_t *parent_ipaddr = NULL;
|
||||
uip_ipaddr_t *dest_ipaddr = NULL;
|
||||
|
||||
/* Destination Advertisement Object */
|
||||
|
||||
|
@ -842,6 +1154,12 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
|||
return;
|
||||
}
|
||||
|
||||
parent_ipaddr = rpl_get_parent_ipaddr(parent);
|
||||
if(parent_ipaddr == NULL) {
|
||||
PRINTF("RPL dao_output_target error parent IP address NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dag = parent->dag;
|
||||
if(dag == NULL) {
|
||||
PRINTF("RPL dao_output_target error dag NULL\n");
|
||||
|
@ -863,8 +1181,6 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
|||
#endif
|
||||
|
||||
buffer = UIP_ICMP_PAYLOAD;
|
||||
|
||||
RPL_LOLLIPOP_INCREMENT(dao_sequence);
|
||||
pos = 0;
|
||||
|
||||
buffer[pos++] = instance->instance_id;
|
||||
|
@ -872,12 +1188,14 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
|||
#if RPL_DAO_SPECIFY_DAG
|
||||
buffer[pos] |= RPL_DAO_D_FLAG;
|
||||
#endif /* RPL_DAO_SPECIFY_DAG */
|
||||
#if RPL_CONF_DAO_ACK
|
||||
#if RPL_WITH_DAO_ACK
|
||||
if(lifetime != RPL_ZERO_LIFETIME) {
|
||||
buffer[pos] |= RPL_DAO_K_FLAG;
|
||||
#endif /* RPL_CONF_DAO_ACK */
|
||||
}
|
||||
#endif /* RPL_WITH_DAO_ACK */
|
||||
++pos;
|
||||
buffer[pos++] = 0; /* reserved */
|
||||
buffer[pos++] = dao_sequence;
|
||||
buffer[pos++] = seq_no;
|
||||
#if RPL_DAO_SPECIFY_DAG
|
||||
memcpy(buffer + pos, &dag->dag_id, sizeof(dag->dag_id));
|
||||
pos+=sizeof(dag->dag_id);
|
||||
|
@ -894,61 +1212,154 @@ dao_output_target(rpl_parent_t *parent, uip_ipaddr_t *prefix, uint8_t lifetime)
|
|||
|
||||
/* Create a transit information sub-option. */
|
||||
buffer[pos++] = RPL_OPTION_TRANSIT;
|
||||
buffer[pos++] = 4;
|
||||
buffer[pos++] = (instance->mop != RPL_MOP_NON_STORING) ? 4 : 20;
|
||||
buffer[pos++] = 0; /* flags - ignored */
|
||||
buffer[pos++] = 0; /* path control - ignored */
|
||||
buffer[pos++] = 0; /* path seq - ignored */
|
||||
buffer[pos++] = lifetime;
|
||||
|
||||
PRINTF("RPL: Sending %sDAO with prefix ", lifetime == RPL_ZERO_LIFETIME ? "No-Path " : "");
|
||||
if(instance->mop != RPL_MOP_NON_STORING) {
|
||||
/* Send DAO to parent */
|
||||
dest_ipaddr = parent_ipaddr;
|
||||
} else {
|
||||
/* Include parent global IP address */
|
||||
memcpy(buffer + pos, &parent->dag->dag_id, 8); /* Prefix */
|
||||
pos += 8;
|
||||
memcpy(buffer + pos, ((const unsigned char *)parent_ipaddr) + 8, 8); /* Interface identifier */
|
||||
pos += 8;
|
||||
/* Send DAO to root */
|
||||
dest_ipaddr = &parent->dag->dag_id;
|
||||
}
|
||||
|
||||
PRINTF("RPL: Sending a %sDAO with sequence number %u, lifetime %u, prefix ",
|
||||
lifetime == RPL_ZERO_LIFETIME ? "No-Path " : "", seq_no, lifetime);
|
||||
|
||||
PRINT6ADDR(prefix);
|
||||
PRINTF(" to ");
|
||||
PRINT6ADDR(rpl_get_parent_ipaddr(parent));
|
||||
PRINT6ADDR(dest_ipaddr);
|
||||
PRINTF(" , parent ");
|
||||
PRINT6ADDR(parent_ipaddr);
|
||||
PRINTF("\n");
|
||||
|
||||
if(rpl_get_parent_ipaddr(parent) != NULL) {
|
||||
uip_icmp6_send(rpl_get_parent_ipaddr(parent), ICMP6_RPL, RPL_CODE_DAO, pos);
|
||||
if(dest_ipaddr != NULL) {
|
||||
uip_icmp6_send(dest_ipaddr, ICMP6_RPL, RPL_CODE_DAO, pos);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
dao_ack_input(void)
|
||||
{
|
||||
#if DEBUG
|
||||
unsigned char *buffer;
|
||||
#if RPL_WITH_DAO_ACK
|
||||
|
||||
uint8_t *buffer;
|
||||
uint8_t instance_id;
|
||||
uint8_t sequence;
|
||||
uint8_t status;
|
||||
rpl_instance_t *instance;
|
||||
rpl_parent_t *parent;
|
||||
|
||||
buffer = UIP_ICMP_PAYLOAD;
|
||||
|
||||
instance_id = buffer[0];
|
||||
sequence = buffer[2];
|
||||
status = buffer[3];
|
||||
|
||||
PRINTF("RPL: Received a DAO ACK with sequence number %d and status %d from ",
|
||||
sequence, status);
|
||||
instance = rpl_get_instance(instance_id);
|
||||
if(instance == NULL) {
|
||||
uip_clear_buf();
|
||||
return;
|
||||
}
|
||||
|
||||
if(RPL_IS_STORING(instance)) {
|
||||
parent = rpl_find_parent(instance->current_dag, &UIP_IP_BUF->srcipaddr);
|
||||
if(parent == NULL) {
|
||||
/* not a known instance - drop the packet and ignore */
|
||||
uip_clear_buf();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
parent = NULL;
|
||||
}
|
||||
|
||||
PRINTF("RPL: Received a DAO %s with sequence number %d (%d) and status %d from ",
|
||||
status < 128 ? "ACK" : "NACK",
|
||||
sequence, instance->my_dao_seqno, status);
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
PRINTF("\n");
|
||||
#endif /* DEBUG */
|
||||
|
||||
if(sequence == instance->my_dao_seqno) {
|
||||
instance->has_downward_route = status < 128;
|
||||
|
||||
/* always stop the retransmit timer when the ACK arrived */
|
||||
ctimer_stop(&instance->dao_retransmit_timer);
|
||||
|
||||
/* Inform objective function on status of the DAO ACK */
|
||||
if(RPL_IS_STORING(instance) && instance->of->dao_ack_callback) {
|
||||
instance->of->dao_ack_callback(parent, status);
|
||||
}
|
||||
|
||||
#if RPL_REPAIR_ON_DAO_NACK
|
||||
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
|
||||
/*
|
||||
* Failed the DAO transmission - need to remove the default route.
|
||||
* Trigger a local repair since we can not get our DAO in.
|
||||
*/
|
||||
rpl_local_repair(instance);
|
||||
}
|
||||
#endif
|
||||
|
||||
} else if(RPL_IS_STORING(instance)) {
|
||||
/* this DAO ACK should be forwarded to another recently registered route */
|
||||
uip_ds6_route_t *re;
|
||||
uip_ipaddr_t *nexthop;
|
||||
if((re = find_route_entry_by_dao_ack(sequence)) != NULL) {
|
||||
/* pick the recorded seq no from that node and forward DAO ACK - and
|
||||
clear the pending flag*/
|
||||
RPL_ROUTE_CLEAR_DAO_PENDING(re);
|
||||
|
||||
nexthop = uip_ds6_route_nexthop(re);
|
||||
if(nexthop == NULL) {
|
||||
PRINTF("RPL: No next hop to fwd DAO ACK to\n");
|
||||
} else {
|
||||
PRINTF("RPL: Fwd DAO ACK to:");
|
||||
PRINT6ADDR(nexthop);
|
||||
PRINTF("\n");
|
||||
buffer[2] = re->state.dao_seqno_in;
|
||||
uip_icmp6_send(nexthop, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
|
||||
}
|
||||
|
||||
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
|
||||
/* this node did not get in to the routing tables above... - remove */
|
||||
uip_ds6_route_rm(re);
|
||||
}
|
||||
} else {
|
||||
PRINTF("RPL: No route entry found to forward DAO ACK (seqno %u)\n", sequence);
|
||||
}
|
||||
}
|
||||
#endif /* RPL_WITH_DAO_ACK */
|
||||
uip_clear_buf();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
dao_ack_output(rpl_instance_t *instance, uip_ipaddr_t *dest, uint8_t sequence)
|
||||
dao_ack_output(rpl_instance_t *instance, uip_ipaddr_t *dest, uint8_t sequence,
|
||||
uint8_t status)
|
||||
{
|
||||
#if RPL_WITH_DAO_ACK
|
||||
unsigned char *buffer;
|
||||
|
||||
PRINTF("RPL: Sending a DAO ACK with sequence number %d to ", sequence);
|
||||
PRINTF("RPL: Sending a DAO %s with sequence number %d to ", status < 128 ? "ACK" : "NACK", sequence);
|
||||
PRINT6ADDR(dest);
|
||||
PRINTF("\n");
|
||||
PRINTF(" with status %d\n", status);
|
||||
|
||||
buffer = UIP_ICMP_PAYLOAD;
|
||||
|
||||
buffer[0] = instance->instance_id;
|
||||
buffer[1] = 0;
|
||||
buffer[2] = sequence;
|
||||
buffer[3] = 0;
|
||||
buffer[3] = status;
|
||||
|
||||
uip_icmp6_send(dest, ICMP6_RPL, RPL_CODE_DAO_ACK, 4);
|
||||
#endif /* RPL_WITH_DAO_ACK */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
/**
|
||||
* \file
|
||||
* The Minimum Rank with Hysteresis Objective Function (MRHOF)
|
||||
* The Minimum Rank with Hysteresis Objective Function (MRHOF), RFC6719
|
||||
*
|
||||
* This implementation uses the estimated number of
|
||||
* transmissions (ETX) as the additive routing metric,
|
||||
|
@ -46,150 +46,187 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
#include "net/rpl/rpl.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/nbr-table.h"
|
||||
#include "net/link-stats.h"
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
static void reset(rpl_dag_t *);
|
||||
static void neighbor_link_callback(rpl_parent_t *, int, int);
|
||||
static rpl_parent_t *best_parent(rpl_parent_t *, rpl_parent_t *);
|
||||
static rpl_dag_t *best_dag(rpl_dag_t *, rpl_dag_t *);
|
||||
static rpl_rank_t calculate_rank(rpl_parent_t *, rpl_rank_t);
|
||||
static void update_metric_container(rpl_instance_t *);
|
||||
/* RFC6551 and RFC6719 do not mandate the use of a specific formula to
|
||||
* compute the ETX value. This MRHOF implementation relies on the value
|
||||
* computed by the link-stats module. It has an optional feature,
|
||||
* RPL_MRHOF_CONF_SQUARED_ETX, that consists in squaring this value.
|
||||
* This basically penalizes bad links while preserving the semantics of ETX
|
||||
* (1 = perfect link, more = worse link). As a result, MRHOF will favor
|
||||
* good links over short paths. Recommended when reliability is a priority.
|
||||
* Without this feature, a hop with 50% PRR (ETX=2) is equivalent to two
|
||||
* perfect hops with 100% PRR (ETX=1+1=2). With this feature, the former
|
||||
* path obtains ETX=2*2=4 and the former ETX=1*1+1*1=2. */
|
||||
#ifdef RPL_MRHOF_CONF_SQUARED_ETX
|
||||
#define RPL_MRHOF_SQUARED_ETX RPL_MRHOF_CONF_SQUARED_ETX
|
||||
#else /* RPL_MRHOF_CONF_SQUARED_ETX */
|
||||
#define RPL_MRHOF_SQUARED_ETX 0
|
||||
#endif /* RPL_MRHOF_CONF_SQUARED_ETX */
|
||||
|
||||
rpl_of_t rpl_mrhof = {
|
||||
reset,
|
||||
neighbor_link_callback,
|
||||
best_parent,
|
||||
best_dag,
|
||||
calculate_rank,
|
||||
update_metric_container,
|
||||
1
|
||||
};
|
||||
|
||||
/* Constants for the ETX moving average */
|
||||
#define ETX_SCALE 100
|
||||
#define ETX_ALPHA 90
|
||||
|
||||
/* Reject parents that have a higher link metric than the following. */
|
||||
#define MAX_LINK_METRIC 10
|
||||
#if !RPL_MRHOF_SQUARED_ETX
|
||||
/* Configuration parameters of RFC6719. Reject parents that have a higher
|
||||
* link metric than the following. The default value is 512 but we use 1024. */
|
||||
#define MAX_LINK_METRIC 1024 /* Eq ETX of 8 */
|
||||
/* Hysteresis of MRHOF: the rank must differ more than PARENT_SWITCH_THRESHOLD_DIV
|
||||
* in order to switch preferred parent. Default in RFC6719: 192, eq ETX of 1.5.
|
||||
* We use a more aggressive setting: 96, eq ETX of 0.75.
|
||||
*/
|
||||
#define PARENT_SWITCH_THRESHOLD 96 /* Eq ETX of 0.75 */
|
||||
#else /* !RPL_MRHOF_SQUARED_ETX */
|
||||
#define MAX_LINK_METRIC 2048 /* Eq ETX of 4 */
|
||||
#define PARENT_SWITCH_THRESHOLD 160 /* Eq ETX of 1.25 (results in a churn comparable
|
||||
to the threshold of 96 in the non-squared case) */
|
||||
#endif /* !RPL_MRHOF_SQUARED_ETX */
|
||||
|
||||
/* Reject parents that have a higher path cost than the following. */
|
||||
#define MAX_PATH_COST 100
|
||||
|
||||
/*
|
||||
* The rank must differ more than 1/PARENT_SWITCH_THRESHOLD_DIV in order
|
||||
* to switch preferred parent.
|
||||
*/
|
||||
#define PARENT_SWITCH_THRESHOLD_DIV 2
|
||||
|
||||
typedef uint16_t rpl_path_metric_t;
|
||||
|
||||
static rpl_path_metric_t
|
||||
calculate_path_metric(rpl_parent_t *p)
|
||||
{
|
||||
uip_ds6_nbr_t *nbr;
|
||||
if(p == NULL) {
|
||||
return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR;
|
||||
}
|
||||
nbr = rpl_get_nbr(p);
|
||||
if(nbr == NULL) {
|
||||
return MAX_PATH_COST * RPL_DAG_MC_ETX_DIVISOR;
|
||||
}
|
||||
#if RPL_DAG_MC == RPL_DAG_MC_NONE
|
||||
{
|
||||
return p->rank + (uint16_t)nbr->link_metric;
|
||||
}
|
||||
#elif RPL_DAG_MC == RPL_DAG_MC_ETX
|
||||
return p->mc.obj.etx + (uint16_t)nbr->link_metric;
|
||||
#elif RPL_DAG_MC == RPL_DAG_MC_ENERGY
|
||||
return p->mc.obj.energy.energy_est + (uint16_t)nbr->link_metric;
|
||||
#else
|
||||
#error "Unsupported RPL_DAG_MC configured. See rpl.h."
|
||||
#endif /* RPL_DAG_MC */
|
||||
}
|
||||
#define MAX_PATH_COST 32768 /* Eq path ETX of 256 */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
reset(rpl_dag_t *dag)
|
||||
{
|
||||
PRINTF("RPL: Reset MRHOF\n");
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if RPL_WITH_DAO_ACK
|
||||
static void
|
||||
neighbor_link_callback(rpl_parent_t *p, int status, int numtx)
|
||||
dao_ack_callback(rpl_parent_t *p, int status)
|
||||
{
|
||||
uint16_t recorded_etx = 0;
|
||||
uint16_t packet_etx = numtx * RPL_DAG_MC_ETX_DIVISOR;
|
||||
uint16_t new_etx;
|
||||
uip_ds6_nbr_t *nbr = NULL;
|
||||
|
||||
nbr = rpl_get_nbr(p);
|
||||
if(nbr == NULL) {
|
||||
/* No neighbor for this parent - something bad has occurred */
|
||||
if(status == RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT) {
|
||||
return;
|
||||
}
|
||||
|
||||
recorded_etx = nbr->link_metric;
|
||||
|
||||
/* Do not penalize the ETX when collisions or transmission errors occur. */
|
||||
if(status == MAC_TX_OK || status == MAC_TX_NOACK) {
|
||||
if(status == MAC_TX_NOACK) {
|
||||
packet_etx = MAX_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
|
||||
}
|
||||
|
||||
if(p->flags & RPL_PARENT_FLAG_LINK_METRIC_VALID) {
|
||||
/* We already have a valid link metric, use weighted moving average to update it */
|
||||
new_etx = ((uint32_t)recorded_etx * ETX_ALPHA +
|
||||
(uint32_t)packet_etx * (ETX_SCALE - ETX_ALPHA)) / ETX_SCALE;
|
||||
} else {
|
||||
/* We don't have a valid link metric, set it to the current packet's ETX */
|
||||
new_etx = packet_etx;
|
||||
/* Set link metric as valid */
|
||||
p->flags |= RPL_PARENT_FLAG_LINK_METRIC_VALID;
|
||||
}
|
||||
|
||||
PRINTF("RPL: ETX changed from %u to %u (packet ETX = %u)\n",
|
||||
(unsigned)(recorded_etx / RPL_DAG_MC_ETX_DIVISOR),
|
||||
(unsigned)(new_etx / RPL_DAG_MC_ETX_DIVISOR),
|
||||
(unsigned)(packet_etx / RPL_DAG_MC_ETX_DIVISOR));
|
||||
/* update the link metric for this nbr */
|
||||
nbr->link_metric = new_etx;
|
||||
/* here we need to handle failed DAO's and other stuff */
|
||||
PRINTF("RPL: MRHOF - DAO ACK received with status: %d\n", status);
|
||||
if(status >= RPL_DAO_ACK_UNABLE_TO_ACCEPT) {
|
||||
/* punish the ETX as if this was 10 packets lost */
|
||||
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
|
||||
} else if(status == RPL_DAO_ACK_TIMEOUT) { /* timeout = no ack */
|
||||
/* punish the total lack of ACK with a similar punishment */
|
||||
link_stats_packet_sent(rpl_get_parent_lladdr(p), MAC_TX_OK, 10);
|
||||
}
|
||||
}
|
||||
|
||||
static rpl_rank_t
|
||||
calculate_rank(rpl_parent_t *p, rpl_rank_t base_rank)
|
||||
#endif /* RPL_WITH_DAO_ACK */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static uint16_t
|
||||
parent_link_metric(rpl_parent_t *p)
|
||||
{
|
||||
rpl_rank_t new_rank;
|
||||
rpl_rank_t rank_increase;
|
||||
uip_ds6_nbr_t *nbr;
|
||||
const struct link_stats *stats = rpl_get_parent_link_stats(p);
|
||||
if(stats != NULL) {
|
||||
#if RPL_MRHOF_SQUARED_ETX
|
||||
uint32_t squared_etx = ((uint32_t)stats->etx * stats->etx) / LINK_STATS_ETX_DIVISOR;
|
||||
return (uint16_t)MIN(squared_etx, 0xffff);
|
||||
#else /* RPL_MRHOF_SQUARED_ETX */
|
||||
return stats->etx;
|
||||
#endif /* RPL_MRHOF_SQUARED_ETX */
|
||||
}
|
||||
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(base_rank == 0) {
|
||||
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
#if RPL_WITH_MC
|
||||
/* Handle the different MC types */
|
||||
switch(p->dag->instance->mc.type) {
|
||||
case RPL_DAG_MC_ETX:
|
||||
base = p->mc.obj.etx;
|
||||
break;
|
||||
case RPL_DAG_MC_ENERGY:
|
||||
base = p->mc.obj.energy.energy_est << 8;
|
||||
break;
|
||||
default:
|
||||
base = p->rank;
|
||||
break;
|
||||
}
|
||||
#else /* RPL_WITH_MC */
|
||||
base = p->rank;
|
||||
#endif /* RPL_WITH_MC */
|
||||
|
||||
/* path cost upper bound: 0xffff */
|
||||
return MIN((uint32_t)base + parent_link_metric(p), 0xffff);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static rpl_rank_t
|
||||
rank_via_parent(rpl_parent_t *p)
|
||||
{
|
||||
uint16_t min_hoprankinc;
|
||||
uint16_t path_cost;
|
||||
|
||||
if(p == NULL || p->dag == NULL || p->dag->instance == NULL) {
|
||||
return INFINITE_RANK;
|
||||
}
|
||||
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;
|
||||
|
||||
min_hoprankinc = p->dag->instance->min_hoprankinc;
|
||||
path_cost = parent_path_cost(p);
|
||||
|
||||
/* Rank lower-bound: parent rank + min_hoprankinc */
|
||||
return MAX(MIN((uint32_t)p->rank + min_hoprankinc, 0xffff), path_cost);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
parent_is_acceptable(rpl_parent_t *p)
|
||||
{
|
||||
uint16_t link_metric = parent_link_metric(p);
|
||||
uint16_t path_cost = parent_path_cost(p);
|
||||
/* Exclude links with too high link metrics or path cost (RFC6719, 3.2.2) */
|
||||
return link_metric <= MAX_LINK_METRIC && path_cost <= MAX_PATH_COST;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
parent_has_usable_link(rpl_parent_t *p)
|
||||
{
|
||||
uint16_t link_metric = parent_link_metric(p);
|
||||
/* Exclude links with too high link metrics */
|
||||
return link_metric <= MAX_LINK_METRIC;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static rpl_parent_t *
|
||||
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
|
||||
{
|
||||
rpl_dag_t *dag;
|
||||
uint16_t p1_cost;
|
||||
uint16_t p2_cost;
|
||||
int p1_is_acceptable;
|
||||
int p2_is_acceptable;
|
||||
|
||||
p1_is_acceptable = p1 != NULL && parent_is_acceptable(p1);
|
||||
p2_is_acceptable = p2 != NULL && parent_is_acceptable(p2);
|
||||
|
||||
if(!p1_is_acceptable) {
|
||||
return p2_is_acceptable ? p2 : NULL;
|
||||
}
|
||||
if(!p2_is_acceptable) {
|
||||
return p1_is_acceptable ? p1 : NULL;
|
||||
}
|
||||
|
||||
dag = p1->dag; /* Both parents are in the same DAG. */
|
||||
p1_cost = parent_path_cost(p1);
|
||||
p2_cost = parent_path_cost(p2);
|
||||
|
||||
/* Maintain stability of the preferred parent in case of similar ranks. */
|
||||
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
|
||||
if(p1_cost < p2_cost + PARENT_SWITCH_THRESHOLD &&
|
||||
p1_cost > p2_cost - PARENT_SWITCH_THRESHOLD) {
|
||||
return dag->preferred_parent;
|
||||
}
|
||||
}
|
||||
|
||||
if(INFINITE_RANK - base_rank < rank_increase) {
|
||||
/* Reached the maximum rank. */
|
||||
new_rank = INFINITE_RANK;
|
||||
} else {
|
||||
/* Calculate the rank based on the new rank information from DIO or
|
||||
stored otherwise. */
|
||||
new_rank = base_rank + rank_increase;
|
||||
return p1_cost < p2_cost ? p1 : p2;
|
||||
}
|
||||
|
||||
return new_rank;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static rpl_dag_t *
|
||||
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
||||
{
|
||||
|
@ -203,93 +240,77 @@ best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
|||
|
||||
return d1->rank < d2->rank ? d1 : d2;
|
||||
}
|
||||
|
||||
static rpl_parent_t *
|
||||
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
|
||||
{
|
||||
rpl_dag_t *dag;
|
||||
rpl_path_metric_t min_diff;
|
||||
rpl_path_metric_t p1_metric;
|
||||
rpl_path_metric_t p2_metric;
|
||||
|
||||
dag = p1->dag; /* Both parents are in the same DAG. */
|
||||
|
||||
min_diff = RPL_DAG_MC_ETX_DIVISOR /
|
||||
PARENT_SWITCH_THRESHOLD_DIV;
|
||||
|
||||
p1_metric = calculate_path_metric(p1);
|
||||
p2_metric = calculate_path_metric(p2);
|
||||
|
||||
/* Maintain stability of the preferred parent in case of similar ranks. */
|
||||
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
|
||||
if(p1_metric < p2_metric + min_diff &&
|
||||
p1_metric > p2_metric - min_diff) {
|
||||
PRINTF("RPL: MRHOF hysteresis: %u <= %u <= %u\n",
|
||||
p2_metric - min_diff,
|
||||
p1_metric,
|
||||
p2_metric + min_diff);
|
||||
return dag->preferred_parent;
|
||||
}
|
||||
}
|
||||
|
||||
return p1_metric < p2_metric ? p1 : p2;
|
||||
}
|
||||
|
||||
#if RPL_DAG_MC == RPL_DAG_MC_NONE
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if !RPL_WITH_MC
|
||||
static void
|
||||
update_metric_container(rpl_instance_t *instance)
|
||||
{
|
||||
instance->mc.type = RPL_DAG_MC;
|
||||
instance->mc.type = RPL_DAG_MC_NONE;
|
||||
}
|
||||
#else
|
||||
#else /* RPL_WITH_MC */
|
||||
static void
|
||||
update_metric_container(rpl_instance_t *instance)
|
||||
{
|
||||
rpl_path_metric_t path_metric;
|
||||
rpl_dag_t *dag;
|
||||
#if RPL_DAG_MC == RPL_DAG_MC_ENERGY
|
||||
uint16_t path_cost;
|
||||
uint8_t type;
|
||||
#endif
|
||||
|
||||
instance->mc.type = RPL_DAG_MC;
|
||||
instance->mc.flags = RPL_DAG_MC_FLAG_P;
|
||||
instance->mc.aggr = RPL_DAG_MC_AGGR_ADDITIVE;
|
||||
instance->mc.prec = 0;
|
||||
|
||||
dag = instance->current_dag;
|
||||
|
||||
if (!dag->joined) {
|
||||
if(dag == NULL || !dag->joined) {
|
||||
PRINTF("RPL: Cannot update the metric container when not joined\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(dag->rank == ROOT_RANK(instance)) {
|
||||
path_metric = 0;
|
||||
/* Configure MC at root only, other nodes are auto-configured when joining */
|
||||
instance->mc.type = RPL_DAG_MC;
|
||||
instance->mc.flags = 0;
|
||||
instance->mc.aggr = RPL_DAG_MC_AGGR_ADDITIVE;
|
||||
instance->mc.prec = 0;
|
||||
path_cost = dag->rank;
|
||||
} else {
|
||||
path_metric = calculate_path_metric(dag->preferred_parent);
|
||||
path_cost = parent_path_cost(dag->preferred_parent);
|
||||
}
|
||||
|
||||
#if RPL_DAG_MC == RPL_DAG_MC_ETX
|
||||
/* Handle the different MC types */
|
||||
switch(instance->mc.type) {
|
||||
case RPL_DAG_MC_NONE:
|
||||
break;
|
||||
case RPL_DAG_MC_ETX:
|
||||
instance->mc.length = sizeof(instance->mc.obj.etx);
|
||||
instance->mc.obj.etx = path_metric;
|
||||
|
||||
PRINTF("RPL: My path ETX to the root is %u.%u\n",
|
||||
instance->mc.obj.etx / RPL_DAG_MC_ETX_DIVISOR,
|
||||
(instance->mc.obj.etx % RPL_DAG_MC_ETX_DIVISOR * 100) /
|
||||
RPL_DAG_MC_ETX_DIVISOR);
|
||||
#elif RPL_DAG_MC == RPL_DAG_MC_ENERGY
|
||||
instance->mc.obj.etx = path_cost;
|
||||
break;
|
||||
case RPL_DAG_MC_ENERGY:
|
||||
instance->mc.length = sizeof(instance->mc.obj.energy);
|
||||
|
||||
if(dag->rank == ROOT_RANK(instance)) {
|
||||
type = RPL_DAG_MC_ENERGY_TYPE_MAINS;
|
||||
} else {
|
||||
type = RPL_DAG_MC_ENERGY_TYPE_BATTERY;
|
||||
}
|
||||
|
||||
instance->mc.obj.energy.flags = type << RPL_DAG_MC_ENERGY_TYPE;
|
||||
instance->mc.obj.energy.energy_est = path_metric;
|
||||
#endif /* RPL_DAG_MC == RPL_DAG_MC_ETX */
|
||||
/* Energy_est is only one byte, use the least significant byte of the path metric. */
|
||||
instance->mc.obj.energy.energy_est = path_cost >> 8;
|
||||
break;
|
||||
default:
|
||||
PRINTF("RPL: MRHOF, non-supported MC %u\n", instance->mc.type);
|
||||
break;
|
||||
}
|
||||
#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.
|
||||
*
|
||||
* This file is part of the Contiki operating system.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* An implementation of RPL's objective function 0.
|
||||
* An implementation of RPL's objective function 0, RFC6552
|
||||
*
|
||||
* \author Joakim Eriksson <joakime@sics.se>, Nicolas Tsiftes <nvt@sics.se>
|
||||
*/
|
||||
|
@ -41,133 +42,197 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
#include "net/rpl/rpl.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/nbr-table.h"
|
||||
#include "net/link-stats.h"
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
static void reset(rpl_dag_t *);
|
||||
static rpl_parent_t *best_parent(rpl_parent_t *, rpl_parent_t *);
|
||||
static rpl_dag_t *best_dag(rpl_dag_t *, rpl_dag_t *);
|
||||
static rpl_rank_t calculate_rank(rpl_parent_t *, rpl_rank_t);
|
||||
static void update_metric_container(rpl_instance_t *);
|
||||
/* Constants from RFC6552. We use the default values. */
|
||||
#define RANK_STRETCH 0 /* Must be in the range [0;5] */
|
||||
#define RANK_FACTOR 1 /* Must be in the range [1;4] */
|
||||
|
||||
rpl_of_t rpl_of0 = {
|
||||
reset,
|
||||
NULL,
|
||||
best_parent,
|
||||
best_dag,
|
||||
calculate_rank,
|
||||
update_metric_container,
|
||||
0
|
||||
};
|
||||
#define MIN_STEP_OF_RANK 1
|
||||
#define MAX_STEP_OF_RANK 9
|
||||
|
||||
#define DEFAULT_RANK_INCREMENT RPL_MIN_HOPRANKINC
|
||||
/* OF0 computes rank increase as follows:
|
||||
* rank_increase = (RANK_FACTOR * STEP_OF_RANK + RANK_STRETCH) * min_hop_rank_increase
|
||||
* STEP_OF_RANK is an implementation-specific scalar value in the range [1;9].
|
||||
* RFC6552 provides a default value of 3 but recommends to use a dynamic link metric
|
||||
* such as ETX.
|
||||
* */
|
||||
|
||||
#define MIN_DIFFERENCE (RPL_MIN_HOPRANKINC + RPL_MIN_HOPRANKINC / 2)
|
||||
#define RPL_OF0_FIXED_SR 0
|
||||
#define RPL_OF0_ETX_BASED_SR 1
|
||||
/* Select RPL_OF0_FIXED_SR or RPL_OF0_ETX_BASED_SR */
|
||||
#ifdef RPL_OF0_CONF_SR
|
||||
#define RPL_OF0_SR RPL_OF0_CONF_SR
|
||||
#else /* RPL_OF0_CONF_SR */
|
||||
#define RPL_OF0_SR RPL_OF0_ETX_BASED_SR
|
||||
#endif /* RPL_OF0_CONF_SR */
|
||||
|
||||
#if RPL_OF0_FIXED_SR
|
||||
#define STEP_OF_RANK(p) (3)
|
||||
#endif /* RPL_OF0_FIXED_SR */
|
||||
|
||||
#if RPL_OF0_ETX_BASED_SR
|
||||
/* Numbers suggested by P. Thubert for in the 6TiSCH WG. Anything that maps ETX to
|
||||
* a step between 1 and 9 works. */
|
||||
#define STEP_OF_RANK(p) (((3 * parent_link_metric(p)) / LINK_STATS_ETX_DIVISOR) - 2)
|
||||
#endif /* RPL_OF0_ETX_BASED_SR */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
reset(rpl_dag_t *dag)
|
||||
{
|
||||
PRINTF("RPL: Resetting OF0\n");
|
||||
PRINTF("RPL: Reset OF0\n");
|
||||
}
|
||||
|
||||
static rpl_rank_t
|
||||
calculate_rank(rpl_parent_t *p, rpl_rank_t base_rank)
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if RPL_WITH_DAO_ACK
|
||||
static void
|
||||
dao_ack_callback(rpl_parent_t *p, int status)
|
||||
{
|
||||
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) {
|
||||
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 *
|
||||
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
parent_is_acceptable(rpl_parent_t *p)
|
||||
{
|
||||
if(d1->grounded) {
|
||||
if (!d2->grounded) {
|
||||
return d1;
|
||||
return STEP_OF_RANK(p) >= MIN_STEP_OF_RANK
|
||||
&& STEP_OF_RANK(p) <= MAX_STEP_OF_RANK;
|
||||
}
|
||||
} else if(d2->grounded) {
|
||||
return d2;
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
parent_has_usable_link(rpl_parent_t *p)
|
||||
{
|
||||
return parent_is_acceptable(p);
|
||||
}
|
||||
|
||||
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 rpl_parent_t *
|
||||
best_parent(rpl_parent_t *p1, rpl_parent_t *p2)
|
||||
{
|
||||
rpl_rank_t r1, r2;
|
||||
rpl_dag_t *dag;
|
||||
uip_ds6_nbr_t *nbr1, *nbr2;
|
||||
nbr1 = rpl_get_nbr(p1);
|
||||
nbr2 = rpl_get_nbr(p2);
|
||||
uint16_t p1_cost;
|
||||
uint16_t p2_cost;
|
||||
int p1_is_acceptable;
|
||||
int p2_is_acceptable;
|
||||
|
||||
dag = (rpl_dag_t *)p1->dag; /* Both parents must be in the same DAG. */
|
||||
p1_is_acceptable = p1 != NULL && parent_is_acceptable(p1);
|
||||
p2_is_acceptable = p2 != NULL && parent_is_acceptable(p2);
|
||||
|
||||
if(nbr1 == NULL || nbr2 == NULL) {
|
||||
return dag->preferred_parent;
|
||||
if(!p1_is_acceptable) {
|
||||
return p2_is_acceptable ? p2 : NULL;
|
||||
}
|
||||
if(!p2_is_acceptable) {
|
||||
return p1_is_acceptable ? p1 : NULL;
|
||||
}
|
||||
|
||||
PRINTF("RPL: Comparing parent ");
|
||||
PRINT6ADDR(rpl_get_parent_ipaddr(p1));
|
||||
PRINTF(" (confidence %d, rank %d) with parent ",
|
||||
nbr1->link_metric, p1->rank);
|
||||
PRINT6ADDR(rpl_get_parent_ipaddr(p2));
|
||||
PRINTF(" (confidence %d, rank %d)\n",
|
||||
nbr2->link_metric, p2->rank);
|
||||
dag = p1->dag; /* Both parents are in the same DAG. */
|
||||
p1_cost = parent_path_cost(p1);
|
||||
p2_cost = parent_path_cost(p2);
|
||||
|
||||
|
||||
r1 = DAG_RANK(p1->rank, p1->dag->instance) * RPL_MIN_HOPRANKINC +
|
||||
nbr1->link_metric;
|
||||
r2 = DAG_RANK(p2->rank, p1->dag->instance) * RPL_MIN_HOPRANKINC +
|
||||
nbr2->link_metric;
|
||||
/* Compare two parents by looking both and their rank and at the ETX
|
||||
for that parent. We choose the parent that has the most
|
||||
favourable combination. */
|
||||
|
||||
if(r1 < r2 + MIN_DIFFERENCE &&
|
||||
r1 > r2 - MIN_DIFFERENCE) {
|
||||
return dag->preferred_parent;
|
||||
} else if(r1 < r2) {
|
||||
return p1;
|
||||
/* Paths costs coarse-grained (multiple of min_hoprankinc), we operate without hysteresis */
|
||||
if(p1_cost != p2_cost) {
|
||||
/* Pick parent with lowest path cost */
|
||||
return p1_cost < p2_cost ? p1 : p2;
|
||||
} else {
|
||||
return p2;
|
||||
/* We have a tie! */
|
||||
/* Stik to current preferred parent if possible */
|
||||
if(p1 == dag->preferred_parent || p2 == dag->preferred_parent) {
|
||||
return dag->preferred_parent;
|
||||
}
|
||||
/* None of the nodes is the current preferred parent,
|
||||
* choose parent with best link metric */
|
||||
return parent_link_metric(p1) < parent_link_metric(p2) ? p1 : p2;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static rpl_dag_t *
|
||||
best_dag(rpl_dag_t *d1, rpl_dag_t *d2)
|
||||
{
|
||||
if(d1->grounded != d2->grounded) {
|
||||
return d1->grounded ? d1 : d2;
|
||||
}
|
||||
|
||||
if(d1->preference != d2->preference) {
|
||||
return d1->preference > d2->preference ? d1 : d2;
|
||||
}
|
||||
|
||||
return d1->rank < d2->rank ? d1 : d2;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
update_metric_container(rpl_instance_t *instance)
|
||||
{
|
||||
instance->mc.type = RPL_DAG_MC_NONE;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_of_t rpl_of0 = {
|
||||
reset,
|
||||
#if RPL_WITH_DAO_ACK
|
||||
dao_ack_callback,
|
||||
#endif
|
||||
parent_link_metric,
|
||||
parent_has_usable_link,
|
||||
parent_path_cost,
|
||||
rank_via_parent,
|
||||
best_parent,
|
||||
best_dag,
|
||||
update_metric_container,
|
||||
RPL_OCP_OF0
|
||||
};
|
||||
|
||||
/** @}*/
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
#include "sys/clock.h"
|
||||
#include "sys/ctimer.h"
|
||||
#include "net/ipv6/uip-ds6.h"
|
||||
#include "net/ipv6/uip-ds6-route.h"
|
||||
#include "net/rpl/rpl-ns.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -90,10 +92,21 @@
|
|||
|
||||
#define RPL_DAO_K_FLAG 0x80 /* DAO ACK requested */
|
||||
#define RPL_DAO_D_FLAG 0x40 /* DODAG ID present */
|
||||
|
||||
#define RPL_DAO_ACK_UNCONDITIONAL_ACCEPT 0
|
||||
#define RPL_DAO_ACK_ACCEPT 1 /* 1 - 127 is OK but not good */
|
||||
#define RPL_DAO_ACK_UNABLE_TO_ACCEPT 128 /* >127 is fail */
|
||||
#define RPL_DAO_ACK_UNABLE_TO_ADD_ROUTE_AT_ROOT 255 /* root can not accept */
|
||||
|
||||
#define RPL_DAO_ACK_TIMEOUT -1
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* RPL IPv6 extension header option. */
|
||||
#define RPL_HDR_OPT_LEN 4
|
||||
#define RPL_HOP_BY_HOP_LEN (RPL_HDR_OPT_LEN + 2 + 2)
|
||||
#define RPL_RH_LEN 4
|
||||
#define RPL_SRH_LEN 4
|
||||
#define RPL_RH_TYPE_SRH 3
|
||||
#define RPL_HDR_OPT_DOWN 0x80
|
||||
#define RPL_HDR_OPT_DOWN_SHIFT 7
|
||||
#define RPL_HDR_OPT_RANK_ERR 0x40
|
||||
|
@ -117,6 +130,18 @@
|
|||
#define RPL_NOPATH_REMOVAL_DELAY 60
|
||||
#endif /* RPL_CONF_NOPATH_REMOVAL_DELAY */
|
||||
|
||||
#ifdef RPL_CONF_DAO_MAX_RETRANSMISSIONS
|
||||
#define RPL_DAO_MAX_RETRANSMISSIONS RPL_CONF_DAO_MAX_RETRANSMISSIONS
|
||||
#else
|
||||
#define RPL_DAO_MAX_RETRANSMISSIONS 5
|
||||
#endif /* RPL_CONF_DAO_MAX_RETRANSMISSIONS */
|
||||
|
||||
#ifdef RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
|
||||
#define RPL_DAO_RETRANSMISSION_TIMEOUT RPL_CONF_DAO_RETRANSMISSION_TIMEOUT
|
||||
#else
|
||||
#define RPL_DAO_RETRANSMISSION_TIMEOUT (5 * CLOCK_SECOND)
|
||||
#endif /* RPL_CONF_DAO_RETRANSMISSION_TIMEOUT */
|
||||
|
||||
/* Special value indicating immediate removal. */
|
||||
#define RPL_ZERO_LIFETIME 0
|
||||
|
||||
|
@ -124,11 +149,28 @@
|
|||
((unsigned long)(instance)->lifetime_unit * (lifetime))
|
||||
|
||||
#ifndef RPL_CONF_MIN_HOPRANKINC
|
||||
/* RFC6550 defines the default MIN_HOPRANKINC as 256.
|
||||
* However, we use MRHOF as a default Objective Function (RFC6719),
|
||||
* which recommends setting MIN_HOPRANKINC with care, in particular
|
||||
* when used with ETX as a metric. ETX is computed as a fixed point
|
||||
* real with a divisor of 128 (RFC6719, RFC6551). We choose to also
|
||||
* use 128 for RPL_MIN_HOPRANKINC, resulting in a rank equal to the
|
||||
* ETX path cost. Larger values may also be desirable, as discussed
|
||||
* in section 6.1 of RFC6719. */
|
||||
#if RPL_OF_OCP == RPL_OCP_MRHOF
|
||||
#define RPL_MIN_HOPRANKINC 128
|
||||
#else /* RPL_OF_OCP == RPL_OCP_MRHOF */
|
||||
#define RPL_MIN_HOPRANKINC 256
|
||||
#else
|
||||
#endif /* RPL_OF_OCP == RPL_OCP_MRHOF */
|
||||
#else /* RPL_CONF_MIN_HOPRANKINC */
|
||||
#define RPL_MIN_HOPRANKINC RPL_CONF_MIN_HOPRANKINC
|
||||
#endif
|
||||
#endif /* RPL_CONF_MIN_HOPRANKINC */
|
||||
|
||||
#ifndef RPL_CONF_MAX_RANKINC
|
||||
#define RPL_MAX_RANKINC (7 * RPL_MIN_HOPRANKINC)
|
||||
#else /* RPL_CONF_MAX_RANKINC */
|
||||
#define RPL_MAX_RANKINC RPL_CONF_MAX_RANKINC
|
||||
#endif /* RPL_CONF_MAX_RANKINC */
|
||||
|
||||
#define DAG_RANK(fixpt_rank, instance) \
|
||||
((fixpt_rank) / (instance)->min_hoprankinc)
|
||||
|
@ -157,6 +199,7 @@
|
|||
#define RPL_MOP_STORING_NO_MULTICAST 2
|
||||
#define RPL_MOP_STORING_MULTICAST 3
|
||||
|
||||
/* RPL Mode of operation */
|
||||
#ifdef RPL_CONF_MOP
|
||||
#define RPL_MOP_DEFAULT RPL_CONF_MOP
|
||||
#else /* RPL_CONF_MOP */
|
||||
|
@ -167,6 +210,43 @@
|
|||
#endif /* UIP_IPV6_MULTICAST_RPL */
|
||||
#endif /* RPL_CONF_MOP */
|
||||
|
||||
/*
|
||||
* Embed support for storing mode
|
||||
*/
|
||||
#ifdef RPL_CONF_WITH_STORING
|
||||
#define RPL_WITH_STORING RPL_CONF_WITH_STORING
|
||||
#else /* RPL_CONF_WITH_STORING */
|
||||
/* By default: embed support for non-storing if and only if the configured MOP is not non-storing */
|
||||
#define RPL_WITH_STORING (RPL_MOP_DEFAULT != RPL_MOP_NON_STORING)
|
||||
#endif /* RPL_CONF_WITH_STORING */
|
||||
|
||||
/*
|
||||
* Embed support for non-storing mode
|
||||
*/
|
||||
#ifdef RPL_CONF_WITH_NON_STORING
|
||||
#define RPL_WITH_NON_STORING RPL_CONF_WITH_NON_STORING
|
||||
#else /* RPL_CONF_WITH_NON_STORING */
|
||||
/* By default: embed support for non-storing if and only if the configured MOP is non-storing */
|
||||
#define RPL_WITH_NON_STORING (RPL_MOP_DEFAULT == RPL_MOP_NON_STORING)
|
||||
#endif /* RPL_CONF_WITH_NON_STORING */
|
||||
|
||||
#if RPL_WITH_STORING && (UIP_DS6_ROUTE_NB == 0)
|
||||
#error "RPL with storing mode included but #routes == 0. Set UIP_CONF_MAX_ROUTES accordingly."
|
||||
#if !RPL_WITH_NON_STORING && (RPL_NS_LINK_NUM > 0)
|
||||
#error "You might also want to set RPL_NS_CONF_LINK_NUM to 0."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if RPL_WITH_NON_STORING && (RPL_NS_LINK_NUM == 0)
|
||||
#error "RPL with non-storing mode included but #links == 0. Set RPL_NS_CONF_LINK_NUM accordingly."
|
||||
#if !RPL_WITH_STORING && (UIP_DS6_ROUTE_NB > 0)
|
||||
#error "You might also want to set UIP_CONF_MAX_ROUTES to 0."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define RPL_IS_STORING(instance) (RPL_WITH_STORING && ((instance) != NULL) && ((instance)->mop > RPL_MOP_NON_STORING))
|
||||
#define RPL_IS_NON_STORING(instance) (RPL_WITH_NON_STORING && ((instance) != NULL) && ((instance)->mop == RPL_MOP_NON_STORING))
|
||||
|
||||
/* Emit a pre-processor error if the user configured multicast with bad MOP */
|
||||
#if RPL_CONF_MULTICAST && (RPL_MOP_DEFAULT != RPL_MOP_STORING_MULTICAST)
|
||||
#error "RPL Multicast requires RPL_MOP_DEFAULT==3. Check contiki-conf.h"
|
||||
|
@ -179,13 +259,6 @@
|
|||
#define RPL_MCAST_LIFETIME 3
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The ETX in the metric container is expressed as a fixed-point value
|
||||
* whose integer part can be obtained by dividing the value by
|
||||
* RPL_DAG_MC_ETX_DIVISOR.
|
||||
*/
|
||||
#define RPL_DAG_MC_ETX_DIVISOR 256
|
||||
|
||||
/* DIS related */
|
||||
#define RPL_DIS_SEND 1
|
||||
|
||||
|
@ -250,6 +323,8 @@ typedef struct rpl_stats rpl_stats_t;
|
|||
|
||||
extern rpl_stats_t rpl_stats;
|
||||
#endif
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* RPL macros. */
|
||||
|
||||
|
@ -268,8 +343,10 @@ void dis_output(uip_ipaddr_t *addr);
|
|||
void dio_output(rpl_instance_t *, uip_ipaddr_t *uc_addr);
|
||||
void dao_output(rpl_parent_t *, uint8_t lifetime);
|
||||
void dao_output_target(rpl_parent_t *, uip_ipaddr_t *, uint8_t lifetime);
|
||||
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t);
|
||||
void dao_ack_output(rpl_instance_t *, uip_ipaddr_t *, uint8_t, uint8_t);
|
||||
void rpl_icmp6_register_handlers(void);
|
||||
uip_ds6_nbr_t *rpl_icmp6_update_nbr_table(uip_ipaddr_t *from,
|
||||
nbr_table_reason_t r, void *data);
|
||||
|
||||
/* RPL logic functions. */
|
||||
void rpl_join_dag(uip_ipaddr_t *from, rpl_dio_t *dio);
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
|
||||
#include "contiki-conf.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/rpl/rpl-ns.h"
|
||||
#include "net/link-stats.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||
#include "lib/random.h"
|
||||
#include "sys/ctimer.h"
|
||||
|
@ -55,6 +57,14 @@
|
|||
void RPL_CALLBACK_NEW_DIO_INTERVAL(uint8_t dio_interval);
|
||||
#endif /* RPL_CALLBACK_NEW_DIO_INTERVAL */
|
||||
|
||||
#ifdef RPL_PROBING_SELECT_FUNC
|
||||
rpl_parent_t *RPL_PROBING_SELECT_FUNC(rpl_dag_t *dag);
|
||||
#endif /* RPL_PROBING_SELECT_FUNC */
|
||||
|
||||
#ifdef RPL_PROBING_DELAY_FUNC
|
||||
clock_time_t RPL_PROBING_DELAY_FUNC(rpl_dag_t *dag);
|
||||
#endif /* RPL_PROBING_DELAY_FUNC */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static struct ctimer periodic_timer;
|
||||
|
||||
|
@ -71,14 +81,23 @@ static uint8_t dio_send_ok;
|
|||
static void
|
||||
handle_periodic_timer(void *ptr)
|
||||
{
|
||||
rpl_dag_t *dag = rpl_get_any_dag();
|
||||
|
||||
rpl_purge_dags();
|
||||
if(dag != NULL) {
|
||||
if(RPL_IS_STORING(dag->instance)) {
|
||||
rpl_purge_routes();
|
||||
}
|
||||
if(RPL_IS_NON_STORING(dag->instance)) {
|
||||
rpl_ns_periodic();
|
||||
}
|
||||
}
|
||||
rpl_recalculate_ranks();
|
||||
|
||||
/* handle DIS */
|
||||
#if RPL_DIS_SEND
|
||||
next_dis++;
|
||||
if(rpl_get_any_dag() == NULL && next_dis >= RPL_DIS_INTERVAL) {
|
||||
if(dag == NULL && next_dis >= RPL_DIS_INTERVAL) {
|
||||
next_dis = 0;
|
||||
dis_output(NULL);
|
||||
}
|
||||
|
@ -161,7 +180,7 @@ handle_dio_timer(void *ptr)
|
|||
#endif /* RPL_CONF_STATS */
|
||||
dio_output(instance, NULL);
|
||||
} else {
|
||||
PRINTF("RPL: Supressing DIO transmission (%d >= %d)\n",
|
||||
PRINTF("RPL: Suppressing DIO transmission (%d >= %d)\n",
|
||||
instance->dio_counter, instance->dio_redundancy);
|
||||
}
|
||||
instance->dio_send = 0;
|
||||
|
@ -224,6 +243,8 @@ set_dao_lifetime_timer(rpl_instance_t *instance)
|
|||
expiration_time = (clock_time_t)instance->default_lifetime *
|
||||
(clock_time_t)instance->lifetime_unit *
|
||||
CLOCK_SECOND / 2;
|
||||
/* make the time for the re registration be betwen 1/2 - 3/4 of lifetime */
|
||||
expiration_time = expiration_time + (random_rand() % (expiration_time / 2));
|
||||
PRINTF("RPL: Scheduling DAO lifetime timer %u ticks in the future\n",
|
||||
(unsigned)expiration_time);
|
||||
ctimer_set(&instance->dao_lifetime_timer, expiration_time,
|
||||
|
@ -356,42 +377,57 @@ rpl_schedule_unicast_dio_immediately(rpl_instance_t *instance)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if RPL_WITH_PROBING
|
||||
static rpl_parent_t *
|
||||
clock_time_t
|
||||
get_probing_delay(rpl_dag_t *dag)
|
||||
{
|
||||
if(dag != NULL && dag->instance != NULL
|
||||
&& dag->instance->urgent_probing_target != NULL) {
|
||||
/* Urgent probing needed (to find out if a neighbor may become preferred parent) */
|
||||
return random_rand() % (CLOCK_SECOND * 10);
|
||||
} else {
|
||||
/* Else, use normal probing interval */
|
||||
return ((RPL_PROBING_INTERVAL) / 2) + random_rand() % (RPL_PROBING_INTERVAL);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_parent_t *
|
||||
get_probing_target(rpl_dag_t *dag)
|
||||
{
|
||||
/* Returns the next probing target. The current implementation probes the current
|
||||
* preferred parent if we have not updated its link for RPL_PROBING_EXPIRATION_TIME.
|
||||
/* Returns the next probing target. The current implementation probes the urgent
|
||||
* probing target if any, or the preferred parent if its link statistics need refresh.
|
||||
* Otherwise, it picks at random between:
|
||||
* (1) selecting the best parent not updated for RPL_PROBING_EXPIRATION_TIME
|
||||
* (1) selecting the best parent with non-fresh link statistics
|
||||
* (2) selecting the least recently updated parent
|
||||
*/
|
||||
|
||||
rpl_parent_t *p;
|
||||
rpl_parent_t *probing_target = NULL;
|
||||
rpl_rank_t probing_target_rank = INFINITE_RANK;
|
||||
/* min_last_tx is the clock time RPL_PROBING_EXPIRATION_TIME in the past */
|
||||
clock_time_t min_last_tx = clock_time();
|
||||
min_last_tx = min_last_tx > 2 * RPL_PROBING_EXPIRATION_TIME
|
||||
? min_last_tx - RPL_PROBING_EXPIRATION_TIME : 1;
|
||||
clock_time_t probing_target_age = 0;
|
||||
clock_time_t clock_now = clock_time();
|
||||
|
||||
if(dag == NULL ||
|
||||
dag->instance == NULL ||
|
||||
dag->preferred_parent == NULL) {
|
||||
dag->instance == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Our preferred parent needs probing */
|
||||
if(dag->preferred_parent->last_tx_time < min_last_tx) {
|
||||
probing_target = dag->preferred_parent;
|
||||
/* There is an urgent probing target */
|
||||
if(dag->instance->urgent_probing_target != NULL) {
|
||||
return dag->instance->urgent_probing_target;
|
||||
}
|
||||
|
||||
/* With 50% probability: probe best parent not updated for RPL_PROBING_EXPIRATION_TIME */
|
||||
if(probing_target == NULL && (random_rand() % 2) == 0) {
|
||||
/* The preferred parent needs probing */
|
||||
if(dag->preferred_parent != NULL && !rpl_parent_is_fresh(dag->preferred_parent)) {
|
||||
return dag->preferred_parent;
|
||||
}
|
||||
|
||||
/* With 50% probability: probe best non-fresh parent */
|
||||
if(random_rand() % 2 == 0) {
|
||||
p = nbr_table_head(rpl_parents);
|
||||
while(p != NULL) {
|
||||
if(p->dag == dag && p->last_tx_time < min_last_tx) {
|
||||
if(p->dag == dag && !rpl_parent_is_fresh(p)) {
|
||||
/* p is in our dag and needs probing */
|
||||
rpl_rank_t p_rank = dag->instance->of->calculate_rank(p, 0);
|
||||
rpl_rank_t p_rank = rpl_rank_via_parent(p);
|
||||
if(probing_target == NULL
|
||||
|| p_rank < probing_target_rank) {
|
||||
probing_target = p;
|
||||
|
@ -402,14 +438,16 @@ get_probing_target(rpl_dag_t *dag)
|
|||
}
|
||||
}
|
||||
|
||||
/* The default probing target is the least recently updated parent */
|
||||
/* If we still do not have a probing target: pick the least recently updated parent */
|
||||
if(probing_target == NULL) {
|
||||
p = nbr_table_head(rpl_parents);
|
||||
while(p != NULL) {
|
||||
if(p->dag == dag) {
|
||||
const struct link_stats *stats =rpl_get_parent_link_stats(p);
|
||||
if(p->dag == dag && stats != NULL) {
|
||||
if(probing_target == NULL
|
||||
|| p->last_tx_time < probing_target->last_tx_time) {
|
||||
|| clock_now - stats->last_tx_time > probing_target_age) {
|
||||
probing_target = p;
|
||||
probing_target_age = clock_now - stats->last_tx_time;
|
||||
}
|
||||
}
|
||||
p = nbr_table_next(rpl_parents, p);
|
||||
|
@ -428,11 +466,17 @@ handle_probing_timer(void *ptr)
|
|||
|
||||
/* Perform probing */
|
||||
if(target_ipaddr != NULL) {
|
||||
PRINTF("RPL: probing %u ((last tx %u min ago))\n",
|
||||
nbr_table_get_lladdr(rpl_parents, probing_target)->u8[7],
|
||||
(unsigned)((clock_time() - probing_target->last_tx_time) / (60 * CLOCK_SECOND)));
|
||||
/* Send probe (unicast DIO or DIS) */
|
||||
const struct link_stats *stats = rpl_get_parent_link_stats(probing_target);
|
||||
(void)stats;
|
||||
PRINTF("RPL: probing %u %s last tx %u min ago\n",
|
||||
rpl_get_parent_lladdr(probing_target)->u8[7],
|
||||
instance->urgent_probing_target != NULL ? "(urgent)" : "",
|
||||
probing_target != NULL ?
|
||||
(unsigned)((clock_time() - stats->last_tx_time) / (60 * CLOCK_SECOND)) : 0
|
||||
);
|
||||
/* Send probe, e.g. unicast DIO or DIS */
|
||||
RPL_PROBING_SEND_FUNC(instance, target_ipaddr);
|
||||
instance->urgent_probing_target = NULL;
|
||||
}
|
||||
|
||||
/* Schedule next probing */
|
||||
|
@ -446,7 +490,7 @@ handle_probing_timer(void *ptr)
|
|||
void
|
||||
rpl_schedule_probing(rpl_instance_t *instance)
|
||||
{
|
||||
ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(),
|
||||
ctimer_set(&instance->probing_timer, RPL_PROBING_DELAY_FUNC(instance->current_dag),
|
||||
handle_probing_timer, instance);
|
||||
}
|
||||
#endif /* RPL_WITH_PROBING */
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "net/ipv6/uip-ds6.h"
|
||||
#include "net/ipv6/uip-icmp6.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/rpl/rpl-ns.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
|
@ -216,11 +217,9 @@ rpl_remove_routes_by_nexthop(uip_ipaddr_t *nexthop, rpl_dag_t *dag)
|
|||
while(r != NULL) {
|
||||
if(uip_ipaddr_cmp(uip_ds6_route_nexthop(r), nexthop) &&
|
||||
r->state.dag == dag) {
|
||||
uip_ds6_route_rm(r);
|
||||
r = uip_ds6_route_head();
|
||||
} else {
|
||||
r = uip_ds6_route_next(r);
|
||||
r->state.lifetime = 0;
|
||||
}
|
||||
r = uip_ds6_route_next(r);
|
||||
}
|
||||
ANNOTATE("#L %u 0\n", nexthop->u8[sizeof(uip_ipaddr_t) - 1]);
|
||||
}
|
||||
|
@ -238,7 +237,8 @@ rpl_add_route(rpl_dag_t *dag, uip_ipaddr_t *prefix, int prefix_len,
|
|||
|
||||
rep->state.dag = dag;
|
||||
rep->state.lifetime = RPL_LIFETIME(dag->instance, dag->instance->default_lifetime);
|
||||
rep->state.learned_from = RPL_ROUTE_FROM_INTERNAL;
|
||||
/* always clear state flags for the no-path received when adding/refreshing */
|
||||
RPL_ROUTE_CLEAR_NOPATH_RECEIVED(rep);
|
||||
|
||||
PRINTF("RPL: Added a route to ");
|
||||
PRINT6ADDR(prefix);
|
||||
|
@ -267,10 +267,6 @@ rpl_link_neighbor_callback(const linkaddr_t *addr, int status, int numtx)
|
|||
/* Trigger DAG rank recalculation. */
|
||||
PRINTF("RPL: rpl_link_neighbor_callback triggering update\n");
|
||||
parent->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||
if(instance->of->neighbor_link_callback != NULL) {
|
||||
instance->of->neighbor_link_callback(parent, status, numtx);
|
||||
parent->last_tx_time = clock_time();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +281,11 @@ rpl_ipv6_neighbor_callback(uip_ds6_nbr_t *nbr)
|
|||
|
||||
PRINTF("RPL: Neighbor state changed for ");
|
||||
PRINT6ADDR(&nbr->ipaddr);
|
||||
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA
|
||||
PRINTF(", nscount=%u, state=%u\n", nbr->nscount, nbr->state);
|
||||
#else /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
|
||||
PRINTF(", state=%u\n", nbr->state);
|
||||
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA */
|
||||
for(instance = &instance_table[0], end = instance + RPL_MAX_INSTANCES; instance < end; ++instance) {
|
||||
if(instance->used == 1 ) {
|
||||
p = rpl_find_parent_any_dag(instance, &nbr->ipaddr);
|
||||
|
@ -346,7 +346,9 @@ rpl_init(void)
|
|||
memset(&rpl_stats, 0, sizeof(rpl_stats));
|
||||
#endif
|
||||
|
||||
RPL_OF.reset(NULL);
|
||||
#if RPL_WITH_NON_STORING
|
||||
rpl_ns_init();
|
||||
#endif /* RPL_WITH_NON_STORING */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue