mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #18515
18515: libschc: initial import as package r=miri64 a=miri64 Co-authored-by: Martine Lenders <m.lenders@fu-berlin.de>
This commit is contained in:
commit
8d800e92b4
@ -99,6 +99,7 @@
|
||||
/drivers/include/periph/ptp.h @maribu
|
||||
|
||||
/pkg/cryptoauthlib/ @Einhornhool @PeterKietzmann
|
||||
/pkg/libschc/ @bartmoons @miri64
|
||||
/pkg/lua/ @jcarrano
|
||||
/pkg/lwip/ @miri64
|
||||
/pkg/gecko_sdk/ @basilfx
|
||||
@ -145,6 +146,7 @@
|
||||
/tests/driver_dht/ @wosym
|
||||
/tests/gnrc* @miri64
|
||||
/tests/lwip* @miri64
|
||||
/tests/pkg_libschc/ @miri64
|
||||
/tests/slip/ @miri64
|
||||
/tests/unittests @miri64
|
||||
/tests/*/tests/*.py @miri64
|
||||
|
@ -40,6 +40,7 @@ rsource "libb2/Kconfig"
|
||||
rsource "libcose/Kconfig"
|
||||
rsource "libfixmath/Kconfig"
|
||||
rsource "libhydrogen/Kconfig"
|
||||
rsource "libschc/Kconfig"
|
||||
rsource "littlefs2/Kconfig"
|
||||
rsource "lorabasics/Kconfig"
|
||||
rsource "lora-serialization/Kconfig"
|
||||
|
39
pkg/libschc/Kconfig
Normal file
39
pkg/libschc/Kconfig
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright (c) 2022 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
menuconfig KCONFIG_USEPKG_LIBSCHC
|
||||
bool "Configure libSCHC"
|
||||
depends on USEPKG_LIBSCHC
|
||||
help
|
||||
Configure libSCHC package via Kconfig.
|
||||
|
||||
if KCONFIG_USEPKG_LIBSCHC
|
||||
|
||||
config LIBSCHC_STATIC_MEMBUF_LEN
|
||||
int "Static memory allocation buffer length"
|
||||
default 1024
|
||||
help
|
||||
Length of the static memory buffer for fragment data allocation in reassembly buffer in
|
||||
bytes.
|
||||
|
||||
config LIBSCHC_MBUF_POOL_SIZE
|
||||
int "Maximum number of mbuf pool entries"
|
||||
default 64
|
||||
help
|
||||
Maximum number of entries in the mbuf used for fragment reassembly.
|
||||
|
||||
config LIBSCHC_MAX_RX_CONNS
|
||||
int "Maximum number of incoming connections"
|
||||
default 1
|
||||
|
||||
config LIBSCHC_MAX_MTU_LEN
|
||||
int "Maximum transfer unit of the underlying technology"
|
||||
default 242
|
||||
|
||||
config LIBSCHC_DEBUG
|
||||
bool "Enable debug output"
|
||||
|
||||
endif # KCONFIG_USEPKG_LIBSCHC
|
9
pkg/libschc/Makefile
Normal file
9
pkg/libschc/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
PKG_NAME=libschc
|
||||
PKG_URL=https://github.com/imec-idlab/libschc
|
||||
PKG_VERSION=303e9f15bf69da5a68cda76796c76de353f44a88
|
||||
PKG_LICENSE=GPL-v3.0
|
||||
|
||||
include $(RIOTBASE)/pkg/pkg.mk
|
||||
|
||||
all:
|
||||
+$(QQ)"$(MAKE)" -C $(PKG_SOURCE_DIR) -f $(RIOTBASE)/Makefile.base
|
9
pkg/libschc/Makefile.dep
Normal file
9
pkg/libschc/Makefile.dep
Normal file
@ -0,0 +1,9 @@
|
||||
ifneq (,$(filter libschc_%,$(USEMODULE)))
|
||||
USEPKG += libschc
|
||||
endif
|
||||
|
||||
ifneq (,$(filter libschc,$(USEPKG)))
|
||||
USEMODULE += libschc_udpv6
|
||||
endif
|
||||
|
||||
FEATURES_BLACKLIST += arch_8bit arch_16bit arch_esp8266
|
12
pkg/libschc/Makefile.include
Normal file
12
pkg/libschc/Makefile.include
Normal file
@ -0,0 +1,12 @@
|
||||
CFLAGS += -Wno-enum-conversion
|
||||
CFLAGS += -Wno-old-style-definition
|
||||
CFLAGS += -Wno-sign-compare
|
||||
CFLAGS += -Wno-strict-prototypes
|
||||
CFLAGS += -Wno-unused-parameter
|
||||
CFLAGS += -Wno-unused-variable
|
||||
|
||||
INCLUDES += -I$(RIOTBASE)/pkg/libschc/include
|
||||
INCLUDES += -I$(PKGDIRBASE)/libschc
|
||||
|
||||
PSEUDOMODULES += libschc_coap
|
||||
PSEUDOMODULES += libschc_udpv6
|
11
pkg/libschc/doc.txt
Normal file
11
pkg/libschc/doc.txt
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @defgroup pkg_libschc libSCHC
|
||||
* @ingroup pkg
|
||||
* @brief Provides support for Static Context Header Compression and Fragmentation (SCHC)
|
||||
* @see https://github.com/imec-idlab/libschc
|
||||
* @see [RFC 8724](https://datatracker.ietf.org/doc/html/rfc8724)
|
||||
* @experimental
|
||||
*
|
||||
* libSCHC is a C implementation of Static Context Header Compression and
|
||||
Fragmentation
|
||||
*/
|
72
pkg/libschc/include/libschc/config.h
Normal file
72
pkg/libschc/include/libschc/config.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup pkg_libschc_config libSCHC compile-time configuration
|
||||
* @ingroup pkg_libschc
|
||||
* @ingroup config
|
||||
* @brief Compile-time configuration for libSCHC
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief RIOT-side compile-time configuration for libSCHC
|
||||
*
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef LIBSCHC_CONFIG_H
|
||||
#define LIBSCHC_CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Static memory buffer length
|
||||
*
|
||||
* Length of the static memory buffer for fragmentation in bytes.
|
||||
*/
|
||||
#ifndef CONFIG_LIBSCHC_STATIC_MEMBUF_LEN
|
||||
#define CONFIG_LIBSCHC_STATIC_MEMBUF_LEN 1024
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum number of mbuf pool entries
|
||||
*
|
||||
* Maximum number of entries in the mbuf used for fragment reassembly.
|
||||
*/
|
||||
#ifndef CONFIG_LIBSCHC_MBUF_POOL_SIZE
|
||||
#define CONFIG_LIBSCHC_MBUF_POOL_SIZE 64
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum number of incoming connections
|
||||
*/
|
||||
#ifndef CONFIG_LIBSCHC_MAX_RX_CONNS
|
||||
#define CONFIG_LIBSCHC_MAX_RX_CONNS 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum transfer unit of the underlying technology
|
||||
*/
|
||||
#ifndef CONFIG_LIBSCHC_MAX_MTU_LEN
|
||||
#define CONFIG_LIBSCHC_MAX_MTU_LEN 242
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enable debug output
|
||||
*/
|
||||
#ifndef CONFIG_LIBSCHC_DEBUG
|
||||
#define CONFIG_LIBSCHC_DEBUG
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBSCHC_CONFIG_H */
|
||||
/** @} */
|
37
pkg/libschc/include/rules/rule_config.h
Normal file
37
pkg/libschc/include/rules/rule_config.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2018 imec IDLab
|
||||
* Copyright (C) 2022 Freie Universität Berlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @author boortmans <bart.moons@gmail.com>
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef RULES_RULE_CONFIG_H
|
||||
#define RULES_RULE_CONFIG_H
|
||||
|
||||
#include "rules.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RULES_RULE_CONFIG_H */
|
344
pkg/libschc/include/rules/rules.h
Normal file
344
pkg/libschc/include/rules/rules.h
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (C) 2018 imec IDLab
|
||||
* Copyright (C) 2022 Freie Universität Berlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @author boortmans <bart.moons@gmail.com>
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef RULES_RULES_H
|
||||
#define RULES_RULES_H
|
||||
|
||||
#include "kernel_defines.h"
|
||||
|
||||
#include "schc.h"
|
||||
#ifdef USE_COAP
|
||||
#include "net/coap.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if USE_IP6
|
||||
static const struct schc_ipv6_rule_t ipv6_rule1 = {
|
||||
.up = 10, .down = 10, .length = 11,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ IP6_V, 0, 4, 1, BI, {6}, &mo_equal, NOTSENT },
|
||||
{ IP6_TC, 0, 8, 1, BI, {0}, &mo_ignore, NOTSENT },
|
||||
{ IP6_FL, 0, 20, 1, BI, {0, 0, 0}, &mo_ignore, NOTSENT },
|
||||
{ IP6_LEN, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPLENGTH },
|
||||
{ IP6_NH, 0, 8, 1, BI, {17}, &mo_equal, NOTSENT },
|
||||
{ IP6_HL, 0, 8, 1, UP, {64}, &mo_equal, NOTSENT },
|
||||
{ IP6_HL, 0, 8, 1, DOWN, {0}, &mo_ignore, VALUESENT },
|
||||
{ IP6_DEVPRE, 0, 64, 1, BI, {
|
||||
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00
|
||||
}, &mo_equal, NOTSENT },
|
||||
{ IP6_DEVIID, 0, 64, 1, BI, {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
}, &mo_equal, NOTSENT },
|
||||
{ IP6_APPPRE, 4, 64, 1, BI, {
|
||||
/* you can store as many IPs as (MAX_FIELD_LENGTH / 8) */
|
||||
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00,
|
||||
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00,
|
||||
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x03, 0x00, 0x00,
|
||||
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x04, 0x00, 0x00
|
||||
}, &mo_matchmap, MAPPINGSENT },
|
||||
{ IP6_APPIID, 0, 64, 1, BI, {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
|
||||
}, &mo_equal, NOTSENT },
|
||||
}
|
||||
};
|
||||
|
||||
/* link local test rule */
|
||||
static const struct schc_ipv6_rule_t ipv6_rule2 = {
|
||||
.up = 10, .down = 10, .length = 10,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ IP6_V, 0, 4, 1, BI, {6}, &mo_equal, NOTSENT },
|
||||
{ IP6_TC, 0, 8, 1, BI, {0}, &mo_ignore, NOTSENT },
|
||||
{ IP6_FL, 0, 20, 1, BI, {0, 0, 0}, &mo_ignore, NOTSENT },
|
||||
{ IP6_LEN, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPLENGTH },
|
||||
{ IP6_NH, 2, 8, 1, BI, {17, 58}, &mo_matchmap, MAPPINGSENT },
|
||||
{ IP6_HL, 2, 8, 1, BI, {64, 255}, &mo_matchmap, NOTSENT },
|
||||
{ IP6_DEVPRE, 0, 64, 1, BI, {
|
||||
0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
}, &mo_equal, NOTSENT },
|
||||
{ IP6_DEVIID, 62, 64, 1, BI, {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
}, &mo_MSB, LSB },
|
||||
{ IP6_APPPRE, 0, 64, 1, BI, {
|
||||
0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
}, &mo_equal, NOTSENT },
|
||||
{ IP6_APPIID, 62, 64, 1, BI, {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
}, &mo_MSB, LSB },
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if USE_UDP
|
||||
static const struct schc_udp_rule_t udp_rule1 = {
|
||||
.up = 4, .down = 4, .length = 4,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
/* set field length to 16 to indicate 16 bit values
|
||||
* MO param length to 2 to indicate 2 indices */
|
||||
{ UDP_DEV, 2, 16, 1, BI, {
|
||||
0x33, 0x16, /* 5683 or */
|
||||
0x33, 0x17 /* 5684 */
|
||||
}, &mo_matchmap, MAPPINGSENT },
|
||||
{ UDP_APP, 2, 16, 1, BI, {
|
||||
0x33, 0x16, /* 5683 or */
|
||||
0x33, 0x17 /* 5684 */
|
||||
}, &mo_matchmap, MAPPINGSENT },
|
||||
{ UDP_LEN, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPLENGTH },
|
||||
{ UDP_CHK, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPCHK },
|
||||
}
|
||||
};
|
||||
|
||||
static const struct schc_udp_rule_t udp_rule2 = {
|
||||
.up = 4, .down = 4, .length = 4,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ UDP_DEV, 12, 16, 1, BI, {0x1F, 0x40}, &mo_MSB, LSB },
|
||||
{ UDP_APP, 12, 16, 1, BI, {0x1F, 0x40}, &mo_MSB, LSB },
|
||||
{ UDP_LEN, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPLENGTH },
|
||||
{ UDP_CHK, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPCHK },
|
||||
}
|
||||
};
|
||||
|
||||
static const struct schc_udp_rule_t udp_rule3 = {
|
||||
.up = 4, .down = 4, .length = 4,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ UDP_DEV, 0, 16, 1, BI, {0x13, 0x89}, &mo_equal, NOTSENT },
|
||||
{ UDP_APP, 0, 16, 1, BI, {0x13, 0x88}, &mo_equal, NOTSENT },
|
||||
{ UDP_LEN, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPLENGTH },
|
||||
{ UDP_CHK, 0, 16, 1, BI, {0, 0}, &mo_ignore, COMPCHK },
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if USE_COAP
|
||||
/* It is important to use strings, identical to the ones defined in coap.h for the options. */
|
||||
|
||||
/* GET /usage */
|
||||
static const struct schc_coap_rule_t coap_rule1 = {
|
||||
.up = 9, .down = 7, .length = 9,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ COAP_V, 0, 2, 1, BI, {COAP_V1}, &mo_equal, NOTSENT },
|
||||
/* the MO_param_length (ML) is used to indicate the true length of the list */
|
||||
{ COAP_T, 4, 2, 1, BI, {
|
||||
COAP_TYPE_CON, COAP_TYPE_NON, COAP_TYPE_ACK, COAP_TYPE_RST
|
||||
}, &mo_matchmap, MAPPINGSENT },
|
||||
{ COAP_TKL, 0, 4, 1, BI, {4}, &mo_equal, NOTSENT },
|
||||
{ COAP_C, 0, 8, 1, BI, {COAP_METHOD_PUT}, &mo_equal, NOTSENT },
|
||||
{ COAP_MID, 0, 16, 1, BI, {0x23, 0xBB}, &mo_equal, NOTSENT },
|
||||
{ COAP_TKN, 24, 32, 1, BI, {
|
||||
0x21, 0xFA, 0x01, 0x00
|
||||
}, &mo_MSB, LSB },
|
||||
{ COAP_URIPATH, 0, 40, 1, BI, "usage", &mo_equal, NOTSENT },
|
||||
{ COAP_NORESP, 0, 8, 1, BI, {0x1A}, &mo_equal, NOTSENT },
|
||||
{ COAP_PAYLOAD, 0, 8, 1, BI, {0xFF}, &mo_equal, NOTSENT }
|
||||
}
|
||||
};
|
||||
|
||||
/* POST temperature value */
|
||||
static const struct schc_coap_rule_t coap_rule2 = {
|
||||
.up = 7, .down = 7, .length = 10,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ COAP_V, 0, 2, 1, BI, {COAP_V1}, &mo_equal, NOTSENT },
|
||||
{ COAP_T, 0, 2, 1, BI, {0}, &mo_ignore, VALUESENT },
|
||||
{ COAP_TKL, 0, 4, 1, BI, {4}, &mo_equal, NOTSENT },
|
||||
{ COAP_C, 0, 8, 1, UP, {COAP_CODE_CONTENT}, &mo_equal, NOTSENT },
|
||||
{ COAP_C, 0, 8, 1, DOWN, {COAP_METHOD_GET}, &mo_equal, NOTSENT },
|
||||
/* match the first 12 bits */
|
||||
{ COAP_MID, 12, 16, 1, UP, {0x23, 0xBB}, &mo_MSB, LSB },
|
||||
{ COAP_MID, 0, 16, 1, DOWN, {0, 0}, &mo_ignore, VALUESENT },
|
||||
{ COAP_TKN, 0, 32, 1, BI, {0, 0, 0, 0}, &mo_ignore, VALUESENT },
|
||||
{ COAP_URIPATH, 0, 32, 1, DOWN, "temp", &mo_equal, NOTSENT },
|
||||
/* respond with CONTENT */
|
||||
{ COAP_PAYLOAD, 0, 8, 1, UP, {0xFF}, &mo_equal, NOTSENT }
|
||||
}
|
||||
};
|
||||
|
||||
static const struct schc_coap_rule_t coap_rule3 = {
|
||||
.up = 1, .down = 1, .length = 1,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ COAP_V, 0, 2, 1, BI, {COAP_V1}, &mo_equal, NOTSENT },
|
||||
}
|
||||
};
|
||||
|
||||
static const struct schc_coap_rule_t coap_rule4 = {
|
||||
.up = 12, .down = 12, .length = 12,
|
||||
{
|
||||
/* field, ML, len, pos, dir, val, MO, CDA */
|
||||
{ COAP_V, 0, 2, 1, BI, {COAP_V1}, &mo_equal, NOTSENT },
|
||||
{ COAP_T, 0, 2, 1, BI, {COAP_TYPE_CON}, &mo_equal, NOTSENT },
|
||||
{ COAP_TKL, 0, 4, 1, BI, {8}, &mo_equal, NOTSENT },
|
||||
{ COAP_C, 0, 8, 1, BI, {COAP_METHOD_POST}, &mo_equal, NOTSENT },
|
||||
{ COAP_MID, 0, 16, 1, BI, {0x23, 0xBB}, &mo_ignore, VALUESENT },
|
||||
/* match the 24 first bits, send the last 8 */
|
||||
{ COAP_TKN, 24, 32, 1, BI, {
|
||||
0x21, 0xFA, 0x01, 0x00
|
||||
}, &mo_MSB, LSB },
|
||||
{ COAP_URIPATH, 0, 16, 1, BI, "rd", &mo_equal, NOTSENT },
|
||||
{ COAP_CONTENTF, 0, 8, 1, BI, {0x28}, &mo_equal, NOTSENT },
|
||||
{ COAP_URIQUERY, 0, 72, 1, BI, "lwm2m=1.0", &mo_equal, NOTSENT },
|
||||
{ COAP_URIQUERY, 0, 88, 1, BI, "ep=magician", &mo_equal, NOTSENT },
|
||||
{ COAP_URIQUERY, 0, 48, 1, BI, "lt=121", &mo_equal, NOTSENT },
|
||||
/* respond with CONTENT */
|
||||
{ COAP_PAYLOAD, 0, 8, 1, BI, {0xff}, &mo_equal, NOTSENT }
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct schc_compression_rule_t comp_rule_1 = {
|
||||
.rule_id = 0x01,
|
||||
.rule_id_size_bits = 8U,
|
||||
#if USE_IP6
|
||||
&ipv6_rule1,
|
||||
#endif
|
||||
#if USE_UDP
|
||||
&udp_rule1,
|
||||
#endif
|
||||
#if USE_COAP
|
||||
&coap_rule1,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct schc_compression_rule_t comp_rule_2 = {
|
||||
.rule_id = 0x02,
|
||||
.rule_id_size_bits = 8U,
|
||||
#if USE_IP6
|
||||
&ipv6_rule1,
|
||||
#endif
|
||||
#if USE_UDP
|
||||
&udp_rule3,
|
||||
#endif
|
||||
#if USE_COAP
|
||||
&coap_rule2,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct schc_compression_rule_t comp_rule_3 = {
|
||||
.rule_id = 0x03,
|
||||
.rule_id_size_bits = 8U,
|
||||
#if USE_IP6
|
||||
&ipv6_rule2,
|
||||
#endif
|
||||
#if USE_UDP
|
||||
&udp_rule2,
|
||||
#endif
|
||||
#if USE_COAP
|
||||
&coap_rule3,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct schc_compression_rule_t comp_rule_4 = {
|
||||
.rule_id = 0x04,
|
||||
.rule_id_size_bits = 8U,
|
||||
#if USE_IP6
|
||||
&ipv6_rule2,
|
||||
#endif
|
||||
#if USE_UDP
|
||||
&udp_rule2,
|
||||
#endif
|
||||
#if USE_COAP
|
||||
&coap_rule4,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct schc_fragmentation_rule_t frag_rule_21 = {
|
||||
.rule_id = 21,
|
||||
.rule_id_size_bits = 8,
|
||||
.mode = NO_ACK,
|
||||
.dir = BI,
|
||||
.FCN_SIZE = 1, /* FCN size */
|
||||
.MAX_WND_FCN = 0, /* Maximum fragments per window */
|
||||
.WINDOW_SIZE = 0, /* Window size */
|
||||
.DTAG_SIZE = 0, /* DTAG size */
|
||||
};
|
||||
|
||||
static const struct schc_fragmentation_rule_t frag_rule_22 = {
|
||||
.rule_id = 22,
|
||||
.rule_id_size_bits = 8,
|
||||
.mode = ACK_ON_ERROR,
|
||||
.dir = BI,
|
||||
.FCN_SIZE = 3, /* FCN size */
|
||||
.MAX_WND_FCN = 6, /* Maximum fragments per window */
|
||||
.WINDOW_SIZE = 1, /* Window size */
|
||||
.DTAG_SIZE = 0, /* DTAG size */
|
||||
};
|
||||
|
||||
static const struct schc_fragmentation_rule_t frag_rule_23 = {
|
||||
.rule_id = 23,
|
||||
.rule_id_size_bits = 8,
|
||||
.mode = ACK_ALWAYS,
|
||||
.dir = BI,
|
||||
.FCN_SIZE = 3, /* FCN size */
|
||||
.MAX_WND_FCN = 6, /* Maximum fragments per window */
|
||||
.WINDOW_SIZE = 1, /* Window size */
|
||||
.DTAG_SIZE = 0, /* DTAG size */
|
||||
};
|
||||
|
||||
/* save compression rules in flash */
|
||||
static const struct schc_compression_rule_t* node1_compression_rules[] = {
|
||||
&comp_rule_1, &comp_rule_2, &comp_rule_3, &comp_rule_4
|
||||
};
|
||||
|
||||
/* save fragmentation rules in flash */
|
||||
static const struct schc_fragmentation_rule_t* node1_fragmentation_rules[] = {
|
||||
&frag_rule_21, &frag_rule_22, &frag_rule_23,
|
||||
};
|
||||
|
||||
/* rules for a particular device */
|
||||
static const struct schc_device node1 = {
|
||||
.device_id = 1,
|
||||
.uncomp_rule_id = 0,
|
||||
.uncomp_rule_id_size_bits = 8,
|
||||
.compression_rule_count = ARRAY_SIZE(node1_compression_rules),
|
||||
.compression_context = &node1_compression_rules,
|
||||
.fragmentation_rule_count = ARRAY_SIZE(node1_fragmentation_rules),
|
||||
.fragmentation_context = &node1_fragmentation_rules
|
||||
};
|
||||
static const struct schc_device node2 = {
|
||||
.device_id = 2,
|
||||
.uncomp_rule_id = 0,
|
||||
.uncomp_rule_id_size_bits = 8,
|
||||
.compression_rule_count = ARRAY_SIZE(node1_compression_rules),
|
||||
.compression_context = &node1_compression_rules,
|
||||
.fragmentation_rule_count = ARRAY_SIZE(node1_fragmentation_rules),
|
||||
.fragmentation_context = &node1_fragmentation_rules
|
||||
};
|
||||
|
||||
/* server keeps track of multiple devices: add devices to device list */
|
||||
static const struct schc_device* devices[] = { &node1, &node2 };
|
||||
|
||||
#define DEVICE_COUNT ((int)ARRAY_SIZE(devices))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RULES_RULES_H */
|
128
pkg/libschc/include/schc_config.h
Normal file
128
pkg/libschc/include/schc_config.h
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2018 imec IDLab
|
||||
* Copyright (C) 2022 Freie Universität Berlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup pkg_libschc_config
|
||||
*
|
||||
* @internal
|
||||
* @name libSCHC-side compile-time config for libSCHC
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* Usually this file and its macros need not to be touched. Use the compile-time
|
||||
* configuration macros in @ref libschc_config.h to configure @ref pkg_libschc.
|
||||
*
|
||||
* @author boortmans <bart.moons@gmail.com>
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef SCHC_CONFIG_H
|
||||
#define SCHC_CONFIG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "libschc/config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CLICK 0
|
||||
|
||||
#define DYNAMIC_MEMORY 0
|
||||
#define STATIC_MEMORY_BUFFER_LENGTH CONFIG_LIBSCHC_STATIC_MEMBUF_LEN
|
||||
|
||||
#define SCHC_CONF_RX_CONNS CONFIG_LIBSCHC_MAX_RX_CONNS
|
||||
#define SCHC_CONF_MBUF_POOL_LEN CONFIG_LIBSCHC_MBUF_POOL_SIZE
|
||||
|
||||
#if IS_USED(MODULE_LIBSCHC_COAP)
|
||||
#define USE_COAP 1
|
||||
#else
|
||||
#define USE_COAP 0
|
||||
#endif
|
||||
|
||||
#if IS_USED(MODULE_LIBSCHC_UDPV6)
|
||||
#define USE_IP6_UDP 1
|
||||
#else
|
||||
#define USE_IP6_UDP 0
|
||||
#endif
|
||||
|
||||
/* the maximum length of a single header field
|
||||
* e.g. you can use 4 ipv6 source iid addresses with match-mapping */
|
||||
#define MAX_FIELD_LENGTH 32
|
||||
|
||||
/* maximum number of header fields present in a rule (vertical, top to bottom) */
|
||||
#define IP6_FIELDS 14
|
||||
#define UDP_FIELDS 4
|
||||
#define COAP_FIELDS 16
|
||||
|
||||
#define MAX_HEADER_LENGTH 256
|
||||
|
||||
#define MAX_COAP_HEADER_LENGTH 64
|
||||
#define MAX_PAYLOAD_LENGTH 256
|
||||
#define MAX_COAP_MSG_SIZE MAX_COAP_HEADER_LENGTH + MAX_PAYLOAD_LENGTH
|
||||
|
||||
/* the maximum transfer unit of the underlying technology */
|
||||
#define MAX_MTU_LENGTH CONFIG_LIBSCHC_MAX_MTU_LEN
|
||||
|
||||
/* the maximum number of tokens inside a JSON structure */
|
||||
#define JSON_TOKENS 16
|
||||
|
||||
#define RULE_SIZE_BITS 8
|
||||
|
||||
#if IS_ACTIVE(CONFIG_LIBSCHC_DEBUG)
|
||||
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
/* the number of ack attempts */
|
||||
#define MAX_ACK_REQUESTS 3
|
||||
|
||||
/* the number of FCN bits */
|
||||
#if IS_USED(MODULE_LORA)
|
||||
#define FCN_SIZE_BITS 6
|
||||
#else
|
||||
#define FCN_SIZE_BITS 3
|
||||
#endif
|
||||
|
||||
/* the number of DTAG bits */
|
||||
#define DTAG_SIZE_BITS 0
|
||||
|
||||
/* the number of bytes the MIC consists of */
|
||||
#define MIC_SIZE_BYTES 4
|
||||
|
||||
/* the length of the bitmap */
|
||||
#if IS_USED(MODULE_LORA)
|
||||
#define BITMAP_SIZE_BYTES 8 /* pow(2, FCN_SIZE_BITS) / 8 */
|
||||
#else
|
||||
#define BITMAP_SIZE_BYTES 2 /* pow(2, FCN_SIZE_BITS) / 8 */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SCHC_CONFIG_H */
|
||||
/**
|
||||
* @internal
|
||||
* @}
|
||||
*/
|
22
tests/pkg_libschc/Makefile
Normal file
22
tests/pkg_libschc/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += embunit
|
||||
USEMODULE += event
|
||||
USEMODULE += event_timeout_ztimer
|
||||
USEMODULE += event_thread
|
||||
USEMODULE += fmt
|
||||
USEMODULE += ipv6_addr
|
||||
USEMODULE += od
|
||||
USEMODULE += shell
|
||||
USEMODULE += ztimer_msec
|
||||
|
||||
USEPKG += libschc
|
||||
USEMODULE += libschc_coap
|
||||
|
||||
CFLAGS += -DEVENT_THREAD_STACKSIZE_DEFAULT=THREAD_STACKSIZE_DEFAULT
|
||||
|
||||
DEBUG_TESTS ?= 0
|
||||
|
||||
$(call target-export-variables, test, DEBUG_TESTS)
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
10
tests/pkg_libschc/Makefile.ci
Normal file
10
tests/pkg_libschc/Makefile.ci
Normal file
@ -0,0 +1,10 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-l011k4 \
|
||||
nucleo-l031k6 \
|
||||
samd10-xmini \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
stm32g0316-disco \
|
||||
#
|
688
tests/pkg_libschc/main.c
Normal file
688
tests/pkg_libschc/main.c
Normal file
@ -0,0 +1,688 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
* @file
|
||||
* @brief Tests for the libSCHC package.
|
||||
*
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bitfield.h"
|
||||
#include "event.h"
|
||||
#include "event/timeout.h"
|
||||
#include "event/thread.h"
|
||||
#include "fmt.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "mutex.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "od.h"
|
||||
#include "shell.h"
|
||||
#include "thread_flags.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#include "compressor.h"
|
||||
#include "bit_operations.h"
|
||||
#include "fragmenter.h"
|
||||
#include "schc.h"
|
||||
|
||||
#define THREAD_FLAG_TX_END (1U << 4)
|
||||
#define FRAG_WIN_SIZE_MAX (8U)
|
||||
#define TIMEOUT_EVENTS_MAX (4U)
|
||||
|
||||
typedef struct {
|
||||
event_t super;
|
||||
event_timeout_t timeout;
|
||||
void (*timer_task)(void *);
|
||||
void *arg;
|
||||
} _timer_event_t;
|
||||
|
||||
static uint32_t _ack_delay = 0;
|
||||
static unsigned _frag_counter = 0;
|
||||
static thread_t *_main_thread;
|
||||
static schc_fragmentation_t _rx_conn, _tx_conn;
|
||||
static char _line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
static _timer_event_t _timer_event;
|
||||
static BITFIELD(_acks, FRAG_WIN_SIZE_MAX);
|
||||
static uint8_t _input_buf[128U];
|
||||
static uint8_t _output_buf[sizeof(_input_buf)];
|
||||
static uint8_t _input_buf_len;
|
||||
static uint8_t _output_buf_len;
|
||||
|
||||
static ssize_t _copy_input(int argc, char **argv)
|
||||
{
|
||||
bool msn = true; /* most significant nibble */
|
||||
ssize_t start = _input_buf_len;
|
||||
|
||||
if (_input_buf_len >= sizeof(_input_buf)) {
|
||||
goto error;
|
||||
}
|
||||
for (int c = 0; c < argc; c++) {
|
||||
char *ptr = argv[c];
|
||||
|
||||
while (*ptr) {
|
||||
uint8_t nibble = scn_u32_hex(ptr++, 1);
|
||||
|
||||
if (_input_buf_len >= sizeof(_input_buf)) {
|
||||
goto error;
|
||||
}
|
||||
if (msn) {
|
||||
_input_buf[_input_buf_len] = nibble << 4;
|
||||
}
|
||||
else {
|
||||
_input_buf[_input_buf_len++] |= nibble;
|
||||
}
|
||||
msn = !msn;
|
||||
}
|
||||
}
|
||||
if (!msn) {
|
||||
_input_buf_len++;
|
||||
}
|
||||
return _input_buf_len - start;
|
||||
error:
|
||||
puts("Too many bytes added to input buffer");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
static int _input(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1 && (strcmp(argv[1], "reset") == 0)) {
|
||||
_input_buf_len = 0U;
|
||||
puts("Successfully reset input buffer.");
|
||||
}
|
||||
else if (argc < 2) {
|
||||
if (_input_buf_len) {
|
||||
od_hex_dump(_input_buf, _input_buf_len, OD_WIDTH_DEFAULT);
|
||||
}
|
||||
else {
|
||||
puts("Input buffer is empty.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (argc > 2 && (strcmp(argv[1], "add") == 0)) {
|
||||
ssize_t size = _copy_input(argc - 2, &argv[2]);
|
||||
if (size >= 0) {
|
||||
puts("Successfully added to input buffer");
|
||||
od_hex_dump(&_input_buf[_input_buf_len - size], size, OD_WIDTH_DEFAULT);
|
||||
puts("");
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("usage: %s {reset|add <hex> ...}\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _output_usage(const char *cmd)
|
||||
{
|
||||
printf("usage: %s [{reset|copy}]\n", cmd);
|
||||
}
|
||||
|
||||
static int _output(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1) {
|
||||
if (strcmp(argv[1], "reset") == 0) {
|
||||
_output_buf_len = 0U;
|
||||
puts("Successfully reset output buffer.");
|
||||
}
|
||||
else if (strcmp(argv[1], "copy") == 0) {
|
||||
if (_output_buf_len) {
|
||||
memcpy(_input_buf, _output_buf, _output_buf_len);
|
||||
}
|
||||
_input_buf_len = _output_buf_len;
|
||||
puts("Successfully copied output buffer to input buffer.");
|
||||
}
|
||||
else {
|
||||
_output_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_output_buf_len) {
|
||||
od_hex_dump(_output_buf, _output_buf_len, OD_WIDTH_DEFAULT);
|
||||
}
|
||||
else {
|
||||
puts("Output buffer is empty.");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _parse_direction(const char *dir)
|
||||
{
|
||||
if (strcmp(dir, "up") == 0) {
|
||||
return UP;
|
||||
}
|
||||
if (strcmp(dir, "down") == 0) {
|
||||
return DOWN;
|
||||
}
|
||||
if (strcmp(dir, "bi") == 0) {
|
||||
return BI;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _compress_usage(char *cmd)
|
||||
{
|
||||
printf("usage: %s {{up|down|bi} <device id>|init <source addr>}\n", cmd);
|
||||
}
|
||||
|
||||
static int _compress(int argc, char **argv)
|
||||
{
|
||||
if (argc > 2 && (strcmp(argv[1], "init") == 0)) {
|
||||
ipv6_addr_t addr;
|
||||
|
||||
if (!ipv6_addr_from_str(&addr, argv[2])) {
|
||||
printf("Unable to parse source IPv6 address %s.\n", argv[2]);
|
||||
_compress_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
int res = schc_compressor_init(addr.u8);
|
||||
/* LCOV_EXCL_START schc_compressor_init always returns 1 */
|
||||
if (res != 1) {
|
||||
printf("Error initializing compressor with source IPv6 address %s.\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
printf("Successfully initialized compressor with source IPv6 address %s.\n", argv[2]);
|
||||
}
|
||||
else if (argc > 2) {
|
||||
int dir = _parse_direction(argv[1]);
|
||||
uint32_t device_id = scn_u32_hex(argv[2], 1);
|
||||
|
||||
if ((device_id == 0) || (dir < 0)) {
|
||||
_compress_usage(argv[0]);
|
||||
}
|
||||
if (_input_buf_len == 0) {
|
||||
puts("No input buffer defined");
|
||||
return 1;
|
||||
}
|
||||
/* compress packet */
|
||||
struct schc_compression_rule_t* comp_rule;
|
||||
memset(_output_buf, 0, sizeof(_output_buf));
|
||||
schc_bitarray_t bit_arr = SCHC_DEFAULT_BIT_ARRAY(0, _output_buf);
|
||||
|
||||
comp_rule = schc_compress(_input_buf, _input_buf_len, &bit_arr, device_id, (direction)dir);
|
||||
if (comp_rule) {
|
||||
printf("Used rule %" PRIu32 "/%u to compress to\n", comp_rule->rule_id,
|
||||
comp_rule->rule_id_size_bits);
|
||||
od_hex_dump(bit_arr.ptr, bit_arr.len, OD_WIDTH_DEFAULT);
|
||||
_output_buf_len = bit_arr.len;
|
||||
}
|
||||
else if (bit_arr.len > 0) {
|
||||
struct schc_device *device = get_device_by_id(device_id);
|
||||
|
||||
assert(device); /* LCOV_EXCL_BR_LINE hopefully always true */
|
||||
printf("Used uncompressed rule %" PRIu32 "/%u to generate\n",
|
||||
device->uncomp_rule_id, device->uncomp_rule_id_size_bits);
|
||||
od_hex_dump(bit_arr.ptr, bit_arr.len, OD_WIDTH_DEFAULT);
|
||||
_output_buf_len = bit_arr.len;
|
||||
}
|
||||
else {
|
||||
printf("Unable to compress (maybe wrong device ID %" PRIu32 "?)\n", device_id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_compress_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _decompress_usage(char *cmd)
|
||||
{
|
||||
printf("usage: %s {up|down|bi} <device id>\n", cmd);
|
||||
}
|
||||
|
||||
static int _decompress(int argc, char **argv)
|
||||
{
|
||||
if (argc > 2) {
|
||||
int dir = _parse_direction(argv[1]);
|
||||
uint32_t device_id = scn_u32_hex(argv[2], 1);
|
||||
|
||||
if ((device_id == 0) || (dir < 0)) {
|
||||
_decompress_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (_input_buf_len == 0) {
|
||||
puts("No input buffer defined");
|
||||
return 1;
|
||||
}
|
||||
schc_bitarray_t bit_arr = SCHC_DEFAULT_BIT_ARRAY(_input_buf_len, _input_buf);
|
||||
memset(_output_buf, 0, sizeof(_output_buf));
|
||||
uint8_t len = schc_decompress(&bit_arr, _output_buf, device_id, bit_arr.len,
|
||||
(direction)dir);
|
||||
/* LCOV_EXCL_START len == 0 impossible to reach with current version of libSCHC */
|
||||
if (len == 0U) {
|
||||
puts("Unable to decompress input");
|
||||
return 1;
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
else {
|
||||
puts("Decompressed to");
|
||||
od_hex_dump(_output_buf, len, OD_WIDTH_DEFAULT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_decompress_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_ack_usage(char *cmd)
|
||||
{
|
||||
printf("usage: %s <delay ms> <bitmap>\n", cmd);
|
||||
}
|
||||
|
||||
static void _ack_print(void)
|
||||
{
|
||||
printf("ACKs set to %" PRIu32 " ms delay with bitmap ", _ack_delay);
|
||||
for (size_t idx = 0; idx < FRAG_WIN_SIZE_MAX; idx++) {
|
||||
printf(bf_isset(_acks, idx) ? "1" : "0");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int _set_ack(int argc, char **argv)
|
||||
{
|
||||
if (argc > 2) {
|
||||
uint32_t delay = scn_u32_dec(argv[1], 10);
|
||||
unsigned idx = 0;
|
||||
static BITFIELD(tmp, FRAG_WIN_SIZE_MAX) = { 0 };
|
||||
|
||||
static_assert(sizeof(tmp) == sizeof(_acks), "sizeof(tmp) != sizeof(_acks)");
|
||||
for (char *bitmap_str = argv[2];
|
||||
*bitmap_str;
|
||||
bitmap_str++, idx++) {
|
||||
if (idx >= FRAG_WIN_SIZE_MAX) {
|
||||
printf("%s does not fit fragment window size %u\n", argv[2],
|
||||
FRAG_WIN_SIZE_MAX);
|
||||
}
|
||||
if (*bitmap_str == '1') {
|
||||
bf_set(tmp, idx);
|
||||
}
|
||||
else if (*bitmap_str == '0') {
|
||||
bf_unset(tmp, idx);
|
||||
}
|
||||
else {
|
||||
_set_ack_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
memcpy(_acks, tmp, sizeof(_acks));
|
||||
_ack_delay = delay;
|
||||
_ack_print();
|
||||
}
|
||||
else if (argc == 1) {
|
||||
_ack_print();
|
||||
}
|
||||
else {
|
||||
_set_ack_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool _ack_all1(void)
|
||||
{
|
||||
#if (FRAG_WIN_SIZE_MAX > 8)
|
||||
for (unsigned idx = 0; idx < sizeof(_acks) - 1; idx++) {
|
||||
if (_acks[idx] != 0xff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (unsigned idx = (sizeof(_acks) - 1) * 8; idx < FRAG_WIN_SIZE_MAX; idx++) {
|
||||
if (!bf_isset(_acks, idx)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _timer_cb(event_t *evt)
|
||||
{
|
||||
_timer_event_t *timer_event = container_of(evt, _timer_event_t, super);
|
||||
schc_fragmentation_t *conn = timer_event->arg;
|
||||
conn->timer_ctx = NULL;
|
||||
if (conn->device_id) { /* prevent firing of expired or canceled timers */
|
||||
timer_event->timer_task(timer_event->arg);
|
||||
}
|
||||
}
|
||||
|
||||
static void _simulate_ack(schc_fragmentation_t *conn, uint32_t device_id)
|
||||
{
|
||||
if ((_tx_conn.fragmentation_rule->mode != ACK_ALWAYS) &&
|
||||
((_tx_conn.fragmentation_rule->mode != ACK_ON_ERROR) || _ack_all1())) {
|
||||
return;
|
||||
}
|
||||
if (_ack_delay > 0) {
|
||||
ztimer_sleep(ZTIMER_MSEC, _ack_delay);
|
||||
}
|
||||
|
||||
uint8_t ack[((_tx_conn.fragmentation_rule->rule_id_size_bits / 8 + 1)) +
|
||||
DTAG_SIZE_BYTES + BITMAP_SIZE_BYTES];
|
||||
uint8_t offset = _tx_conn.fragmentation_rule->rule_id_size_bits;
|
||||
|
||||
uint8_t rule_id[4] = { 0 };
|
||||
memset(ack, 0, sizeof(ack));
|
||||
little_end_uint8_from_uint32(rule_id, conn->fragmentation_rule->rule_id);
|
||||
copy_bits(ack, 0, rule_id, 0, offset);
|
||||
copy_bits(ack, offset, conn->ack.dtag, 0, conn->fragmentation_rule->DTAG_SIZE);
|
||||
offset += conn->fragmentation_rule->DTAG_SIZE;
|
||||
|
||||
uint8_t window[1] = { conn->window << (8 - conn->fragmentation_rule->WINDOW_SIZE) };
|
||||
copy_bits(ack, offset, window, 0, conn->fragmentation_rule->WINDOW_SIZE);
|
||||
offset += conn->fragmentation_rule->WINDOW_SIZE;
|
||||
|
||||
if (_ack_all1()) {
|
||||
uint8_t c[1] = { 1 << (8 - MIC_C_SIZE_BITS) };
|
||||
copy_bits(ack, offset, c, 0, MIC_C_SIZE_BITS);
|
||||
offset += MIC_C_SIZE_BITS;
|
||||
}
|
||||
else {
|
||||
uint8_t bitmap[BITMAP_SIZE_BYTES] = { 0 };
|
||||
bool set_true = true;
|
||||
for (unsigned idx = 0; idx < FRAG_WIN_SIZE_MAX; idx++) {
|
||||
if (bf_isset(_acks, idx)) {
|
||||
set_bits(bitmap, idx, 1);
|
||||
}
|
||||
else {
|
||||
clear_bits(bitmap, idx, 1);
|
||||
if (set_true) {
|
||||
/* set first 0 for next round */
|
||||
bf_set(_acks, idx);
|
||||
set_true = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
copy_bits(ack, offset, bitmap, 0, conn->fragmentation_rule->MAX_WND_FCN + 1);
|
||||
offset += conn->fragmentation_rule->MAX_WND_FCN + 1; /* TODO must be encoded? */
|
||||
}
|
||||
uint8_t packet_len = ((offset - 1) / 8) + 1;
|
||||
puts("Simulate ACK");
|
||||
od_hex_dump(ack, packet_len, OD_WIDTH_DEFAULT);
|
||||
schc_input(ack, packet_len, conn, device_id);
|
||||
}
|
||||
|
||||
static uint8_t _tx_cb(uint8_t* data, uint16_t length, uint32_t device_id)
|
||||
{
|
||||
printf("TX Fragment %u on dev 0x%lx\n", ++_frag_counter, (long unsigned)device_id);
|
||||
od_hex_dump(data, length, OD_WIDTH_DEFAULT);
|
||||
_simulate_ack(&_tx_conn, device_id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t _rx_cb(uint8_t* data, uint16_t length, uint32_t device_id)
|
||||
{
|
||||
printf("Packet sent on dev 0x%lx\n", (long unsigned)device_id);
|
||||
od_hex_dump(data, length, OD_WIDTH_DEFAULT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _tx_end(schc_fragmentation_t *conn)
|
||||
{
|
||||
(void)conn;
|
||||
_frag_counter = 0;
|
||||
puts("TX End");
|
||||
thread_flags_set(_main_thread, THREAD_FLAG_TX_END);
|
||||
}
|
||||
|
||||
/* LCOV_EXCL_START _remove_timer is never called by libSCHC in tests */
|
||||
static void _free_event(_timer_event_t *evt)
|
||||
{
|
||||
event_timeout_clear(&evt->timeout);
|
||||
/* cancel event in case it already was posted */
|
||||
event_cancel(evt->timeout.queue, evt->timeout.event);
|
||||
}
|
||||
|
||||
static void _remove_timer(struct schc_fragmentation_t *conn)
|
||||
{
|
||||
_timer_event_t *evt = conn->timer_ctx;
|
||||
if (evt) {
|
||||
_free_event(evt);
|
||||
conn->timer_ctx = NULL;
|
||||
}
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
||||
static void _rx_end(schc_fragmentation_t *conn)
|
||||
{
|
||||
_output_buf_len = get_mbuf_len(conn);
|
||||
mbuf_copy(conn, _output_buf);
|
||||
od_hex_dump(_output_buf, _output_buf_len, OD_WIDTH_DEFAULT);
|
||||
}
|
||||
|
||||
static void _reset_timers(void)
|
||||
{
|
||||
_timer_event.super.handler = _timer_cb;
|
||||
event_timeout_ztimer_init(&_timer_event.timeout, ZTIMER_MSEC, EVENT_PRIO_HIGHEST,
|
||||
&_timer_event.super);
|
||||
}
|
||||
|
||||
static int _timer(int argc, char **argv)
|
||||
{
|
||||
if ((argc > 1) && (strcmp(argv[1], "reset") == 0)) {
|
||||
_reset_timers();
|
||||
puts("Reset all timers");
|
||||
}
|
||||
else {
|
||||
printf("Free timers: %u (of %u)\n",
|
||||
TIMEOUT_EVENTS_MAX,
|
||||
TIMEOUT_EVENTS_MAX);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _set_schc_timer(schc_fragmentation_t *conn,
|
||||
void (*callback)(void* conn), uint32_t delay, void *arg)
|
||||
{
|
||||
assert(conn->timer_ctx == NULL); /* LCOV_EXCL_BR_LINE hopefully always true */
|
||||
_timer_event.timer_task = callback;
|
||||
_timer_event.arg = arg;
|
||||
conn->timer_ctx = &_timer_event;
|
||||
event_timeout_set(&_timer_event.timeout, delay);
|
||||
}
|
||||
|
||||
static reliability_mode _parse_reliability_mode(const char *mode)
|
||||
{
|
||||
if (strcmp(mode, "no-ack") == 0) {
|
||||
return NO_ACK;
|
||||
}
|
||||
if (strcmp(mode, "ack-always") == 0) {
|
||||
return ACK_ALWAYS;
|
||||
}
|
||||
if (strcmp(mode, "ack-on-error") == 0) {
|
||||
return ACK_ON_ERROR;
|
||||
}
|
||||
if (strcmp(mode, "not-fragmented") == 0) {
|
||||
return NOT_FRAGMENTED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _fragment_usage(char *cmd)
|
||||
{
|
||||
printf("usage: %s <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}\n", cmd);
|
||||
}
|
||||
|
||||
static int _fragment(int argc, char **argv)
|
||||
{
|
||||
if (argc > 4) {
|
||||
uint32_t device_id = scn_u32_hex(argv[1], 1);
|
||||
uint32_t mtu = scn_u32_dec(argv[2], 4);
|
||||
uint32_t dc = scn_u32_dec(argv[3], 5);
|
||||
reliability_mode mode = _parse_reliability_mode(argv[4]);
|
||||
|
||||
if ((device_id == 0) || (mtu == 0) || (mtu > 1280) || (dc == 0) || (mode == 0)) {
|
||||
_fragment_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (_input_buf_len == 0) {
|
||||
puts("No input buffer defined");
|
||||
return 1;
|
||||
}
|
||||
struct schc_fragmentation_rule_t *frag_rule = NULL;
|
||||
schc_bitarray_t bit_arr = SCHC_DEFAULT_BIT_ARRAY(_input_buf_len, _input_buf);
|
||||
|
||||
frag_rule = get_fragmentation_rule_by_reliability_mode(mode, device_id);
|
||||
|
||||
if (!frag_rule) {
|
||||
printf("No fragmentation rule known for mode %s on device %s\n",
|
||||
argv[4], argv[1]);
|
||||
return 1;
|
||||
}
|
||||
_tx_conn.bit_arr = &bit_arr;
|
||||
_tx_conn.device_id = device_id;
|
||||
_tx_conn.mtu = mtu;
|
||||
_tx_conn.dc = dc;
|
||||
_tx_conn.send = _tx_cb;
|
||||
_tx_conn.end_tx = _tx_end;
|
||||
_tx_conn.fragmentation_rule = frag_rule;
|
||||
_tx_conn.post_timer_task = _set_schc_timer;
|
||||
_tx_conn.remove_timer_entry = _remove_timer;
|
||||
int ret = schc_fragment(&_tx_conn);
|
||||
if (ret == -2) {
|
||||
puts("No fragmentation needed");
|
||||
_frag_counter = 0;
|
||||
}
|
||||
else {
|
||||
assert(ret >= 0); /* LCOV_EXCL_BR_LINE hopefully always true */
|
||||
thread_flags_wait_one(THREAD_FLAG_TX_END);
|
||||
puts("Fragmented!");
|
||||
}
|
||||
}
|
||||
else if ((argc > 1) && (strcmp(argv[1], "init") == 0)) {
|
||||
memset(&_tx_conn, 0, sizeof(_tx_conn));
|
||||
int res = schc_fragmenter_init(&_tx_conn);
|
||||
/* LCOV_EXCL_START schc_fragmenter_init always returns 1 */
|
||||
if (res != 1) {
|
||||
puts("Error initializing fragmenter.");
|
||||
return 1;
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
puts("Fragmenter initialized");
|
||||
}
|
||||
else {
|
||||
_fragment_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _reassemble_usage(char *cmd)
|
||||
{
|
||||
printf("usage: %s <device id> <timeout>\n", cmd);
|
||||
}
|
||||
|
||||
static int _reassemble(int argc, char **argv)
|
||||
{
|
||||
if (argc > 2) {
|
||||
uint32_t device_id = scn_u32_hex(argv[1], 1);
|
||||
uint32_t timeout = scn_u32_dec(argv[2], 5);
|
||||
|
||||
if ((device_id == 0) || (timeout == 0)) {
|
||||
_reassemble_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (_input_buf_len == 0) {
|
||||
puts("No input buffer defined");
|
||||
return 1;
|
||||
}
|
||||
|
||||
schc_bitarray_t bit_arr = SCHC_DEFAULT_BIT_ARRAY(_input_buf_len, _input_buf);
|
||||
_rx_conn.bit_arr = &bit_arr;
|
||||
/* get active connection and set the correct rule for this connection */
|
||||
schc_fragmentation_t *conn = schc_input(_input_buf, _input_buf_len, &_rx_conn, device_id);
|
||||
|
||||
/* if returned value is &_rx_conn: acknowledgement is received, which is handled by the
|
||||
* library */
|
||||
if (conn != &_rx_conn) { /* LCOV_EXCL_BR_LINE */
|
||||
conn->post_timer_task = _set_schc_timer;
|
||||
conn->dc = timeout;
|
||||
|
||||
if (!conn->fragmentation_rule || conn->fragmentation_rule->mode == NOT_FRAGMENTED) {
|
||||
/* packet was not fragmented */
|
||||
printf("RX Unfragmented on dev 0x%lx\n", (long unsigned)conn->device_id);
|
||||
_rx_end(conn); /* final packet arrived */
|
||||
} else {
|
||||
int ret = schc_reassemble(conn);
|
||||
|
||||
if (ret) {
|
||||
printf("RX Reassembly complete on dev 0x%lx\n",
|
||||
(long unsigned)conn->device_id);
|
||||
}
|
||||
/* use the connection to reassemble */
|
||||
if (ret && (conn->fragmentation_rule->mode == NO_ACK)) {
|
||||
_rx_end(conn); /* final packet arrived */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((argc > 1) && (strcmp(argv[1], "init") == 0)) {
|
||||
memset(&_tx_conn, 0, sizeof(_tx_conn));
|
||||
memset(&_rx_conn, 0, sizeof(_rx_conn));
|
||||
int res = schc_fragmenter_init(&_tx_conn);
|
||||
/* LCOV_EXCL_START schc_fragmenter_init always returns 1 */
|
||||
if (res != 1) {
|
||||
puts("Error initializing fragmenter.");
|
||||
return 1;
|
||||
}
|
||||
/* LCOV_EXCL_STOP */
|
||||
_rx_conn.send = _rx_cb;
|
||||
_rx_conn.end_rx = &_rx_end;
|
||||
_rx_conn.remove_timer_entry = &_remove_timer;
|
||||
puts("Reassembler initialized");
|
||||
}
|
||||
else {
|
||||
_reassemble_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int unittests(int argc, char **argv);
|
||||
|
||||
static const shell_command_t _shell_commands[] = {
|
||||
{ "input", "Add bytes to input buffer", _input },
|
||||
{ "output", "Handle output buffer", _output },
|
||||
{ "compress", "Compress input buffer using libSCHC with preconfigured rules", _compress },
|
||||
{ "decompress", "Decompress input buffer using libSCHC with preconfigured rules", _decompress },
|
||||
{ "set_ack", "Set fragments to ACK with libSCHC", _set_ack },
|
||||
{ "timer", "Check or reset running timers", _timer },
|
||||
{ "fragment", "Fragment input buffer using libSCHC with preconfigured rules", _fragment },
|
||||
{ "reassemble", "Reassemble fragment from input buffer using libSCHC with preconfigured rules",
|
||||
_reassemble },
|
||||
{ "unittests", "Run unittests for libSCHC", unittests },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
_main_thread = thread_get_active();
|
||||
memset(_acks, 0xff, sizeof(_acks));
|
||||
_reset_timers();
|
||||
shell_run(_shell_commands, _line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
return 0; /* LCOV_EXCL_LINE never reached */
|
||||
}
|
886
tests/pkg_libschc/tests/01-run.py
Executable file
886
tests/pkg_libschc/tests/01-run.py
Executable file
@ -0,0 +1,886 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2022 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from scapy.all import load_contrib, IPv6, UDP
|
||||
from scapy.contrib.coap import CoAP
|
||||
|
||||
from testrunner import check_unittests
|
||||
from testrunner.unittest import PexpectTestCase
|
||||
|
||||
|
||||
DEBUG_TESTS = bool(int(os.environ.get("DEBUG_TESTS", 0)))
|
||||
|
||||
|
||||
class TestCompiledUnittests(PexpectTestCase):
|
||||
LOGFILE = sys.stdout if DEBUG_TESTS else None
|
||||
|
||||
def test_compiled_unittests(self):
|
||||
self.spawn.sendline("unittests")
|
||||
check_unittests(self.spawn)
|
||||
|
||||
|
||||
class InputMixin:
|
||||
MAX = 16
|
||||
|
||||
def input(self, inp):
|
||||
if isinstance(inp, str):
|
||||
input_hex = inp
|
||||
else:
|
||||
input_hex = bytes(inp).hex()
|
||||
while input_hex:
|
||||
self.spawn.sendline("input add {}".format(input_hex[: self.MAX]))
|
||||
self.spawn.expect_exact("Successfully added to input buffer")
|
||||
self.expect_od_dump_of(input_hex[:self.MAX])
|
||||
input_hex = input_hex[self.MAX:]
|
||||
|
||||
def input_reset(self):
|
||||
self.spawn.sendline("input reset")
|
||||
|
||||
def expect_od_dump_of(self, byts):
|
||||
if isinstance(byts, str):
|
||||
hexbytes = byts
|
||||
else:
|
||||
hexbytes = bytes(byts).hex()
|
||||
for i in range((len(hexbytes) // 32) + 1):
|
||||
rang = hexbytes[(i * 32) : ((i * 32) + 32)] # noqa: E203
|
||||
if not rang:
|
||||
# reached end directly at line break
|
||||
break
|
||||
od_bytes = "".join(
|
||||
[
|
||||
" {}{}".format(a.upper(), b.upper())
|
||||
for a, b in zip(rang[::2], rang[1::2])
|
||||
]
|
||||
)
|
||||
self.spawn.expect_exact("{:08x}{}".format(i * 16, od_bytes))
|
||||
|
||||
|
||||
class TestSelfTest(PexpectTestCase, InputMixin):
|
||||
LOGFILE = sys.stdout if DEBUG_TESTS else None
|
||||
|
||||
def tearDown(self):
|
||||
self.input_reset()
|
||||
self.spawn.expect_exact("Successfully reset input buffer.")
|
||||
self.spawn.sendline("output reset")
|
||||
self.spawn.expect_exact("Successfully reset output buffer.")
|
||||
self.spawn.sendline("compress init ::")
|
||||
self.spawn.sendline("set_ack 0 11111111")
|
||||
self.spawn.expect_exact("ACKs set to 0 ms delay with bitmap 11111111")
|
||||
self.spawn.sendline("timer reset")
|
||||
self.spawn.expect_exact("Reset all timers")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_input_add(self):
|
||||
self.spawn.sendline("input")
|
||||
self.spawn.expect_exact("Input buffer is empty.")
|
||||
self.spawn.sendline("input add 0123456789abcdef")
|
||||
self.spawn.expect_exact("Successfully added to input buffer")
|
||||
self.spawn.expect_exact("00000000 01 23 45 67 89 AB CD EF")
|
||||
self.spawn.sendline("input")
|
||||
self.spawn.expect_exact("00000000 01 23 45 67 89 AB CD EF")
|
||||
self.spawn.sendline("input add dead c0ffee 20b07 42")
|
||||
self.spawn.expect_exact("Successfully added to input buffer")
|
||||
self.spawn.expect_exact("00000000 DE AD C0 FF EE 20 B0 74 20")
|
||||
self.spawn.sendline("input")
|
||||
self.spawn.expect_exact(
|
||||
"00000000 01 23 45 67 89 AB CD EF DE AD C0 FF EE 20 B0 74"
|
||||
)
|
||||
self.spawn.expect_exact("00000010 20")
|
||||
|
||||
def test_input_add_full_buf_mod_128(self):
|
||||
for _ in range(0, 128, 16):
|
||||
self.spawn.sendline("input add {}".format("1a" * 16))
|
||||
self.spawn.expect_exact("Successfully added to input buffer")
|
||||
self.spawn.expect_exact(
|
||||
"00000000 {}".format(" ".join("1A" for _ in range(16)))
|
||||
)
|
||||
self.spawn.sendline("input")
|
||||
for offset in range(0, 128, 16):
|
||||
self.spawn.expect_exact(
|
||||
"000000{:02x} {}".format(offset, " ".join("1A" for _ in range(16)))
|
||||
)
|
||||
self.spawn.sendline("input add 2b")
|
||||
self.spawn.expect_exact("Too many bytes added to input buffer")
|
||||
|
||||
def test_input_add_full_buf_not_mod_128(self):
|
||||
for offset in range(0, 126, 21):
|
||||
self.spawn.sendline("input add {}".format("1a" * 21))
|
||||
self.spawn.expect_exact("Successfully added to input buffer")
|
||||
self.spawn.expect_exact(
|
||||
"00000000 {}".format(" ".join("1A" for _ in range(16)))
|
||||
)
|
||||
self.spawn.expect_exact(
|
||||
"00000010 {}".format(" ".join("1A" for _ in range(5)))
|
||||
)
|
||||
self.spawn.sendline("input add {}".format("1a" * 5))
|
||||
self.spawn.expect_exact("Too many bytes added to input buffer")
|
||||
self.spawn.sendline("input")
|
||||
for offset in range(0, 128, 16):
|
||||
self.spawn.expect_exact(
|
||||
"000000{:02x} {}".format(offset, " ".join("1A" for _ in range(16)))
|
||||
)
|
||||
self.spawn.sendline("input add 2b")
|
||||
self.spawn.expect_exact("Too many bytes added to input buffer")
|
||||
|
||||
def test_input_usage(self):
|
||||
self.spawn.sendline("input help")
|
||||
self.spawn.expect_exact("usage: input {reset|add <hex> ...}")
|
||||
self.spawn.sendline("input add")
|
||||
self.spawn.expect_exact("usage: input {reset|add <hex> ...}")
|
||||
self.spawn.sendline("input foobar test")
|
||||
self.spawn.expect_exact("usage: input {reset|add <hex> ...}")
|
||||
|
||||
def test_input_reset(self):
|
||||
self.spawn.sendline("input")
|
||||
self.spawn.expect_exact("Input buffer is empty.")
|
||||
self.spawn.sendline("input reset")
|
||||
self.spawn.expect_exact("Successfully reset input buffer.")
|
||||
self.spawn.sendline("input")
|
||||
self.spawn.expect_exact("Input buffer is empty.")
|
||||
self.spawn.sendline("input add 0123456789abcdef")
|
||||
self.spawn.expect_exact("Successfully added to input buffer")
|
||||
self.spawn.expect_exact("00000000 01 23 45 67 89 AB CD EF")
|
||||
self.spawn.sendline("input")
|
||||
self.spawn.expect_exact("00000000 01 23 45 67 89 AB CD EF")
|
||||
self.spawn.sendline("input reset")
|
||||
self.spawn.expect_exact("Successfully reset input buffer.")
|
||||
self.spawn.sendline("input")
|
||||
self.spawn.expect_exact("Input buffer is empty.")
|
||||
|
||||
def test_output_usage(self):
|
||||
self.spawn.sendline("output help")
|
||||
self.spawn.expect_exact("usage: output [{reset|copy}]")
|
||||
|
||||
def test_output_show(self):
|
||||
self.spawn.sendline("output")
|
||||
self.spawn.expect_exact("Output buffer is empty")
|
||||
|
||||
def test_output_copy(self):
|
||||
self.spawn.sendline("output copy")
|
||||
self.spawn.expect_exact("Successfully copied output buffer to input buffer.")
|
||||
|
||||
def test_compress_init_parse_error(self):
|
||||
self.spawn.sendline("compress init foobar")
|
||||
self.spawn.expect_exact("Unable to parse source IPv6 address foobar.")
|
||||
self.spawn.expect_exact(
|
||||
"usage: compress {{up|down|bi} <device id>|init <source addr>}"
|
||||
)
|
||||
|
||||
def test_compress_init_success(self):
|
||||
self.spawn.sendline("compress init 2001:db8::1")
|
||||
self.spawn.expect_exact(
|
||||
"Successfully initialized compressor with source IPv6 address 2001:db8::1"
|
||||
)
|
||||
|
||||
def test_compress_usage(self):
|
||||
self.spawn.sendline("compress")
|
||||
self.spawn.expect_exact(
|
||||
"usage: compress {{up|down|bi} <device id>|init <source addr>}"
|
||||
)
|
||||
self.spawn.sendline("compress bi 0")
|
||||
self.spawn.expect_exact(
|
||||
"usage: compress {{up|down|bi} <device id>|init <source addr>}"
|
||||
)
|
||||
self.spawn.sendline("compress xyz test 1337")
|
||||
self.spawn.expect_exact(
|
||||
"usage: compress {{up|down|bi} <device id>|init <source addr>}"
|
||||
)
|
||||
self.spawn.sendline("compress xyz 1337")
|
||||
self.spawn.expect_exact(
|
||||
"usage: compress {{up|down|bi} <device id>|init <source addr>}"
|
||||
)
|
||||
|
||||
def test_compress_no_input(self):
|
||||
self.spawn.sendline("compress up 1")
|
||||
self.spawn.expect_exact("No input buffer defined")
|
||||
|
||||
def test_compress_wrong_device_id(self):
|
||||
pkt = (
|
||||
IPv6(src="2001:db8:1::1", dst="2001:db8:1::2", hlim=56)
|
||||
/ UDP(sport=0x1F42, dport=0x1F42)
|
||||
/ "the payload"
|
||||
)
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("compress init {}".format(pkt[IPv6].src))
|
||||
self.spawn.sendline("compress down 4")
|
||||
self.spawn.expect_exact("Unable to compress (maybe wrong device ID 4?)")
|
||||
|
||||
def test_compress_success(self):
|
||||
pkt = (
|
||||
IPv6(src="2001:db8:1::1", dst="2001:db8:1::2", hlim=56)
|
||||
/ UDP(sport=0x1F42, dport=0x1F42)
|
||||
/ "the payload"
|
||||
)
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("compress init {}".format(pkt[IPv6].src))
|
||||
self.spawn.sendline("compress down 1")
|
||||
self.spawn.expect_exact("Used uncompressed rule 0/8 to generate")
|
||||
self.expect_od_dump_of(b"\0" + bytes(pkt))
|
||||
self.input_reset()
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
self.spawn.sendline("compress init {}".format(pkt[IPv6].src))
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("compress up 1")
|
||||
self.spawn.expect_exact("Used rule 3/8 to compress to")
|
||||
exp_pkt = b"\x03\x30\x90"
|
||||
self.expect_od_dump_of(exp_pkt)
|
||||
self.spawn.sendline("output")
|
||||
self.expect_od_dump_of(exp_pkt)
|
||||
return exp_pkt
|
||||
|
||||
def test_decompress_usage(self):
|
||||
self.spawn.sendline("decompress")
|
||||
self.spawn.expect_exact("usage: decompress {up|down|bi} <device id>")
|
||||
self.spawn.sendline("decompress bi 0")
|
||||
self.spawn.expect_exact("usage: decompress {up|down|bi} <device id>")
|
||||
self.spawn.sendline("decompress xyz test")
|
||||
self.spawn.expect_exact("usage: decompress {up|down|bi} <device id>")
|
||||
self.spawn.sendline("decompress xyz 123")
|
||||
self.spawn.expect_exact("usage: decompress {up|down|bi} <device id>")
|
||||
|
||||
def test_decompress_success(self):
|
||||
pkt = IPv6(src="2001:db8:1::1", dst="2001:db8:1::2", hlim=56) / UDP(
|
||||
sport=0x1F42, dport=0x1F42
|
||||
) / "payload"
|
||||
self.input(b"\0" + bytes(pkt))
|
||||
self.spawn.sendline("decompress down 1")
|
||||
self.expect_od_dump_of(pkt)
|
||||
pkt = IPv6(src="2001:db8:1::1", dst="2001:db8:1::2", hlim=56) / UDP(
|
||||
sport=0x1F42, dport=0x1F42
|
||||
)
|
||||
pkt = b"\x03\x30\x90"
|
||||
self.input_reset()
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("decompress up 1")
|
||||
exp_pkt = (
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
self.expect_od_dump_of(exp_pkt)
|
||||
|
||||
def test_decompress_no_input(self):
|
||||
self.spawn.sendline("decompress up 1")
|
||||
self.spawn.expect_exact("No input buffer defined")
|
||||
|
||||
def test_set_ack_usage(self):
|
||||
self.spawn.sendline("set_ack foobar")
|
||||
self.spawn.expect_exact("usage: set_ack <delay ms> <bitmap>")
|
||||
self.spawn.sendline("set_ack foobar 1test")
|
||||
self.spawn.expect_exact("usage: set_ack <delay ms> <bitmap>")
|
||||
self.spawn.sendline("set_ack 12 1test")
|
||||
self.spawn.expect_exact("usage: set_ack <delay ms> <bitmap>")
|
||||
|
||||
def test_set_ack_too_large(self):
|
||||
self.spawn.sendline("set_ack 0 010001100")
|
||||
self.spawn.expect_exact("010001100 does not fit fragment window size 8")
|
||||
|
||||
def test_set_ack_success(self):
|
||||
self.spawn.sendline("set_ack")
|
||||
self.spawn.expect_exact("ACKs set to 0 ms delay with bitmap 11111111")
|
||||
self.spawn.sendline("set_ack 10 01000110")
|
||||
self.spawn.expect_exact("ACKs set to 10 ms delay with bitmap 01000110")
|
||||
self.spawn.sendline("set_ack 5 00000000")
|
||||
self.spawn.expect_exact("ACKs set to 5 ms delay with bitmap 00000000")
|
||||
self.spawn.sendline("set_ack 0 11111111")
|
||||
self.spawn.expect_exact("ACKs set to 0 ms delay with bitmap 11111111")
|
||||
|
||||
def test_timer_success(self):
|
||||
self.spawn.sendline("timer foobar")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_fragment_usage(self):
|
||||
self.spawn.sendline("fragment")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment 1")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment 1 25")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment 1 25 5000")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment 1 25 5000 foobar")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment 1 25 dc no-ack")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment 1 mtu 5000 ack-always")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment 1 9000 5000 ack-on-error")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
self.spawn.sendline("fragment x 25 5000 not-fragmented")
|
||||
self.spawn.expect_exact(
|
||||
"usage: fragment <device id> <MTU> <duty cycle> "
|
||||
"{no-ack|ack-always|ack-on-error|not-fragmented}"
|
||||
)
|
||||
|
||||
def test_fragment_init(self):
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.expect_exact("Fragmenter initialized")
|
||||
|
||||
def test_fragment_success_no_frag(self):
|
||||
pkt = self.test_compress_success()
|
||||
self.spawn.sendline("output copy")
|
||||
self.spawn.expect_exact("Successfully copied output buffer to input buffer.")
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("fragment 1 25 5000 no-ack")
|
||||
self.spawn.expect_exact("TX Fragment 1 on dev 0x1")
|
||||
self.expect_od_dump_of(pkt)
|
||||
self.spawn.expect_exact("No fragmentation needed")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_fragment_success_no_ack(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
self.input(bytes(pkt))
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("fragment 1 25 50 no-ack")
|
||||
self.spawn.expect_exact("TX Fragment 1 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x15\x30\x00\x00\x00\x00\x06\x08\xa0\x7f\x40\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
self.spawn.expect_exact("TX Fragment 2 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x15\x7f\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x87\xd0\x47\xd0\x80\x03\x21\x13"
|
||||
)
|
||||
self.spawn.expect_exact("TX Fragment 3 on dev 0x1")
|
||||
self.expect_od_dump_of(b"\x15\x98\xa6\x43\xbf\xc8\x00\x00\x00\x00")
|
||||
self.spawn.expect_exact("TX End")
|
||||
self.spawn.expect_exact("Fragmented!")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_fragment_success_ack_always_all1(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("set_ack 0 11111111")
|
||||
self.spawn.sendline("fragment 1 25 50 ack-always")
|
||||
self.spawn.expect_exact("TX Fragment 1 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x17\x66\x00\x00\x00\x00\x00\xc1\x14\x0f\xe8\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x40")
|
||||
self.spawn.expect_exact("TX Fragment 2 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x17\x51\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
b"\x1f\x41\x1f\x42\x00\x0c\x84"
|
||||
)
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x40")
|
||||
self.spawn.expect_exact("TX Fragment 3 on dev 0x1")
|
||||
self.expect_od_dump_of(b"\x17\x73\x14\xc8\x77\xf4\xe4\x00\x00\x00\x00")
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x40")
|
||||
self.spawn.expect_exact("TX Fragment 4 on dev 0x1")
|
||||
self.expect_od_dump_of(b"\x17\x73\x14\xc8\x77\xf0")
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x40")
|
||||
self.spawn.expect_exact("TX End")
|
||||
self.spawn.expect_exact("Fragmented!")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_fragment_success_ack_always_001(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("set_ack 10 00111111")
|
||||
self.spawn.sendline("fragment 1 25 50 ack-always")
|
||||
self.spawn.expect_exact("TX Fragment 1 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x17\x66\x00\x00\x00\x00\x00\xc1\x14\x0f\xe8\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x1f")
|
||||
self.spawn.expect_exact("TX Fragment 2 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x17\x51\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
b"\x1f\x41\x1f\x42\x00\x0c\x84"
|
||||
)
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x5f")
|
||||
self.spawn.expect_exact("TX Fragment 3 on dev 0x1")
|
||||
self.expect_od_dump_of(b"\x17\x73\x14\xc8\x77\xf4\xe4\x00\x00\x00\x00")
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x40")
|
||||
self.spawn.expect_exact("TX Fragment 4 on dev 0x1")
|
||||
self.expect_od_dump_of(b"\x17\x73\x14\xc8\x77\xf0")
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x17\x40")
|
||||
self.spawn.expect_exact("TX End")
|
||||
self.spawn.expect_exact("Fragmented!")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_fragment_success_ack_on_error_all1(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("set_ack 0 11111111")
|
||||
self.spawn.sendline("fragment 1 25 50 ack-on-error")
|
||||
self.spawn.expect_exact("TX Fragment 1 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x16\x66\x00\x00\x00\x00\x00\xc1\x14\x0f\xe8\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
self.spawn.expect_exact("TX Fragment 2 on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
b"\x16\x51\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
b"\x1f\x41\x1f\x42\x00\x0c\x84"
|
||||
)
|
||||
self.spawn.expect_exact("TX Fragment 3 on dev 0x1")
|
||||
self.expect_od_dump_of(b"\x16\x73\x14\xc8\x77\xf4\xe4\x00\x00\x00\x00")
|
||||
self.spawn.expect_exact("TX End")
|
||||
self.spawn.expect_exact("Fragmented")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_fragment_success_ack_on_error_10(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("set_ack 0 10111111")
|
||||
self.spawn.sendline("fragment 1 25 50 ack-on-error")
|
||||
self.spawn.expect_exact("TX Fragment 1 on dev 0x")
|
||||
self.expect_od_dump_of(
|
||||
b"\x16\x66\x00\x00\x00\x00\x00\xc1\x14\x0f\xe8\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
self.spawn.expect_exact("Simulate ACK")
|
||||
self.expect_od_dump_of(b"\x16\x5f")
|
||||
self.spawn.expect_exact("TX Fragment 2 on dev 0x")
|
||||
self.expect_od_dump_of(
|
||||
b"\x16\x51\xFE\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
b"\x1F\x41\x1F\x42\x00\x0C\x84"
|
||||
)
|
||||
self.spawn.expect_exact("TX Fragment 3 on dev 0x")
|
||||
self.expect_od_dump_of(b"\x16\x73\x14\xc8\x77\xf4\xe4\x00\x00\x00\x00")
|
||||
self.spawn.expect_exact("TX End")
|
||||
self.spawn.expect_exact("Fragmented")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_fragment_no_input(self):
|
||||
self.spawn.sendline("compress init fe80::1")
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("fragment 1 25 5000 no-ack")
|
||||
self.spawn.expect_exact("No input buffer defined")
|
||||
|
||||
def test_fragment_no_rule(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="2001:db8::1",
|
||||
dst="2001:db8::2",
|
||||
hlim=64,
|
||||
)
|
||||
/ UDP(sport=0x1F40, dport=0x1F40)
|
||||
/ CoAP(paymark=b"\xff")
|
||||
/ (2 * "0123456789abcdef")
|
||||
)
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("compress init {}".format(pkt[IPv6].src))
|
||||
self.spawn.sendline("fragment init")
|
||||
self.spawn.sendline("fragment 4 25 5000 ack-always")
|
||||
self.spawn.expect_exact(
|
||||
"No fragmentation rule known for mode ack-always on device 4"
|
||||
)
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_reassemble_usage(self):
|
||||
self.spawn.sendline("reassemble")
|
||||
self.spawn.expect_exact("usage: reassemble <device id> <timeout>")
|
||||
self.spawn.sendline("reassemble foobar")
|
||||
self.spawn.expect_exact("usage: reassemble <device id> <timeout>")
|
||||
self.spawn.sendline("reassemble test 5000")
|
||||
self.spawn.expect_exact("usage: reassemble <device id> <timeout>")
|
||||
self.spawn.sendline("reassemble 1 foobar")
|
||||
self.spawn.expect_exact("usage: reassemble <device id> <timeout>")
|
||||
|
||||
def test_reassemble_no_input(self):
|
||||
self.spawn.sendline("reassemble init")
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.spawn.expect_exact("No input buffer defined")
|
||||
|
||||
def test_reassemble_no_frag(self):
|
||||
pkt = b"\x03\x30\x90"
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("reassemble init")
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.spawn.expect_exact("RX Unfragmented on dev 0x1")
|
||||
self.expect_od_dump_of(pkt)
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_reassemble_success_no_ack(self):
|
||||
self.input(
|
||||
b"\x15\x30\x00\x00\x00\x00\x06\x08\xa0\x7f\x40\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
self.spawn.sendline("reassemble init")
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.input_reset()
|
||||
self.input(
|
||||
b"\x15\x7f\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x87\xd0\x47\xd0\x80\x03\x21\x13"
|
||||
)
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.input_reset()
|
||||
self.input(b"\x15\x98\xa6\x43\xbf\xc8\x00\x00\x00\x00")
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.spawn.expect_exact("RX Reassembly complete on dev 0x1")
|
||||
self.expect_od_dump_of(
|
||||
bytes(
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
)
|
||||
free_timers = 0
|
||||
total_timers = 4
|
||||
while free_timers != total_timers:
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
free_timers = int(self.spawn.match.group(1))
|
||||
total_timers = int(self.spawn.match.group(2))
|
||||
|
||||
def test_reassemble_success_ack_always(self):
|
||||
self.input(
|
||||
b"\x17\x66\x00\x00\x00\x00\x00\xc1\x14\x0f\xe8\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
self.spawn.sendline("reassemble init")
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.input_reset()
|
||||
self.input(
|
||||
b"\x17\x51\xfe\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
b"\x1f\x41\x1f\x42\x00\x0c\x84"
|
||||
)
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.input_reset()
|
||||
self.input(b"\x17\x73\x14\xc8\x77\xf4\xe4\x00\x00\x00\x00")
|
||||
self.spawn.sendline("reassemble 1 100")
|
||||
self.spawn.expect_exact("Packet sent on dev 0x1")
|
||||
self.expect_od_dump_of(b"\x17\x40")
|
||||
self.expect_od_dump_of(
|
||||
IPv6(
|
||||
src="fe80::1",
|
||||
dst="fe80::2",
|
||||
)
|
||||
/ UDP(sport=0x1F41, dport=0x1F42)
|
||||
/ CoAP()
|
||||
)
|
||||
free_timers = 0
|
||||
total_timers = 4
|
||||
while free_timers != total_timers:
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
free_timers = int(self.spawn.match.group(1))
|
||||
total_timers = int(self.spawn.match.group(2))
|
||||
|
||||
|
||||
class TestFragmentation(PexpectTestCase, InputMixin):
|
||||
LOGFILE = sys.stdout if DEBUG_TESTS else None
|
||||
|
||||
def tearDown(self):
|
||||
self.input_reset()
|
||||
self.spawn.sendline("output reset")
|
||||
self.spawn.sendline("compress init ::")
|
||||
self.spawn.expect_exact("Successfully reset input buffer.")
|
||||
self.spawn.sendline("set_ack 0 11111111")
|
||||
self.spawn.expect_exact("ACKs set to 0 ms delay with bitmap 11111111")
|
||||
self.spawn.sendline("timer reset")
|
||||
self.spawn.expect_exact("Reset all timers")
|
||||
self.spawn.sendline("timer")
|
||||
self.spawn.expect(r"Free timers: (\d+) \(of (\d+)\)")
|
||||
self.assertEqual(int(self.spawn.match.group(1)), int(self.spawn.match.group(2)))
|
||||
|
||||
def test_compress_rule_2_up(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="2001:db8:1::1",
|
||||
dst="2001:db8:2::2",
|
||||
)
|
||||
/ UDP(sport=0x1389, dport=0x1388)
|
||||
/ CoAP(
|
||||
type="ACK",
|
||||
tkl=4,
|
||||
code="2.05 Content",
|
||||
msg_id=0x23BC,
|
||||
token=b"TOK!",
|
||||
paymark=b"\xff",
|
||||
)
|
||||
/ "payload"
|
||||
)
|
||||
self.spawn.sendline("compress init {}".format(pkt[IPv6].src))
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("compress up 1")
|
||||
self.spawn.expect_exact("Used rule 2/8 to compress to")
|
||||
exp_pkt = [
|
||||
0x02, # rule ID = 2
|
||||
# IP6_APPPRE mapping index = 1 (2b),
|
||||
# COAP_T = 2 [ACK] (2b)
|
||||
# COAP_MID LSB = 0xC (4b)
|
||||
(1 << 6) | (2 << 4) | (0xC),
|
||||
# Token
|
||||
ord("T"),
|
||||
ord("O"),
|
||||
ord("K"),
|
||||
ord("!"),
|
||||
# payload
|
||||
ord("p"),
|
||||
ord("a"),
|
||||
ord("y"),
|
||||
ord("l"),
|
||||
ord("o"),
|
||||
ord("a"),
|
||||
ord("d"),
|
||||
]
|
||||
self.expect_od_dump_of(exp_pkt)
|
||||
|
||||
def test_decompress_rule_2_up(self):
|
||||
exp_pkt = (
|
||||
IPv6(
|
||||
src="2001:db8:1::1",
|
||||
dst="2001:db8:2::2",
|
||||
)
|
||||
/ UDP(sport=0x1389, dport=0x1388)
|
||||
/ CoAP(
|
||||
type="ACK",
|
||||
tkl=4,
|
||||
code="2.05 Content",
|
||||
msg_id=0x23BC,
|
||||
token=b"TOK!",
|
||||
paymark=b"\xff",
|
||||
)
|
||||
/ "payload"
|
||||
)
|
||||
self.spawn.sendline("decompress init {}".format(exp_pkt[IPv6].src))
|
||||
pkt = [
|
||||
# Rule ID: 2
|
||||
0x02,
|
||||
# IP6_APPPRE mapping index = 1 (2b),
|
||||
# COAP_T = 2 [ACK] (2b) (libSCHC bug, should be 2)
|
||||
# COAP_MID LSB = 0xC (4b)
|
||||
(1 << 6) | (2 << 4) | (0xC),
|
||||
# Token
|
||||
ord("T"),
|
||||
ord("O"),
|
||||
ord("K"),
|
||||
ord("!"),
|
||||
# payload
|
||||
ord("p"),
|
||||
ord("a"),
|
||||
ord("y"),
|
||||
ord("l"),
|
||||
ord("o"),
|
||||
ord("a"),
|
||||
ord("d"),
|
||||
]
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("decompress up 1")
|
||||
self.spawn.expect_exact("Decompressed to")
|
||||
self.expect_od_dump_of(exp_pkt)
|
||||
|
||||
def test_compress_rule_2_down(self):
|
||||
pkt = (
|
||||
IPv6(
|
||||
src="2001:db8:2::2",
|
||||
dst="2001:db8:1::1",
|
||||
hlim=64,
|
||||
)
|
||||
/ UDP(sport=0x1388, dport=0x1389)
|
||||
/ CoAP(
|
||||
type="CON",
|
||||
tkl=4,
|
||||
code="GET",
|
||||
msg_id=0x23BC,
|
||||
token=b"TOK!",
|
||||
options=[("Uri-Path", "temp")],
|
||||
)
|
||||
)
|
||||
self.spawn.sendline("compress init {}".format(pkt[IPv6].src))
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("compress down 1")
|
||||
self.spawn.expect_exact("Used rule 2/8 to compress to")
|
||||
exp_pkt = [
|
||||
# Rule ID: 2
|
||||
0x02,
|
||||
# Hop-Limit: 64 (8b)
|
||||
64,
|
||||
# IP6_APPPRE mapping index = 1 (2b),
|
||||
# COAP_T = 0 [CON] (2b)
|
||||
# COAP_MID[0] 4 msb = 0x23 >> 4
|
||||
(1 << 6) | (0 << 4) | (0x23 >> 4),
|
||||
# COAP_MID[0] 4 lsb = (0x23 << 4) & 0xff
|
||||
# COAP_MID[1] 4 msb = 0xBC >> 4
|
||||
((0x23 << 4) & 0xFF) | (0xBC >> 4),
|
||||
# COAP_MID[1] 4 lsb = (0xBC << 4) & 0xff
|
||||
# Token[0] 4 msb = ord("T") >> 4
|
||||
((0xBC << 4) & 0xFF) | (ord("T") >> 4),
|
||||
# Token[0] 4 lsb = ord("T") >> 4
|
||||
# Token[1] 4 msb = (ord("O") << 4) & 0xff
|
||||
((ord("T") << 4) & 0xFF) | (ord("O") >> 4),
|
||||
# Token[1] 4 lsb = ord("O") >> 4
|
||||
# Token[2] 4 msb = (ord("K") << 4) & 0xff
|
||||
((ord("O") << 4) & 0xFF) | (ord("K") >> 4),
|
||||
# Token[2] 4 lsb = ord("K") >> 4
|
||||
# Token[3] 4 msb = (ord("!") << 4) & 0xff
|
||||
((ord("K") << 4) & 0xFF) | (ord("!") >> 4),
|
||||
# Token[2] 4 lsb = ord("K") >> 4
|
||||
# 4b padding
|
||||
((ord("!") << 4) & 0xFF),
|
||||
]
|
||||
self.expect_od_dump_of(exp_pkt)
|
||||
|
||||
def test_decompress_rule_2_down(self):
|
||||
exp_pkt = (
|
||||
IPv6(
|
||||
src="2001:db8:2::2",
|
||||
dst="2001:db8:1::1",
|
||||
hlim=64,
|
||||
)
|
||||
/ UDP(sport=0x1388, dport=0x1389)
|
||||
/ CoAP(
|
||||
type="CON",
|
||||
tkl=4,
|
||||
code="GET",
|
||||
msg_id=0x23BC,
|
||||
token=b"TOK!",
|
||||
options=[("Uri-Path", "temp")],
|
||||
)
|
||||
)
|
||||
self.spawn.sendline("decompress init {}".format(exp_pkt[IPv6].src))
|
||||
pkt = [
|
||||
# Rule ID: 2
|
||||
0x02,
|
||||
# Hop-Limit: 64 (8b)
|
||||
64,
|
||||
# IP6_APPPRE mapping index = 1 (2b),
|
||||
# COAP_T = 0 [CON] (2b)
|
||||
# COAP_MID[0] 4 msb = 0x23 >> 4
|
||||
(1 << 6) | (0 << 4) | (0x23 >> 4),
|
||||
# COAP_MID[0] 4 lsb = (0x23 << 4) & 0xff
|
||||
# COAP_MID[1] 4 msb = 0xBC >> 4
|
||||
((0x23 << 4) & 0xFF) | (0xBC >> 4),
|
||||
# COAP_MID[1] 4 lsb = (0xBC << 4) & 0xff
|
||||
# Token[0] 4 msb = ord("T") >> 4
|
||||
((0xBC << 4) & 0xFF) | (ord("T") >> 4),
|
||||
# Token[0] 4 lsb = ord("T") >> 4
|
||||
# Token[1] 4 msb = (ord("O") << 4) & 0xff
|
||||
((ord("T") << 4) & 0xFF) | (ord("O") >> 4),
|
||||
# Token[1] 4 lsb = ord("O") >> 4
|
||||
# Token[2] 4 msb = (ord("K") << 4) & 0xff
|
||||
((ord("O") << 4) & 0xFF) | (ord("K") >> 4),
|
||||
# Token[2] 4 lsb = ord("K") >> 4
|
||||
# Token[3] 4 msb = (ord("!") << 4) & 0xff
|
||||
((ord("K") << 4) & 0xFF) | (ord("!") >> 4),
|
||||
# Token[2] 4 lsb = ord("K") >> 4
|
||||
# 4b padding
|
||||
((ord("!") << 4) & 0xFF),
|
||||
]
|
||||
self.input(pkt)
|
||||
self.spawn.sendline("decompress down 1")
|
||||
self.spawn.expect_exact("Decompressed to")
|
||||
self.expect_od_dump_of(exp_pkt)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
load_contrib("coap")
|
||||
unittest.main(verbosity=2)
|
87
tests/pkg_libschc/unittests.c
Normal file
87
tests/pkg_libschc/unittests.c
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include "embUnit.h"
|
||||
|
||||
#include "bit_operations.h"
|
||||
|
||||
static void test_copy_bits__1bit_shift(void)
|
||||
{
|
||||
static const uint8_t src[] = { 0xE0, 0xBC, 0x7D, 0xFF, 0xFE, 0xCB, 0xF5, 0x50 };
|
||||
static const uint8_t exp_dst[] = {
|
||||
(0xE0 >> 1),
|
||||
((0xE0 << 7) & 0xFF) | 0xBC >> 1,
|
||||
((0xBC << 7) & 0xFF) | 0x7D >> 1,
|
||||
((0x7D << 7) & 0xFF) | 0xFF >> 1,
|
||||
((0xFF << 7) & 0xFF) | 0xFE >> 1,
|
||||
((0xFE << 7) & 0xFF) | 0xCB >> 1,
|
||||
((0xCB << 7) & 0xFF) | 0xF5 >> 1,
|
||||
((0xF5 << 7) & 0xFF) | 0x50 >> 1,
|
||||
((0x50 << 7) & 0xFF)
|
||||
};
|
||||
static uint8_t dst[sizeof(exp_dst)] = { 0 };
|
||||
|
||||
copy_bits(dst, 1, src, 0, sizeof(src) * 8);
|
||||
TEST_ASSERT_EQUAL_INT(sizeof(exp_dst), sizeof(dst));
|
||||
for (unsigned i = 0; i < sizeof(exp_dst); i++) {
|
||||
TEST_ASSERT_EQUAL_INT(exp_dst[i], dst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_copy_bits__2bit_shift(void)
|
||||
{
|
||||
static const uint8_t src[] = {
|
||||
0x80, 0x00, 0x94, 0x11, 0xB9, 0x14, 0x00, 0x02, 0x48, 0x32, 0x9A, 0x00
|
||||
};
|
||||
static const uint8_t exp_dst[] = {
|
||||
(0x80 >> 2),
|
||||
((0x80 << 6) & 0xFF) | 0x00 >> 2,
|
||||
((0x00 << 6) & 0xFF) | 0x94 >> 2,
|
||||
((0x94 << 6) & 0xFF) | 0x11 >> 2,
|
||||
((0x11 << 6) & 0xFF) | 0xB9 >> 2,
|
||||
((0xB9 << 6) & 0xFF) | 0x14 >> 2,
|
||||
((0x14 << 6) & 0xFF) | 0x00 >> 2,
|
||||
((0x00 << 6) & 0xFF) | 0x02 >> 2,
|
||||
((0x02 << 6) & 0xFF) | 0x48 >> 2,
|
||||
((0x48 << 6) & 0xFF) | 0x32 >> 2,
|
||||
((0x32 << 6) & 0xFF) | 0x9A >> 2,
|
||||
((0x9A << 6) & 0xFF) | 0x00 >> 2,
|
||||
((0x00 << 6) & 0xFF),
|
||||
};
|
||||
static uint8_t dst[sizeof(exp_dst)] = { 0 };
|
||||
|
||||
copy_bits(dst, 2, src, 0, sizeof(src) * 8);
|
||||
TEST_ASSERT_EQUAL_INT(sizeof(exp_dst), sizeof(dst));
|
||||
for (unsigned i = 0; i < sizeof(exp_dst); i++) {
|
||||
TEST_ASSERT_EQUAL_INT(exp_dst[i], dst[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int unittests(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
EMB_UNIT_TESTFIXTURES(fixtures) {
|
||||
new_TestFixture(test_copy_bits__1bit_shift),
|
||||
new_TestFixture(test_copy_bits__2bit_shift),
|
||||
};
|
||||
EMB_UNIT_TESTCALLER(libschc_tests, NULL, NULL, fixtures);
|
||||
TESTS_START();
|
||||
TESTS_RUN((Test *)&libschc_tests);
|
||||
TESTS_END();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user