diff --git a/Makefile.dep b/Makefile.dep index fd0b79337..e859a6e38 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -231,6 +231,10 @@ ifneq (,$(filter newlib,$(USEMODULE))) USEMODULE += uart_stdio endif +ifneq (,$(filter ipv6_addr,$(USEMODULE))) + USEMODULE += ipv4_addr +endif + ifneq (,$(filter posix,$(USEMODULE))) USEMODULE += timex USEMODULE += vtimer diff --git a/sys/Makefile b/sys/Makefile index 2a3048695..aceb6dbf5 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -16,6 +16,9 @@ endif ifneq (,$(filter oneway_malloc,$(USEMODULE))) DIRS += oneway-malloc endif +ifneq (,$(filter ipv4_addr,$(USEMODULE))) + DIRS += net/network_layer/ipv4/addr +endif ifneq (,$(filter ipv6_addr,$(USEMODULE))) DIRS += net/network_layer/ipv6/addr endif diff --git a/sys/include/net/ipv4.h b/sys/include/net/ipv4.h new file mode 100644 index 000000000..4632e484e --- /dev/null +++ b/sys/include/net/ipv4.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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_ipv4 IPv4 + * @ingroup net + * @brief IPv4 types and helper functions + * @{ + * + * @file + * @brief IPv4 type and helper function definitions + * + * @author Martine Lenders + */ +#ifndef IPV4_H_ +#define IPV4_H_ + +#include "net/ipv4/addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* IPV4_H_ */ +/** @} */ diff --git a/sys/include/net/ipv4/addr.h b/sys/include/net/ipv4/addr.h new file mode 100644 index 000000000..612228473 --- /dev/null +++ b/sys/include/net/ipv4/addr.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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_ipv4_addr IPv4 addresses + * @ingroup net_ipv4 + * @brief IPv4 address types and helper function + * @{ + * + * @file + * @brief IPv6 address type and helper functions definitions + * + * @author Martine Lenders + */ +#ifndef IPV4_ADDR_H_ +#define IPV4_ADDR_H_ + +#include +#include + +#include "byteorder.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum length of an IPv4 address as string. + */ +#define IPV4_ADDR_MAX_STR_LEN (sizeof("255.255.255.255")) + +/** + * @brief Data type to represent an IPv4 address. + */ +typedef union { + uint8_t u8[4]; /**< as 4 8-bit unsigned integer */ + network_uint32_t u32; /**< as 32-bit unsigned integer */ +} ipv4_addr_t; + +/** + * @brief Checks if two IPv4 addresses are equal. + * + * @param[in] a An IPv4 address. + * @param[in] b Another IPv4 address. + * + * @return true, if @p a and @p b are equal + * @return false, otherwise. + */ +static inline bool ipv4_addr_equal(ipv4_addr_t *a, ipv4_addr_t *b) +{ + return (a->u32.u32 == b->u32.u32); +} + +/** + * @brief Converts an IPv4 address to its string representation + * + * @param[out] result The resulting string representation of at least + * @ref IPV4_ADDR_MAX_STR_LEN. + * @param[in] addr An IPv4 address + * @param[in] result_len Length of @p result + * + * @return @p result, on success + * @return NULL, if @p result_len was lesser than IPV4_ADDR_MAX_STR_LEN + * @return NULL, if @p result or @p addr was NULL + */ +char *ipv4_addr_to_str(char *result, const ipv4_addr_t *addr, uint8_t result_len); + +/** + * @brief Converts an IPv4 address string representation to a byte-represented + * IPv4 address + * + * @param[in] result The resulting byte representation + * @param[in] addr An IPv4 address string representation + * + * @return @p result, on success + * @return NULL, if @p addr was malformed + * @return NULL, if @p result or @p addr was NULL + */ +ipv4_addr_t *ipv4_addr_from_str(ipv4_addr_t *result, const char *addr); + +#ifdef __cplusplus +} +#endif + +#endif /* IPV4_ADDR_H_ */ +/** @} */ diff --git a/sys/net/network_layer/ipv4/addr/Makefile b/sys/net/network_layer/ipv4/addr/Makefile new file mode 100644 index 000000000..58eab09ec --- /dev/null +++ b/sys/net/network_layer/ipv4/addr/Makefile @@ -0,0 +1,3 @@ +MODULE = ipv4_addr + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ipv4/addr/ipv4_addr_from_str.c b/sys/net/network_layer/ipv4/addr/ipv4_addr_from_str.c new file mode 100644 index 000000000..8c10c8508 --- /dev/null +++ b/sys/net/network_layer/ipv4/addr/ipv4_addr_from_str.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 + */ + +#include +#include + +#include "net/ipv4/addr.h" + +#define DEC "0123456789" + +/* based on inet_pton4() by Paul Vixie */ +ipv4_addr_t *ipv4_addr_from_str(ipv4_addr_t *result, const char *addr) +{ + uint8_t saw_digit, octets, ch; + uint8_t tmp[sizeof(ipv4_addr_t)], *tp; + + if ((result == NULL) || (addr == NULL)) { + return NULL; + } + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + + while ((ch = *addr++) != '\0') { + const char *pch; + + if ((pch = strchr(DEC, ch)) != NULL) { + uint16_t new = *tp * 10 + (uint16_t)(pch - DEC); + + if (new > 255) { + return NULL; + } + + *tp = new; + + if (!saw_digit) { + if (++octets > 4) { + return NULL; + } + + saw_digit = 1; + } + } + else if (ch == '.' && saw_digit) { + if (octets == 4) { + return NULL; + } + + *++tp = 0; + saw_digit = 0; + } + else { + return NULL; + } + } + + if (octets < 4) { + return NULL; + } + + memcpy(result, tmp, sizeof(ipv4_addr_t)); + return result; +} + + +/** @} */ diff --git a/sys/net/network_layer/ipv4/addr/ipv4_addr_to_str.c b/sys/net/network_layer/ipv4/addr/ipv4_addr_to_str.c new file mode 100644 index 000000000..438248a43 --- /dev/null +++ b/sys/net/network_layer/ipv4/addr/ipv4_addr_to_str.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 + */ + +#include + +#include "net/ipv4/addr.h" + +/* based on inet_ntop4() by Paul Vixie */ +char *ipv4_addr_to_str(char *result, const ipv4_addr_t *addr, uint8_t result_len) +{ + uint8_t n = 0; + char *next = result; + + if ((result == NULL) || (addr == NULL) || (result_len < IPV4_ADDR_MAX_STR_LEN)) { + return NULL; + } + + do { + uint8_t u = addr->u8[n]; + + if (u > 99) { + *next++ = '0' + u / 100; + u %= 100; + *next++ = '0' + u / 10; + u %= 10; + } + else if (u > 9) { + *next++ = '0' + u / 10; + u %= 10; + } + + *next++ = '0' + u; + *next++ = '.'; + n++; + } while (n < 4); + + *--next = '\0'; + return result; +} + +/** @} */ diff --git a/sys/net/network_layer/ipv6/addr/ipv6_addr_from_str.c b/sys/net/network_layer/ipv6/addr/ipv6_addr_from_str.c index 2ea89b1d6..8d62e72ca 100644 --- a/sys/net/network_layer/ipv6/addr/ipv6_addr_from_str.c +++ b/sys/net/network_layer/ipv6/addr/ipv6_addr_from_str.c @@ -31,65 +31,12 @@ #include #include "byteorder.h" +#include "net/ipv4/addr.h" #include "net/ipv6/addr.h" -#define DEC "0123456789" #define HEX_L "0123456789abcdef" #define HEX_U "0123456789ABCDEF" -/* XXX: move this to IPv4 when we have it */ -/* based on inet_pton4() by Paul Vixie */ -static network_uint32_t *ipv4_addr_from_str(network_uint32_t *result, - const char *addr) -{ - uint8_t saw_digit, octets, ch; - uint8_t tmp[sizeof(network_uint32_t)], *tp; - - saw_digit = 0; - octets = 0; - *(tp = tmp) = 0; - - while ((ch = *addr++) != '\0') { - const char *pch; - - if ((pch = strchr(DEC, ch)) != NULL) { - uint16_t new = *tp * 10 + (uint16_t)(pch - DEC); - - if (new > 255) { - return NULL; - } - - *tp = new; - - if (!saw_digit) { - if (++octets > 4) { - return NULL; - } - - saw_digit = 1; - } - } - else if (ch == '.' && saw_digit) { - if (octets == 4) { - return NULL; - } - - *++tp = 0; - saw_digit = 0; - } - else { - return NULL; - } - } - - if (octets < 4) { - return NULL; - } - - memcpy(result, tmp, sizeof(network_uint32_t)); - return result; -} - /* based on inet_pton6() by Paul Vixie */ ipv6_addr_t *ipv6_addr_from_str(ipv6_addr_t *result, const char *addr) { @@ -157,9 +104,9 @@ ipv6_addr_t *ipv6_addr_from_str(ipv6_addr_t *result, const char *addr) } if (ch == '.' && (i <= sizeof(ipv6_addr_t)) && - ipv4_addr_from_str((network_uint32_t *)(&(result->u8[i])), + ipv4_addr_from_str((ipv4_addr_t *)(&(result->u8[i])), curtok) != NULL) { - i += sizeof(network_uint32_t); + i += sizeof(ipv4_addr_t); saw_xdigit = 0; break; /* '\0' was seen by ipv4_addr_from_str(). */ } diff --git a/sys/net/network_layer/ipv6/addr/ipv6_addr_to_str.c b/sys/net/network_layer/ipv6/addr/ipv6_addr_to_str.c index d10c9381e..b089eb802 100644 --- a/sys/net/network_layer/ipv6/addr/ipv6_addr_to_str.c +++ b/sys/net/network_layer/ipv6/addr/ipv6_addr_to_str.c @@ -30,50 +30,14 @@ #include #include "byteorder.h" +#include "net/ipv4/addr.h" #include "net/ipv6/addr.h" /* Length of an IPv6 address in 16-bit words */ #define IPV6_ADDR_WORD_LEN (sizeof(ipv6_addr_t) / sizeof(uint16_t)) -#define IPV4_ADDR_MIN_STR_LEN (sizeof("255.255.255.255")) - #define HEX_L "0123456789abcdef" -/* XXX: move this to IPv4 when we have it */ -/* based on inet_ntop4() by Paul Vixie */ -static char *ipv4_addr_to_str(char *result, const network_uint32_t *addr, - uint8_t result_len) -{ - uint8_t n = 0; - char *next = result; - - if (result_len < IPV4_ADDR_MIN_STR_LEN) { - return NULL; - } - - do { - uint8_t u = addr->u8[n]; - - if (u > 99) { - *next++ = '0' + u / 100; - u %= 100; - *next++ = '0' + u / 10; - u %= 10; - } - else if (u > 9) { - *next++ = '0' + u / 10; - u %= 10; - } - - *next++ = '0' + u; - *next++ = '.'; - n++; - } while (n < 4); - - *--next = '\0'; - return result; -} - /* based on inet_ntop6() by Paul Vixie */ char *ipv6_addr_to_str(char *result, const ipv6_addr_t *addr, uint8_t result_len) { @@ -142,7 +106,8 @@ char *ipv6_addr_to_str(char *result, const ipv6_addr_t *addr, uint8_t result_len /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && addr->u16[5].u16 == 0xffff))) { - if (!ipv4_addr_to_str(tp, &addr->u32[3], sizeof(tmp) - (tp - tmp))) { + if (!ipv4_addr_to_str(tp, (const ipv4_addr_t *)&addr->u32[3], + sizeof(tmp) - (tp - tmp))) { return (NULL); } diff --git a/tests/unittests/tests-ipv4_addr/Makefile b/tests/unittests/tests-ipv4_addr/Makefile new file mode 100644 index 000000000..48422e909 --- /dev/null +++ b/tests/unittests/tests-ipv4_addr/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-ipv4_addr/Makefile.include b/tests/unittests/tests-ipv4_addr/Makefile.include new file mode 100644 index 000000000..7471d7934 --- /dev/null +++ b/tests/unittests/tests-ipv4_addr/Makefile.include @@ -0,0 +1 @@ +USEMODULE += ipv4_addr diff --git a/tests/unittests/tests-ipv4_addr/tests-ipv4_addr.c b/tests/unittests/tests-ipv4_addr/tests-ipv4_addr.c new file mode 100644 index 000000000..bab4918e8 --- /dev/null +++ b/tests/unittests/tests-ipv4_addr/tests-ipv4_addr.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 + */ +#include +#include + +#include "embUnit/embUnit.h" + +#include "byteorder.h" +#include "net/ipv4/addr.h" + +#include "tests-ipv4_addr.h" + +static void test_ipv4_addr_equal__unequal(void) +{ + ipv4_addr_t a = { { 1, 1, 1, 0 } }; + ipv4_addr_t b = { { 1, 1, 1, 1 } }; + + TEST_ASSERT(!ipv4_addr_equal(&a, &b)); +} + +static void test_ipv4_addr_equal__equal(void) +{ + ipv4_addr_t a = { { 1, 1, 1, 1 } }; + ipv4_addr_t b = { { 1, 1, 1, 1 } }; + + TEST_ASSERT(ipv4_addr_equal(&a, &b)); +} + +static void test_ipv4_addr_to_str__string_too_short(void) +{ + ipv4_addr_t a = { { 1, 1, 1, 1 } }; + char result[1]; + + TEST_ASSERT_NULL(ipv4_addr_to_str(result, &a, sizeof(result))); +} + +static void test_ipv4_addr_to_str__addr_NULL(void) +{ + char result[IPV4_ADDR_MAX_STR_LEN]; + + TEST_ASSERT_NULL(ipv4_addr_to_str(result, NULL, sizeof(result))); +} + +static void test_ipv4_addr_to_str__result_NULL(void) +{ + ipv4_addr_t a; + + TEST_ASSERT_NULL(ipv4_addr_to_str(NULL, &a, IPV4_ADDR_MAX_STR_LEN)); +} + +static void test_ipv4_addr_to_str__success(void) +{ + ipv4_addr_t a = { { 1, 1, 1, 1 } }; + char result[IPV4_ADDR_MAX_STR_LEN]; + + TEST_ASSERT_EQUAL_STRING("1.1.1.1", ipv4_addr_to_str(result, &a, sizeof(result))); +} + +static void test_ipv4_addr_to_str__success2(void) +{ + ipv4_addr_t a = { { 0, 1, 12, 123 } }; + char result[IPV4_ADDR_MAX_STR_LEN]; + + TEST_ASSERT_EQUAL_STRING("0.1.12.123", ipv4_addr_to_str(result, &a, sizeof(result))); +} + +static void test_ipv4_addr_from_str__dot_start(void) +{ + ipv4_addr_t result; + + TEST_ASSERT_NULL(ipv4_addr_from_str(&result, ".1.12.123")); +} + +static void test_ipv4_addr_from_str__double_dot(void) +{ + ipv4_addr_t result; + + TEST_ASSERT_NULL(ipv4_addr_from_str(&result, "0..12.123")); +} + +static void test_ipv4_addr_from_str__string_too_long(void) +{ + ipv4_addr_t result; + + TEST_ASSERT_NULL(ipv4_addr_from_str(&result, "255.255.255.255.255")); +} + +static void test_ipv4_addr_from_str__illegal_chars(void) +{ + ipv4_addr_t result; + + TEST_ASSERT_NULL(ipv4_addr_from_str(&result, "0.::ab-)")); +} + +static void test_ipv4_addr_from_str__addr_NULL(void) +{ + ipv4_addr_t result; + + TEST_ASSERT_NULL(ipv4_addr_from_str(&result, NULL)); +} + +static void test_ipv4_addr_from_str__result_NULL(void) +{ + TEST_ASSERT_NULL(ipv4_addr_from_str(NULL, "::")); +} + +static void test_ipv4_addr_from_str__success(void) +{ + ipv4_addr_t a = { { 1, 1, 1, 1 } }; + ipv4_addr_t result; + + TEST_ASSERT_NOT_NULL(ipv4_addr_from_str(&result, "1.1.1.1")); + TEST_ASSERT(ipv4_addr_equal(&a, &result)); +} + +static void test_ipv4_addr_from_str__success2(void) +{ + ipv4_addr_t a = { { 0, 1, 12, 123 } }, result; + + TEST_ASSERT_NOT_NULL(ipv4_addr_from_str(&result, "0.1.12.123")); + TEST_ASSERT(ipv4_addr_equal(&a, &result)); +} + +Test *tests_ipv4_addr_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_ipv4_addr_equal__unequal), + new_TestFixture(test_ipv4_addr_equal__equal), + new_TestFixture(test_ipv4_addr_to_str__string_too_short), + new_TestFixture(test_ipv4_addr_to_str__addr_NULL), + new_TestFixture(test_ipv4_addr_to_str__result_NULL), + new_TestFixture(test_ipv4_addr_to_str__success), + new_TestFixture(test_ipv4_addr_to_str__success2), + new_TestFixture(test_ipv4_addr_from_str__dot_start), + new_TestFixture(test_ipv4_addr_from_str__double_dot), + new_TestFixture(test_ipv4_addr_from_str__string_too_long), + new_TestFixture(test_ipv4_addr_from_str__illegal_chars), + new_TestFixture(test_ipv4_addr_from_str__addr_NULL), + new_TestFixture(test_ipv4_addr_from_str__result_NULL), + new_TestFixture(test_ipv4_addr_from_str__success), + new_TestFixture(test_ipv4_addr_from_str__success2), + }; + + EMB_UNIT_TESTCALLER(ipv4_addr_tests, NULL, NULL, fixtures); + + return (Test *)&ipv4_addr_tests; +} + +void tests_ipv4_addr(void) +{ + TESTS_RUN(tests_ipv4_addr_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-ipv4_addr/tests-ipv4_addr.h b/tests/unittests/tests-ipv4_addr/tests-ipv4_addr.h new file mode 100644 index 000000000..df743ee96 --- /dev/null +++ b/tests/unittests/tests-ipv4_addr/tests-ipv4_addr.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the ``ipv4_addr`` module + * + * @author Martine Lenders + */ +#ifndef TESTS_IPV4_ADDR_H_ +#define TESTS_IPV4_ADDR_H_ + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_ipv4_addr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_IPV4_ADDR_H_ */ +/** @} */