Merge branch 'osd' into ico
Conflicts resolved: core/net/mac/contikimac/contikimac.c
This commit is contained in:
commit
7d63a0f487
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
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -4,9 +4,6 @@
|
|||
[submodule "tools/cc2538-bsl"]
|
||||
path = tools/cc2538-bsl
|
||||
url = https://github.com/JelmerT/cc2538-bsl.git
|
||||
[submodule "platform/osd-merkur/dev/LED_Strip_Suli"]
|
||||
path = platform/osd-merkur/dev/LED_Strip_Suli
|
||||
url = https://github.com/osdomotics/LED_Strip_Suli.git
|
||||
[submodule "cpu/cc26xx-cc13xx/lib/cc26xxware"]
|
||||
path = cpu/cc26xx-cc13xx/lib/cc26xxware
|
||||
url = https://github.com/g-oikonomou/cc26xxware.git
|
||||
|
|
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
|
||||
|
||||
|
@ -89,6 +92,18 @@ before_script:
|
|||
rm -rf /tmp/ba-elf-gcc* /tmp/jn516x-sdk* &&
|
||||
ba-elf-gcc --version ;
|
||||
fi
|
||||
|
||||
## Install mainline ARM toolchain and download nRF52 SDK
|
||||
- if [ ${BUILD_ARCH:-0} = nrf52dk ] ; then
|
||||
sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa &&
|
||||
sudo apt-get -qq update &&
|
||||
sudo apt-get -qq install gcc-arm-embedded srecord &&
|
||||
arm-none-eabi-gcc --version &&
|
||||
$WGET https://developer.nordicsemi.com/nRF5_IoT_SDK/nRF5_IoT_SDK_v0.9.x/nrf5_iot_sdk_3288530.zip &&
|
||||
mkdir /tmp/nrf52-sdk &&
|
||||
unzip nrf5_iot_sdk_3288530.zip -d /tmp/nrf52-sdk &&
|
||||
export NRF52_SDK_ROOT=/tmp/nrf52-sdk ;
|
||||
fi
|
||||
|
||||
## Compile cooja.jar only when it's going to be needed
|
||||
- if [ ${BUILD_CATEGORY:-sim} = sim ] ; then
|
||||
|
@ -120,6 +135,7 @@ env:
|
|||
- BUILD_TYPE='collect'
|
||||
- BUILD_TYPE='collect-lossy'
|
||||
- BUILD_TYPE='rpl'
|
||||
- BUILD_TYPE='rpl-non-storing'
|
||||
- BUILD_TYPE='large-rpl'
|
||||
- BUILD_TYPE='rime'
|
||||
- BUILD_TYPE='ipv6'
|
||||
|
@ -138,5 +154,7 @@ env:
|
|||
- BUILD_TYPE='compile-6502-ports' BUILD_CATEGORY='compile' BUILD_ARCH='6502'
|
||||
- BUILD_TYPE='compile-arm-ports' BUILD_CATEGORY='compile' BUILD_ARCH='arm-aapcs'
|
||||
- BUILD_TYPE='compile-nxp-ports' BUILD_CATEGORY='compile' BUILD_ARCH='jn516x'
|
||||
- BUILD_TYPE='compile-nrf52-ports' BUILD_CATEGORY='compile' BUILD_ARCH='nrf52dk'
|
||||
- BUILD_TYPE='slip-radio' MAKE_TARGETS='cooja'
|
||||
- BUILD_TYPE='llsec' MAKE_TARGETS='cooja'
|
||||
- BUILD_TYPE='compile-avr' BUILD_CATEGORY='compile' BUILD_ARCH='avr-rss2'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -54,6 +54,34 @@
|
|||
#include "adc.h"
|
||||
#include "hw-arduino.h"
|
||||
|
||||
extern volatile uint8_t mcusleepcycle;
|
||||
volatile uint8_t mcusleepcycleval;
|
||||
|
||||
/*-------------- enabled sleep mode ----------------------------------------*/
|
||||
void
|
||||
mcu_sleep_init(void)
|
||||
{
|
||||
mcusleepcycleval=mcusleepcycle;
|
||||
}
|
||||
void
|
||||
mcu_sleep_on(void)
|
||||
{
|
||||
mcusleepcycle= mcusleepcycleval;
|
||||
}
|
||||
/*--------------- disable sleep mode ---------------------------------------*/
|
||||
void
|
||||
mcu_sleep_off(void)
|
||||
{
|
||||
mcusleepcycle=0;
|
||||
}
|
||||
/*---------------- set duty cycle value ------------------------------------*/
|
||||
void
|
||||
mcu_sleep_set(uint8_t value)
|
||||
{
|
||||
mcusleepcycleval= value;
|
||||
mcusleepcycle = mcusleepcycleval;
|
||||
}
|
||||
|
||||
PROCESS(arduino_sketch, "Arduino Sketch Wrapper");
|
||||
|
||||
#ifndef LOOP_INTERVAL
|
||||
|
@ -65,8 +93,8 @@ PROCESS_THREAD(arduino_sketch, ev, data)
|
|||
static struct etimer loop_periodic_timer;
|
||||
|
||||
PROCESS_BEGIN();
|
||||
|
||||
adc_init ();
|
||||
mcu_sleep_init ();
|
||||
setup ();
|
||||
/* Define application-specific events here. */
|
||||
etimer_set(&loop_periodic_timer, LOOP_INTERVAL);
|
||||
|
|
|
@ -51,6 +51,13 @@
|
|||
|
||||
#include "contiki.h"
|
||||
|
||||
/*--------------- enable sleep mode ---------------------------------------*/
|
||||
void mcu_sleep_on(void);
|
||||
/*--------------- disable sleep mode ---------------------------------------*/
|
||||
void mcu_sleep_off(void);
|
||||
/*---------------- set sleep value ------------------------------------*/
|
||||
void mcu_sleep_set(uint8_t value);
|
||||
|
||||
extern void loop (void);
|
||||
extern void setup (void);
|
||||
extern void arduino_init (void);
|
||||
|
|
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;
|
||||
}
|
||||
|
||||
while ((c = state->json[state->pos]) && c != ' ' && c != ',' && c != ']' && c != '}') {
|
||||
state->pos++;
|
||||
}
|
||||
|
||||
state->vlen = state->pos - state->vstart;
|
||||
len = strlen(str);
|
||||
len = state->vlen > len ? state->vlen : len;
|
||||
|
||||
if (strncmp(str, &state->json[state->vstart], len) != 0) {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
}
|
||||
/* no other types for now... */
|
||||
|
||||
state->vtype = type;
|
||||
return state->vtype;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
static void
|
||||
|
@ -97,6 +134,17 @@ skip_ws(struct jsonparse_state *state)
|
|||
}
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
static int
|
||||
is_atomic(struct jsonparse_state *state)
|
||||
{
|
||||
char v = state->vtype;
|
||||
if(v == 'N' || v == '"' || v == '0' || v == 'n' || v == 't' || v == 'f') {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
void
|
||||
jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
|
||||
{
|
||||
|
@ -105,6 +153,7 @@ jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
|
|||
state->pos = 0;
|
||||
state->depth = 0;
|
||||
state->error = 0;
|
||||
state->vtype = 0;
|
||||
state->stack[0] = 0;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
@ -113,31 +162,33 @@ jsonparse_next(struct jsonparse_state *state)
|
|||
{
|
||||
char c;
|
||||
char s;
|
||||
char v;
|
||||
|
||||
skip_ws(state);
|
||||
c = state->json[state->pos];
|
||||
s = jsonparse_get_type(state);
|
||||
v = state->vtype;
|
||||
state->pos++;
|
||||
|
||||
switch(c) {
|
||||
case '{':
|
||||
push(state, c);
|
||||
if((s == 0 && v == 0) || s == '[' || s == ':') {
|
||||
push(state, c);
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_OBJECT;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case '}':
|
||||
if(s == ':' && state->vtype != 0) {
|
||||
/* printf("Popping vtype: '%c'\n", state->vtype); */
|
||||
pop(state);
|
||||
s = jsonparse_get_type(state);
|
||||
}
|
||||
if(s == '{') {
|
||||
if((s == ':' && v != ',' && v != 0 ) || (s == '{' && v == 0)) {
|
||||
pop(state);
|
||||
} else {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
state->error = JSON_ERROR_UNEXPECTED_END_OF_OBJECT;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case ']':
|
||||
if(s == '[') {
|
||||
if(s == '[' && v != ',') {
|
||||
pop(state);
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY;
|
||||
|
@ -145,41 +196,67 @@ jsonparse_next(struct jsonparse_state *state)
|
|||
}
|
||||
return c;
|
||||
case ':':
|
||||
push(state, c);
|
||||
return c;
|
||||
if(s == '{' && v == 'N') {
|
||||
modify(state, ':');
|
||||
state->vtype = 0;
|
||||
} else {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return jsonparse_next(state);
|
||||
case ',':
|
||||
/* if x:y ... , */
|
||||
if(s == ':' && state->vtype != 0) {
|
||||
pop(state);
|
||||
if(s == ':' && v != 0) {
|
||||
modify(state, '{');
|
||||
state->vtype = c;
|
||||
} else if(s == '[') {
|
||||
/* ok! */
|
||||
state->vtype = c;
|
||||
} else {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case '"':
|
||||
if(s == '{' || s == '[' || s == ':') {
|
||||
atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
|
||||
if((s == 0 && v == 0) || s == '{' || s == '[' || s == ':') {
|
||||
return atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_STRING;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case '[':
|
||||
if(s == '{' || s == '[' || s == ':') {
|
||||
if((s == 0 && v == 0) || s == '[' || s == ':') {
|
||||
push(state, c);
|
||||
} else {
|
||||
state->error = JSON_ERROR_UNEXPECTED_ARRAY;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
return c;
|
||||
case 0:
|
||||
if(v == 0 || state->depth > 0) {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
}
|
||||
return JSON_TYPE_ERROR;
|
||||
default:
|
||||
if(s == ':' || s == '[') {
|
||||
if(c <= '9' && c >= '0') {
|
||||
atomic(state, JSON_TYPE_NUMBER);
|
||||
return JSON_TYPE_NUMBER;
|
||||
if(s == 0 || s == ':' || s == '[') {
|
||||
if (v != 0 && v != ',') {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
if(c == '-' || (c <= '9' && c >= '0')) {
|
||||
return atomic(state, JSON_TYPE_NUMBER);
|
||||
} else if(c == 'n') {
|
||||
return atomic(state, JSON_TYPE_NULL);
|
||||
} else if(c == 't') {
|
||||
return atomic(state, JSON_TYPE_TRUE);
|
||||
} else if(c == 'f') {
|
||||
return atomic(state, JSON_TYPE_FALSE);
|
||||
} else {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
} else if(s == '{') {
|
||||
state->error = JSON_ERROR_SYNTAX;
|
||||
return JSON_TYPE_ERROR;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -192,16 +269,31 @@ jsonparse_next(struct jsonparse_state *state)
|
|||
int
|
||||
jsonparse_copy_value(struct jsonparse_state *state, char *str, int size)
|
||||
{
|
||||
int i;
|
||||
int i, o;
|
||||
char c;
|
||||
|
||||
if(state->vtype == 0) {
|
||||
if(!is_atomic(state)) {
|
||||
return 0;
|
||||
}
|
||||
size = size <= state->vlen ? (size - 1) : state->vlen;
|
||||
for(i = 0; i < size; i++) {
|
||||
str[i] = state->json[state->vstart + i];
|
||||
for(i = 0, o = 0; i < state->vlen && o < size - 1; i++) {
|
||||
c = state->json[state->vstart + i];
|
||||
if(c == '\\') {
|
||||
i++;
|
||||
switch(state->json[state->vstart + i]) {
|
||||
case '"': str[o++] = '"'; break;
|
||||
case '\\': str[o++] = '\\'; break;
|
||||
case '/': str[o++] = '/'; break;
|
||||
case 'b': str[o++] = '\b'; break;
|
||||
case 'f': str[o++] = '\f'; break;
|
||||
case 'n': str[o++] = '\n'; break;
|
||||
case 'r': str[o++] = '\r'; break;
|
||||
case 't': str[o++] = '\t'; break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
str[o++] = c;
|
||||
}
|
||||
str[i] = 0;
|
||||
str[o] = 0;
|
||||
return state->vtype;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
@ -228,7 +320,7 @@ jsonparse_get_value_as_long(struct jsonparse_state *state)
|
|||
int
|
||||
jsonparse_strcmp_value(struct jsonparse_state *state, const char *str)
|
||||
{
|
||||
if(state->vtype == 0) {
|
||||
if(!is_atomic(state)) {
|
||||
return -1;
|
||||
}
|
||||
return strncmp(str, &state->json[state->vstart], state->vlen);
|
||||
|
|
|
@ -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 */
|
||||
tsch_schedule_remove_link_by_timeslot(sf_eb, old_ts);
|
||||
if(old_ts == get_node_timeslot(&linkaddr_node_addr)) {
|
||||
/* This was the same timeslot as slot. Reset original link options */
|
||||
tsch_schedule_add_link(sf_eb, LINK_OPTION_TX, LINK_TYPE_ADVERTISING_ONLY,
|
||||
&tsch_broadcast_address, old_ts, 0);
|
||||
} else {
|
||||
/* Remove slot */
|
||||
tsch_schedule_remove_link_by_timeslot(sf_eb, old_ts);
|
||||
}
|
||||
}
|
||||
if(new_ts != 0xffff) {
|
||||
uint8_t link_options = LINK_OPTION_RX;
|
||||
if(new_ts == get_node_timeslot(&linkaddr_node_addr)) {
|
||||
/* This is also our timeslot, add necessary flags */
|
||||
link_options |= LINK_OPTION_TX;
|
||||
}
|
||||
/* Listen to the time source's EBs */
|
||||
tsch_schedule_add_link(sf_eb,
|
||||
LINK_OPTION_RX,
|
||||
LINK_TYPE_ADVERTISING_ONLY, NULL,
|
||||
new_ts, 0);
|
||||
tsch_schedule_add_link(sf_eb, link_options, LINK_TYPE_ADVERTISING_ONLY,
|
||||
&tsch_broadcast_address, new_ts, 0);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
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,10 +87,16 @@ add_uc_link(const linkaddr_t *linkaddr)
|
|||
{
|
||||
if(linkaddr != NULL) {
|
||||
uint16_t timeslot = get_node_timeslot(linkaddr);
|
||||
tsch_schedule_add_link(sf_unicast,
|
||||
ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG,
|
||||
LINK_TYPE_NORMAL, &tsch_broadcast_address,
|
||||
timeslot, channel_offset);
|
||||
uint8_t link_options = ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_RX : LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG;
|
||||
|
||||
if(timeslot == get_node_timeslot(&linkaddr_node_addr)) {
|
||||
/* This is also our timeslot, add necessary flags */
|
||||
link_options |= ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG: LINK_OPTION_RX;
|
||||
}
|
||||
|
||||
/* Add/update link */
|
||||
tsch_schedule_add_link(sf_unicast, link_options, LINK_TYPE_NORMAL, &tsch_broadcast_address,
|
||||
timeslot, channel_offset);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -123,7 +131,17 @@ remove_uc_link(const linkaddr_t *linkaddr)
|
|||
}
|
||||
item = nbr_table_next(nbr_routes, item);
|
||||
}
|
||||
tsch_schedule_remove_link(sf_unicast, l);
|
||||
|
||||
/* Do we need this timeslot? */
|
||||
if(timeslot == get_node_timeslot(&linkaddr_node_addr)) {
|
||||
/* This is our link, keep it but update the link options */
|
||||
uint8_t link_options = ORCHESTRA_UNICAST_SENDER_BASED ? LINK_OPTION_TX | UNICAST_SLOT_SHARED_FLAG: LINK_OPTION_RX;
|
||||
tsch_schedule_add_link(sf_unicast, link_options, LINK_TYPE_NORMAL, &tsch_broadcast_address,
|
||||
timeslot, channel_offset);
|
||||
} else {
|
||||
/* Remove link */
|
||||
tsch_schedule_remove_link(sf_unicast, l);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
|
@ -160,13 +178,14 @@ static void
|
|||
new_time_source(const struct tsch_neighbor *old, const struct tsch_neighbor *new)
|
||||
{
|
||||
if(new != old) {
|
||||
const linkaddr_t *old_addr = old != NULL ? &old->addr : NULL;
|
||||
const linkaddr_t *new_addr = new != NULL ? &new->addr : NULL;
|
||||
if(new_addr != NULL) {
|
||||
linkaddr_copy(&orchestra_parent_linkaddr, new_addr);
|
||||
} else {
|
||||
linkaddr_copy(&orchestra_parent_linkaddr, &linkaddr_null);
|
||||
}
|
||||
remove_uc_link(new_addr);
|
||||
remove_uc_link(old_addr);
|
||||
add_uc_link(new_addr);
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +204,7 @@ init(uint16_t sf_handle)
|
|||
timeslot, channel_offset);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
struct orchestra_rule unicast_per_neighbor = {
|
||||
struct orchestra_rule unicast_per_neighbor_rpl_storing = {
|
||||
init,
|
||||
new_time_source,
|
||||
select_packet,
|
|
@ -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,61 +88,66 @@
|
|||
#error COFFEE_START must point to the first byte in a sector.
|
||||
#endif
|
||||
|
||||
/* File descriptor flags. */
|
||||
#define COFFEE_FD_FREE 0x0
|
||||
#define COFFEE_FD_READ 0x1
|
||||
#define COFFEE_FD_WRITE 0x2
|
||||
#define COFFEE_FD_APPEND 0x4
|
||||
|
||||
/* File object flags. */
|
||||
#define COFFEE_FILE_MODIFIED 0x1
|
||||
|
||||
#define INVALID_PAGE ((coffee_page_t)-1)
|
||||
/* Internal Coffee markers. */
|
||||
#define INVALID_PAGE ((coffee_page_t)-1)
|
||||
#define UNKNOWN_OFFSET ((cfs_offset_t)-1)
|
||||
|
||||
#define REMOVE_LOG 1
|
||||
#define CLOSE_FDS 1
|
||||
#define ALLOW_GC 1
|
||||
/* File removal actions. They can have the same values because
|
||||
they are passed as separate parameters. */
|
||||
#define REMOVE_LOG 1
|
||||
#define CLOSE_FDS 1
|
||||
#define ALLOW_GC 1
|
||||
|
||||
/* "Greedy" garbage collection erases as many sectors as possible. */
|
||||
#define GC_GREEDY 0
|
||||
#define GC_GREEDY 0
|
||||
/* "Reluctant" garbage collection stops after erasing one sector. */
|
||||
#define GC_RELUCTANT 1
|
||||
#define GC_RELUCTANT 1
|
||||
|
||||
/* File descriptor macros. */
|
||||
#define FD_VALID(fd) \
|
||||
((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
|
||||
coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
|
||||
#define FD_VALID(fd) ((fd) >= 0 && (fd) < COFFEE_FD_SET_SIZE && \
|
||||
coffee_fd_set[(fd)].flags != COFFEE_FD_FREE)
|
||||
#define FD_READABLE(fd) (coffee_fd_set[(fd)].flags & CFS_READ)
|
||||
#define FD_WRITABLE(fd) (coffee_fd_set[(fd)].flags & CFS_WRITE)
|
||||
#define FD_APPENDABLE(fd) (coffee_fd_set[(fd)].flags & CFS_APPEND)
|
||||
|
||||
/* File object macros. */
|
||||
#define FILE_MODIFIED(file) ((file)->flags & COFFEE_FILE_MODIFIED)
|
||||
#define FILE_FREE(file) ((file)->max_pages == 0)
|
||||
#define FILE_MODIFIED(file) ((file)->flags & COFFEE_FILE_MODIFIED)
|
||||
#define FILE_FREE(file) ((file)->max_pages == 0)
|
||||
#define FILE_UNREFERENCED(file) ((file)->references == 0)
|
||||
|
||||
/* File header flags. */
|
||||
#define HDR_FLAG_VALID 0x1 /* Completely written header. */
|
||||
#define HDR_FLAG_ALLOCATED 0x2 /* Allocated file. */
|
||||
#define HDR_FLAG_OBSOLETE 0x4 /* File marked for GC. */
|
||||
#define HDR_FLAG_MODIFIED 0x8 /* Modified file, log exists. */
|
||||
#define HDR_FLAG_LOG 0x10 /* Log file. */
|
||||
#define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */
|
||||
#define HDR_FLAG_VALID 0x01 /* Completely written header. */
|
||||
#define HDR_FLAG_ALLOCATED 0x02 /* Allocated file. */
|
||||
#define HDR_FLAG_OBSOLETE 0x04 /* File marked for GC. */
|
||||
#define HDR_FLAG_MODIFIED 0x08 /* Modified file, log exists. */
|
||||
#define HDR_FLAG_LOG 0x10 /* Log file. */
|
||||
#define HDR_FLAG_ISOLATED 0x20 /* Isolated page. */
|
||||
|
||||
/* File header macros. */
|
||||
#define CHECK_FLAG(hdr, flag) ((hdr).flags & (flag))
|
||||
#define HDR_VALID(hdr) CHECK_FLAG(hdr, HDR_FLAG_VALID)
|
||||
#define HDR_ALLOCATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED)
|
||||
#define HDR_FREE(hdr) !HDR_ALLOCATED(hdr)
|
||||
#define HDR_LOG(hdr) CHECK_FLAG(hdr, HDR_FLAG_LOG)
|
||||
#define HDR_MODIFIED(hdr) CHECK_FLAG(hdr, HDR_FLAG_MODIFIED)
|
||||
#define HDR_ISOLATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ISOLATED)
|
||||
#define HDR_OBSOLETE(hdr) CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE)
|
||||
#define HDR_ACTIVE(hdr) (HDR_ALLOCATED(hdr) && \
|
||||
!HDR_OBSOLETE(hdr) && \
|
||||
!HDR_ISOLATED(hdr))
|
||||
#define HDR_VALID(hdr) CHECK_FLAG(hdr, HDR_FLAG_VALID)
|
||||
#define HDR_ALLOCATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ALLOCATED)
|
||||
#define HDR_FREE(hdr) !HDR_ALLOCATED(hdr)
|
||||
#define HDR_LOG(hdr) CHECK_FLAG(hdr, HDR_FLAG_LOG)
|
||||
#define HDR_MODIFIED(hdr) CHECK_FLAG(hdr, HDR_FLAG_MODIFIED)
|
||||
#define HDR_ISOLATED(hdr) CHECK_FLAG(hdr, HDR_FLAG_ISOLATED)
|
||||
#define HDR_OBSOLETE(hdr) CHECK_FLAG(hdr, HDR_FLAG_OBSOLETE)
|
||||
#define HDR_ACTIVE(hdr) (HDR_ALLOCATED(hdr) && \
|
||||
!HDR_OBSOLETE(hdr) && \
|
||||
!HDR_ISOLATED(hdr))
|
||||
|
||||
/* Shortcuts derived from the hardware-dependent configuration of Coffee. */
|
||||
#define COFFEE_SECTOR_COUNT (unsigned)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
|
||||
#define COFFEE_SECTOR_COUNT \
|
||||
(coffee_page_t)(COFFEE_SIZE / COFFEE_SECTOR_SIZE)
|
||||
#define COFFEE_PAGE_COUNT \
|
||||
((coffee_page_t)(COFFEE_SIZE / COFFEE_PAGE_SIZE))
|
||||
#define COFFEE_PAGES_PER_SECTOR \
|
||||
|
@ -176,9 +175,7 @@ struct file_desc {
|
|||
cfs_offset_t offset;
|
||||
struct file *file;
|
||||
uint8_t flags;
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
uint8_t io_flags;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* The file header structure mimics the representation of file headers
|
||||
|
@ -196,26 +193,18 @@ struct file_header {
|
|||
/* This is needed because of a buggy compiler. */
|
||||
struct log_param {
|
||||
cfs_offset_t offset;
|
||||
const char *buf;
|
||||
char *buf;
|
||||
uint16_t size;
|
||||
};
|
||||
|
||||
/*
|
||||
* The protected memory consists of structures that should not be
|
||||
* overwritten during system checkpointing because they may be used by
|
||||
* the checkpointing implementation. These structures need not be
|
||||
* protected if checkpointing is not used.
|
||||
* Variables that keep track of opened files and internal
|
||||
* optimization information for Coffee.
|
||||
*/
|
||||
static struct protected_mem_t {
|
||||
struct file coffee_files[COFFEE_MAX_OPEN_FILES];
|
||||
struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
|
||||
coffee_page_t next_free;
|
||||
char gc_wait;
|
||||
} protected_mem;
|
||||
static struct file *const coffee_files = protected_mem.coffee_files;
|
||||
static struct file_desc *const coffee_fd_set = protected_mem.coffee_fd_set;
|
||||
static coffee_page_t *const next_free = &protected_mem.next_free;
|
||||
static char *const gc_wait = &protected_mem.gc_wait;
|
||||
static struct file coffee_files[COFFEE_MAX_OPEN_FILES];
|
||||
static struct file_desc coffee_fd_set[COFFEE_FD_SET_SIZE];
|
||||
static coffee_page_t next_free;
|
||||
static char gc_wait;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
|
@ -229,11 +218,9 @@ static void
|
|||
read_header(struct file_header *hdr, coffee_page_t page)
|
||||
{
|
||||
COFFEE_READ(hdr, sizeof(*hdr), page * COFFEE_PAGE_SIZE);
|
||||
#if DEBUG
|
||||
if(HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
|
||||
PRINTF("Invalid header at page %u!\n", (unsigned)page);
|
||||
if(DEBUG && HDR_ACTIVE(*hdr) && !HDR_VALID(*hdr)) {
|
||||
PRINTF("Coffee: Invalid header at page %u!\n", (unsigned)page);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static cfs_offset_t
|
||||
|
@ -243,7 +230,7 @@ absolute_offset(coffee_page_t page, cfs_offset_t offset)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static coffee_page_t
|
||||
get_sector_status(uint16_t sector, struct sector_status *stats)
|
||||
get_sector_status(coffee_page_t sector, struct sector_status *stats)
|
||||
{
|
||||
static coffee_page_t skip_pages;
|
||||
static char last_pages_are_active;
|
||||
|
@ -313,7 +300,7 @@ get_sector_status(uint16_t sector, struct sector_status *stats)
|
|||
/*
|
||||
* Determine the amount of pages in the following sectors that
|
||||
* should be remembered for the next iteration. This is necessary
|
||||
* because no page except the first of a file contains information
|
||||
* because no file page except the first contains information
|
||||
* about what type of page it is. A side effect of remembering this
|
||||
* amount is that there is no need to read in the headers of each
|
||||
* of these pages from the storage.
|
||||
|
@ -364,11 +351,11 @@ isolate_pages(coffee_page_t start, coffee_page_t skip_pages)
|
|||
static void
|
||||
collect_garbage(int mode)
|
||||
{
|
||||
uint16_t sector;
|
||||
coffee_page_t sector;
|
||||
struct sector_status stats;
|
||||
coffee_page_t first_page, isolation_count;
|
||||
|
||||
PRINTF("Coffee: Running the file system garbage collector in %s mode\n",
|
||||
PRINTF("Coffee: Running the garbage collector in %s mode\n",
|
||||
mode == GC_RELUCTANT ? "reluctant" : "greedy");
|
||||
/*
|
||||
* The garbage collector erases as many sectors as possible. A sector is
|
||||
|
@ -377,7 +364,7 @@ collect_garbage(int mode)
|
|||
for(sector = 0; sector < COFFEE_SECTOR_COUNT; sector++) {
|
||||
isolation_count = get_sector_status(sector, &stats);
|
||||
PRINTF("Coffee: Sector %u has %u active, %u obsolete, and %u free pages.\n",
|
||||
sector, (unsigned)stats.active,
|
||||
(unsigned)sector, (unsigned)stats.active,
|
||||
(unsigned)stats.obsolete, (unsigned)stats.free);
|
||||
|
||||
if(stats.active > 0) {
|
||||
|
@ -387,8 +374,8 @@ collect_garbage(int mode)
|
|||
if((mode == GC_RELUCTANT && stats.free == 0) ||
|
||||
(mode == GC_GREEDY && stats.obsolete > 0)) {
|
||||
first_page = sector * COFFEE_PAGES_PER_SECTOR;
|
||||
if(first_page < *next_free) {
|
||||
*next_free = first_page;
|
||||
if(first_page < next_free) {
|
||||
next_free = first_page;
|
||||
}
|
||||
|
||||
if(isolation_count > 0) {
|
||||
|
@ -410,7 +397,7 @@ next_file(coffee_page_t page, struct file_header *hdr)
|
|||
{
|
||||
/*
|
||||
* The quick-skip algorithm for finding file extents is the most
|
||||
* essential part of Coffee. The file allocation rules enables this
|
||||
* essential part of Coffee. The file allocation rules enable this
|
||||
* algorithm to quickly jump over free areas and allocated extents
|
||||
* after reading single headers and determining their status.
|
||||
*
|
||||
|
@ -458,10 +445,7 @@ load_file(coffee_page_t start, struct file_header *hdr)
|
|||
file->page = start;
|
||||
file->end = UNKNOWN_OFFSET;
|
||||
file->max_pages = hdr->max_pages;
|
||||
file->flags = 0;
|
||||
if(HDR_MODIFIED(*hdr)) {
|
||||
file->flags |= COFFEE_FILE_MODIFIED;
|
||||
}
|
||||
file->flags = HDR_MODIFIED(*hdr) ? COFFEE_FILE_MODIFIED : 0;
|
||||
/* We don't know the amount of records yet. */
|
||||
file->record_count = -1;
|
||||
|
||||
|
@ -539,7 +523,7 @@ find_contiguous_pages(coffee_page_t amount)
|
|||
struct file_header hdr;
|
||||
|
||||
start = INVALID_PAGE;
|
||||
for(page = *next_free; page < COFFEE_PAGE_COUNT;) {
|
||||
for(page = next_free; page < COFFEE_PAGE_COUNT;) {
|
||||
read_header(&hdr, page);
|
||||
if(HDR_FREE(hdr)) {
|
||||
if(start == INVALID_PAGE) {
|
||||
|
@ -555,8 +539,8 @@ find_contiguous_pages(coffee_page_t amount)
|
|||
page = next_file(page, &hdr);
|
||||
|
||||
if(start + amount <= page) {
|
||||
if(start == *next_free) {
|
||||
*next_free = start + amount;
|
||||
if(start == next_free) {
|
||||
next_free = start + amount;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
@ -569,8 +553,8 @@ find_contiguous_pages(coffee_page_t amount)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
remove_by_page(coffee_page_t page, int remove_log, int close_fds,
|
||||
int gc_allowed)
|
||||
remove_by_page(coffee_page_t page, int remove_log,
|
||||
int close_fds, int gc_allowed)
|
||||
{
|
||||
struct file_header hdr;
|
||||
int i;
|
||||
|
@ -589,7 +573,7 @@ remove_by_page(coffee_page_t page, int remove_log, int close_fds,
|
|||
hdr.flags |= HDR_FLAG_OBSOLETE;
|
||||
write_header(&hdr, page);
|
||||
|
||||
*gc_wait = 0;
|
||||
gc_wait = 0;
|
||||
|
||||
/* Close all file descriptors that reference the removed file. */
|
||||
if(close_fds) {
|
||||
|
@ -608,11 +592,9 @@ remove_by_page(coffee_page_t page, int remove_log, int close_fds,
|
|||
}
|
||||
}
|
||||
|
||||
#if !COFFEE_EXTENDED_WEAR_LEVELLING
|
||||
if(gc_allowed) {
|
||||
if(!COFFEE_EXTENDED_WEAR_LEVELLING && gc_allowed) {
|
||||
collect_garbage(GC_RELUCTANT);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -638,13 +620,13 @@ reserve(const char *name, coffee_page_t pages,
|
|||
|
||||
page = find_contiguous_pages(pages);
|
||||
if(page == INVALID_PAGE) {
|
||||
if(*gc_wait) {
|
||||
if(gc_wait) {
|
||||
return NULL;
|
||||
}
|
||||
collect_garbage(GC_GREEDY);
|
||||
page = find_contiguous_pages(pages);
|
||||
if(page == INVALID_PAGE) {
|
||||
*gc_wait = 1;
|
||||
gc_wait = 1;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -656,7 +638,7 @@ reserve(const char *name, coffee_page_t pages,
|
|||
write_header(&hdr, page);
|
||||
|
||||
PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n",
|
||||
pages, page, name);
|
||||
(unsigned)pages, (unsigned)page, name);
|
||||
|
||||
file = load_file(page, &hdr);
|
||||
if(file != NULL) {
|
||||
|
@ -851,7 +833,7 @@ merge_log(coffee_page_t file_page, int extend)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Copy the log configuration and the EOF hint. */
|
||||
/* Copy the log configuration. */
|
||||
read_header(&hdr2, new_file->page);
|
||||
hdr2.log_record_size = hdr.log_record_size;
|
||||
hdr2.log_records = hdr.log_records;
|
||||
|
@ -1005,6 +987,7 @@ cfs_open(const char *name, int flags)
|
|||
|
||||
fdp = &coffee_fd_set[fd];
|
||||
fdp->flags = 0;
|
||||
fdp->io_flags = 0;
|
||||
|
||||
fdp->file = find_file(name);
|
||||
if(fdp->file == NULL) {
|
||||
|
@ -1063,7 +1046,12 @@ cfs_seek(int fd, cfs_offset_t offset, int whence)
|
|||
}
|
||||
|
||||
if(fdp->file->end < new_offset) {
|
||||
fdp->file->end = new_offset;
|
||||
if(FD_WRITABLE(fd)) {
|
||||
fdp->file->end = new_offset;
|
||||
} else {
|
||||
/* Disallow seeking past the end of the file for read only FDs */
|
||||
return (cfs_offset_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
return fdp->offset = new_offset;
|
||||
|
@ -1106,11 +1094,16 @@ cfs_read(int fd, void *buf, unsigned size)
|
|||
|
||||
fdp = &coffee_fd_set[fd];
|
||||
file = fdp->file;
|
||||
if(fdp->offset + size > file->end) {
|
||||
|
||||
if(fdp->io_flags & CFS_COFFEE_IO_ENSURE_READ_LENGTH) {
|
||||
while(fdp->offset + size > file->end) {
|
||||
((char *)buf)[--size] = '\0';
|
||||
}
|
||||
} else if(fdp->offset + size > file->end) {
|
||||
size = file->end - fdp->offset;
|
||||
}
|
||||
|
||||
/* If the file is allocated, read directly in the file. */
|
||||
/* If the file is not modified, read directly from the file extent. */
|
||||
if(!FILE_MODIFIED(file)) {
|
||||
COFFEE_READ(buf, size, absolute_offset(file->page, fdp->offset));
|
||||
fdp->offset += size;
|
||||
|
@ -1121,8 +1114,9 @@ cfs_read(int fd, void *buf, unsigned size)
|
|||
read_header(&hdr, file->page);
|
||||
|
||||
/*
|
||||
* Fill the buffer by copying from the log in first hand, or the
|
||||
* ordinary file if the page has no log record.
|
||||
* Copy the contents of the most recent log record. If there is
|
||||
* no log record for the file area to read from, we simply read
|
||||
* from the original file extent.
|
||||
*/
|
||||
for(bytes_left = size; bytes_left > 0; bytes_left -= r) {
|
||||
lp.offset = fdp->offset;
|
||||
|
@ -1164,32 +1158,24 @@ cfs_write(int fd, const void *buf, unsigned size)
|
|||
file = fdp->file;
|
||||
|
||||
/* Attempt to extend the file if we try to write past the end. */
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
if(!(fdp->io_flags & CFS_COFFEE_IO_FIRM_SIZE)) {
|
||||
#endif
|
||||
while(size + fdp->offset + sizeof(struct file_header) >
|
||||
(file->max_pages * COFFEE_PAGE_SIZE)) {
|
||||
if(merge_log(file->page, 1) < 0) {
|
||||
return -1;
|
||||
while(size + fdp->offset + sizeof(struct file_header) >
|
||||
(file->max_pages * COFFEE_PAGE_SIZE)) {
|
||||
if(merge_log(file->page, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
file = fdp->file;
|
||||
PRINTF("Extended the file at page %u\n", (unsigned)file->page);
|
||||
}
|
||||
file = fdp->file;
|
||||
PRINTF("Extended the file at page %u\n", (unsigned)file->page);
|
||||
}
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
}
|
||||
#endif
|
||||
|
||||
#if COFFEE_MICRO_LOGS
|
||||
#if COFFEE_IO_SEMANTICS
|
||||
if(!(fdp->io_flags & CFS_COFFEE_IO_FLASH_AWARE) &&
|
||||
(FILE_MODIFIED(file) || fdp->offset < file->end)) {
|
||||
#else
|
||||
if(FILE_MODIFIED(file) || fdp->offset < file->end) {
|
||||
#endif
|
||||
need_dummy_write = 0;
|
||||
for(bytes_left = size; bytes_left > 0;) {
|
||||
lp.offset = fdp->offset;
|
||||
lp.buf = buf;
|
||||
lp.buf = (void *)buf;
|
||||
lp.size = bytes_left;
|
||||
i = write_log_page(file, &lp);
|
||||
if(i < 0) {
|
||||
|
@ -1227,16 +1213,14 @@ cfs_write(int fd, const void *buf, unsigned size)
|
|||
}
|
||||
} else {
|
||||
#endif /* COFFEE_MICRO_LOGS */
|
||||
#if COFFEE_APPEND_ONLY
|
||||
if(fdp->offset < file->end) {
|
||||
return -1;
|
||||
}
|
||||
#endif /* COFFEE_APPEND_ONLY */
|
||||
if(COFFEE_APPEND_ONLY && fdp->offset < file->end) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
|
||||
fdp->offset += size;
|
||||
COFFEE_WRITE(buf, size, absolute_offset(file->page, fdp->offset));
|
||||
fdp->offset += size;
|
||||
#if COFFEE_MICRO_LOGS
|
||||
}
|
||||
}
|
||||
#endif /* COFFEE_MICRO_LOGS */
|
||||
|
||||
if(fdp->offset > file->end) {
|
||||
|
@ -1250,10 +1234,10 @@ int
|
|||
cfs_opendir(struct cfs_dir *dir, const char *name)
|
||||
{
|
||||
/*
|
||||
* Coffee is only guaranteed to support "/" and ".", but it does not
|
||||
* currently enforce this.
|
||||
* Coffee is only guaranteed to support the directory names "/" and ".",
|
||||
* but it does not enforce this currently.
|
||||
*/
|
||||
memset(dir->dummy_space, 0, sizeof(coffee_page_t));
|
||||
memset(dir->state, 0, sizeof(coffee_page_t));
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -1262,19 +1246,19 @@ cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
|
|||
{
|
||||
struct file_header hdr;
|
||||
coffee_page_t page;
|
||||
coffee_page_t next_page;
|
||||
|
||||
memcpy(&page, dir->dummy_space, sizeof(coffee_page_t));
|
||||
memcpy(&page, dir->state, sizeof(coffee_page_t));
|
||||
|
||||
while(page < COFFEE_PAGE_COUNT) {
|
||||
read_header(&hdr, page);
|
||||
if(HDR_ACTIVE(hdr) && !HDR_LOG(hdr)) {
|
||||
coffee_page_t next_page;
|
||||
memcpy(record->name, hdr.name, sizeof(record->name));
|
||||
record->name[sizeof(record->name) - 1] = '\0';
|
||||
record->size = file_end(page);
|
||||
|
||||
next_page = next_file(page, &hdr);
|
||||
memcpy(dir->dummy_space, &next_page, sizeof(coffee_page_t));
|
||||
memcpy(dir->state, &next_page, sizeof(coffee_page_t));
|
||||
return 0;
|
||||
}
|
||||
page = next_file(page, &hdr);
|
||||
|
@ -1342,11 +1326,9 @@ cfs_coffee_set_io_semantics(int fd, unsigned flags)
|
|||
int
|
||||
cfs_coffee_format(void)
|
||||
{
|
||||
unsigned i;
|
||||
coffee_page_t i;
|
||||
|
||||
PRINTF("Coffee: Formatting %u sectors", COFFEE_SECTOR_COUNT);
|
||||
|
||||
*next_free = 0;
|
||||
PRINTF("Coffee: Formatting %u sectors", (unsigned)COFFEE_SECTOR_COUNT);
|
||||
|
||||
for(i = 0; i < COFFEE_SECTOR_COUNT; i++) {
|
||||
COFFEE_ERASE(i);
|
||||
|
@ -1354,16 +1336,13 @@ cfs_coffee_format(void)
|
|||
}
|
||||
|
||||
/* Formatting invalidates the file information. */
|
||||
memset(&protected_mem, 0, sizeof(protected_mem));
|
||||
memset(&coffee_files, 0, sizeof(coffee_files));
|
||||
memset(&coffee_fd_set, 0, sizeof(coffee_fd_set));
|
||||
next_free = 0;
|
||||
gc_wait = 1;
|
||||
|
||||
PRINTF(" done!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void *
|
||||
cfs_coffee_get_protected_mem(unsigned *size)
|
||||
{
|
||||
*size = sizeof(protected_mem);
|
||||
return &protected_mem;
|
||||
}
|
||||
|
|
|
@ -46,15 +46,15 @@
|
|||
* invoke its own micro logs when file modifications occur.
|
||||
*
|
||||
* This semantical I/O setting is useful when implementing flash storage
|
||||
* algorithms on top of Coffee.
|
||||
* algorithms such as database indices on top of Coffee.
|
||||
*
|
||||
* \sa cfs_coffee_set_io_semantics()
|
||||
*/
|
||||
#define CFS_COFFEE_IO_FLASH_AWARE 0x1
|
||||
|
||||
/**
|
||||
* Instruct Coffee not to attempt to extend the file when there is
|
||||
* an attempt to write past the reserved file size.
|
||||
* Instruct Coffee not to attempt to extend the file upon a request
|
||||
* to write past the reserved file size.
|
||||
*
|
||||
* A case when this is necessary is when the file has a firm size limit,
|
||||
* and a safeguard is needed to protect against writes beyond this limit.
|
||||
|
@ -63,6 +63,15 @@
|
|||
*/
|
||||
#define CFS_COFFEE_IO_FIRM_SIZE 0x2
|
||||
|
||||
/**
|
||||
* Instruct Coffee to set unused bytes in the destination buffer to zero.
|
||||
* Trailing zeros may cause a wrong file size, this option ensures that
|
||||
* the corresponding bytes get set, so Coffee does not read unexpected data.
|
||||
*
|
||||
* \sa cfs_coffee_set_io_semantics()
|
||||
*/
|
||||
#define CFS_COFFEE_IO_ENSURE_READ_LENGTH 0x4
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Header for the Coffee file system.
|
||||
|
@ -75,8 +84,8 @@
|
|||
|
||||
/**
|
||||
* \brief Reserve space for a file.
|
||||
* \param name The filename.
|
||||
* \param size The size of the file.
|
||||
* \param name The file name.
|
||||
* \param size The initial size to be reserved for the file.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*
|
||||
* Coffee uses sequential page structures for files. The sequential
|
||||
|
@ -88,15 +97,15 @@ int cfs_coffee_reserve(const char *name, cfs_offset_t size);
|
|||
|
||||
/**
|
||||
* \brief Configure the on-demand log file.
|
||||
* \param file The filename.
|
||||
* \param log_size The total log size.
|
||||
* \param file The file name.
|
||||
* \param log_size The total log file size.
|
||||
* \param log_entry_size The log entry size.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*
|
||||
* When file data is first modified, Coffee creates a micro log for the
|
||||
* file. The micro log stores a table of modifications whose
|
||||
* parameters--the log size and the log entry size--can be modified
|
||||
* through the cfs_coffee_configure_log function.
|
||||
* file. The micro log stores a table of modifications whose parameters --
|
||||
* the log size and the log entry size -- can be modified through the
|
||||
* cfs_coffee_configure_log function.
|
||||
*/
|
||||
int cfs_coffee_configure_log(const char *file, unsigned log_size,
|
||||
unsigned log_entry_size);
|
||||
|
@ -109,8 +118,8 @@ int cfs_coffee_configure_log(const char *file, unsigned log_size,
|
|||
*
|
||||
* Coffee is used on a wide range of storage types, and the default
|
||||
* I/O file semantics may not be optimal for the access pattern
|
||||
* of a certain file. Hence, this functions allows programmers to
|
||||
* switch the /O semantics on a file that is accessed through a
|
||||
* of a certain file. Hence, this function allows programmers to
|
||||
* switch the I/O semantics on a file that is accessed through a
|
||||
* particular file descriptor.
|
||||
*
|
||||
*/
|
||||
|
@ -123,21 +132,14 @@ int cfs_coffee_set_io_semantics(int fd, unsigned flags);
|
|||
* Coffee formats the underlying storage by setting all bits to zero.
|
||||
* Formatting must be done before using Coffee for the first time in
|
||||
* a mote.
|
||||
*
|
||||
* Notice that the erased bits may be set to 1 on the physical storage
|
||||
* when using flash memory. In this case, Coffee requires that the
|
||||
* COFFEE_READ and COFFEE_WRITE functions used to access the flash memory
|
||||
* invert all bits.
|
||||
*/
|
||||
int cfs_coffee_format(void);
|
||||
|
||||
/**
|
||||
* \brief Points out a memory region that may not be altered during
|
||||
* checkpointing operations that use the file system.
|
||||
* \param size
|
||||
* \return A pointer to the protected memory.
|
||||
*
|
||||
* This function returns the protected memory pointer and writes its size
|
||||
* to the given parameter. Mainly used by sensornet checkpointing to protect
|
||||
* the coffee state during CFS-based checkpointing operations.
|
||||
*/
|
||||
void *cfs_coffee_get_protected_mem(unsigned *size);
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -219,7 +224,7 @@ struct uip_conn *
|
|||
tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
|
||||
{
|
||||
struct uip_conn *c;
|
||||
|
||||
|
||||
c = uip_connect(ripaddr, port);
|
||||
if(c == NULL) {
|
||||
return NULL;
|
||||
|
@ -227,9 +232,9 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
|
|||
|
||||
c->appstate.p = PROCESS_CURRENT();
|
||||
c->appstate.state = appstate;
|
||||
|
||||
|
||||
tcpip_poll_tcp(c);
|
||||
|
||||
|
||||
return c;
|
||||
}
|
||||
#endif /* UIP_ACTIVE_OPEN */
|
||||
|
@ -237,7 +242,7 @@ tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
|
|||
void
|
||||
tcp_unlisten(uint16_t port)
|
||||
{
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
struct listenport *l;
|
||||
|
||||
l = s.listenports;
|
||||
|
@ -255,7 +260,7 @@ tcp_unlisten(uint16_t port)
|
|||
void
|
||||
tcp_listen(uint16_t port)
|
||||
{
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
struct listenport *l;
|
||||
|
||||
l = s.listenports;
|
||||
|
@ -300,7 +305,7 @@ udp_new(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
|
|||
{
|
||||
struct uip_udp_conn *c;
|
||||
uip_udp_appstate_t *s;
|
||||
|
||||
|
||||
c = uip_udp_new(ripaddr, port);
|
||||
if(c == NULL) {
|
||||
return NULL;
|
||||
|
@ -359,157 +364,157 @@ static void
|
|||
eventhandler(process_event_t ev, process_data_t data)
|
||||
{
|
||||
#if UIP_TCP
|
||||
static unsigned char i;
|
||||
unsigned char i;
|
||||
register struct listenport *l;
|
||||
#endif /*UIP_TCP*/
|
||||
struct process *p;
|
||||
|
||||
switch(ev) {
|
||||
case PROCESS_EVENT_EXITED:
|
||||
/* This is the event we get if a process has exited. We go through
|
||||
case PROCESS_EVENT_EXITED:
|
||||
/* This is the event we get if a process has exited. We go through
|
||||
the TCP/IP tables to see if this process had any open
|
||||
connections or listening TCP ports. If so, we'll close those
|
||||
connections. */
|
||||
|
||||
p = (struct process *)data;
|
||||
p = (struct process *)data;
|
||||
#if UIP_TCP
|
||||
l = s.listenports;
|
||||
for(i = 0; i < UIP_LISTENPORTS; ++i) {
|
||||
if(l->p == p) {
|
||||
uip_unlisten(l->port);
|
||||
l->port = 0;
|
||||
l->p = PROCESS_NONE;
|
||||
}
|
||||
++l;
|
||||
l = s.listenports;
|
||||
for(i = 0; i < UIP_LISTENPORTS; ++i) {
|
||||
if(l->p == p) {
|
||||
uip_unlisten(l->port);
|
||||
l->port = 0;
|
||||
l->p = PROCESS_NONE;
|
||||
}
|
||||
|
||||
{
|
||||
struct uip_conn *cptr;
|
||||
|
||||
for(cptr = &uip_conns[0]; cptr < &uip_conns[UIP_CONNS]; ++cptr) {
|
||||
if(cptr->appstate.p == p) {
|
||||
cptr->appstate.p = PROCESS_NONE;
|
||||
cptr->tcpstateflags = UIP_CLOSED;
|
||||
}
|
||||
++l;
|
||||
}
|
||||
|
||||
{
|
||||
struct uip_conn *cptr;
|
||||
|
||||
for(cptr = &uip_conns[0]; cptr < &uip_conns[UIP_CONNS]; ++cptr) {
|
||||
if(cptr->appstate.p == p) {
|
||||
cptr->appstate.p = PROCESS_NONE;
|
||||
cptr->tcpstateflags = UIP_CLOSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* UIP_TCP */
|
||||
#if UIP_UDP
|
||||
{
|
||||
struct uip_udp_conn *cptr;
|
||||
{
|
||||
struct uip_udp_conn *cptr;
|
||||
|
||||
for(cptr = &uip_udp_conns[0];
|
||||
cptr < &uip_udp_conns[UIP_UDP_CONNS]; ++cptr) {
|
||||
if(cptr->appstate.p == p) {
|
||||
cptr->lport = 0;
|
||||
}
|
||||
for(cptr = &uip_udp_conns[0];
|
||||
cptr < &uip_udp_conns[UIP_UDP_CONNS]; ++cptr) {
|
||||
if(cptr->appstate.p == p) {
|
||||
cptr->lport = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* UIP_UDP */
|
||||
break;
|
||||
break;
|
||||
|
||||
case PROCESS_EVENT_TIMER:
|
||||
/* We get this event if one of our timers have expired. */
|
||||
{
|
||||
/* Check the clock so see if we should call the periodic uIP
|
||||
case PROCESS_EVENT_TIMER:
|
||||
/* We get this event if one of our timers have expired. */
|
||||
{
|
||||
/* Check the clock so see if we should call the periodic uIP
|
||||
processing. */
|
||||
if(data == &periodic &&
|
||||
etimer_expired(&periodic)) {
|
||||
if(data == &periodic &&
|
||||
etimer_expired(&periodic)) {
|
||||
#if UIP_TCP
|
||||
for(i = 0; i < UIP_CONNS; ++i) {
|
||||
if(uip_conn_active(i)) {
|
||||
/* Only restart the timer if there are active
|
||||
for(i = 0; i < UIP_CONNS; ++i) {
|
||||
if(uip_conn_active(i)) {
|
||||
/* Only restart the timer if there are active
|
||||
connections. */
|
||||
etimer_restart(&periodic);
|
||||
uip_periodic(i);
|
||||
etimer_restart(&periodic);
|
||||
uip_periodic(i);
|
||||
#if NETSTACK_CONF_WITH_IPV6
|
||||
tcpip_ipv6_output();
|
||||
tcpip_ipv6_output();
|
||||
#else
|
||||
if(uip_len > 0) {
|
||||
PRINTF("tcpip_output from periodic len %d\n", uip_len);
|
||||
tcpip_output();
|
||||
PRINTF("tcpip_output after periodic len %d\n", uip_len);
|
||||
}
|
||||
#endif /* NETSTACK_CONF_WITH_IPV6 */
|
||||
}
|
||||
if(uip_len > 0) {
|
||||
PRINTF("tcpip_output from periodic len %d\n", uip_len);
|
||||
tcpip_output();
|
||||
PRINTF("tcpip_output after periodic len %d\n", uip_len);
|
||||
}
|
||||
#endif /* NETSTACK_CONF_WITH_IPV6 */
|
||||
}
|
||||
}
|
||||
#endif /* UIP_TCP */
|
||||
#if UIP_CONF_IP_FORWARD
|
||||
uip_fw_periodic();
|
||||
uip_fw_periodic();
|
||||
#endif /* UIP_CONF_IP_FORWARD */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if NETSTACK_CONF_WITH_IPV6
|
||||
#if UIP_CONF_IPV6_REASSEMBLY
|
||||
/*
|
||||
* check the timer for reassembly
|
||||
*/
|
||||
if(data == &uip_reass_timer &&
|
||||
etimer_expired(&uip_reass_timer)) {
|
||||
uip_reass_over();
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
/*
|
||||
* check the timer for reassembly
|
||||
*/
|
||||
if(data == &uip_reass_timer &&
|
||||
etimer_expired(&uip_reass_timer)) {
|
||||
uip_reass_over();
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
#endif /* UIP_CONF_IPV6_REASSEMBLY */
|
||||
/*
|
||||
* check the different timers for neighbor discovery and
|
||||
* stateless autoconfiguration
|
||||
*/
|
||||
/*if(data == &uip_ds6_timer_periodic &&
|
||||
/*
|
||||
* check the different timers for neighbor discovery and
|
||||
* stateless autoconfiguration
|
||||
*/
|
||||
/*if(data == &uip_ds6_timer_periodic &&
|
||||
etimer_expired(&uip_ds6_timer_periodic)) {
|
||||
uip_ds6_periodic();
|
||||
tcpip_ipv6_output();
|
||||
}*/
|
||||
#if !UIP_CONF_ROUTER
|
||||
if(data == &uip_ds6_timer_rs &&
|
||||
etimer_expired(&uip_ds6_timer_rs)) {
|
||||
uip_ds6_send_rs();
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
if(data == &uip_ds6_timer_rs &&
|
||||
etimer_expired(&uip_ds6_timer_rs)) {
|
||||
uip_ds6_send_rs();
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
#endif /* !UIP_CONF_ROUTER */
|
||||
if(data == &uip_ds6_timer_periodic &&
|
||||
etimer_expired(&uip_ds6_timer_periodic)) {
|
||||
uip_ds6_periodic();
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
if(data == &uip_ds6_timer_periodic &&
|
||||
etimer_expired(&uip_ds6_timer_periodic)) {
|
||||
uip_ds6_periodic();
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
#endif /* NETSTACK_CONF_WITH_IPV6 */
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
#if UIP_TCP
|
||||
case TCP_POLL:
|
||||
if(data != NULL) {
|
||||
uip_poll_conn(data);
|
||||
case TCP_POLL:
|
||||
if(data != NULL) {
|
||||
uip_poll_conn(data);
|
||||
#if NETSTACK_CONF_WITH_IPV6
|
||||
tcpip_ipv6_output();
|
||||
tcpip_ipv6_output();
|
||||
#else /* NETSTACK_CONF_WITH_IPV6 */
|
||||
if(uip_len > 0) {
|
||||
PRINTF("tcpip_output from tcp poll len %d\n", uip_len);
|
||||
tcpip_output();
|
||||
}
|
||||
#endif /* NETSTACK_CONF_WITH_IPV6 */
|
||||
/* Start the periodic polling, if it isn't already active. */
|
||||
start_periodic_tcp_timer();
|
||||
if(uip_len > 0) {
|
||||
PRINTF("tcpip_output from tcp poll len %d\n", uip_len);
|
||||
tcpip_output();
|
||||
}
|
||||
break;
|
||||
#endif /* NETSTACK_CONF_WITH_IPV6 */
|
||||
/* Start the periodic polling, if it isn't already active. */
|
||||
start_periodic_tcp_timer();
|
||||
}
|
||||
break;
|
||||
#endif /* UIP_TCP */
|
||||
#if UIP_UDP
|
||||
case UDP_POLL:
|
||||
if(data != NULL) {
|
||||
uip_udp_periodic_conn(data);
|
||||
case UDP_POLL:
|
||||
if(data != NULL) {
|
||||
uip_udp_periodic_conn(data);
|
||||
#if NETSTACK_CONF_WITH_IPV6
|
||||
tcpip_ipv6_output();
|
||||
tcpip_ipv6_output();
|
||||
#else
|
||||
if(uip_len > 0) {
|
||||
tcpip_output();
|
||||
}
|
||||
#endif /* UIP_UDP */
|
||||
if(uip_len > 0) {
|
||||
tcpip_output();
|
||||
}
|
||||
break;
|
||||
#endif /* UIP_UDP */
|
||||
}
|
||||
break;
|
||||
#endif /* UIP_UDP */
|
||||
|
||||
case PACKET_INPUT:
|
||||
packet_input();
|
||||
break;
|
||||
case PACKET_INPUT:
|
||||
packet_input();
|
||||
break;
|
||||
};
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -525,7 +530,7 @@ void
|
|||
tcpip_ipv6_output(void)
|
||||
{
|
||||
uip_ds6_nbr_t *nbr = NULL;
|
||||
uip_ipaddr_t *nexthop;
|
||||
uip_ipaddr_t *nexthop = NULL;
|
||||
|
||||
if(uip_len == 0) {
|
||||
return;
|
||||
|
@ -545,14 +550,25 @@ tcpip_ipv6_output(void)
|
|||
|
||||
if(!uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) {
|
||||
/* Next hop determination */
|
||||
|
||||
#if UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING
|
||||
uip_ipaddr_t ipaddr;
|
||||
/* Look for a RPL Source Route */
|
||||
if(rpl_srh_get_next_hop(&ipaddr)) {
|
||||
nexthop = &ipaddr;
|
||||
}
|
||||
#endif /* UIP_CONF_IPV6_RPL && RPL_WITH_NON_STORING */
|
||||
|
||||
nbr = NULL;
|
||||
|
||||
/* We first check if the destination address is on our immediate
|
||||
link. If so, we simply use the destination address as our
|
||||
nexthop address. */
|
||||
if(uip_ds6_is_addr_onlink(&UIP_IP_BUF->destipaddr)){
|
||||
if(nexthop == NULL && uip_ds6_is_addr_onlink(&UIP_IP_BUF->destipaddr)){
|
||||
nexthop = &UIP_IP_BUF->destipaddr;
|
||||
} else {
|
||||
}
|
||||
|
||||
if(nexthop == NULL) {
|
||||
uip_ds6_route_t *route;
|
||||
/* Check if we have a route to the destination address. */
|
||||
route = uip_ds6_route_lookup(&UIP_IP_BUF->destipaddr);
|
||||
|
@ -563,25 +579,25 @@ tcpip_ipv6_output(void)
|
|||
nexthop = uip_ds6_defrt_choose();
|
||||
if(nexthop == NULL) {
|
||||
#ifdef UIP_FALLBACK_INTERFACE
|
||||
PRINTF("FALLBACK: removing ext hdrs & setting proto %d %d\n",
|
||||
uip_ext_len, *((uint8_t *)UIP_IP_BUF + 40));
|
||||
if(uip_ext_len > 0) {
|
||||
extern void remove_ext_hdr(void);
|
||||
uint8_t proto = *((uint8_t *)UIP_IP_BUF + 40);
|
||||
remove_ext_hdr();
|
||||
/* This should be copied from the ext header... */
|
||||
UIP_IP_BUF->proto = proto;
|
||||
}
|
||||
/* Inform the other end that the destination is not reachable. If it's
|
||||
* not informed routes might get lost unexpectedly until there's a need
|
||||
* to send a new packet to the peer */
|
||||
if(UIP_FALLBACK_INTERFACE.output() < 0) {
|
||||
PRINTF("FALLBACK: output error. Reporting DST UNREACH\n");
|
||||
uip_icmp6_error_output(ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0);
|
||||
uip_flags = 0;
|
||||
tcpip_ipv6_output();
|
||||
return;
|
||||
}
|
||||
PRINTF("FALLBACK: removing ext hdrs & setting proto %d %d\n",
|
||||
uip_ext_len, *((uint8_t *)UIP_IP_BUF + 40));
|
||||
if(uip_ext_len > 0) {
|
||||
extern void remove_ext_hdr(void);
|
||||
uint8_t proto = *((uint8_t *)UIP_IP_BUF + 40);
|
||||
remove_ext_hdr();
|
||||
/* This should be copied from the ext header... */
|
||||
UIP_IP_BUF->proto = proto;
|
||||
}
|
||||
/* Inform the other end that the destination is not reachable. If it's
|
||||
* not informed routes might get lost unexpectedly until there's a need
|
||||
* to send a new packet to the peer */
|
||||
if(UIP_FALLBACK_INTERFACE.output() < 0) {
|
||||
PRINTF("FALLBACK: output error. Reporting DST UNREACH\n");
|
||||
uip_icmp6_error_output(ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0);
|
||||
uip_flags = 0;
|
||||
tcpip_ipv6_output();
|
||||
return;
|
||||
}
|
||||
#else
|
||||
PRINTF("tcpip_ipv6_output: Destination off-link but no route\n");
|
||||
#endif /* !UIP_FALLBACK_INTERFACE */
|
||||
|
@ -636,7 +652,7 @@ tcpip_ipv6_output(void)
|
|||
/* End of next hop determination */
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
if(rpl_update_header_final(nexthop)) {
|
||||
if(!rpl_finalize_header(nexthop)) {
|
||||
uip_clear_buf();
|
||||
return;
|
||||
}
|
||||
|
@ -644,8 +660,9 @@ tcpip_ipv6_output(void)
|
|||
nbr = uip_ds6_nbr_lookup(nexthop);
|
||||
if(nbr == NULL) {
|
||||
#if UIP_ND6_SEND_NA
|
||||
if((nbr = uip_ds6_nbr_add(nexthop, NULL, 0, NBR_INCOMPLETE)) == NULL) {
|
||||
if((nbr = uip_ds6_nbr_add(nexthop, NULL, 0, NBR_INCOMPLETE, NBR_TABLE_REASON_IPV6_ND, NULL)) == NULL) {
|
||||
uip_clear_buf();
|
||||
PRINTF("tcpip_ipv6_output: failed to add neighbor to cache\n");
|
||||
return;
|
||||
} else {
|
||||
#if UIP_CONF_IPV6_QUEUE_PKT
|
||||
|
@ -655,13 +672,13 @@ tcpip_ipv6_output(void)
|
|||
uip_packetqueue_set_buflen(&nbr->packethandle, uip_len);
|
||||
}
|
||||
#endif
|
||||
/* RFC4861, 7.2.2:
|
||||
* "If the source address of the packet prompting the solicitation is the
|
||||
* same as one of the addresses assigned to the outgoing interface, that
|
||||
* address SHOULD be placed in the IP Source Address of the outgoing
|
||||
* solicitation. Otherwise, any one of the addresses assigned to the
|
||||
* interface should be used."*/
|
||||
if(uip_ds6_is_my_addr(&UIP_IP_BUF->srcipaddr)){
|
||||
/* RFC4861, 7.2.2:
|
||||
* "If the source address of the packet prompting the solicitation is the
|
||||
* same as one of the addresses assigned to the outgoing interface, that
|
||||
* address SHOULD be placed in the IP Source Address of the outgoing
|
||||
* solicitation. Otherwise, any one of the addresses assigned to the
|
||||
* interface should be used."*/
|
||||
if(uip_ds6_is_my_addr(&UIP_IP_BUF->srcipaddr)){
|
||||
uip_nd6_ns_output(&UIP_IP_BUF->srcipaddr, NULL, &nbr->ipaddr);
|
||||
} else {
|
||||
uip_nd6_ns_output(NULL, NULL, &nbr->ipaddr);
|
||||
|
@ -672,6 +689,7 @@ tcpip_ipv6_output(void)
|
|||
/* Send the first NS try from here (multicast destination IP address). */
|
||||
}
|
||||
#else /* UIP_ND6_SEND_NA */
|
||||
PRINTF("tcpip_ipv6_output: neighbor not in cache\n");
|
||||
uip_len = 0;
|
||||
return;
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
|
@ -747,7 +765,7 @@ void
|
|||
tcpip_uipcall(void)
|
||||
{
|
||||
uip_udp_appstate_t *ts;
|
||||
|
||||
|
||||
#if UIP_UDP
|
||||
if(uip_conn != NULL) {
|
||||
ts = &uip_conn->appstate;
|
||||
|
@ -759,30 +777,30 @@ tcpip_uipcall(void)
|
|||
#endif /* UIP_UDP */
|
||||
|
||||
#if UIP_TCP
|
||||
{
|
||||
static unsigned char i;
|
||||
struct listenport *l;
|
||||
|
||||
/* If this is a connection request for a listening port, we must
|
||||
{
|
||||
unsigned char i;
|
||||
struct listenport *l;
|
||||
|
||||
/* If this is a connection request for a listening port, we must
|
||||
mark the connection with the right process ID. */
|
||||
if(uip_connected()) {
|
||||
l = &s.listenports[0];
|
||||
for(i = 0; i < UIP_LISTENPORTS; ++i) {
|
||||
if(l->port == uip_conn->lport &&
|
||||
l->p != PROCESS_NONE) {
|
||||
ts->p = l->p;
|
||||
ts->state = NULL;
|
||||
break;
|
||||
}
|
||||
++l;
|
||||
}
|
||||
|
||||
/* Start the periodic polling, if it isn't already active. */
|
||||
start_periodic_tcp_timer();
|
||||
}
|
||||
}
|
||||
if(uip_connected()) {
|
||||
l = &s.listenports[0];
|
||||
for(i = 0; i < UIP_LISTENPORTS; ++i) {
|
||||
if(l->port == uip_conn->lport &&
|
||||
l->p != PROCESS_NONE) {
|
||||
ts->p = l->p;
|
||||
ts->state = NULL;
|
||||
break;
|
||||
}
|
||||
++l;
|
||||
}
|
||||
|
||||
/* Start the periodic polling, if it isn't already active. */
|
||||
start_periodic_tcp_timer();
|
||||
}
|
||||
}
|
||||
#endif /* UIP_TCP */
|
||||
|
||||
|
||||
if(ts->p != NULL) {
|
||||
process_post_synch(ts->p, tcpip_event, ts->state);
|
||||
}
|
||||
|
@ -791,16 +809,16 @@ tcpip_uipcall(void)
|
|||
PROCESS_THREAD(tcpip_process, ev, data)
|
||||
{
|
||||
PROCESS_BEGIN();
|
||||
|
||||
|
||||
#if UIP_TCP
|
||||
{
|
||||
static unsigned char i;
|
||||
|
||||
for(i = 0; i < UIP_LISTENPORTS; ++i) {
|
||||
s.listenports[i].port = 0;
|
||||
}
|
||||
s.p = PROCESS_CURRENT();
|
||||
}
|
||||
{
|
||||
unsigned char i;
|
||||
|
||||
for(i = 0; i < UIP_LISTENPORTS; ++i) {
|
||||
s.listenports[i].port = 0;
|
||||
}
|
||||
s.p = PROCESS_CURRENT();
|
||||
}
|
||||
#endif
|
||||
|
||||
tcpip_event = process_alloc_event();
|
||||
|
@ -813,7 +831,7 @@ PROCESS_THREAD(tcpip_process, ev, data)
|
|||
#ifdef UIP_FALLBACK_INTERFACE
|
||||
UIP_FALLBACK_INTERFACE.init();
|
||||
#endif
|
||||
/* initialize RPL if configured for using RPL */
|
||||
/* initialize RPL if configured for using RPL */
|
||||
#if NETSTACK_CONF_WITH_IPV6 && UIP_CONF_IPV6_RPL
|
||||
rpl_init();
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
@ -822,7 +840,7 @@ PROCESS_THREAD(tcpip_process, ev, data)
|
|||
PROCESS_YIELD();
|
||||
eventhandler(ev, data);
|
||||
}
|
||||
|
||||
|
||||
PROCESS_END();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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,15 +51,13 @@ 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
|
||||
#if UIP_IPV6_MULTICAST
|
||||
/* Let the multicast engine process the datagram before we send it */
|
||||
if(uip_is_addr_mcast_routable(&uip_udp_conn->ripaddr)) {
|
||||
UIP_MCAST6.out();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -98,7 +98,7 @@ In order to extend multicast with a new engine, perform the following steps:
|
|||
- Open `uip-mcast6.h` and add a section in the `#if` spree. This aims to
|
||||
configure the uIPv6 core. More specifically, you need to:
|
||||
* Specify if you want to put RPL in MOP3 by defining
|
||||
`RPL_CONF_MULTICAST`: 1: MOP 3, 0: non-multicast MOP
|
||||
`RPL_WITH_MULTICAST`: 1: MOP 3, 0: non-multicast MOP
|
||||
* Define your engine details
|
||||
|
||||
#define UIP_MCAST6 foo_driver
|
||||
|
|
406
core/net/ipv6/multicast/esmrf.c
Normal file
406
core/net/ipv6/multicast/esmrf.c
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* Copyright (c) 2010, Loughborough University - Computer Science
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the Contiki operating system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* This file shows the implementations of the Enhanced Stateless
|
||||
* Multicast RPL Forwarding (ESMRF)
|
||||
*
|
||||
* It will only work in RPL networks in MOP 3 "Storing with Multicast"
|
||||
*
|
||||
* \author
|
||||
* Khaled Qorany kqorany2@gmail.com
|
||||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "contiki-net.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6-route.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6-stats.h"
|
||||
#include "net/ipv6/multicast/esmrf.h"
|
||||
#include "net/rpl/rpl.h"
|
||||
#include "net/ip/uip.h"
|
||||
#include "net/netstack.h"
|
||||
#include <string.h>
|
||||
|
||||
extern uint16_t uip_slen;
|
||||
|
||||
#define DEBUG NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
#define ESMRF_VERBOSE NONE
|
||||
|
||||
#if DEBUG && ESMRF_VERBOSE
|
||||
#define VERBOSE_PRINTF(...) PRINTF(__VA_ARGS__)
|
||||
#define VERBOSE_PRINT_SEED(s) PRINT_SEED(s)
|
||||
#else
|
||||
#define VERBOSE_PRINTF(...)
|
||||
#define VERBOSE_PRINT_SEED(...)
|
||||
#endif
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Maintain Stats */
|
||||
#if UIP_MCAST6_STATS
|
||||
static struct esmrf_stats stats;
|
||||
|
||||
#define ESMRF_STATS_ADD(x) stats.x++
|
||||
#define ESMRF_STATS_INIT() do { memset(&stats, 0, sizeof(stats)); } while(0)
|
||||
#else /* UIP_MCAST6_STATS */
|
||||
#define ESMRF_STATS_ADD(x)
|
||||
#define ESMRF_STATS_INIT()
|
||||
#endif
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Macros */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* CCI */
|
||||
#define ESMRF_FWD_DELAY() NETSTACK_RDC.channel_check_interval()
|
||||
/* Number of slots in the next 500ms */
|
||||
#define ESMRF_INTERVAL_COUNT ((CLOCK_SECOND >> 2) / fwd_delay)
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Internal Data */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static struct ctimer mcast_periodic;
|
||||
static uint8_t mcast_len;
|
||||
static uip_buf_t mcast_buf;
|
||||
static uint8_t fwd_delay;
|
||||
static uint8_t fwd_spread;
|
||||
static struct uip_udp_conn *c;
|
||||
static uip_ipaddr_t src_ip;
|
||||
static uip_ipaddr_t des_ip;
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* uIPv6 Pointers */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
|
||||
#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
|
||||
#define UIP_ICMP_PAYLOAD ((unsigned char *)&uip_buf[uip_l2_l3_icmp_hdr_len])
|
||||
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLH_LEN + UIP_IPH_LEN])
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Local function prototypes */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void icmp_input(void);
|
||||
static void icmp_output(void);
|
||||
static void mcast_fwd(void *p);
|
||||
int remove_ext_hdr(void);
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Internal Data Structures */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
struct multicast_on_behalf{ /* ICMP message of multicast_on_behalf */
|
||||
uint16_t mcast_port;
|
||||
uip_ipaddr_t mcast_ip;
|
||||
uint8_t mcast_payload[UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN];
|
||||
};
|
||||
#define UIP_ICMP_MOB 18 /* Size of multicast_on_behalf ICMP header */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Temporary Stores */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static struct multicast_on_behalf *locmobptr;
|
||||
static int loclen;
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* ESMRF ICMPv6 handler declaration */
|
||||
UIP_ICMP6_HANDLER(esmrf_icmp_handler, ICMP6_ESMRF,
|
||||
UIP_ICMP6_HANDLER_CODE_ANY, icmp_input);
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
icmp_output()
|
||||
{
|
||||
uint16_t payload_len=0;
|
||||
rpl_dag_t *dag_t;
|
||||
|
||||
struct multicast_on_behalf *mob;
|
||||
mob = (struct multicast_on_behalf *)UIP_ICMP_PAYLOAD;
|
||||
memcpy(&mob->mcast_payload, &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], uip_slen);
|
||||
|
||||
UIP_IP_BUF->vtc = 0x60;
|
||||
UIP_IP_BUF->tcflow = 0;
|
||||
UIP_IP_BUF->flow = 0;
|
||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||
UIP_IP_BUF->ttl = ESMRF_IP_HOP_LIMIT;
|
||||
|
||||
mob->mcast_port = (uint16_t) uip_udp_conn->rport;
|
||||
uip_ipaddr_copy(&mob->mcast_ip, &UIP_IP_BUF->destipaddr);
|
||||
|
||||
payload_len = UIP_ICMP_MOB + uip_slen;
|
||||
|
||||
dag_t = rpl_get_any_dag();
|
||||
uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &dag_t->dag_id);
|
||||
uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
|
||||
|
||||
VERBOSE_PRINTF("ESMRF: ICMPv6 Out - Hdr @ %p, payload @ %p to: ", UIP_ICMP_BUF, mob);
|
||||
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
|
||||
PRINTF("\n");
|
||||
|
||||
UIP_IP_BUF->len[0] = (UIP_ICMPH_LEN + payload_len) >> 8;
|
||||
UIP_IP_BUF->len[1] = (UIP_ICMPH_LEN + payload_len) & 0xff;
|
||||
|
||||
UIP_ICMP_BUF->type = ICMP6_ESMRF;
|
||||
UIP_ICMP_BUF->icode = ESMRF_ICMP_CODE;
|
||||
|
||||
UIP_ICMP_BUF->icmpchksum = 0;
|
||||
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
|
||||
|
||||
uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
|
||||
|
||||
VERBOSE_PRINTF("ESMRF: ICMPv6 Out - %u bytes, uip_len %u bytes, uip_ext_len %u bytes\n",
|
||||
payload_len, uip_len, uip_ext_len);
|
||||
|
||||
tcpip_ipv6_output();
|
||||
ESMRF_STATS_ADD(icmp_out);
|
||||
return;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
icmp_input()
|
||||
{
|
||||
#if UIP_CONF_IPV6_CHECKS
|
||||
if(UIP_ICMP_BUF->icode != ESMRF_ICMP_CODE) {
|
||||
PRINTF("ESMRF: ICMPv6 In, bad ICMP code\n");
|
||||
ESMRF_STATS_ADD(icmp_bad);
|
||||
return;
|
||||
}
|
||||
if(UIP_IP_BUF->ttl <= 1) {
|
||||
PRINTF("ESMRF: ICMPv6 In, bad TTL\n");
|
||||
ESMRF_STATS_ADD(icmp_bad);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
remove_ext_hdr();
|
||||
|
||||
PRINTF("ESMRF: ICMPv6 In from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
PRINTF(" len %u, ext %u\n", uip_len, uip_ext_len);
|
||||
|
||||
ESMRF_STATS_ADD(icmp_in);
|
||||
|
||||
VERBOSE_PRINTF("ESMRF: ICMPv6 In, parse from %p to %p\n",
|
||||
UIP_ICMP_PAYLOAD,
|
||||
(uint8_t *)UIP_ICMP_PAYLOAD + uip_len -
|
||||
uip_l2_l3_icmp_hdr_len);
|
||||
|
||||
|
||||
locmobptr = (struct multicast_on_behalf *) UIP_ICMP_PAYLOAD;
|
||||
loclen = uip_len - (uip_l2_l3_icmp_hdr_len + UIP_ICMP_MOB);
|
||||
|
||||
uip_ipaddr_copy(&src_ip, &UIP_IP_BUF->srcipaddr);
|
||||
uip_ipaddr_copy(&des_ip, &UIP_IP_BUF->destipaddr);
|
||||
|
||||
/* Extract the original multicast message */
|
||||
uip_ipaddr_copy(&c->ripaddr, &locmobptr->mcast_ip);
|
||||
c->rport = locmobptr->mcast_port;
|
||||
uip_slen = loclen;
|
||||
uip_udp_conn=c;
|
||||
memcpy(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], locmobptr->mcast_payload,
|
||||
loclen > UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN?
|
||||
UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN: loclen);
|
||||
|
||||
uip_process(UIP_UDP_SEND_CONN);
|
||||
|
||||
memcpy(&mcast_buf, uip_buf, uip_len);
|
||||
mcast_len = uip_len;
|
||||
/* pass the packet to our uip_process to check if it is allowed to
|
||||
* accept this packet or not */
|
||||
uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &src_ip);
|
||||
uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &des_ip);
|
||||
UIP_UDP_BUF->udpchksum = 0;
|
||||
|
||||
uip_process(UIP_DATA);
|
||||
|
||||
memcpy(uip_buf, &mcast_buf, mcast_len);
|
||||
uip_len = mcast_len;
|
||||
/* Return the IP of the original Multicast sender */
|
||||
uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &src_ip);
|
||||
UIP_UDP_BUF->udpchksum = 0;
|
||||
/* If we have an entry in the multicast routing table, something with
|
||||
* a higher RPL rank (somewhere down the tree) is a group member */
|
||||
if(uip_mcast6_route_lookup(&UIP_IP_BUF->destipaddr)) {
|
||||
PRINTF("ESMRF: Forward this packet\n");
|
||||
/* If we enter here, we will definitely forward */
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
uip_clear_buf();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
mcast_fwd(void *p)
|
||||
{
|
||||
memcpy(uip_buf, &mcast_buf, mcast_len);
|
||||
uip_len = mcast_len;
|
||||
UIP_IP_BUF->ttl--;
|
||||
tcpip_output(NULL);
|
||||
uip_clear_buf();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static uint8_t
|
||||
in()
|
||||
{
|
||||
rpl_dag_t *d; /* Our DODAG */
|
||||
uip_ipaddr_t *parent_ipaddr; /* Our pref. parent's IPv6 address */
|
||||
const uip_lladdr_t *parent_lladdr; /* Our pref. parent's LL address */
|
||||
|
||||
/*
|
||||
* Fetch a pointer to the LL address of our preferred parent
|
||||
*
|
||||
* ToDo: This rpl_get_any_dag() call is a dirty replacement of the previous
|
||||
* rpl_get_dag(RPL_DEFAULT_INSTANCE);
|
||||
* so that things can compile with the new RPL code. This needs updated to
|
||||
* read instance ID from the RPL HBHO and use the correct parent accordingly
|
||||
*/
|
||||
d = rpl_get_any_dag();
|
||||
if(!d) {
|
||||
PRINTF("ESMRF: No DODAG\n");
|
||||
UIP_MCAST6_STATS_ADD(mcast_dropped);
|
||||
return UIP_MCAST6_DROP;
|
||||
}
|
||||
|
||||
/* Retrieve our preferred parent's LL address */
|
||||
parent_ipaddr = rpl_get_parent_ipaddr(d->preferred_parent);
|
||||
parent_lladdr = uip_ds6_nbr_lladdr_from_ipaddr(parent_ipaddr);
|
||||
|
||||
if(parent_lladdr == NULL) {
|
||||
PRINTF("ESMRF: NO Parent exist \n");
|
||||
UIP_MCAST6_STATS_ADD(mcast_dropped);
|
||||
return UIP_MCAST6_DROP;
|
||||
}
|
||||
|
||||
/*
|
||||
* We accept a datagram if it arrived from our preferred parent, discard
|
||||
* otherwise.
|
||||
*/
|
||||
if(memcmp(parent_lladdr, packetbuf_addr(PACKETBUF_ADDR_SENDER),
|
||||
UIP_LLADDR_LEN)) {
|
||||
PRINTF("ESMRF: Routable in but ESMRF ignored it\n");
|
||||
UIP_MCAST6_STATS_ADD(mcast_dropped);
|
||||
return UIP_MCAST6_DROP;
|
||||
}
|
||||
|
||||
if(UIP_IP_BUF->ttl <= 1) {
|
||||
UIP_MCAST6_STATS_ADD(mcast_dropped);
|
||||
return UIP_MCAST6_DROP;
|
||||
}
|
||||
|
||||
UIP_MCAST6_STATS_ADD(mcast_in_all);
|
||||
UIP_MCAST6_STATS_ADD(mcast_in_unique);
|
||||
|
||||
/* If we have an entry in the mcast routing table, something with
|
||||
* a higher RPL rank (somewhere down the tree) is a group member */
|
||||
if(uip_mcast6_route_lookup(&UIP_IP_BUF->destipaddr)) {
|
||||
/* If we enter here, we will definitely forward */
|
||||
UIP_MCAST6_STATS_ADD(mcast_fwd);
|
||||
|
||||
/*
|
||||
* Add a delay (D) of at least ESMRF_FWD_DELAY() to compensate for how
|
||||
* contikimac handles broadcasts. We can't start our TX before the sender
|
||||
* has finished its own.
|
||||
*/
|
||||
fwd_delay = ESMRF_FWD_DELAY();
|
||||
|
||||
/* Finalise D: D = min(ESMRF_FWD_DELAY(), ESMRF_MIN_FWD_DELAY) */
|
||||
#if ESMRF_MIN_FWD_DELAY
|
||||
if(fwd_delay < ESMRF_MIN_FWD_DELAY) {
|
||||
fwd_delay = ESMRF_MIN_FWD_DELAY;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(fwd_delay == 0) {
|
||||
/* No delay required, send it, do it now, why wait? */
|
||||
UIP_IP_BUF->ttl--;
|
||||
tcpip_output(NULL);
|
||||
UIP_IP_BUF->ttl++; /* Restore before potential upstack delivery */
|
||||
} else {
|
||||
/* Randomise final delay in [D , D*Spread], step D */
|
||||
fwd_spread = ESMRF_INTERVAL_COUNT;
|
||||
if(fwd_spread > ESMRF_MAX_SPREAD) {
|
||||
fwd_spread = ESMRF_MAX_SPREAD;
|
||||
}
|
||||
if(fwd_spread) {
|
||||
fwd_delay = fwd_delay * (1 + ((random_rand() >> 11) % fwd_spread));
|
||||
}
|
||||
|
||||
memcpy(&mcast_buf, uip_buf, uip_len);
|
||||
mcast_len = uip_len;
|
||||
ctimer_set(&mcast_periodic, fwd_delay, mcast_fwd, NULL);
|
||||
}
|
||||
PRINTF("ESMRF: %u bytes: fwd in %u [%u]\n",
|
||||
uip_len, fwd_delay, fwd_spread);
|
||||
}
|
||||
|
||||
/* Done with this packet unless we are a member of the mcast group */
|
||||
if(!uip_ds6_is_my_maddr(&UIP_IP_BUF->destipaddr)) {
|
||||
PRINTF("ESMRF: Not a group member. No further processing\n");
|
||||
return UIP_MCAST6_DROP;
|
||||
} else {
|
||||
PRINTF("ESMRF: Ours. Deliver to upper layers\n");
|
||||
UIP_MCAST6_STATS_ADD(mcast_in_ours);
|
||||
return UIP_MCAST6_ACCEPT;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
init()
|
||||
{
|
||||
UIP_MCAST6_STATS_INIT(NULL);
|
||||
uip_mcast6_route_init();
|
||||
/* Register the ICMPv6 input handler */
|
||||
uip_icmp6_register_input_handler(&esmrf_icmp_handler);
|
||||
c = udp_new(NULL, 0, NULL);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
out(void)
|
||||
{
|
||||
rpl_dag_t *dag_t;
|
||||
dag_t = rpl_get_any_dag();
|
||||
if (!dag_t){
|
||||
PRINTF("ESMRF: There is no DODAG\n");
|
||||
return;
|
||||
}
|
||||
if(dag_t->rank == 256){
|
||||
PRINTF("ESMRF: I am the Root, thus send the multicast packet normally. \n");
|
||||
return;
|
||||
}
|
||||
else{
|
||||
PRINTF("ESMRF: I am not the Root\n");
|
||||
PRINTF("Send multicast-on-befalf message (ICMPv6) instead to ");
|
||||
PRINT6ADDR(&dag_t->dag_id);
|
||||
PRINTF("\n");
|
||||
icmp_output();
|
||||
uip_slen=0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
const struct uip_mcast6_driver esmrf_driver = {
|
||||
"ESMRF",
|
||||
init,
|
||||
out,
|
||||
in,
|
||||
};
|
||||
/*---------------------------------------------------------------------------*/
|
83
core/net/ipv6/multicast/esmrf.h
Normal file
83
core/net/ipv6/multicast/esmrf.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2011, Loughborough University - Computer Science
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the Contiki operating system.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Header file for the Enhanced Stateless Multicast RPL Forwarding (ESMRF)
|
||||
*
|
||||
* \author
|
||||
* Khaled Qorany kqorany2@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef ESMRF_H_
|
||||
#define ESMRF_H_
|
||||
|
||||
#include "contiki-conf.h"
|
||||
|
||||
#include <stdint.h>
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Protocol Constants */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#define ESMRF_ICMP_CODE 0 /* ICMPv6 code field */
|
||||
#define ESMRF_IP_HOP_LIMIT 0xFF /* Hop limit for ICMP messages */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Configuration */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Fmin */
|
||||
#ifdef ESMRF_CONF_MIN_FWD_DELAY
|
||||
#define ESMRF_MIN_FWD_DELAY ESMRF_CONF_MIN_FWD_DELAY
|
||||
#else
|
||||
#define ESMRF_MIN_FWD_DELAY 4
|
||||
#endif
|
||||
|
||||
/* Max Spread */
|
||||
#ifdef ESMRF_CONF_MAX_SPREAD
|
||||
#define ESMRF_MAX_SPREAD ESMRF_CONF_MAX_SPREAD
|
||||
#else
|
||||
#define ESMRF_MAX_SPREAD 4
|
||||
#endif
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Stats datatype */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
struct esmrf_stats {
|
||||
uint16_t mcast_in_unique;
|
||||
uint16_t mcast_in_all; /* At layer 3 */
|
||||
uint16_t mcast_in_ours; /* Unique and we are a group member */
|
||||
uint16_t mcast_fwd; /* Forwarded by us but we are not the seed */
|
||||
uint16_t mcast_out; /* We are the seed */
|
||||
uint16_t mcast_bad;
|
||||
uint16_t mcast_dropped;
|
||||
uint16_t icmp_out;
|
||||
uint16_t icmp_in;
|
||||
uint16_t icmp_bad;
|
||||
};
|
||||
|
||||
#endif /* ESMRF_H_ */
|
|
@ -50,6 +50,7 @@
|
|||
#define UIP_MCAST6_ENGINE_NONE 0 /**< Selecting this disables mcast */
|
||||
#define UIP_MCAST6_ENGINE_SMRF 1 /**< The SMRF engine */
|
||||
#define UIP_MCAST6_ENGINE_ROLL_TM 2 /**< The ROLL TM engine */
|
||||
#define UIP_MCAST6_ENGINE_ESMRF 3 /**< The ESMRF engine */
|
||||
|
||||
#endif /* UIP_MCAST6_ENGINES_H_ */
|
||||
/** @} */
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include "net/ipv6/multicast/uip-mcast6-engines.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6-route.h"
|
||||
#include "net/ipv6/multicast/smrf.h"
|
||||
#include "net/ipv6/multicast/esmrf.h"
|
||||
#include "net/ipv6/multicast/roll-tm.h"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -147,17 +148,23 @@ struct uip_mcast6_driver {
|
|||
#if UIP_MCAST6_ENGINE
|
||||
|
||||
/* Enable Multicast hooks in the uip6 core */
|
||||
#define UIP_CONF_IPV6_MULTICAST 1
|
||||
#define UIP_IPV6_MULTICAST 1
|
||||
|
||||
#if UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_ROLL_TM
|
||||
#define RPL_CONF_MULTICAST 0 /* Not used by trickle */
|
||||
#define RPL_WITH_MULTICAST 0 /* Not used by trickle */
|
||||
#define UIP_CONF_IPV6_ROLL_TM 1 /* ROLL Trickle ICMP type support */
|
||||
|
||||
#define UIP_MCAST6 roll_tm_driver
|
||||
|
||||
#elif UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_SMRF
|
||||
#define RPL_CONF_MULTICAST 1
|
||||
#define RPL_WITH_MULTICAST 1
|
||||
|
||||
#define UIP_MCAST6 smrf_driver
|
||||
|
||||
#elif UIP_MCAST6_ENGINE == UIP_MCAST6_ENGINE_ESMRF
|
||||
#define RPL_CONF_MULTICAST 1
|
||||
#define UIP_MCAST6 esmrf_driver
|
||||
|
||||
#else
|
||||
#error "Multicast Enabled with an Unknown Engine."
|
||||
#error "Check the value of UIP_MCAST6_CONF_ENGINE in conf files."
|
||||
|
@ -168,7 +175,7 @@ extern const struct uip_mcast6_driver UIP_MCAST6;
|
|||
/*---------------------------------------------------------------------------*/
|
||||
/* Configuration Checks */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if RPL_CONF_MULTICAST && (!UIP_CONF_IPV6_RPL)
|
||||
#if RPL_WITH_MULTICAST && (!UIP_CONF_IPV6_RPL)
|
||||
#error "The selected Multicast mode requires UIP_CONF_IPV6_RPL != 0"
|
||||
#error "Check the value of UIP_CONF_IPV6_RPL in conf files."
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
#if UIP_CONF_IPV6_QUEUE_PKT
|
||||
uip_packetqueue_new(&nbr->packethandle);
|
||||
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
|
||||
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
|
||||
#if UIP_ND6_SEND_NA
|
||||
/* timers are set separately, for now we put them in expired state */
|
||||
stimer_set(&nbr->reachable, 0);
|
||||
stimer_set(&nbr->sendns, 0);
|
||||
nbr->nscount = 0;
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
PRINTF("Adding neighbor with ip addr ");
|
||||
PRINT6ADDR(ipaddr);
|
||||
PRINTF(" link addr ");
|
||||
|
@ -111,7 +119,7 @@ uip_ds6_nbr_add(const uip_ipaddr_t *ipaddr, const uip_lladdr_t *lladdr,
|
|||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
int
|
||||
uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
|
||||
{
|
||||
if(nbr != NULL) {
|
||||
|
@ -119,9 +127,9 @@ uip_ds6_nbr_rm(uip_ds6_nbr_t *nbr)
|
|||
uip_packetqueue_free(&nbr->packethandle);
|
||||
#endif /* UIP_CONF_IPV6_QUEUE_PKT */
|
||||
NEIGHBOR_STATE_CHANGED(nbr);
|
||||
nbr_table_remove(ds6_neighbors, nbr);
|
||||
return nbr_table_remove(ds6_neighbors, nbr);
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -198,6 +206,9 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Update neighbor link statistics */
|
||||
link_stats_packet_sent(dest, status, numtx);
|
||||
/* Call upper-layer callback (e.g. RPL) */
|
||||
LINK_NEIGHBOR_CALLBACK(dest, status, numtx);
|
||||
|
||||
#if UIP_DS6_LL_NUD
|
||||
|
@ -230,6 +241,7 @@ uip_ds6_link_neighbor_callback(int status, int numtx)
|
|||
#endif /* UIP_DS6_LL_NUD */
|
||||
|
||||
}
|
||||
#if UIP_ND6_SEND_NA
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** Periodic processing on neighbors */
|
||||
void
|
||||
|
@ -241,7 +253,7 @@ uip_ds6_neighbor_periodic(void)
|
|||
case NBR_REACHABLE:
|
||||
if(stimer_expired(&nbr->reachable)) {
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
/* when a neighbor leave it's REACHABLE state and is a default router,
|
||||
/* when a neighbor leave its REACHABLE state and is a default router,
|
||||
instead of going to STALE state it enters DELAY state in order to
|
||||
force a NUD on it. Otherwise, if there is no upward traffic, the
|
||||
node never knows if the default router is still reachable. This
|
||||
|
@ -268,7 +280,6 @@ uip_ds6_neighbor_periodic(void)
|
|||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
}
|
||||
break;
|
||||
#if UIP_ND6_SEND_NA
|
||||
case NBR_INCOMPLETE:
|
||||
if(nbr->nscount >= UIP_ND6_MAX_MULTICAST_SOLICIT) {
|
||||
uip_ds6_nbr_rm(nbr);
|
||||
|
@ -304,7 +315,6 @@ uip_ds6_neighbor_periodic(void)
|
|||
stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000);
|
||||
}
|
||||
break;
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -330,5 +340,6 @@ uip_ds6_get_least_lifetime_neighbor(void)
|
|||
}
|
||||
return nbr_expiring;
|
||||
}
|
||||
#endif /* UIP_ND6_SEND_NA */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @} */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "lib/random.h"
|
||||
#include "net/ipv6/uip-nd6.h"
|
||||
#include "net/ipv6/uip-ds6.h"
|
||||
#include "net/ipv6/multicast/uip-mcast6.h"
|
||||
#include "net/ip/uip-packetqueue.h"
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
|
@ -186,7 +187,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
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
/**
|
||||
* \file
|
||||
* ICMPv6 (RFC 4443) implementation, with message and error handling
|
||||
* \author Julien Abeille <jabeille@cisco.com>
|
||||
* \author Julien Abeille <jabeille@cisco.com>
|
||||
* \author Mathilde Durvy <mdurvy@cisco.com>
|
||||
*/
|
||||
|
||||
|
@ -120,9 +120,6 @@ uip_icmp6_register_input_handler(uip_icmp6_input_handler_t *handler)
|
|||
static void
|
||||
echo_request_input(void)
|
||||
{
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
uint8_t temp_ext_len;
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/*
|
||||
* we send an echo reply. It is trivial if there was no extension
|
||||
* headers in the request otherwise we need to remove the extension
|
||||
|
@ -147,44 +144,27 @@ echo_request_input(void)
|
|||
}
|
||||
|
||||
if(uip_ext_len > 0) {
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
if((temp_ext_len = rpl_invert_header())) {
|
||||
/* If there were other extension headers*/
|
||||
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
|
||||
if (uip_ext_len != temp_ext_len) {
|
||||
uip_len -= (uip_ext_len - temp_ext_len);
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo request payload (starting after the icmp header)
|
||||
* to the new location in the reply.
|
||||
* The shift is equal to the length of the remaining extension headers present
|
||||
* Note: UIP_ICMP_BUF still points to the echo request at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
|
||||
}
|
||||
uip_ext_len = temp_ext_len;
|
||||
} else {
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/* If there were extension headers*/
|
||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||
uip_len -= uip_ext_len;
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo request payload (starting after the icmp header)
|
||||
* to the new location in the reply.
|
||||
* The shift is equal to the length of the extension headers present
|
||||
* Note: UIP_ICMP_BUF still points to the echo request at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||
uip_ext_len = 0;
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
}
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/* Remove extension headers if any */
|
||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||
uip_len -= uip_ext_len;
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo request payload (starting after the icmp header)
|
||||
* to the new location in the reply.
|
||||
* The shift is equal to the length of the extension headers present
|
||||
* Note: UIP_ICMP_BUF still points to the echo request at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||
uip_ext_len = 0;
|
||||
}
|
||||
|
||||
/* Insert RPL extension headers */
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
rpl_insert_header();
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
/* Below is important for the correctness of UIP_ICMP_BUF and the
|
||||
* checksum
|
||||
*/
|
||||
|
@ -206,23 +186,22 @@ echo_request_input(void)
|
|||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
|
||||
|
||||
/* check if originating packet is not an ICMP error*/
|
||||
if (uip_ext_len) {
|
||||
if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
|
||||
/* check if originating packet is not an ICMP error */
|
||||
if(uip_ext_len) {
|
||||
if(UIP_EXT_BUF->next == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
|
||||
uip_clear_buf();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(UIP_IP_BUF->proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128){
|
||||
if(UIP_IP_BUF->proto == UIP_PROTO_ICMP6 && UIP_ICMP_BUF->type < 128) {
|
||||
uip_clear_buf();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
uip_ext_len = rpl_invert_header();
|
||||
#else /* UIP_CONF_IPV6_RPL */
|
||||
rpl_remove_header();
|
||||
#else
|
||||
uip_ext_len = 0;
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
|
@ -231,8 +210,9 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
|
|||
|
||||
uip_len += UIP_IPICMPH_LEN + UIP_ICMP6_ERROR_LEN;
|
||||
|
||||
if(uip_len > UIP_LINK_MTU)
|
||||
if(uip_len > UIP_LINK_MTU) {
|
||||
uip_len = UIP_LINK_MTU;
|
||||
}
|
||||
|
||||
memmove((uint8_t *)UIP_ICMP6_ERROR_BUF + uip_ext_len + UIP_ICMP6_ERROR_LEN,
|
||||
(void *)UIP_IP_BUF, uip_len - UIP_IPICMPH_LEN - uip_ext_len - UIP_ICMP6_ERROR_LEN);
|
||||
|
@ -280,6 +260,10 @@ uip_icmp6_error_output(uint8_t type, uint8_t code, uint32_t param) {
|
|||
UIP_ICMP_BUF->icmpchksum = 0;
|
||||
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
rpl_insert_header();
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
UIP_STAT(++uip_stat.icmp.sent);
|
||||
|
||||
PRINTF("Sending ICMPv6 ERROR message type %d code %d to ", type, code);
|
||||
|
@ -313,6 +297,13 @@ uip_icmp6_send(const uip_ipaddr_t *dest, int type, int code, int payload_len)
|
|||
UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
|
||||
|
||||
uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + payload_len;
|
||||
|
||||
UIP_STAT(++uip_stat.icmp.sent);
|
||||
UIP_STAT(++uip_stat.ip.sent);
|
||||
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
rpl_insert_header();
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
tcpip_ipv6_output();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -321,53 +312,31 @@ echo_reply_input(void)
|
|||
{
|
||||
int ttl;
|
||||
uip_ipaddr_t sender;
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
uint8_t temp_ext_len;
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
|
||||
PRINTF("Received Echo Reply from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
PRINTF(" to ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->destipaddr);
|
||||
PRINTF("\n");
|
||||
|
||||
uip_ipaddr_copy(&sender, &UIP_IP_BUF->srcipaddr);
|
||||
ttl = UIP_IP_BUF->ttl;
|
||||
|
||||
if(uip_ext_len > 0) {
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
if((temp_ext_len = rpl_invert_header())) {
|
||||
/* If there were other extension headers*/
|
||||
UIP_FIRST_EXT_BUF->next = UIP_PROTO_ICMP6;
|
||||
if (uip_ext_len != temp_ext_len) {
|
||||
uip_len -= (uip_ext_len - temp_ext_len);
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo reply payload (starting after the icmp
|
||||
* header) to the new location in the reply. The shift is
|
||||
* equal to the length of the remaining extension headers
|
||||
* present Note: UIP_ICMP_BUF still points to the echo reply
|
||||
* at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - (uip_ext_len - temp_ext_len),
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - temp_ext_len - UIP_ICMPH_LEN));
|
||||
}
|
||||
uip_ext_len = temp_ext_len;
|
||||
uip_len -= uip_ext_len;
|
||||
} else {
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/* If there were extension headers*/
|
||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||
uip_len -= uip_ext_len;
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo reply payload (starting after the icmp header)
|
||||
* to the new location in the reply. The shift is equal to the
|
||||
* length of the extension headers present Note: UIP_ICMP_BUF
|
||||
* still points to the echo request at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||
uip_ext_len = 0;
|
||||
#if UIP_CONF_IPV6_RPL
|
||||
}
|
||||
#endif /* UIP_CONF_IPV6_RPL */
|
||||
/* Remove extension headers if any */
|
||||
UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
|
||||
uip_len -= uip_ext_len;
|
||||
UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
|
||||
UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);
|
||||
/* move the echo reply payload (starting after the icmp header)
|
||||
* to the new location in the reply. The shift is equal to the
|
||||
* length of the extension headers present Note: UIP_ICMP_BUF
|
||||
* still points to the echo request at this stage
|
||||
*/
|
||||
memmove((uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len,
|
||||
(uint8_t *)UIP_ICMP_BUF + UIP_ICMPH_LEN,
|
||||
(uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN));
|
||||
uip_ext_len = 0;
|
||||
}
|
||||
|
||||
/* Call all registered applications to let them know an echo reply
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#define ICMP6_PRIV_EXP_200 200 /**< Private Experimentation */
|
||||
#define ICMP6_PRIV_EXP_201 201 /**< Private Experimentation */
|
||||
#define ICMP6_ROLL_TM ICMP6_PRIV_EXP_200 /**< ROLL Trickle Multicast */
|
||||
#define ICMP6_ESMRF ICMP6_PRIV_EXP_201 /**< ESMRF Multicast */
|
||||
/** @} */
|
||||
|
||||
|
||||
|
|
|
@ -135,11 +135,13 @@ static uip_ds6_prefix_t *prefix; /** Pointer to a prefix list entry */
|
|||
#if UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Copy link-layer address from LLAO option to a word-aligned uip_lladdr_t */
|
||||
static void
|
||||
extract_lladdr_aligned(uip_lladdr_t *dest) {
|
||||
static int
|
||||
extract_lladdr_from_llao_aligned(uip_lladdr_t *dest) {
|
||||
if(dest != NULL && nd6_opt_llao != NULL) {
|
||||
memcpy(dest, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* UIP_ND6_SEND_NA || UIP_ND6_SEND_RA || !UIP_CONF_ROUTER */
|
||||
/*------------------------------------------------------------------*/
|
||||
|
@ -199,16 +201,23 @@ ns_input(void)
|
|||
goto discard;
|
||||
} else {
|
||||
#endif /*UIP_CONF_IPV6_CHECKS */
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_from_llao_aligned(&lladdr_aligned);
|
||||
nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
|
||||
if(nbr == NULL) {
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_aligned(&lladdr_aligned);
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
} else {
|
||||
uip_lladdr_t *lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
|
||||
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
lladdr, UIP_LLADDR_LEN) != 0) {
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN);
|
||||
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
nbr->state = NBR_STALE;
|
||||
} else {
|
||||
if(nbr->state == NBR_INCOMPLETE) {
|
||||
|
@ -427,6 +436,7 @@ na_input(void)
|
|||
uint8_t is_router;
|
||||
uint8_t is_solicited;
|
||||
uint8_t is_override;
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
|
||||
PRINTF("Received NA from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
|
@ -489,23 +499,29 @@ na_input(void)
|
|||
PRINTF("NA received is bad\n");
|
||||
goto discard;
|
||||
} else {
|
||||
uip_lladdr_t *lladdr;
|
||||
const uip_lladdr_t *lladdr;
|
||||
nbr = uip_ds6_nbr_lookup(&UIP_ND6_NA_BUF->tgtipaddr);
|
||||
lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
|
||||
if(nbr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(nd6_opt_llao != 0) {
|
||||
lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(nd6_opt_llao != NULL) {
|
||||
is_llchange =
|
||||
memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], (void *)lladdr,
|
||||
memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], lladdr,
|
||||
UIP_LLADDR_LEN);
|
||||
}
|
||||
if(nbr->state == NBR_INCOMPLETE) {
|
||||
if(nd6_opt_llao == NULL) {
|
||||
if(nd6_opt_llao == NULL || !extract_lladdr_from_llao_aligned(&lladdr_aligned)) {
|
||||
goto discard;
|
||||
}
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
UIP_LLADDR_LEN);
|
||||
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
|
||||
if(is_solicited) {
|
||||
nbr->state = NBR_REACHABLE;
|
||||
nbr->nscount = 0;
|
||||
|
@ -517,27 +533,29 @@ na_input(void)
|
|||
nbr->state = NBR_STALE;
|
||||
}
|
||||
nbr->isrouter = is_router;
|
||||
} else {
|
||||
} else { /* NBR is not INCOMPLETE */
|
||||
if(!is_override && is_llchange) {
|
||||
if(nbr->state == NBR_REACHABLE) {
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
goto discard;
|
||||
} else {
|
||||
if(is_override || (!is_override && nd6_opt_llao != 0 && !is_llchange)
|
||||
|| nd6_opt_llao == 0) {
|
||||
if(nd6_opt_llao != 0) {
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
UIP_LLADDR_LEN);
|
||||
/**
|
||||
* If this is an cache override, or same lladdr, or no llao -
|
||||
* do updates of nbr states.
|
||||
*/
|
||||
if(is_override || !is_llchange || nd6_opt_llao == NULL) {
|
||||
if(nd6_opt_llao != NULL && is_llchange) {
|
||||
if(!extract_lladdr_from_llao_aligned(&lladdr_aligned) ||
|
||||
nbr_table_update_lladdr((const linkaddr_t *) lladdr, (const linkaddr_t *) &lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
}
|
||||
if(is_solicited) {
|
||||
nbr->state = NBR_REACHABLE;
|
||||
/* reachable time is stored in ms */
|
||||
stimer_set(&(nbr->reachable), uip_ds6_if.reachable_time / 1000);
|
||||
} else {
|
||||
if(nd6_opt_llao != 0 && is_llchange) {
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -631,17 +649,23 @@ rs_input(void)
|
|||
} else {
|
||||
#endif /*UIP_CONF_IPV6_CHECKS */
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_aligned(&lladdr_aligned);
|
||||
extract_lladdr_from_llao_aligned(&lladdr_aligned);
|
||||
if((nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr)) == NULL) {
|
||||
/* we need to add the neighbor */
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
|
||||
uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
} else {
|
||||
/* If LL address changed, set neighbor state to stale */
|
||||
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
uip_ds6_nbr_get_ll(nbr), UIP_LLADDR_LEN) != 0) {
|
||||
lladdr, UIP_LLADDR_LEN) != 0) {
|
||||
uip_ds6_nbr_t nbr_data = *nbr;
|
||||
uip_ds6_nbr_rm(nbr);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 0, NBR_STALE);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
0, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
nbr->reachable = nbr_data.reachable;
|
||||
nbr->sendns = nbr_data.sendns;
|
||||
nbr->nscount = nbr_data.nscount;
|
||||
|
@ -823,6 +847,8 @@ uip_nd6_rs_output(void)
|
|||
void
|
||||
ra_input(void)
|
||||
{
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
|
||||
PRINTF("Received RA from ");
|
||||
PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
|
||||
PRINTF(" to ");
|
||||
|
@ -867,19 +893,28 @@ ra_input(void)
|
|||
PRINTF("Processing SLLAO option in RA\n");
|
||||
nd6_opt_llao = (uint8_t *) UIP_ND6_OPT_HDR_BUF;
|
||||
nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
|
||||
if(!extract_lladdr_from_llao_aligned(&lladdr_aligned)) {
|
||||
/* failed to extract llao - discard packet */
|
||||
goto discard;
|
||||
}
|
||||
if(nbr == NULL) {
|
||||
uip_lladdr_t lladdr_aligned;
|
||||
extract_lladdr_aligned(&lladdr_aligned);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned, 1, NBR_STALE);
|
||||
nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr, &lladdr_aligned,
|
||||
1, NBR_STALE, NBR_TABLE_REASON_IPV6_ND, NULL);
|
||||
} else {
|
||||
uip_lladdr_t *lladdr = (uip_lladdr_t *)uip_ds6_nbr_get_ll(nbr);
|
||||
const uip_lladdr_t *lladdr = uip_ds6_nbr_get_ll(nbr);
|
||||
if(lladdr == NULL) {
|
||||
goto discard;
|
||||
}
|
||||
if(nbr->state == NBR_INCOMPLETE) {
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
lladdr, UIP_LLADDR_LEN) != 0) {
|
||||
memcpy(lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
|
||||
UIP_LLADDR_LEN);
|
||||
/* change of link layer address */
|
||||
if(nbr_table_update_lladdr((const linkaddr_t *)lladdr, (const linkaddr_t *)&lladdr_aligned, 1) == 0) {
|
||||
/* failed to update the lladdr */
|
||||
goto discard;
|
||||
}
|
||||
nbr->state = NBR_STALE;
|
||||
}
|
||||
nbr->isrouter = 1;
|
||||
|
|
File diff suppressed because it is too large
Load diff
211
core/net/link-stats.c
Normal file
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_FRAME_TYPE, FRAME802154_DATAFRAME);
|
||||
packetbuf_set_attr(PACKETBUF_ATTR_SECURITY_LEVEL, SEC_LVL);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
send(mac_callback_t sent, void *ptr)
|
||||
{
|
||||
add_security_header();
|
||||
anti_replay_set_counter();
|
||||
NETSTACK_MAC.send(sent, ptr);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -145,8 +160,7 @@ create(void)
|
|||
{
|
||||
int result;
|
||||
|
||||
add_security_header();
|
||||
result = framer_802154.create();
|
||||
result = DECORATED_FRAMER.create();
|
||||
if(result == FRAMER_FAILED) {
|
||||
return result;
|
||||
}
|
||||
|
@ -163,12 +177,12 @@ parse(void)
|
|||
const linkaddr_t *sender;
|
||||
struct anti_replay_info* info;
|
||||
|
||||
result = framer_802154.parse();
|
||||
result = DECORATED_FRAMER.parse();
|
||||
if(result == FRAMER_FAILED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) != LLSEC802154_SECURITY_LEVEL) {
|
||||
if(packetbuf_attr(PACKETBUF_ATTR_SECURITY_LEVEL) != SEC_LVL) {
|
||||
PRINTF("noncoresec: received frame with wrong security level\n");
|
||||
return FRAMER_FAILED;
|
||||
}
|
||||
|
@ -178,17 +192,17 @@ parse(void)
|
|||
return FRAMER_FAILED;
|
||||
}
|
||||
|
||||
packetbuf_set_datalen(packetbuf_datalen() - LLSEC802154_MIC_LENGTH);
|
||||
packetbuf_set_datalen(packetbuf_datalen() - MIC_LEN);
|
||||
|
||||
if(!aead(result, 0)) {
|
||||
PRINTF("noncoresec: received unauthentic frame %"PRIu32"\n",
|
||||
PRINTF("noncoresec: received unauthentic frame %lu\n",
|
||||
anti_replay_get_counter());
|
||||
return FRAMER_FAILED;
|
||||
}
|
||||
|
||||
info = nbr_table_get_from_lladdr(anti_replay_table, sender);
|
||||
if(!info) {
|
||||
info = nbr_table_add_lladdr(anti_replay_table, sender);
|
||||
info = nbr_table_add_lladdr(anti_replay_table, sender, NBR_TABLE_REASON_LLSEC, NULL);
|
||||
if(!info) {
|
||||
PRINTF("noncoresec: could not get nbr_table_item\n");
|
||||
return FRAMER_FAILED;
|
||||
|
@ -214,7 +228,7 @@ parse(void)
|
|||
anti_replay_init_info(info);
|
||||
} else {
|
||||
if(anti_replay_was_replayed(info)) {
|
||||
PRINTF("noncoresec: received replayed frame %"PRIu32"\n",
|
||||
PRINTF("noncoresec: received replayed frame %lu\n",
|
||||
anti_replay_get_counter());
|
||||
return FRAMER_FAILED;
|
||||
}
|
||||
|
@ -233,7 +247,7 @@ static int
|
|||
length(void)
|
||||
{
|
||||
add_security_header();
|
||||
return framer_802154.length() + LLSEC802154_MIC_LENGTH;
|
||||
return DECORATED_FRAMER.length() + MIC_LEN;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
|
@ -256,5 +270,6 @@ const struct framer noncoresec_framer = {
|
|||
parse
|
||||
};
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#endif /* LLSEC802154_USES_AUX_HEADER && SEC_LVL && LLSEC802154_USES_FRAME_COUNTER */
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -540,6 +540,7 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr,
|
|||
rtimer_clock_t t0;
|
||||
#if WITH_PHASE_OPTIMIZATION
|
||||
rtimer_clock_t encounter_time = 0;
|
||||
uint8_t is_known_receiver = 0;
|
||||
#endif
|
||||
int strobes;
|
||||
uint8_t got_strobe_ack = 0;
|
||||
|
|
|
@ -63,26 +63,35 @@
|
|||
#define PRINTF(...)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef CSMA_MAX_BACKOFF_EXPONENT
|
||||
#ifdef CSMA_CONF_MAX_BACKOFF_EXPONENT
|
||||
#define CSMA_MAX_BACKOFF_EXPONENT CSMA_CONF_MAX_BACKOFF_EXPONENT
|
||||
#else
|
||||
#define CSMA_MAX_BACKOFF_EXPONENT 3
|
||||
#endif /* CSMA_CONF_MAX_BACKOFF_EXPONENT */
|
||||
#endif /* CSMA_MAX_BACKOFF_EXPONENT */
|
||||
/* Constants of the IEEE 802.15.4 standard */
|
||||
|
||||
#ifndef CSMA_MAX_MAC_TRANSMISSIONS
|
||||
#ifdef CSMA_CONF_MAX_MAC_TRANSMISSIONS
|
||||
#define CSMA_MAX_MAC_TRANSMISSIONS CSMA_CONF_MAX_MAC_TRANSMISSIONS
|
||||
/* macMinBE: Initial backoff exponent. Range 0--CSMA_MAX_BE */
|
||||
#ifdef CSMA_CONF_MIN_BE
|
||||
#define CSMA_MIN_BE CSMA_CONF_MIN_BE
|
||||
#else
|
||||
#define CSMA_MAX_MAC_TRANSMISSIONS 3
|
||||
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS */
|
||||
#endif /* CSMA_MAX_MAC_TRANSMISSIONS */
|
||||
#define CSMA_MIN_BE 0
|
||||
#endif
|
||||
|
||||
#if CSMA_MAX_MAC_TRANSMISSIONS < 1
|
||||
#error CSMA_CONF_MAX_MAC_TRANSMISSIONS must be at least 1.
|
||||
#error Change CSMA_CONF_MAX_MAC_TRANSMISSIONS in contiki-conf.h or in your Makefile.
|
||||
#endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS < 1 */
|
||||
/* macMaxBE: Maximum backoff exponent. Range 3--8 */
|
||||
#ifdef CSMA_CONF_MAX_BE
|
||||
#define CSMA_MAX_BE CSMA_CONF_MAX_BE
|
||||
#else
|
||||
#define CSMA_MAX_BE 4
|
||||
#endif
|
||||
|
||||
/* macMaxCSMABackoffs: Maximum number of backoffs in case of channel busy/collision. Range 0--5 */
|
||||
#ifdef CSMA_CONF_MAX_BACKOFF
|
||||
#define CSMA_MAX_BACKOFF CSMA_CONF_MAX_BACKOFF
|
||||
#else
|
||||
#define CSMA_MAX_BACKOFF 5
|
||||
#endif
|
||||
|
||||
/* macMaxFrameRetries: Maximum number of re-transmissions attampts. Range 0--7 */
|
||||
#ifdef CSMA_CONF_MAX_FRAME_RETRIES
|
||||
#define CSMA_MAX_MAX_FRAME_RETRIES CSMA_CONF_MAX_FRAME_RETRIES
|
||||
#else
|
||||
#define CSMA_MAX_MAX_FRAME_RETRIES 7
|
||||
#endif
|
||||
|
||||
/* Packet metadata */
|
||||
struct qbuf_metadata {
|
||||
|
@ -97,7 +106,7 @@ struct neighbor_queue {
|
|||
linkaddr_t addr;
|
||||
struct ctimer transmit_timer;
|
||||
uint8_t transmissions;
|
||||
uint8_t collisions, deferrals;
|
||||
uint8_t collisions;
|
||||
LIST_STRUCT(queued_packet_list);
|
||||
};
|
||||
|
||||
|
@ -123,7 +132,6 @@ LIST(neighbor_list);
|
|||
|
||||
static void packet_sent(void *ptr, int status, int num_transmissions);
|
||||
static void transmit_packet_list(void *ptr);
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static struct neighbor_queue *
|
||||
neighbor_queue_from_addr(const linkaddr_t *addr)
|
||||
|
@ -139,18 +147,18 @@ neighbor_queue_from_addr(const linkaddr_t *addr)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static clock_time_t
|
||||
default_timebase(void)
|
||||
backoff_period(void)
|
||||
{
|
||||
clock_time_t time;
|
||||
/* The retransmission time must be proportional to the channel
|
||||
check interval of the underlying radio duty cycling layer. */
|
||||
time = NETSTACK_RDC.channel_check_interval();
|
||||
|
||||
/* If the radio duty cycle has no channel check interval (i.e., it
|
||||
does not turn the radio off), we make the retransmission time
|
||||
proportional to the configured MAC channel check rate. */
|
||||
/* If the radio duty cycle has no channel check interval, we use
|
||||
* the default in IEEE 802.15.4: aUnitBackoffPeriod which is
|
||||
* 20 symbols i.e. 320 usec. That is, 1/3125 second. */
|
||||
if(time == 0) {
|
||||
time = CLOCK_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE;
|
||||
time = MAX(CLOCK_SECOND / 3125, 1);
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
@ -171,10 +179,28 @@ transmit_packet_list(void *ptr)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
schedule_transmission(struct neighbor_queue *n)
|
||||
{
|
||||
clock_time_t delay;
|
||||
int backoff_exponent; /* BE in IEEE 802.15.4 */
|
||||
|
||||
backoff_exponent = MIN(n->collisions, CSMA_MAX_BE);
|
||||
|
||||
/* Compute max delay as per IEEE 802.15.4: 2^BE-1 backoff periods */
|
||||
delay = ((1 << backoff_exponent) - 1) * backoff_period();
|
||||
if(delay > 0) {
|
||||
/* Pick a time for next transmission */
|
||||
delay = random_rand() % delay;
|
||||
}
|
||||
|
||||
PRINTF("csma: scheduling transmission in %u ticks, NB=%u, BE=%u\n",
|
||||
(unsigned)delay, n->collisions, backoff_exponent);
|
||||
ctimer_set(&n->transmit_timer, delay, transmit_packet_list, n);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
|
||||
{
|
||||
clock_time_t tx_delay;
|
||||
|
||||
if(p != NULL) {
|
||||
/* Remove packet from list and deallocate */
|
||||
list_remove(n->queued_packet_list, p);
|
||||
|
@ -187,11 +213,9 @@ free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
|
|||
if(list_head(n->queued_packet_list) != NULL) {
|
||||
/* There is a next packet. We reset current tx information */
|
||||
n->transmissions = 0;
|
||||
n->collisions = 0;
|
||||
n->deferrals = 0;
|
||||
/* Set a timer for next transmissions */
|
||||
tx_delay = (status == MAC_TX_OK) ? 0 : default_timebase();
|
||||
ctimer_set(&n->transmit_timer, tx_delay, transmit_packet_list, n);
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
/* Schedule next transmissions */
|
||||
schedule_transmission(n);
|
||||
} else {
|
||||
/* This was the last packet in the queue, we free the neighbor */
|
||||
ctimer_stop(&n->transmit_timer);
|
||||
|
@ -202,34 +226,103 @@ free_packet(struct neighbor_queue *n, struct rdc_buf_list *p, int status)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
tx_done(int status, struct rdc_buf_list *q, struct neighbor_queue *n)
|
||||
{
|
||||
mac_callback_t sent;
|
||||
struct qbuf_metadata *metadata;
|
||||
void *cptr;
|
||||
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
sent = metadata->sent;
|
||||
cptr = metadata->cptr;
|
||||
|
||||
switch(status) {
|
||||
case MAC_TX_OK:
|
||||
PRINTF("csma: rexmit ok %d\n", n->transmissions);
|
||||
break;
|
||||
case MAC_TX_COLLISION:
|
||||
case MAC_TX_NOACK:
|
||||
PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n",
|
||||
status, n->transmissions, n->collisions);
|
||||
break;
|
||||
default:
|
||||
PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status);
|
||||
break;
|
||||
}
|
||||
|
||||
free_packet(n, q, status);
|
||||
mac_call_sent_callback(sent, cptr, status, n->transmissions);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
rexmit(struct rdc_buf_list *q, struct neighbor_queue *n)
|
||||
{
|
||||
schedule_transmission(n);
|
||||
/* This is needed to correctly attribute energy that we spent
|
||||
transmitting this packet. */
|
||||
queuebuf_update_attr_from_packetbuf(q->buf);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
collision(struct rdc_buf_list *q, struct neighbor_queue *n,
|
||||
int num_transmissions)
|
||||
{
|
||||
struct qbuf_metadata *metadata;
|
||||
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
|
||||
n->collisions += num_transmissions;
|
||||
|
||||
if(n->collisions > CSMA_MAX_BACKOFF) {
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
/* Increment to indicate a next retry */
|
||||
n->transmissions++;
|
||||
}
|
||||
|
||||
if(n->transmissions >= metadata->max_transmissions) {
|
||||
tx_done(MAC_TX_COLLISION, q, n);
|
||||
} else {
|
||||
PRINTF("csma: rexmit collision %d\n", n->transmissions);
|
||||
rexmit(q, n);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
noack(struct rdc_buf_list *q, struct neighbor_queue *n, int num_transmissions)
|
||||
{
|
||||
struct qbuf_metadata *metadata;
|
||||
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
n->transmissions += num_transmissions;
|
||||
|
||||
if(n->transmissions >= metadata->max_transmissions) {
|
||||
tx_done(MAC_TX_NOACK, q, n);
|
||||
} else {
|
||||
PRINTF("csma: rexmit noack %d\n", n->transmissions);
|
||||
rexmit(q, n);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
tx_ok(struct rdc_buf_list *q, struct neighbor_queue *n, int num_transmissions)
|
||||
{
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
n->transmissions += num_transmissions;
|
||||
tx_done(MAC_TX_OK, q, n);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
packet_sent(void *ptr, int status, int num_transmissions)
|
||||
{
|
||||
struct neighbor_queue *n;
|
||||
struct rdc_buf_list *q;
|
||||
struct qbuf_metadata *metadata;
|
||||
clock_time_t time = 0;
|
||||
mac_callback_t sent;
|
||||
void *cptr;
|
||||
int num_tx;
|
||||
int backoff_exponent;
|
||||
int backoff_transmissions;
|
||||
|
||||
n = ptr;
|
||||
if(n == NULL) {
|
||||
return;
|
||||
}
|
||||
switch(status) {
|
||||
case MAC_TX_OK:
|
||||
case MAC_TX_NOACK:
|
||||
n->transmissions += num_transmissions;
|
||||
break;
|
||||
case MAC_TX_COLLISION:
|
||||
n->collisions += num_transmissions;
|
||||
break;
|
||||
case MAC_TX_DEFERRED:
|
||||
n->deferrals += num_transmissions;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find out what packet this callback refers to */
|
||||
for(q = list_head(n->queued_packet_list);
|
||||
|
@ -240,78 +333,30 @@ packet_sent(void *ptr, int status, int num_transmissions)
|
|||
}
|
||||
}
|
||||
|
||||
if(q != NULL) {
|
||||
metadata = (struct qbuf_metadata *)q->ptr;
|
||||
if(q == NULL) {
|
||||
PRINTF("csma: seqno %d not found\n",
|
||||
packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
|
||||
return;
|
||||
} else if(q->ptr == NULL) {
|
||||
PRINTF("csma: no metadata\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(metadata != NULL) {
|
||||
sent = metadata->sent;
|
||||
cptr = metadata->cptr;
|
||||
num_tx = n->transmissions;
|
||||
if(status == MAC_TX_COLLISION ||
|
||||
status == MAC_TX_NOACK) {
|
||||
|
||||
/* If the transmission was not performed because of a
|
||||
collision or noack, we must retransmit the packet. */
|
||||
|
||||
switch(status) {
|
||||
case MAC_TX_COLLISION:
|
||||
PRINTF("csma: rexmit collision %d\n", n->transmissions);
|
||||
break;
|
||||
case MAC_TX_NOACK:
|
||||
PRINTF("csma: rexmit noack %d\n", n->transmissions);
|
||||
break;
|
||||
default:
|
||||
PRINTF("csma: rexmit err %d, %d\n", status, n->transmissions);
|
||||
}
|
||||
|
||||
/* The retransmission time must be proportional to the channel
|
||||
check interval of the underlying radio duty cycling layer. */
|
||||
time = default_timebase();
|
||||
|
||||
/* The retransmission time uses a truncated exponential backoff
|
||||
* so that the interval between the transmissions increase with
|
||||
* each retransmit. */
|
||||
backoff_exponent = num_tx;
|
||||
|
||||
/* Truncate the exponent if needed. */
|
||||
if(backoff_exponent > CSMA_MAX_BACKOFF_EXPONENT) {
|
||||
backoff_exponent = CSMA_MAX_BACKOFF_EXPONENT;
|
||||
}
|
||||
|
||||
/* Proceed to exponentiation. */
|
||||
backoff_transmissions = 1 << backoff_exponent;
|
||||
|
||||
/* Pick a time for next transmission, within the interval:
|
||||
* [time, time + 2^backoff_exponent * time[ */
|
||||
time = time + (random_rand() % (backoff_transmissions * time));
|
||||
|
||||
if(n->transmissions < metadata->max_transmissions) {
|
||||
PRINTF("csma: retransmitting with time %lu %p\n", time, q);
|
||||
ctimer_set(&n->transmit_timer, time,
|
||||
transmit_packet_list, n);
|
||||
/* This is needed to correctly attribute energy that we spent
|
||||
transmitting this packet. */
|
||||
queuebuf_update_attr_from_packetbuf(q->buf);
|
||||
} else {
|
||||
PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n",
|
||||
status, n->transmissions, n->collisions);
|
||||
free_packet(n, q, status);
|
||||
mac_call_sent_callback(sent, cptr, status, num_tx);
|
||||
}
|
||||
} else {
|
||||
if(status == MAC_TX_OK) {
|
||||
PRINTF("csma: rexmit ok %d\n", n->transmissions);
|
||||
} else {
|
||||
PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status);
|
||||
}
|
||||
free_packet(n, q, status);
|
||||
mac_call_sent_callback(sent, cptr, status, num_tx);
|
||||
}
|
||||
} else {
|
||||
PRINTF("csma: no metadata\n");
|
||||
}
|
||||
} else {
|
||||
PRINTF("csma: seqno %d not found\n", packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO));
|
||||
switch(status) {
|
||||
case MAC_TX_OK:
|
||||
tx_ok(q, n, num_transmissions);
|
||||
break;
|
||||
case MAC_TX_NOACK:
|
||||
noack(q, n, num_transmissions);
|
||||
break;
|
||||
case MAC_TX_COLLISION:
|
||||
collision(q, n, num_transmissions);
|
||||
break;
|
||||
case MAC_TX_DEFERRED:
|
||||
break;
|
||||
default:
|
||||
tx_done(status, q, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -346,8 +391,7 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
/* Init neighbor entry */
|
||||
linkaddr_copy(&n->addr, addr);
|
||||
n->transmissions = 0;
|
||||
n->collisions = 0;
|
||||
n->deferrals = 0;
|
||||
n->collisions = CSMA_MIN_BE;
|
||||
/* Init packet list for this neighbor */
|
||||
LIST_STRUCT_INIT(n, queued_packet_list);
|
||||
/* Add neighbor to the list */
|
||||
|
@ -368,7 +412,7 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
/* Neighbor and packet successfully allocated */
|
||||
if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) {
|
||||
/* Use default configuration for max transmissions */
|
||||
metadata->max_transmissions = CSMA_MAX_MAC_TRANSMISSIONS;
|
||||
metadata->max_transmissions = CSMA_MAX_MAX_FRAME_RETRIES + 1;
|
||||
} else {
|
||||
metadata->max_transmissions =
|
||||
packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS);
|
||||
|
@ -389,7 +433,7 @@ send_packet(mac_callback_t sent, void *ptr)
|
|||
list_length(n->queued_packet_list), memb_numfree(&packet_memb));
|
||||
/* If q is the first packet in the neighbor's queue, send asap */
|
||||
if(list_head(n->queued_packet_list) == q) {
|
||||
ctimer_set(&n->transmit_timer, 0, transmit_packet_list, n);
|
||||
schedule_transmission(n);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
|
||||
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
|
||||
log->asn.ms1b, log->asn.ls4b,
|
||||
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
|
||||
tsch_calculate_channel(&log->asn, log->link->channel_offset));
|
||||
if(log->link == NULL) {
|
||||
printf("TSCH: {asn-%x.%lx link-NULL} ", log->asn.ms1b, log->asn.ls4b);
|
||||
} else {
|
||||
struct tsch_slotframe *sf = tsch_schedule_get_slotframe_by_handle(log->link->slotframe_handle);
|
||||
printf("TSCH: {asn-%x.%lx link-%u-%u-%u-%u ch-%u} ",
|
||||
log->asn.ms1b, log->asn.ls4b,
|
||||
log->link->slotframe_handle, sf ? sf->size.val : 0, log->link->timeslot, log->link->channel_offset,
|
||||
tsch_calculate_channel(&log->asn, log->link->channel_offset));
|
||||
}
|
||||
switch(log->type) {
|
||||
case tsch_log_tx:
|
||||
printf("%s-%u-%u %u tx %d, st %d-%d",
|
||||
|
@ -103,7 +107,7 @@ tsch_log_process_pending(void)
|
|||
break;
|
||||
case tsch_log_rx:
|
||||
printf("%s-%u-%u %u rx %d",
|
||||
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->tx.sec_level,
|
||||
log->rx.is_unicast == 0 ? "bc" : "uc", log->rx.is_data, log->rx.sec_level,
|
||||
log->rx.datalen,
|
||||
log->rx.src);
|
||||
if(log->rx.drift_used) {
|
||||
|
|
|
@ -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,26 +952,28 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr))
|
|||
current_link = backup_link;
|
||||
current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor);
|
||||
}
|
||||
/* 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;
|
||||
/* Decide whether it is a TX/RX/IDLE or OFF slot */
|
||||
/* Actual slot operation */
|
||||
if(current_packet != NULL) {
|
||||
/* We have something to transmit, do the following:
|
||||
* 1. send
|
||||
* 2. update_backoff_state(current_neighbor)
|
||||
* 3. post tx callback
|
||||
**/
|
||||
static struct pt slot_tx_pt;
|
||||
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
|
||||
} else if((current_link->link_options & LINK_OPTION_RX)) {
|
||||
/* Listen */
|
||||
static struct pt slot_rx_pt;
|
||||
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
|
||||
is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX);
|
||||
if(is_active_slot) {
|
||||
/* Hop channel */
|
||||
current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset);
|
||||
NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel);
|
||||
/* Turn the radio on already here if configured so; necessary for radios with slow startup */
|
||||
tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT);
|
||||
/* Decide whether it is a TX/RX/IDLE or OFF slot */
|
||||
/* Actual slot operation */
|
||||
if(current_packet != NULL) {
|
||||
/* We have something to transmit, do the following:
|
||||
* 1. send
|
||||
* 2. update_backoff_state(current_neighbor)
|
||||
* 3. post tx callback
|
||||
**/
|
||||
static struct pt slot_tx_pt;
|
||||
PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t));
|
||||
} else {
|
||||
/* Listen */
|
||||
static struct pt slot_rx_pt;
|
||||
PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t));
|
||||
}
|
||||
}
|
||||
TSCH_DEBUG_SLOT_END();
|
||||
}
|
||||
|
|
|
@ -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,59 +207,72 @@ nbr_table_allocate(void)
|
|||
key = memb_alloc(&neighbor_addr_mem);
|
||||
if(key != NULL) {
|
||||
return key;
|
||||
} else { /* No more space, try to free a neighbor.
|
||||
* The replacement policy is the following: remove neighbor that is:
|
||||
* (1) not locked
|
||||
* (2) used by fewest tables
|
||||
* (3) oldest (the list is ordered by insertion time)
|
||||
* */
|
||||
/* Get item from first key */
|
||||
key = list_head(nbr_table_keys);
|
||||
while(key != NULL) {
|
||||
int item_index = index_from_key(key);
|
||||
int locked = locked_map[item_index];
|
||||
/* Never delete a locked item */
|
||||
if(!locked) {
|
||||
int used = used_map[item_index];
|
||||
int used_count = 0;
|
||||
/* Count how many tables are using this item */
|
||||
while(used != 0) {
|
||||
if((used & 1) == 1) {
|
||||
used_count++;
|
||||
}
|
||||
used >>= 1;
|
||||
}
|
||||
/* Find least used item */
|
||||
if(least_used_key == NULL || used_count < least_used_count) {
|
||||
least_used_key = key;
|
||||
least_used_count = used_count;
|
||||
if(used_count == 0) { /* We won't find any least used item */
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef NBR_TABLE_FIND_REMOVABLE
|
||||
const linkaddr_t *lladdr;
|
||||
lladdr = NBR_TABLE_FIND_REMOVABLE(reason, data);
|
||||
if(lladdr == NULL) {
|
||||
/* Nothing found that can be deleted - return NULL to indicate failure */
|
||||
PRINTF("*** Not removing entry to allocate new\n");
|
||||
return NULL;
|
||||
} else {
|
||||
/* used least_used_key to indicate what is the least useful entry */
|
||||
int index;
|
||||
int locked = 0;
|
||||
if((index = index_from_lladdr(lladdr)) != -1) {
|
||||
least_used_key = key_from_index(index);
|
||||
locked = locked_map[index];
|
||||
}
|
||||
/* Allow delete of locked item? */
|
||||
if(least_used_key != NULL && locked) {
|
||||
PRINTF("Deleting locked item!\n");
|
||||
locked_map[index] = 0;
|
||||
}
|
||||
key = list_item_next(key);
|
||||
}
|
||||
#endif /* NBR_TABLE_FIND_REMOVABLE */
|
||||
|
||||
if(least_used_key == NULL) {
|
||||
/* No more space, try to free a neighbor.
|
||||
* The replacement policy is the following: remove neighbor that is:
|
||||
* (1) not locked
|
||||
* (2) used by fewest tables
|
||||
* (3) oldest (the list is ordered by insertion time)
|
||||
* */
|
||||
/* Get item from first key */
|
||||
key = list_head(nbr_table_keys);
|
||||
while(key != NULL) {
|
||||
int item_index = index_from_key(key);
|
||||
int locked = locked_map[item_index];
|
||||
/* Never delete a locked item */
|
||||
if(!locked) {
|
||||
int used = used_map[item_index];
|
||||
int used_count = 0;
|
||||
/* Count how many tables are using this item */
|
||||
while(used != 0) {
|
||||
if((used & 1) == 1) {
|
||||
used_count++;
|
||||
}
|
||||
used >>= 1;
|
||||
}
|
||||
/* Find least used item */
|
||||
if(least_used_key == NULL || used_count < least_used_count) {
|
||||
least_used_key = key;
|
||||
least_used_count = used_count;
|
||||
if(used_count == 0) { /* We won't find any least used item */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
key = list_item_next(key);
|
||||
}
|
||||
}
|
||||
|
||||
if(least_used_key == NULL) {
|
||||
/* We haven't found any unlocked item, allocation fails */
|
||||
return NULL;
|
||||
} else {
|
||||
/* Reuse least used item */
|
||||
int i;
|
||||
for(i = 0; i<MAX_NUM_TABLES; i++) {
|
||||
if(all_tables[i] != NULL && all_tables[i]->callback != NULL) {
|
||||
/* Call table callback for each table that uses this item */
|
||||
nbr_table_item_t *removed_item = item_from_key(all_tables[i], least_used_key);
|
||||
if(nbr_get_bit(used_map, all_tables[i], removed_item) == 1) {
|
||||
all_tables[i]->callback(removed_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Empty used map */
|
||||
used_map[index_from_key(least_used_key)] = 0;
|
||||
/* Remove neighbor from list */
|
||||
list_remove(nbr_table_keys, least_used_key);
|
||||
/* Return associated key */
|
||||
remove_key(least_used_key);
|
||||
return least_used_key;
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +330,7 @@ nbr_table_next(nbr_table_t *table, nbr_table_item_t *item)
|
|||
/*---------------------------------------------------------------------------*/
|
||||
/* Add a neighbor indexed with its link-layer address */
|
||||
nbr_table_item_t *
|
||||
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
|
||||
nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr, nbr_table_reason_t reason, void *data)
|
||||
{
|
||||
int index;
|
||||
nbr_table_item_t *item;
|
||||
|
@ -303,7 +344,7 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
|
|||
|
||||
if((index = index_from_lladdr(lladdr)) == -1) {
|
||||
/* Neighbor not yet in table, let's try to allocate one */
|
||||
key = nbr_table_allocate();
|
||||
key = nbr_table_allocate(reason, data);
|
||||
|
||||
/* No space available for new entry */
|
||||
if(key == NULL) {
|
||||
|
@ -327,6 +368,9 @@ nbr_table_add_lladdr(nbr_table_t *table, const linkaddr_t *lladdr)
|
|||
memset(item, 0, table->item_size);
|
||||
nbr_set_bit(used_map, table, item, 1);
|
||||
|
||||
#if DEBUG
|
||||
print_table();
|
||||
#endif
|
||||
return item;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -377,9 +421,42 @@ nbr_table_get_lladdr(nbr_table_t *table, const void *item)
|
|||
return key != NULL ? &key->lladdr : NULL;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Update link-layer address of an item */
|
||||
int
|
||||
nbr_table_update_lladdr(const linkaddr_t *old_addr, const linkaddr_t *new_addr,
|
||||
int remove_if_duplicate)
|
||||
{
|
||||
int index;
|
||||
int new_index;
|
||||
nbr_table_key_t *key;
|
||||
index = index_from_lladdr(old_addr);
|
||||
if(index == -1) {
|
||||
/* Failure to change since there is nothing to change. */
|
||||
return 0;
|
||||
}
|
||||
if((new_index = index_from_lladdr(new_addr)) != -1) {
|
||||
/* check if it is a change or not - do not remove / fail if same */
|
||||
if(new_index == index) {
|
||||
return 1;
|
||||
}
|
||||
/* This new entry already exists - failure! - remove if requested. */
|
||||
if(remove_if_duplicate) {
|
||||
remove_key(key_from_index(index));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
key = key_from_index(index);
|
||||
/**
|
||||
* Copy the new lladdr into the key - since we know that there is no
|
||||
* conflicting entry.
|
||||
*/
|
||||
memcpy(&key->lladdr, new_addr, sizeof(linkaddr_t));
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if DEBUG
|
||||
static void
|
||||
handle_periodic_timer(void *ptr)
|
||||
print_table()
|
||||
{
|
||||
int i, j;
|
||||
/* Printout all neighbors and which tables they are used in */
|
||||
|
@ -394,6 +471,12 @@ handle_periodic_timer(void *ptr)
|
|||
PRINTF("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
handle_periodic_timer(void *ptr)
|
||||
{
|
||||
print_table();
|
||||
ctimer_reset(&periodic_timer);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -45,31 +45,52 @@
|
|||
#define RPL_CONF_STATS 0
|
||||
#endif /* RPL_CONF_STATS */
|
||||
|
||||
/*
|
||||
* Select routing metric supported at runtime. This must be a valid
|
||||
* DAG Metric Container Object Type (see below). Currently, we only
|
||||
* support RPL_DAG_MC_ETX and RPL_DAG_MC_ENERGY.
|
||||
* When MRHOF (RFC6719) is used with ETX, no metric container must
|
||||
* be used; instead the rank carries ETX directly.
|
||||
/*
|
||||
* The objective function (OF) used by a RPL root is configurable through
|
||||
* the RPL_CONF_OF_OCP parameter. This is defined as the objective code
|
||||
* point (OCP) of the OF, RPL_OCP_OF0 or RPL_OCP_MRHOF. This flag is of
|
||||
* no relevance to non-root nodes, which run the OF advertised in the
|
||||
* instance they join.
|
||||
* Make sure the selected of is inRPL_SUPPORTED_OFS.
|
||||
*/
|
||||
#ifdef RPL_CONF_OF_OCP
|
||||
#define RPL_OF_OCP RPL_CONF_OF_OCP
|
||||
#else /* RPL_CONF_OF_OCP */
|
||||
#define RPL_OF_OCP RPL_OCP_MRHOF
|
||||
#endif /* RPL_CONF_OF_OCP */
|
||||
|
||||
/*
|
||||
* The set of objective functions supported at runtime. Nodes are only
|
||||
* able to join instances that advertise an OF in this set. To include
|
||||
* both OF0 and MRHOF, use {&rpl_of0, &rpl_mrhof}.
|
||||
*/
|
||||
#ifdef RPL_CONF_SUPPORTED_OFS
|
||||
#define RPL_SUPPORTED_OFS RPL_CONF_SUPPORTED_OFS
|
||||
#else /* RPL_CONF_SUPPORTED_OFS */
|
||||
#define RPL_SUPPORTED_OFS {&rpl_mrhof}
|
||||
#endif /* RPL_CONF_SUPPORTED_OFS */
|
||||
|
||||
/*
|
||||
* Enable/disable RPL Metric Containers (MC). The actual MC in use
|
||||
* for a given DODAG is decided at runtime, when joining. Note that
|
||||
* OF0 (RFC6552) operates without MC, and so does MRHOF (RFC6719) when
|
||||
* used with ETX as a metric (the rank is the metric). We disable MC
|
||||
* by default, but note it must be enabled to support joining a DODAG
|
||||
* that requires MC (e.g., MRHOF with a metric other than ETX).
|
||||
*/
|
||||
#ifdef RPL_CONF_WITH_MC
|
||||
#define RPL_WITH_MC RPL_CONF_WITH_MC
|
||||
#else /* RPL_CONF_WITH_MC */
|
||||
#define RPL_WITH_MC 0
|
||||
#endif /* RPL_CONF_WITH_MC */
|
||||
|
||||
/* The MC advertised in DIOs and propagating from the root */
|
||||
#ifdef RPL_CONF_DAG_MC
|
||||
#define RPL_DAG_MC RPL_CONF_DAG_MC
|
||||
#else
|
||||
#define RPL_DAG_MC RPL_DAG_MC_NONE
|
||||
#endif /* RPL_CONF_DAG_MC */
|
||||
|
||||
/*
|
||||
* The objective function used by RPL is configurable through the
|
||||
* RPL_CONF_OF parameter. This should be defined to be the name of an
|
||||
* rpl_of object linked into the system image, e.g., rpl_of0.
|
||||
*/
|
||||
#ifdef RPL_CONF_OF
|
||||
#define RPL_OF RPL_CONF_OF
|
||||
#else
|
||||
/* ETX is the default objective function. */
|
||||
#define RPL_OF rpl_mrhof
|
||||
#endif /* RPL_CONF_OF */
|
||||
|
||||
/* This value decides which DAG instance we should participate in by default. */
|
||||
#ifdef RPL_CONF_DEFAULT_INSTANCE
|
||||
#define RPL_DEFAULT_INSTANCE RPL_CONF_DEFAULT_INSTANCE
|
||||
|
@ -118,7 +139,7 @@
|
|||
#ifdef RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
|
||||
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME
|
||||
#else
|
||||
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 0
|
||||
#define RPL_DEFAULT_ROUTE_INFINITE_LIFETIME 1
|
||||
#endif /* RPL_CONF_DEFAULT_ROUTE_INFINITE_LIFETIME */
|
||||
|
||||
/*
|
||||
|
@ -186,21 +207,12 @@
|
|||
#define RPL_DIO_REDUNDANCY 10
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initial metric attributed to a link when the ETX is unknown
|
||||
*/
|
||||
#ifndef RPL_CONF_INIT_LINK_METRIC
|
||||
#define RPL_INIT_LINK_METRIC 2
|
||||
#else
|
||||
#define RPL_INIT_LINK_METRIC RPL_CONF_INIT_LINK_METRIC
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default route lifetime unit. This is the granularity of time
|
||||
* used in RPL lifetime values, in seconds.
|
||||
*/
|
||||
#ifndef RPL_CONF_DEFAULT_LIFETIME_UNIT
|
||||
#define RPL_DEFAULT_LIFETIME_UNIT 0xffff
|
||||
#define RPL_DEFAULT_LIFETIME_UNIT 60
|
||||
#else
|
||||
#define RPL_DEFAULT_LIFETIME_UNIT RPL_CONF_DEFAULT_LIFETIME_UNIT
|
||||
#endif
|
||||
|
@ -209,7 +221,7 @@
|
|||
* Default route lifetime as a multiple of the lifetime unit.
|
||||
*/
|
||||
#ifndef RPL_CONF_DEFAULT_LIFETIME
|
||||
#define RPL_DEFAULT_LIFETIME 0xff
|
||||
#define RPL_DEFAULT_LIFETIME 30
|
||||
#else
|
||||
#define RPL_DEFAULT_LIFETIME RPL_CONF_DEFAULT_LIFETIME
|
||||
#endif
|
||||
|
@ -224,16 +236,38 @@
|
|||
#endif
|
||||
|
||||
/*
|
||||
* Hop-by-hop option
|
||||
* This option control the insertion of the RPL Hop-by-Hop extension header
|
||||
* into packets originating from this node. Incoming Hop-by-hop extension
|
||||
* header are still processed and forwarded.
|
||||
*/
|
||||
#ifdef RPL_CONF_INSERT_HBH_OPTION
|
||||
#define RPL_INSERT_HBH_OPTION RPL_CONF_INSERT_HBH_OPTION
|
||||
* RPL DAO ACK support. When enabled, DAO ACK will be sent and requested.
|
||||
* This will also enable retransmission of DAO when no ack is received.
|
||||
* */
|
||||
#ifdef RPL_CONF_WITH_DAO_ACK
|
||||
#define RPL_WITH_DAO_ACK RPL_CONF_WITH_DAO_ACK
|
||||
#else
|
||||
#define RPL_INSERT_HBH_OPTION 1
|
||||
#endif
|
||||
#define RPL_WITH_DAO_ACK 0
|
||||
#endif /* RPL_CONF_WITH_DAO_ACK */
|
||||
|
||||
/*
|
||||
* RPL REPAIR ON DAO NACK. When enabled, DAO NACK will trigger a local
|
||||
* repair in order to quickly find a new parent to send DAO's to.
|
||||
* NOTE: this is too agressive in some cases so use with care.
|
||||
* */
|
||||
#ifdef RPL_CONF_RPL_REPAIR_ON_DAO_NACK
|
||||
#define RPL_REPAIR_ON_DAO_NACK RPL_CONF_RPL_REPAIR_ON_DAO_NACK
|
||||
#else
|
||||
#define RPL_REPAIR_ON_DAO_NACK 0
|
||||
#endif /* RPL_CONF_RPL_REPAIR_ON_DAO_NACK */
|
||||
|
||||
/*
|
||||
* Setting the DIO_REFRESH_DAO_ROUTES will make the RPL root always
|
||||
* increase the DTSN (Destination Advertisement Trigger Sequence Number)
|
||||
* when sending multicast DIO. This is to get all children to re-register
|
||||
* their DAO route. This is needed when DAO-ACK is not enabled to add
|
||||
* reliability to route maintenance.
|
||||
* */
|
||||
#ifdef RPL_CONF_DIO_REFRESH_DAO_ROUTES
|
||||
#define RPL_DIO_REFRESH_DAO_ROUTES RPL_CONF_DIO_REFRESH_DAO_ROUTES
|
||||
#else
|
||||
#define RPL_DIO_REFRESH_DAO_ROUTES 1
|
||||
#endif /* RPL_CONF_DIO_REFRESH_DAO_ROUTES */
|
||||
|
||||
/*
|
||||
* RPL probing. When enabled, probes will be sent periodically to keep
|
||||
|
@ -254,22 +288,13 @@
|
|||
#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 (10 * 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 +317,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 */
|
||||
rpl_remove_routes(dag);
|
||||
if(RPL_IS_STORING(dag->instance)) {
|
||||
rpl_remove_routes(dag);
|
||||
}
|
||||
if(dag->instance != NULL &&
|
||||
dag->instance->def_route != NULL) {
|
||||
uip_ds6_defrt_rm(dag->instance->def_route);
|
||||
dag->instance->def_route = NULL;
|
||||
}
|
||||
|
||||
uip_ip6addr(&prefix, 0xaaaa, 0, 0, 0, 0, 0, 0, 0);
|
||||
uip_ip6addr(&prefix, UIP_DS6_DEFAULT_PREFIX, 0, 0, 0, 0, 0, 0, 0);
|
||||
rpl_set_prefix(dag, &prefix, 64);
|
||||
PRINTF("rpl_dag_root_init_dag: created a new RPL dag\n");
|
||||
return 0;
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "net/link-stats.h"
|
||||
#include "net/rpl/rpl-private.h"
|
||||
#include "net/ip/uip.h"
|
||||
#include "net/ipv6/uip-nd6.h"
|
||||
|
@ -66,8 +67,8 @@ void RPL_CALLBACK_PARENT_SWITCH(rpl_parent_t *old, rpl_parent_t *new);
|
|||
#endif /* RPL_CALLBACK_PARENT_SWITCH */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
extern rpl_of_t RPL_OF;
|
||||
static rpl_of_t * const objective_functions[] = {&RPL_OF};
|
||||
extern rpl_of_t rpl_of0, rpl_mrhof;
|
||||
static rpl_of_t * const objective_functions[] = RPL_SUPPORTED_OFS;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* RPL definitions. */
|
||||
|
@ -88,25 +89,29 @@ rpl_instance_t *default_instance;
|
|||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_print_neighbor_list()
|
||||
rpl_print_neighbor_list(void)
|
||||
{
|
||||
if(default_instance != NULL && default_instance->current_dag != NULL &&
|
||||
default_instance->of != NULL && default_instance->of->calculate_rank != NULL) {
|
||||
default_instance->of != NULL) {
|
||||
int curr_dio_interval = default_instance->dio_intcurrent;
|
||||
int curr_rank = default_instance->current_dag->rank;
|
||||
rpl_parent_t *p = nbr_table_head(rpl_parents);
|
||||
clock_time_t now = clock_time();
|
||||
clock_time_t clock_now = clock_time();
|
||||
|
||||
printf("RPL: rank %u dioint %u, %u nbr(s)\n", curr_rank, curr_dio_interval, uip_ds6_nbr_num());
|
||||
printf("RPL: MOP %u OCP %u rank %u dioint %u, nbr count %u\n",
|
||||
default_instance->mop, default_instance->of->ocp, curr_rank, curr_dio_interval, uip_ds6_nbr_num());
|
||||
while(p != NULL) {
|
||||
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
|
||||
printf("RPL: nbr %3u %5u, %5u => %5u %c%c (last tx %u min ago)\n",
|
||||
nbr_table_get_lladdr(rpl_parents, p)->u8[7],
|
||||
p->rank, nbr ? nbr->link_metric : 0,
|
||||
default_instance->of->calculate_rank(p, 0),
|
||||
default_instance->current_dag == p->dag ? 'd' : ' ',
|
||||
p == default_instance->current_dag->preferred_parent ? '*' : ' ',
|
||||
(unsigned)((now - p->last_tx_time) / (60 * CLOCK_SECOND)));
|
||||
const struct link_stats *stats = rpl_get_parent_link_stats(p);
|
||||
printf("RPL: nbr %3u %5u, %5u => %5u -- %2u %c%c (last tx %u min ago)\n",
|
||||
rpl_get_parent_ipaddr(p)->u8[15],
|
||||
p->rank,
|
||||
rpl_get_parent_link_metric(p),
|
||||
rpl_rank_via_parent(p),
|
||||
stats != NULL ? stats->freshness : 0,
|
||||
link_stats_is_fresh(stats) ? 'f' : ' ',
|
||||
p == default_instance->current_dag->preferred_parent ? 'p' : ' ',
|
||||
(unsigned)((clock_now - stats->last_tx_time) / (60 * CLOCK_SECOND))
|
||||
);
|
||||
p = nbr_table_next(rpl_parents, p);
|
||||
}
|
||||
printf("RPL: end of list\n");
|
||||
|
@ -116,8 +121,7 @@ rpl_print_neighbor_list()
|
|||
uip_ds6_nbr_t *
|
||||
rpl_get_nbr(rpl_parent_t *parent)
|
||||
{
|
||||
linkaddr_t *lladdr = NULL;
|
||||
lladdr = nbr_table_get_lladdr(rpl_parents, parent);
|
||||
const linkaddr_t *lladdr = rpl_get_parent_lladdr(parent);
|
||||
if(lladdr != NULL) {
|
||||
return nbr_table_get_from_lladdr(ds6_neighbors, lladdr);
|
||||
} else {
|
||||
|
@ -151,30 +155,78 @@ rpl_get_parent_rank(uip_lladdr_t *addr)
|
|||
if(p != NULL) {
|
||||
return p->rank;
|
||||
} else {
|
||||
return 0;
|
||||
return INFINITE_RANK;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uint16_t
|
||||
rpl_get_parent_link_metric(const uip_lladdr_t *addr)
|
||||
rpl_get_parent_link_metric(rpl_parent_t *p)
|
||||
{
|
||||
uip_ds6_nbr_t *nbr;
|
||||
nbr = nbr_table_get_from_lladdr(ds6_neighbors, (const linkaddr_t *)addr);
|
||||
|
||||
if(nbr != NULL) {
|
||||
return nbr->link_metric;
|
||||
} else {
|
||||
return 0;
|
||||
if(p != NULL && p->dag != NULL) {
|
||||
rpl_instance_t *instance = p->dag->instance;
|
||||
if(instance != NULL && instance->of != NULL && instance->of->parent_link_metric != NULL) {
|
||||
return instance->of->parent_link_metric(p);
|
||||
}
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_rank_t
|
||||
rpl_rank_via_parent(rpl_parent_t *p)
|
||||
{
|
||||
if(p != NULL && p->dag != NULL) {
|
||||
rpl_instance_t *instance = p->dag->instance;
|
||||
if(instance != NULL && instance->of != NULL && instance->of->rank_via_parent != NULL) {
|
||||
return instance->of->rank_via_parent(p);
|
||||
}
|
||||
}
|
||||
return INFINITE_RANK;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
const linkaddr_t *
|
||||
rpl_get_parent_lladdr(rpl_parent_t *p)
|
||||
{
|
||||
return nbr_table_get_lladdr(rpl_parents, p);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
uip_ipaddr_t *
|
||||
rpl_get_parent_ipaddr(rpl_parent_t *p)
|
||||
{
|
||||
linkaddr_t *lladdr = nbr_table_get_lladdr(rpl_parents, p);
|
||||
const linkaddr_t *lladdr = rpl_get_parent_lladdr(p);
|
||||
return uip_ds6_nbr_ipaddr_from_lladdr((uip_lladdr_t *)lladdr);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
const struct link_stats *
|
||||
rpl_get_parent_link_stats(rpl_parent_t *p)
|
||||
{
|
||||
const linkaddr_t *lladdr = rpl_get_parent_lladdr(p);
|
||||
return link_stats_from_lladdr(lladdr);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_parent_is_fresh(rpl_parent_t *p)
|
||||
{
|
||||
const struct link_stats *stats = rpl_get_parent_link_stats(p);
|
||||
return link_stats_is_fresh(stats);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_parent_is_reachable(rpl_parent_t *p) {
|
||||
if(p == NULL || p->dag == NULL || p->dag->instance == NULL || p->dag->instance->of == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
#ifndef UIP_CONF_ND6_SEND_NA
|
||||
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
|
||||
/* Exclude links to a neighbor that is not reachable at a NUD level */
|
||||
if(nbr == NULL || nbr->state != NBR_REACHABLE) {
|
||||
return 0;
|
||||
}
|
||||
#endif /* UIP_CONF_ND6_SEND_NA */
|
||||
/* If we don't have fresh link information, assume the parent is reachable. */
|
||||
return !rpl_parent_is_fresh(p) || p->dag->instance->of->parent_has_usable_link(p);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
rpl_set_preferred_parent(rpl_dag_t *dag, rpl_parent_t *p)
|
||||
{
|
||||
|
@ -340,7 +392,12 @@ rpl_set_root(uint8_t instance_id, uip_ipaddr_t *dag_id)
|
|||
dag->grounded = RPL_GROUNDED;
|
||||
dag->preference = RPL_PREFERENCE;
|
||||
instance->mop = RPL_MOP_DEFAULT;
|
||||
instance->of = &RPL_OF;
|
||||
instance->of = rpl_find_of(RPL_OF_OCP);
|
||||
if(instance->of == NULL) {
|
||||
PRINTF("RPL: OF with OCP %u not supported\n", RPL_OF_OCP);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rpl_set_preferred_parent(dag, NULL);
|
||||
|
||||
memcpy(&dag->dag_id, dag_id, sizeof(dag->dag_id));
|
||||
|
@ -361,7 +418,9 @@ rpl_set_root(uint8_t instance_id, uip_ipaddr_t *dag_id)
|
|||
|
||||
if(instance->current_dag != dag && instance->current_dag != NULL) {
|
||||
/* Remove routes installed by DAOs. */
|
||||
rpl_remove_routes(instance->current_dag);
|
||||
if(RPL_IS_STORING(instance)) {
|
||||
rpl_remove_routes(instance->current_dag);
|
||||
}
|
||||
|
||||
instance->current_dag->joined = 0;
|
||||
}
|
||||
|
@ -610,7 +669,9 @@ rpl_free_dag(rpl_dag_t *dag)
|
|||
dag->joined = 0;
|
||||
|
||||
/* Remove routes installed by DAOs. */
|
||||
rpl_remove_routes(dag);
|
||||
if(RPL_IS_STORING(dag->instance)) {
|
||||
rpl_remove_routes(dag);
|
||||
}
|
||||
|
||||
/* Remove autoconfigured address */
|
||||
if((dag->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS)) {
|
||||
|
@ -634,25 +695,18 @@ rpl_add_parent(rpl_dag_t *dag, rpl_dio_t *dio, uip_ipaddr_t *addr)
|
|||
PRINT6ADDR(addr);
|
||||
PRINTF("\n");
|
||||
if(lladdr != NULL) {
|
||||
/* Add parent in rpl_parents */
|
||||
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr);
|
||||
/* Add parent in rpl_parents - again this is due to DIO */
|
||||
p = nbr_table_add_lladdr(rpl_parents, (linkaddr_t *)lladdr,
|
||||
NBR_TABLE_REASON_RPL_DIO, dio);
|
||||
if(p == NULL) {
|
||||
PRINTF("RPL: rpl_add_parent p NULL\n");
|
||||
} else {
|
||||
uip_ds6_nbr_t *nbr;
|
||||
nbr = rpl_get_nbr(p);
|
||||
|
||||
p->dag = dag;
|
||||
p->rank = dio->rank;
|
||||
p->dtsn = dio->dtsn;
|
||||
|
||||
/* Check whether we have a neighbor that has not gotten a link metric yet */
|
||||
if(nbr != NULL && nbr->link_metric == 0) {
|
||||
nbr->link_metric = RPL_INIT_LINK_METRIC * RPL_DAG_MC_ETX_DIVISOR;
|
||||
}
|
||||
#if RPL_DAG_MC != RPL_DAG_MC_NONE
|
||||
#if RPL_WITH_MC
|
||||
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
|
||||
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
|
||||
#endif /* RPL_WITH_MC */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -737,7 +791,9 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
|
||||
if(instance->current_dag != best_dag) {
|
||||
/* Remove routes installed by DAOs. */
|
||||
rpl_remove_routes(instance->current_dag);
|
||||
if(RPL_IS_STORING(instance)) {
|
||||
rpl_remove_routes(instance->current_dag);
|
||||
}
|
||||
|
||||
PRINTF("RPL: New preferred DAG: ");
|
||||
PRINT6ADDR(&best_dag->dag_id);
|
||||
|
@ -756,13 +812,17 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
|
||||
instance->of->update_metric_container(instance);
|
||||
/* Update the DAG rank. */
|
||||
best_dag->rank = instance->of->calculate_rank(best_dag->preferred_parent, 0);
|
||||
best_dag->rank = rpl_rank_via_parent(best_dag->preferred_parent);
|
||||
if(last_parent == NULL || best_dag->rank < best_dag->min_rank) {
|
||||
/* This is a slight departure from RFC6550: if we had no preferred parent before,
|
||||
* reset min_rank. This helps recovering from temporary bad link conditions. */
|
||||
best_dag->min_rank = best_dag->rank;
|
||||
} else if(!acceptable_rank(best_dag, best_dag->rank)) {
|
||||
}
|
||||
|
||||
if(!acceptable_rank(best_dag, best_dag->rank)) {
|
||||
PRINTF("RPL: New rank unacceptable!\n");
|
||||
rpl_set_preferred_parent(instance->current_dag, NULL);
|
||||
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES && last_parent != NULL) {
|
||||
if(RPL_IS_STORING(instance) && last_parent != NULL) {
|
||||
/* Send a No-Path DAO to the removed preferred parent. */
|
||||
dao_output(last_parent, RPL_ZERO_LIFETIME);
|
||||
}
|
||||
|
@ -774,15 +834,13 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
PRINTF("RPL: Changed preferred parent, rank changed from %u to %u\n",
|
||||
(unsigned)old_rank, best_dag->rank);
|
||||
RPL_STAT(rpl_stats.parent_switch++);
|
||||
if(instance->mop != RPL_MOP_NO_DOWNWARD_ROUTES) {
|
||||
if(last_parent != NULL) {
|
||||
/* Send a No-Path DAO to the removed preferred parent. */
|
||||
dao_output(last_parent, RPL_ZERO_LIFETIME);
|
||||
}
|
||||
/* The DAO parent set changed - schedule a DAO transmission. */
|
||||
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
|
||||
rpl_schedule_dao(instance);
|
||||
if(RPL_IS_STORING(instance) && last_parent != NULL) {
|
||||
/* Send a No-Path DAO to the removed preferred parent. */
|
||||
dao_output(last_parent, RPL_ZERO_LIFETIME);
|
||||
}
|
||||
/* The DAO parent set changed - schedule a DAO transmission. */
|
||||
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
|
||||
rpl_schedule_dao(instance);
|
||||
rpl_reset_dio_timer(instance);
|
||||
#if DEBUG
|
||||
rpl_print_neighbor_list();
|
||||
|
@ -795,22 +853,42 @@ rpl_select_dag(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static rpl_parent_t *
|
||||
best_parent(rpl_dag_t *dag)
|
||||
best_parent(rpl_dag_t *dag, int fresh_only)
|
||||
{
|
||||
rpl_parent_t *p, *best;
|
||||
rpl_parent_t *p;
|
||||
rpl_of_t *of;
|
||||
rpl_parent_t *best = NULL;
|
||||
|
||||
best = NULL;
|
||||
if(dag == NULL || dag->instance == NULL || dag->instance->of == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = nbr_table_head(rpl_parents);
|
||||
while(p != NULL) {
|
||||
of = dag->instance->of;
|
||||
/* Search for the best parent according to the OF */
|
||||
for(p = nbr_table_head(rpl_parents); p != NULL; p = nbr_table_next(rpl_parents, p)) {
|
||||
|
||||
/* Exclude parents from other DAGs or announcing an infinite rank */
|
||||
if(p->dag != dag || p->rank == INFINITE_RANK) {
|
||||
/* ignore this neighbor */
|
||||
} else if(best == NULL) {
|
||||
best = p;
|
||||
} else {
|
||||
best = dag->instance->of->best_parent(best, p);
|
||||
continue;
|
||||
}
|
||||
p = nbr_table_next(rpl_parents, p);
|
||||
|
||||
if(fresh_only && !rpl_parent_is_fresh(p)) {
|
||||
/* Filter out non-fresh parents if fresh_only is set */
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifndef UIP_CONF_ND6_SEND_NA
|
||||
{
|
||||
uip_ds6_nbr_t *nbr = rpl_get_nbr(p);
|
||||
/* Exclude links to a neighbor that is not reachable at a NUD level */
|
||||
if(nbr == NULL || nbr->state != NBR_REACHABLE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif /* UIP_CONF_ND6_SEND_NA */
|
||||
|
||||
/* Now we have an acceptable parent, check if it is the new best */
|
||||
best = of->best_parent(best, p);
|
||||
}
|
||||
|
||||
return best;
|
||||
|
@ -819,16 +897,37 @@ best_parent(rpl_dag_t *dag)
|
|||
rpl_parent_t *
|
||||
rpl_select_parent(rpl_dag_t *dag)
|
||||
{
|
||||
rpl_parent_t *best = best_parent(dag);
|
||||
/* Look for best parent (regardless of freshness) */
|
||||
rpl_parent_t *best = best_parent(dag, 0);
|
||||
|
||||
if(best != NULL) {
|
||||
rpl_set_preferred_parent(dag, best);
|
||||
dag->rank = dag->instance->of->calculate_rank(dag->preferred_parent, 0);
|
||||
#if RPL_WITH_PROBING
|
||||
if(rpl_parent_is_fresh(best)) {
|
||||
rpl_set_preferred_parent(dag, best);
|
||||
} else {
|
||||
/* The best is not fresh. Look for the best fresh now. */
|
||||
rpl_parent_t *best_fresh = best_parent(dag, 1);
|
||||
if(best_fresh == NULL) {
|
||||
/* No fresh parent around, use best (non-fresh) */
|
||||
rpl_set_preferred_parent(dag, best);
|
||||
} else {
|
||||
/* Use best fresh */
|
||||
rpl_set_preferred_parent(dag, best_fresh);
|
||||
}
|
||||
/* Probe the best parent shortly in order to get a fresh estimate */
|
||||
dag->instance->urgent_probing_target = best;
|
||||
rpl_schedule_probing(dag->instance);
|
||||
#else /* RPL_WITH_PROBING */
|
||||
rpl_set_preferred_parent(dag, best);
|
||||
dag->rank = rpl_rank_via_parent(dag->preferred_parent);
|
||||
#endif /* RPL_WITH_PROBING */
|
||||
}
|
||||
} else {
|
||||
dag->rank = INFINITE_RANK;
|
||||
rpl_set_preferred_parent(dag, NULL);
|
||||
}
|
||||
|
||||
return best;
|
||||
dag->rank = rpl_rank_via_parent(dag->preferred_parent);
|
||||
return dag->preferred_parent;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
|
@ -859,9 +958,11 @@ rpl_nullify_parent(rpl_parent_t *parent)
|
|||
uip_ds6_defrt_rm(dag->instance->def_route);
|
||||
dag->instance->def_route = NULL;
|
||||
}
|
||||
/* Send No-Path DAO only to preferred parent, if any */
|
||||
/* Send No-Path DAO only when nullifying preferred parent */
|
||||
if(parent == dag->preferred_parent) {
|
||||
dao_output(parent, RPL_ZERO_LIFETIME);
|
||||
if(RPL_IS_STORING(dag->instance)) {
|
||||
dao_output(parent, RPL_ZERO_LIFETIME);
|
||||
}
|
||||
rpl_set_preferred_parent(dag, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -887,8 +988,10 @@ rpl_move_parent(rpl_dag_t *dag_src, rpl_dag_t *dag_dst, rpl_parent_t *parent)
|
|||
dag_src->instance->def_route = NULL;
|
||||
}
|
||||
} else if(dag_src->joined) {
|
||||
/* Remove uIPv6 routes that have this parent as the next hop. */
|
||||
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
|
||||
if(RPL_IS_STORING(dag_src->instance)) {
|
||||
/* Remove uIPv6 routes that have this parent as the next hop. */
|
||||
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(parent), dag_src);
|
||||
}
|
||||
}
|
||||
|
||||
PRINTF("RPL: Moving parent ");
|
||||
|
@ -898,6 +1001,37 @@ rpl_move_parent(rpl_dag_t *dag_src, rpl_dag_t *dag_dst, rpl_parent_t *parent)
|
|||
parent->dag = dag_dst;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
rpl_has_downward_route(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
|
||||
if(instance_table[i].used && instance_table[i].has_downward_route) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_dag_t *
|
||||
rpl_get_dag(const uip_ipaddr_t *addr)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for(i = 0; i < RPL_MAX_INSTANCES; ++i) {
|
||||
if(instance_table[i].used) {
|
||||
for(j = 0; j < RPL_MAX_DAG_PER_INSTANCE; ++j) {
|
||||
if(instance_table[i].dag_table[j].joined
|
||||
&& uip_ipaddr_prefixcmp(&instance_table[i].dag_table[j].dag_id, addr,
|
||||
instance_table[i].dag_table[j].prefix_info.length)) {
|
||||
return &instance_table[i].dag_table[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
rpl_dag_t *
|
||||
rpl_get_any_dag(void)
|
||||
{
|
||||
|
@ -948,6 +1082,12 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
rpl_parent_t *p;
|
||||
rpl_of_t *of;
|
||||
|
||||
if((!RPL_WITH_NON_STORING && dio->mop == RPL_MOP_NON_STORING)
|
||||
|| (!RPL_WITH_STORING && (dio->mop == RPL_MOP_STORING_NO_MULTICAST
|
||||
|| dio->mop == RPL_MOP_STORING_MULTICAST))) {
|
||||
PRINTF("RPL: DIO advertising a non-supported MOP %u\n", dio->mop);
|
||||
}
|
||||
|
||||
/* Determine the objective function by using the
|
||||
objective code point of the DIO. */
|
||||
of = rpl_find_of(dio->ocp);
|
||||
|
@ -990,6 +1130,10 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
|
||||
instance->of = of;
|
||||
instance->mop = dio->mop;
|
||||
instance->mc.type = dio->mc.type;
|
||||
instance->mc.flags = dio->mc.flags;
|
||||
instance->mc.aggr = dio->mc.aggr;
|
||||
instance->mc.prec = dio->mc.prec;
|
||||
instance->current_dag = dag;
|
||||
instance->dtsn_out = RPL_LOLLIPOP_INIT;
|
||||
|
||||
|
@ -1009,7 +1153,7 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
|
||||
rpl_set_preferred_parent(dag, p);
|
||||
instance->of->update_metric_container(instance);
|
||||
dag->rank = instance->of->calculate_rank(p, 0);
|
||||
dag->rank = rpl_rank_via_parent(p);
|
||||
/* So far this is the lowest rank we are aware of. */
|
||||
dag->min_rank = dag->rank;
|
||||
|
||||
|
@ -1032,6 +1176,8 @@ rpl_join_instance(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
} else {
|
||||
PRINTF("RPL: The DIO does not meet the prerequisites for sending a DAO\n");
|
||||
}
|
||||
|
||||
instance->of->reset(dag);
|
||||
}
|
||||
|
||||
#if RPL_MAX_DAG_PER_INSTANCE > 1
|
||||
|
@ -1102,7 +1248,7 @@ rpl_add_dag(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
memcpy(&dag->prefix_info, &dio->prefix_info, sizeof(rpl_prefix_t));
|
||||
|
||||
rpl_set_preferred_parent(dag, p);
|
||||
dag->rank = instance->of->calculate_rank(p, 0);
|
||||
dag->rank = rpl_rank_via_parent(p);
|
||||
dag->min_rank = dag->rank; /* So far this is the lowest rank we know of. */
|
||||
|
||||
PRINTF("RPL: Joined DAG with instance ID %u, rank %hu, DAG ID ",
|
||||
|
@ -1144,7 +1290,7 @@ global_repair(uip_ipaddr_t *from, rpl_dag_t *dag, rpl_dio_t *dio)
|
|||
PRINTF("RPL: Failed to add a parent during the global repair\n");
|
||||
dag->rank = INFINITE_RANK;
|
||||
} else {
|
||||
dag->rank = dag->instance->of->calculate_rank(p, 0);
|
||||
dag->rank = rpl_rank_via_parent(p);
|
||||
dag->min_rank = dag->rank;
|
||||
PRINTF("RPL: rpl_process_parent_event global repair\n");
|
||||
rpl_process_parent_event(dag->instance, p);
|
||||
|
@ -1155,6 +1301,7 @@ global_repair(uip_ipaddr_t *from, rpl_dag_t *dag, rpl_dio_t *dio)
|
|||
|
||||
RPL_STAT(rpl_stats.global_repairs++);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_local_repair(rpl_instance_t *instance)
|
||||
|
@ -1173,7 +1320,12 @@ rpl_local_repair(rpl_instance_t *instance)
|
|||
}
|
||||
}
|
||||
|
||||
/* no downward route anymore */
|
||||
instance->has_downward_route = 0;
|
||||
|
||||
rpl_reset_dio_timer(instance);
|
||||
/* Request refresh of DAO registrations next DIO */
|
||||
RPL_LOLLIPOP_INCREMENT(instance->dtsn_out);
|
||||
|
||||
RPL_STAT(rpl_stats.local_repairs++);
|
||||
}
|
||||
|
@ -1205,6 +1357,7 @@ int
|
|||
rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
|
||||
{
|
||||
int return_value;
|
||||
rpl_parent_t *last_parent = instance->current_dag->preferred_parent;
|
||||
|
||||
#if DEBUG
|
||||
rpl_rank_t old_rank;
|
||||
|
@ -1213,10 +1366,20 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
|
||||
return_value = 1;
|
||||
|
||||
if(RPL_IS_STORING(instance)
|
||||
&& uip_ds6_route_is_nexthop(rpl_get_parent_ipaddr(p))
|
||||
&& !rpl_parent_is_reachable(p) && instance->mop > RPL_MOP_NON_STORING) {
|
||||
PRINTF("RPL: Unacceptable link %u, removing routes via: ", rpl_get_parent_link_metric(p));
|
||||
PRINT6ADDR(rpl_get_parent_ipaddr(p));
|
||||
PRINTF("\n");
|
||||
rpl_remove_routes_by_nexthop(rpl_get_parent_ipaddr(p), p->dag);
|
||||
}
|
||||
|
||||
if(!acceptable_rank(p->dag, p->rank)) {
|
||||
/* The candidate parent is no longer valid: the rank increase resulting
|
||||
from the choice of it as a parent would be too high. */
|
||||
PRINTF("RPL: Unacceptable rank %u\n", (unsigned)p->rank);
|
||||
PRINTF("RPL: Unacceptable rank %u (Current min %u, MaxRankInc %u)\n", (unsigned)p->rank,
|
||||
p->dag->min_rank, p->dag->instance->max_rankinc);
|
||||
rpl_nullify_parent(p);
|
||||
if(p != instance->current_dag->preferred_parent) {
|
||||
return 0;
|
||||
|
@ -1226,10 +1389,12 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
}
|
||||
|
||||
if(rpl_select_dag(instance, p) == NULL) {
|
||||
/* No suitable parent; trigger a local repair. */
|
||||
PRINTF("RPL: No parents found in any DAG\n");
|
||||
rpl_local_repair(instance);
|
||||
return 0;
|
||||
if(last_parent != NULL) {
|
||||
/* No suitable parent anymore; trigger a local repair. */
|
||||
PRINTF("RPL: No parents found in any DAG\n");
|
||||
rpl_local_repair(instance);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
@ -1250,6 +1415,19 @@ rpl_process_parent_event(rpl_instance_t *instance, rpl_parent_t *p)
|
|||
return return_value;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
add_nbr_from_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
||||
{
|
||||
/* add this to the neighbor cache if not already there */
|
||||
if(rpl_icmp6_update_nbr_table(from, NBR_TABLE_REASON_RPL_DIO, dio) == NULL) {
|
||||
PRINTF("RPL: Out of memory, dropping DIO from ");
|
||||
PRINT6ADDR(from);
|
||||
PRINTF("\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
||||
{
|
||||
|
@ -1257,7 +1435,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
rpl_dag_t *dag, *previous_dag;
|
||||
rpl_parent_t *p;
|
||||
|
||||
#if RPL_CONF_MULTICAST
|
||||
#if RPL_WITH_MULTICAST
|
||||
/* If the root is advertising MOP 2 but we support MOP 3 we can still join
|
||||
* In that scenario, we suppress DAOs for multicast targets */
|
||||
if(dio->mop < RPL_MOP_STORING_NO_MULTICAST) {
|
||||
|
@ -1282,7 +1460,7 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
PRINTF("RPL: Global repair\n");
|
||||
if(dio->prefix_info.length != 0) {
|
||||
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
|
||||
PRINTF("RPL : Prefix announced in DIO\n");
|
||||
PRINTF("RPL: Prefix announced in DIO\n");
|
||||
rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length);
|
||||
}
|
||||
}
|
||||
|
@ -1303,7 +1481,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
|
||||
if(instance == NULL) {
|
||||
PRINTF("RPL: New instance detected (ID=%u): Joining...\n", dio->instance_id);
|
||||
rpl_join_instance(from, dio);
|
||||
if(add_nbr_from_dio(from, dio)) {
|
||||
rpl_join_instance(from, dio);
|
||||
} else {
|
||||
PRINTF("RPL: Not joining since could not add parent\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1315,6 +1497,10 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
if(dag == NULL) {
|
||||
#if RPL_MAX_DAG_PER_INSTANCE > 1
|
||||
PRINTF("RPL: Adding new DAG to known instance.\n");
|
||||
if(!add_nbr_from_dio(from, dio)) {
|
||||
PRINTF("RPL: Could not add new DAG, could not add parent\n");
|
||||
return;
|
||||
}
|
||||
dag = rpl_add_dag(from, dio);
|
||||
if(dag == NULL) {
|
||||
PRINTF("RPL: Failed to add DAG.\n");
|
||||
|
@ -1331,18 +1517,21 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
PRINTF("RPL: Ignoring DIO with too low rank: %u\n",
|
||||
(unsigned)dio->rank);
|
||||
return;
|
||||
} else if(dio->rank == INFINITE_RANK && dag->joined) {
|
||||
rpl_reset_dio_timer(instance);
|
||||
}
|
||||
|
||||
/* Prefix Information Option treated to add new prefix */
|
||||
if(dio->prefix_info.length != 0) {
|
||||
if(dio->prefix_info.flags & UIP_ND6_RA_FLAG_AUTONOMOUS) {
|
||||
PRINTF("RPL : Prefix announced in DIO\n");
|
||||
PRINTF("RPL: Prefix announced in DIO\n");
|
||||
rpl_set_prefix(dag, &dio->prefix_info.prefix, dio->prefix_info.length);
|
||||
}
|
||||
}
|
||||
|
||||
if(!add_nbr_from_dio(from, dio)) {
|
||||
PRINTF("RPL: Could not add parent based on DIO\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(dag->rank == ROOT_RANK(instance)) {
|
||||
if(dio->rank != INFINITE_RANK) {
|
||||
instance->dio_counter++;
|
||||
|
@ -1394,6 +1583,11 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
}
|
||||
p->rank = dio->rank;
|
||||
|
||||
if(dio->rank == INFINITE_RANK && p == dag->preferred_parent) {
|
||||
/* Our preferred parent advertised an infinite rank, reset DIO timer */
|
||||
rpl_reset_dio_timer(instance);
|
||||
}
|
||||
|
||||
/* Parent info has been updated, trigger rank recalculation */
|
||||
p->flags |= RPL_PARENT_FLAG_UPDATED;
|
||||
|
||||
|
@ -1401,14 +1595,14 @@ rpl_process_dio(uip_ipaddr_t *from, rpl_dio_t *dio)
|
|||
PRINT6ADDR(&instance->current_dag->dag_id);
|
||||
PRINTF(", rank %u, min_rank %u, ",
|
||||
instance->current_dag->rank, instance->current_dag->min_rank);
|
||||
PRINTF("parent rank %u, parent etx %u, link metric %u, instance etx %u\n",
|
||||
p->rank, -1/*p->mc.obj.etx*/, rpl_get_nbr(p)->link_metric, instance->mc.obj.etx);
|
||||
PRINTF("parent rank %u, link metric %u\n",
|
||||
p->rank, rpl_get_parent_link_metric(p));
|
||||
|
||||
/* We have allocated a candidate parent; process the DIO further. */
|
||||
|
||||
#if RPL_DAG_MC != RPL_DAG_MC_NONE
|
||||
#if RPL_WITH_MC
|
||||
memcpy(&p->mc, &dio->mc, sizeof(p->mc));
|
||||
#endif /* RPL_DAG_MC != RPL_DAG_MC_NONE */
|
||||
#endif /* RPL_WITH_MC */
|
||||
if(rpl_process_parent_event(instance, p) == 0) {
|
||||
PRINTF("RPL: The candidate parent is rejected\n");
|
||||
return;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue