diff --git a/sys/include/net/ipv6/ext.h b/sys/include/net/ipv6/ext.h index 7f586b63f2..2771e6132e 100644 --- a/sys/include/net/ipv6/ext.h +++ b/sys/include/net/ipv6/ext.h @@ -22,6 +22,7 @@ #include +#include "net/ipv6/ext/frag.h" #include "net/ipv6/ext/rh.h" #ifdef __cplusplus diff --git a/sys/include/net/ipv6/ext/frag.h b/sys/include/net/ipv6/ext/frag.h new file mode 100644 index 0000000000..9cc033f761 --- /dev/null +++ b/sys/include/net/ipv6/ext/frag.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 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 net_ipv6_ext_frag IPv6 fragmentation extension + * @ingroup net_ipv6_ext + * @brief Definitions for IPv6 fragmentation extension + * @see [RFC 8200, section 4.5](https://tools.ietf.org/html/rfc8200#section-4.5) + * @{ + * + * @file + * @brief Fragmentation extension definitions + * + * @author Martine Lenders + */ +#ifndef NET_IPV6_EXT_FRAG_H +#define NET_IPV6_EXT_FRAG_H + +#include +#include + +#include "byteorder.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IPV6_EXT_FRAG_OFFSET_MASK (0xFFF8) /**< Mask for the offset */ +#define IPV6_EXT_FRAG_M (0x0001) /**< M flag */ + +/** + * @brief Fragment header definition + */ +typedef struct __attribute__((packed)) { + uint8_t nh; /**< next header */ + uint8_t resv; /**< reserved */ + /** + * @brief 11-bit fragment offset and flags + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Fragment Offset |Res|M| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + network_uint16_t offset_flags; + network_uint32_t id; /**< identification */ +} ipv6_ext_frag_t; + +/** + * @brief Get offset of fragment in bytes + * + * @param[in] frag A fragment header + * + * @return Offset of fragment in bytes. + */ +static inline unsigned ipv6_ext_frag_get_offset(const ipv6_ext_frag_t *frag) +{ + /* The offset is left-shifted by 3 bytes in the header * (equivalent to + * multiplication with 8), and the offset is in units 8 bytes + * so no shifting or multiplying necessary to get offset in bytes */ + return (byteorder_ntohs(frag->offset_flags) & IPV6_EXT_FRAG_OFFSET_MASK); +} + +/** + * @brief Checks if more fragments are coming after the given fragment + * + * @param[in] frag A fragment header + * + * @return true, when more fragments are coming after the given fragment. + * @return false, when the given fragment is the last. + */ +static inline bool ipv6_ext_frag_more(const ipv6_ext_frag_t *frag) +{ + return (byteorder_ntohs(frag->offset_flags) & IPV6_EXT_FRAG_M); +} + +/** + * @brief Sets the offset field of a fragment header + * + * @note Must be called before @ref ipv6_ext_frag_set_more() + * + * @param[in,out] frag A fragment header + * @param[in] offset The offset of the fragment in bytes. + * Is assumed to be a multiple of 8. + * Is assumed to be lesser or equal to 65528. + */ +static inline void ipv6_ext_frag_set_offset(ipv6_ext_frag_t *frag, + unsigned offset) +{ + /* The offset is left-shifted by 3 bytes in the header * (equivalent to + * multiplication with 8), and the offset is a multiple of 8 bytes + * so no shifting or division necessary to set offset in units of 8 bytes */ + frag->offset_flags = byteorder_htons(offset & IPV6_EXT_FRAG_OFFSET_MASK); +} + +/** + * @brief Sets the M flag of a fragment header + * + * @note Must be called after @ref ipv6_ext_frag_set_offset() + * + * @param[in,out] frag A fragment header + */ +static inline void ipv6_ext_frag_set_more(ipv6_ext_frag_t *frag) +{ + frag->offset_flags.u8[1] |= IPV6_EXT_FRAG_M; +} + +#ifdef __cplusplus +} +#endif + +#endif /* NET_IPV6_EXT_FRAG_H */ +/** @} */ diff --git a/tests/unittests/tests-ipv6_hdr/tests-ipv6_hdr.c b/tests/unittests/tests-ipv6_hdr/tests-ipv6_hdr.c index 05dbccb739..b0e1cd0f2c 100644 --- a/tests/unittests/tests-ipv6_hdr/tests-ipv6_hdr.c +++ b/tests/unittests/tests-ipv6_hdr/tests-ipv6_hdr.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Martine Lenders + * 2019 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 @@ -18,6 +19,7 @@ #include "net/ipv6/addr.h" #include "net/ipv6/hdr.h" +#include "net/ipv6/ext/frag.h" #include "net/gnrc/pktbuf.h" #include "net/protnum.h" #include "net/inet_csum.h" @@ -297,7 +299,58 @@ static void test_ipv6_hdr_inet_csum__initial_sum_0(void) TEST_ASSERT_EQUAL_INT(0xab32, res); } -Test *tests_ipv6_hdr_tests(void) +static void test_ipv6_ext_frag_get(void) +{ + static const uint8_t fix_data1[] = { + PROTNUM_SNP, 0x00, 0x12, 0x29, 0xAB, 0x4A, 0x2D, 0xF3 + }; + static const uint8_t fix_data2[] = { + PROTNUM_WB_EXPAK, 0x00, 0x1D, 0x78, 0xAE, 0x49, 0x01, 0x9F + }; + const ipv6_ext_frag_t *frag_hdr; + + frag_hdr = (ipv6_ext_frag_t *)fix_data1; + TEST_ASSERT_EQUAL_INT(PROTNUM_SNP, frag_hdr->nh); + TEST_ASSERT_EQUAL_INT(0U, frag_hdr->resv); + TEST_ASSERT_EQUAL_INT(4648, ipv6_ext_frag_get_offset(frag_hdr)); + TEST_ASSERT(ipv6_ext_frag_more(frag_hdr)); + TEST_ASSERT_EQUAL_INT(0xAB4A2DF3, byteorder_ntohl(frag_hdr->id)); + + frag_hdr = (ipv6_ext_frag_t *)fix_data2; + TEST_ASSERT_EQUAL_INT(PROTNUM_WB_EXPAK, frag_hdr->nh); + TEST_ASSERT_EQUAL_INT(0U, frag_hdr->resv); + TEST_ASSERT_EQUAL_INT(7544, ipv6_ext_frag_get_offset(frag_hdr)); + TEST_ASSERT(!ipv6_ext_frag_more(frag_hdr)); + TEST_ASSERT_EQUAL_INT(0xAE49019F, byteorder_ntohl(frag_hdr->id)); +} + +static void test_ipv6_ext_frag_set(void) +{ + ipv6_ext_frag_t frag_hdr = { .nh = PROTNUM_TPPLUSPLUS, + .id = { .u8 = { 0xA7, 0xE8, 0x3D, 0x35 } } }; + + ipv6_ext_frag_set_offset(&frag_hdr, 0); + TEST_ASSERT_EQUAL_INT(PROTNUM_TPPLUSPLUS, frag_hdr.nh); + TEST_ASSERT_EQUAL_INT(0U, frag_hdr.resv); + TEST_ASSERT_EQUAL_INT(0, ipv6_ext_frag_get_offset(&frag_hdr)); + TEST_ASSERT(!ipv6_ext_frag_more(&frag_hdr)); + TEST_ASSERT_EQUAL_INT(0xA7E83D35, byteorder_ntohl(frag_hdr.id)); + ipv6_ext_frag_set_more(&frag_hdr); + TEST_ASSERT_EQUAL_INT(PROTNUM_TPPLUSPLUS, frag_hdr.nh); + TEST_ASSERT_EQUAL_INT(0U, frag_hdr.resv); + TEST_ASSERT_EQUAL_INT(0, ipv6_ext_frag_get_offset(&frag_hdr)); + TEST_ASSERT(ipv6_ext_frag_more(&frag_hdr)); + TEST_ASSERT_EQUAL_INT(0xA7E83D35, byteorder_ntohl(frag_hdr.id)); + + ipv6_ext_frag_set_offset(&frag_hdr, 504); + TEST_ASSERT_EQUAL_INT(PROTNUM_TPPLUSPLUS, frag_hdr.nh); + TEST_ASSERT_EQUAL_INT(0U, frag_hdr.resv); + TEST_ASSERT_EQUAL_INT(504, ipv6_ext_frag_get_offset(&frag_hdr)); + TEST_ASSERT(!ipv6_ext_frag_more(&frag_hdr)); + TEST_ASSERT_EQUAL_INT(0xA7E83D35, byteorder_ntohl(frag_hdr.id)); +} + +static Test *tests_ipv6_hdr_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { new_TestFixture(test_ipv6_hdr_set_version), @@ -321,8 +374,21 @@ Test *tests_ipv6_hdr_tests(void) return (Test *)&ipv6_hdr_tests; } +static Test *tests_ipv6_ext_frag_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_ipv6_ext_frag_get), + new_TestFixture(test_ipv6_ext_frag_set), + }; + + EMB_UNIT_TESTCALLER(ipv6_ext_frag_tests, NULL, NULL, fixtures); + + return (Test *)&ipv6_ext_frag_tests; +} + void tests_ipv6_hdr(void) { TESTS_RUN(tests_ipv6_hdr_tests()); + TESTS_RUN(tests_ipv6_ext_frag_tests()); } /** @} */