From beeca503d986a3f217463b3f7be6360bf044943f Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 3 Mar 2015 22:20:21 +0100 Subject: [PATCH] ng_ipv6: initial import --- sys/Makefile | 3 + sys/include/net/ng_ipv6.h | 273 ++++++++- sys/include/net/ng_ipv6/hdr.h | 264 +++++++++ sys/include/net/ng_netconf.h | 3 + sys/net/network_layer/ng_ipv6/Makefile | 1 + sys/net/network_layer/ng_ipv6/ng_ipv6.c | 704 ++++++++++++++++++++++++ 6 files changed, 1247 insertions(+), 1 deletion(-) create mode 100644 sys/include/net/ng_ipv6/hdr.h create mode 100644 sys/net/network_layer/ng_ipv6/Makefile create mode 100644 sys/net/network_layer/ng_ipv6/ng_ipv6.c diff --git a/sys/Makefile b/sys/Makefile index 4d81ae0032e5..762d15365c6d 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -62,6 +62,9 @@ endif ifneq (,$(filter oneway_malloc,$(USEMODULE))) DIRS += oneway-malloc endif +ifneq (,$(filter ng_ipv6,$(USEMODULE))) + DIRS += net/network_layer/ng_ipv6 +endif ifneq (,$(filter ng_ipv6_addr,$(USEMODULE))) DIRS += net/network_layer/ng_ipv6/addr endif diff --git a/sys/include/net/ng_ipv6.h b/sys/include/net/ng_ipv6.h index 0b0086918f18..055ce568d0bf 100644 --- a/sys/include/net/ng_ipv6.h +++ b/sys/include/net/ng_ipv6.h @@ -10,6 +10,138 @@ * @defgroup net_ng_ipv6 IPv6 * @ingroup net * @brief New IPv6 implementation + * + * The IPv6 control thread understands messages of type + * + * * @ref NG_NETAPI_MSG_TYPE_RCV, + * * @ref NG_NETAPI_MSG_TYPE_SND, + * * @ref NG_NETAPI_MSG_TYPE_GET, and + * * @ref NG_NETAPI_MSG_TYPE_SET + * + * For @ref NG_NETAPI_MSG_TYPE_GET the following values are supported for + * ng_netapi_opt_t::opt + * + * * @ref NETCONF_OPT_ADDRESS : Get IPv6 addresses + * - ng_netapi_opt_t::context: + * + **expected type:** kernel_pid_t + * + **description**: identifies an interface (via its PID) of which + * you want the IPv6 addresses from. Can be @ref KERNEL_PID_UNDEF + * for all interfaces. + * - ng_netapi_opt_t::data: + * + **expected type:** ng_ipv6_addr_t* + * + **precondition:** unequal NULL + * + **description:** Will be set to an array of all IPv6 addresses + * registered to the interface identified by ng_netapi_opt_t::context. + * - ng_netapi_opt_t::data_len: + * + **precondition:** greater than or equal to `sizeof(ng_ipv6_addr_t)` + * + **description:** Will be set to `sizeof(ng_ipv6_addr_t) * array_size`. + * If it is shorter than required, the data will be truncated to the + * last fitting IPv6 address. + * - @ref NG_NETAPI_MSG_TYPE_ACK values: + * + 0 on success + * + -EFAULT, if ng_netapi_opt_t::data was NULL + * + -ENOENT, if ng_netapi_opt_t::context was unequal @ref KERNEL_PID_UNDEF + * and interface specified by it does not exist + * + -EOVERFLOW, if ng_netapi_opt_t::data_len was lesser than + * `sizeof(ng_ipv6_addr_t)` + * * @ref NETCONF_OPT_ADDR_LEN : Get IPv6 address length + * - ng_netapi_opt_t::context: + * + **ignored** + * - ng_netapi_opt_t::data: + * + **expected type:** uint16_t + * + **precondition:** unequal NULL + * + **description:** Will always be set to `sizeof(ng_ipv6_addr_t)` + * - ng_netapi_opt_t::data_len: + * + **precondition:** equal to `sizeof(uint16_t)` + * - @ref NG_NETAPI_MSG_TYPE_ACK values: + * + 0 on success + * + -EFAULT, if ng_netapi_opt_t::data was NULL + * + -EOVERFLOW, if ng_netapi_opt_t::data_len was unequal to + * `sizeof(uint16_t)` + * * @ref NETCONF_OPT_MIN_PACKET_SIZE : Get MTU + * - ng_netapi_opt_t::context: + * + **expected type:** kernel_pid_t + * + **description**: identifies an interface (via its PID) of which + * you want the MTU from. Can be @ref KERNEL_PID_UNDEF for the + * minimum MTU of all interfaces. + * - ng_netapi_opt_t::data: + * + **expected type:** uint16_t + * + **precondition:** unequal NULL + * + **description:** Will be set to the MTU set to the interface + * identified by ng_netapi_opt_t::context. + * - ng_netapi_opt_t::data_len: + * + **precondition:** equal to `sizeof(uint16_t)` + * - @ref NG_NETAPI_MSG_TYPE_ACK values: + * + 0 on success + * + -EFAULT, if ng_netapi_opt_t::data was NULL + * + -ENOENT, if ng_netapi_opt_t::context was unequal @ref KERNEL_PID_UNDEF + * and interface specified by it does not exist + * + -EOVERFLOW, if ng_netapi_opt_t::data_len was unequal to + * `sizeof(uint16_t)` + * * @ref NETCONF_OPT_PROTO : Get the protocol of this layer. + * - ng_netapi_opt_t::context: + * + **ignored**: + * - ng_netapi_opt_t::data: + * + **expected type:** ng_nettype_t + * + **precondition:** unequal NULL + * + **description:** Will always be set to `NG_NETTYPE_IPV6` + * - ng_netapi_opt_t::data_len: + * + **precondition:** equal to `sizeof(ng_nettype_t)` + * - @ref NG_NETAPI_MSG_TYPE_ACK values: + * + 0 on success + * + -EFAULT, if ng_netapi_opt_t::data was NULL + * + -EOVERFLOW, if ng_netapi_opt_t::data_len was unequal to + * `sizeof(ng_nettype_t)` + * + * For @ref NG_NETAPI_MSG_TYPE_SET the following values are supported for + * ng_netapi_opt_t::opt + * + * * @ref NETCONF_OPT_ADDRESS : Add or remove IPv6 addresses + * - ng_netapi_opt_t::context: + * + **expected type:** kernel_pid_t + * + **precondition**: unequal @ref KERNEL_PID_UNDEF + * + **description**: identifies an interface (via its PID) of which + * you want to add the IPv6 address to or remove it from. + * - ng_netapi_opt_t::data: + * + **expected type:** ng_ipv6_addr_t + * + **precondition:** unequal NULL + * + **description:** An IPv6 address. If the address does not exist + * on the interface it will be added, if it already exists it will + * be removed. + * - ng_netapi_opt_t::data_len: + * + **precondition:** equal to `sizeof(ng_ipv6_addr_t)` + * - @ref NG_NETAPI_MSG_TYPE_ACK values: + * + 0 on success + * + -EFAULT, if ng_netapi_opt_t::data was NULL + * + -EINVAL, if ng_netapi_opt_t::data was the unspecified address + * (all zero) + * + -ENOENT, if ng_netapi_opt_t::context was @ref KERNEL_PID_UNDEF + * or interface specified by it does not exist + * + -ENOMEM, if there is no space left to store the address in + * ng_netapi_opt_t::data. + * + -EOVERFLOW, if ng_netapi_opt_t::data_len was unequal to + * `sizeof(ng_ipv6_addr_t)` + * * @ref NETCONF_OPT_MIN_PACKET_SIZE : Set MTU + * - ng_netapi_opt_t::context: + * + **expected type:** kernel_pid_t + * + **description**: identifies an interface (via its PID) of which + * you want set the MTU for. + * - ng_netapi_opt_t::data: + * + **expected type:** uint16_t + * + **precondition:** unequal NULL + * + **description:** The MTU for the interface identified by + * ng_netapi_opt_t::context. + * - ng_netapi_opt_t::data_len: + * + **precondition:** equal to `sizeof(uint16_t)` + * - @ref NG_NETAPI_MSG_TYPE_ACK values: + * + 0 on success + * + -EFAULT, if ng_netapi_opt_t::data was NULL + * + -ENOENT, if ng_netapi_opt_t::context was @ref KERNEL_PID_UNDEF + * or interface specified by it does not exist + * + -EOVERFLOW, if ng_netapi_opt_t::data_len was unequal to + * + -EOVERFLOW, if ng_netapi_opt_t::data_len was unequal to + * `sizeof(uint16_t)` + * * @{ * * @file @@ -22,13 +154,38 @@ #ifndef NG_IPV6_H_ #define NG_IPV6_H_ +#include "kernel_types.h" +#include "net/ng_pkt.h" +#include "net/ng_netapi.h" + #include "net/ng_ipv6/addr.h" -#include "net/ng_ipv6/netif.h" +#include "net/ng_ipv6/hdr.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Default stack size to use for the IPv6 thread + */ +#ifndef NG_IPV6_STACK_SIZE +#define NG_IPV6_STACK_SIZE (KERNEL_CONF_STACKSIZE_DEFAULT) +#endif + +/** + * @brief Default name for the IPv6 thread + */ +#ifndef NG_IPV6_THREAD_NAME +#define NG_IPV6_THREAD_NAME "IPv6" +#endif + +/** + * @brief Default message queue size to use for the IPv6 thread. + */ +#ifndef NG_IPV6_MSG_QUEUE_SIZE +#define NG_IPV6_MSG_QUEUE_SIZE (8U) +#endif + /** * @brief Default maximum transition unit * @@ -38,6 +195,120 @@ extern "C" { */ #define NG_IPV6_DEFAULT_MTU (1280) +/** + * @brief Initialization of the IPv6 thread. + * + * @param[in] priority The priority for the IPv6 thread + * + * @return The PID to the IPv6 thread, on success. + * @return a negative errno on error. + * @return -EINVAL, if @p priority is higher or equal @ref SCHED_PRIO_LEVELS + * @return -EOVERFLOW, if there are too many threads running already + * @return -EEXIST, if IPv6 was already initialized. + */ +kernel_pid_t ng_ipv6_init(char priority); + +/** + * @brief Get all IPv6 addresses registered to one or all interfaces + * + * Wrapper for @ref NG_NETAPI_MSG_TYPE_GET with @ref NETCONF_OPT_ADDRESS. + * + * @param[in] ipv6_pid The PID of the IPv6 control thread. + * @param[in] iface The interface you want the IPv6 addresses from. + * Can be @ref KERNEL_PID_UNDEF for all interfaces. + * @param[out] addrs An array of IPv6 addresses. Must not be NULL + * @param[in] addrs_len The length of @p addrs. Must be greater than or equal + * to `sizeof(ng_ipv6_addr_t)`. If smaller than required + * for all addresses, the result in @p addrs will be + * truncated to the last fitting IPv6 address. + * + * @return 0 on success + * @return -EFAULT, if @p addrs was NULL. + * @return -ENOENT, if @p iface was unequal @ref KERNEL_PID_UNDEF and interface + * does not exist. + * @return -EOVERFLOW, if @p addrs_len was `< sizeof(ng_ipv6_addr_t)`. + */ +static inline int ng_ipv6_get_addresses(kernel_pid_t ipv6_pid, kernel_pid_t iface, + ng_ipv6_addr_t *addrs, uint16_t addrs_len) +{ + return ng_netapi_get(ipv6_pid, NETCONF_OPT_ADDRESS, (uint16_t)iface, + addrs, addrs_len); +} + +/** + * @brief Adds an IPv6 address to an interface or removes it from it. + * + * Wrapper for @ref NG_NETAPI_MSG_TYPE_SET with @ref NETCONF_OPT_ADDRESS. + * + * If @p addr does not exists on @p iface it will be added. If it already + * exists it will be removed. + * + * @param[in] ipv6_pid The PID of the IPv6 control thread. + * @param[in] iface The interface you want add the IPv6 address to or + * remove it from. Must not be @ref KERNEL_PID_UNDEF + * @param[in] addr An IPv6 address. Must not be NULL + * + * @return 0 on success + * @return -EINVAL, if @p addr was unspecified address (::). + * @return -EFAULT, if @p addr was NULL. + * @return -ENOMEM, if there is no space left to store @p addr. + * @return -ENOENT, if @p iface was @ref KERNEL_PID_UNDEF or interface + * does not exist. + */ +static inline int ng_ipv6_toggle_address(kernel_pid_t ipv6_pid, kernel_pid_t iface, + ng_ipv6_addr_t *addr) +{ + return ng_netapi_set(ipv6_pid, NETCONF_OPT_ADDRESS, (uint16_t)iface, + addr, (uint16_t)sizeof(ng_ipv6_addr_t)); +} + +/** + * @brief Gets the MTU for an interface or of all interfaces + * + * @see + * RFC 2460, section 2 + * + * + * @param[in] ipv6_pid The PID of the IPv6 control thread. + * @param[in] iface The interface you want the MTU from. Can be + * @ref KERNEL_PID_UNDEF for minimum MTU of all interfaces. + * @param[out] mtu The MTU of @p iface. + * + * @return 0 on success + * @return -EFAULT, if @p mtu was NULL. + * @return -ENOENT, if @p iface was unequal @ref KERNEL_PID_UNDEF and interface + * does not exist. + */ +static inline int ng_ipv6_get_mtu(kernel_pid_t ipv6_pid, kernel_pid_t iface, + uint16_t *mtu) +{ + return ng_netapi_get(ipv6_pid, NETCONF_OPT_MIN_PACKET_SIZE, (uint16_t)iface, + mtu, (uint16_t)sizeof(uint16_t)); +} + +/** + * @brief Sets the MTU for an interface. + * + * @see + * RFC 2460, section 2 + * + * + * @param[in] ipv6_pid The PID of the IPv6 control thread. + * @param[in] iface The interface you want add the IPv6 address to or + * remove it from. Must not be @ref KERNEL_PID_UNDEF + * @param[in] mtu The MTU for @p iface. + * + * @return -EFAULT, if @p mtu was NULL. + * @return -ENOENT, if @p iface was @ref KERNEL_PID_UNDEF or interface + * does not exist. + */ +static inline int ng_ipv6_set_mtu(kernel_pid_t ipv6_pid, kernel_pid_t iface, + uint16_t mtu) +{ + return ng_netapi_set(ipv6_pid, NETCONF_OPT_MIN_PACKET_SIZE, (uint16_t)iface, + &mtu, (uint16_t)sizeof(uint16_t)); +} + #ifdef __cplusplus } #endif diff --git a/sys/include/net/ng_ipv6/hdr.h b/sys/include/net/ng_ipv6/hdr.h new file mode 100644 index 000000000000..4b9c3eb5b253 --- /dev/null +++ b/sys/include/net/ng_ipv6/hdr.h @@ -0,0 +1,264 @@ +/* + * 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_ng_ipv6_hdr IPv6 header defintions + * @ingroup net_ng_ipv6 + * @{ + * + * @file + * @brief IPv6 header + * + * @author Martine Lenders + */ +#ifndef NG_IPV6_HDR_H_ +#define NG_IPV6_HDR_H_ + +#include "byteorder.h" +#include "net/ng_ipv6/addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Data type to represent an IPv6 packet header + * + * @see + * RFC 2460, section 3 + * + */ +typedef struct __attribute__((packed)) { + /** + * @brief Version, traffic class, and flow label + * + * @detail The version are the 4 most significant bits, the traffic class + * the 8 next bit, and the remainding 20 bits are the flow label + * + * @code{.unparsed} + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Version| Traffic Class | Flow Label | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * @endcode + * + * This module provides helper functions to set, get, and check these + * fields accordingly: + * * ng_ipv6_hdr_set_version() + * * ng_ipv6_hdr_get_version() + * * ng_ipv6_hdr_is_ipv6_hdr() + * * ng_ipv6_hdr_set_tc() + * * ng_ipv6_hdr_set_tc_ecn() + * * ng_ipv6_hdr_set_tc_dscp() + * * ng_ipv6_hdr_get_tc() + * * ng_ipv6_hdr_get_tc_ecn() + * * ng_ipv6_hdr_get_tc_dscp() + * * ng_ipv6_hdr_set_fl() + * * ng_ipv6_hdr_get_fl() + */ + network_uint32_t v_tc_fl; + network_uint16_t len; /**< payload length of this packet. */ + uint8_t nh; /**< type of next header in this packet. */ + uint8_t hl; /**< hop limit for this packet. */ + ng_ipv6_addr_t src; /**< source address of this packet. */ + ng_ipv6_addr_t dest; /**< destination address of this packet. */ +} ng_ipv6_hdr_t; + +/** + * @brief Sets the version field of @p hdr to 6 + * + * @param[out] hdr Pointer to an IPv6 header. + */ +static inline void ng_ipv6_hdr_set_version(ng_ipv6_hdr_t *hdr) +{ + hdr->v_tc_fl.u8[0] &= 0x0f; + hdr->v_tc_fl.u8[0] |= 0x60; +} + +/** + * @brief Gets the value of the version field of @p hdr + * + * @param[in] hdr Pointer to an IPv6 header. + * + * @return Value of the version field of @p hdr. + */ +static inline uint8_t ng_ipv6_hdr_get_version(const ng_ipv6_hdr_t *hdr) +{ + return ((hdr->v_tc_fl.u8[0]) >> 4); +} + +/** + * @brief Checks if the version field is set to 6 + * + * @param[in] hdr Pointer to an IPv6 header. + * + * @return 1, if version field is 6 + * @return 0, otherwise + */ +static inline uint8_t ng_ipv6_hdr_is_ipv6_hdr(const ng_ipv6_hdr_t *hdr) +{ + return (((hdr->v_tc_fl.u8[0]) & 0xf0) == 0x60); +} + +/** + * @brief Sets the traffic class field of @p hdr + * + * @param[out] hdr Pointer to an IPv6 header. + * @param[in] tc The new value for the traffic class field. + */ +static inline void ng_ipv6_hdr_set_tc(ng_ipv6_hdr_t *hdr, uint8_t tc) +{ + hdr->v_tc_fl.u8[0] &= 0xf0; + hdr->v_tc_fl.u8[0] |= (0x0f & (tc >> 4)); + hdr->v_tc_fl.u8[1] &= 0x0f; + hdr->v_tc_fl.u8[1] |= (0xf0 & (tc << 4)); +} + +/** + * @brief Sets the value of the Explicit Congestion Notification (ECN) part + * of the traffic class field of @p hdr + * + * @detail The field is needed e.g. in context of 6LoWPAN header compression + * + * @see + * RFC 3168, section 5 + * + * + * @param[out] hdr Pointer to an IPv6 header. + * @param[in] ecn The new value for the 2-bit ECN part of the traffic class + * field. + */ +static inline void ng_ipv6_hdr_set_tc_ecn(ng_ipv6_hdr_t *hdr, uint8_t ecn) +{ + hdr->v_tc_fl.u8[0] &= 0xf3; + hdr->v_tc_fl.u8[0] |= (0x0c & (ecn << 2)); +} + +/** + * @brief Sets the value of the Defferentiaated Service Codepoint (DSCP) part + * of the traffic class field of @p hdr + * + * @detail The field is needed e.g. in context of 6LoWPAN header compression + * + * @see + * RFC 2474, section 3 + * + * + * @param[out] hdr Pointer to an IPv6 header. + * @param[in] dscp The new value for the 6-bit DSCP ng_part of the traffic class + * field. + */ +static inline void ng_ipv6_hdr_set_tc_dscp(ng_ipv6_hdr_t *hdr, uint8_t dscp) +{ + hdr->v_tc_fl.u8[0] &= 0xfc; + hdr->v_tc_fl.u8[0] |= (0x03 & (dscp >> 4)); + hdr->v_tc_fl.u8[1] &= 0x0f; + hdr->v_tc_fl.u8[1] |= (0xf0 & (dscp << 4)); +} + +/** + * @brief Gets the value of the traffic class field of @p hdr + * + * @param[in] hdr Pointer to an IPv6 header. + * + * @return Value of the traffic class field of @p hdr. + */ +static inline uint8_t ng_ipv6_hdr_get_tc(const ng_ipv6_hdr_t *hdr) +{ + return ((((hdr->v_tc_fl.u8[0]) & 0x0f) << 4) | + ((hdr->v_tc_fl.u8[1] & 0xf0) >> 4)); +} + +/** + * @brief Gets the value of the Explicit Congestion Notification (ECN) part + * of the traffic class field of @p hdr + * + * @detail The field is needed e.g. in context of 6LoWPAN header compression + * + * @see + * RFC 3168, section 5 + * + * + * @param[in] hdr Pointer to an IPv6 header. + * + * @return Value of the ECN part of the traffic class field of @p hdr. + */ +static inline uint8_t ng_ipv6_hdr_get_tc_ecn(const ng_ipv6_hdr_t *hdr) +{ + return (((hdr->v_tc_fl.u8[0]) & 0x0c) >> 2); +} + + +/** + * @brief Gets the value of the Defferentiaated Service Codepoint (DSCP) part + * of the traffic class field of @p hdr + * + * @detail The field is needed e.g. in context of 6LoWPAN header compression + * + * @see + * RFC 2474, section 3 + * + * + * @param[in] hdr Pointer to an IPv6 header. + * + * @return Value of the DSCP part of the traffic class field of @p hdr. + */ +static inline uint8_t ng_ipv6_hdr_get_tc_dscp(const ng_ipv6_hdr_t *hdr) +{ + return ((((hdr->v_tc_fl.u8[0]) & 0x03) << 4) | + ((hdr->v_tc_fl.u8[1] & 0xf0) >> 4)); +} + +/** + * @brief Sets the flow label field of @p hdr + * + * @param[out] hdr Pointer to an IPv6 header. + * @param[in] fl The new value for the flow label field in host byte order. + */ +static inline void ng_ipv6_hdr_set_fl(ng_ipv6_hdr_t *hdr, uint32_t fl) +{ + hdr->v_tc_fl.u8[1] &= 0xf0; + hdr->v_tc_fl.u8[1] |= (0x0f & (byteorder_htonl(fl).u8[1])); + hdr->v_tc_fl.u16[1] = byteorder_htonl(fl).u16[1]; +} + +/** + * @brief Gets the value of the flow label field of @p hdr + * + * @param[in] hdr Pointer to an IPv6 header. + * + * @return Value of the flow label field of @p hdr. + */ +static inline uint32_t ng_ipv6_hdr_get_fl(const ng_ipv6_hdr_t *hdr) +{ + return byteorder_ntohl(hdr->v_tc_fl) & 0x000fffff; +} + +/** + * @brief Builds an IPv6 header for sending and adds it to the packet buffer. + * + * @param[in] payload Payload for the packet. + * @param[in] src Source address for the header. Can be NULL if not + * known or required. + * @param[in] src_len Length of @p src. Can be 0 if not known or required. + * @param[in] dest Destination address for the header. Can be NULL if not + * known or required. + * @param[in] dest_len Length of @p dest. Can be 0 if not known or required. + * + * @return The IPv6 header on success. + * @return NULL on error. + */ +ng_pktsnip_t *ng_ipv6_hdr_build(ng_pktsnip_t *payload, + uint8_t *src, uint8_t src_len, + uint8_t *dest, uint8_t dest_len); + +#ifdef __cplusplus +} +#endif + +#endif /* NG_IPV6_HDR_H_ */ +/** @} */ diff --git a/sys/include/net/ng_netconf.h b/sys/include/net/ng_netconf.h index 4567f5ceb490..3164087fae4e 100644 --- a/sys/include/net/ng_netconf.h +++ b/sys/include/net/ng_netconf.h @@ -58,6 +58,9 @@ typedef enum { NETCONF_OPT_MAX_PACKET_SIZE, /**< get/set the maximum packet size a * network module can handle as uint16_t * in host byte order */ + NETCONF_OPT_MIN_PACKET_SIZE, /**< get/set the minimum packet size a + * network module can handle as uint16_t + * in host byte order */ /** * @brief en/disable preloading or read the current state. * diff --git a/sys/net/network_layer/ng_ipv6/Makefile b/sys/net/network_layer/ng_ipv6/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/net/network_layer/ng_ipv6/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/ng_ipv6/ng_ipv6.c b/sys/net/network_layer/ng_ipv6/ng_ipv6.c new file mode 100644 index 000000000000..839f7df488b7 --- /dev/null +++ b/sys/net/network_layer/ng_ipv6/ng_ipv6.c @@ -0,0 +1,704 @@ +/* + * 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 + +#include "byteorder.h" +#include "cpu-conf.h" +#include "kernel_types.h" +#include "net/ng_netapi.h" +#include "net/ng_netconf.h" +#include "net/ng_netif.h" +#include "net/ng_netif/hdr.h" +#include "net/ng_netreg.h" +#include "net/ng_nettype.h" +#include "net/ng_pkt.h" +#include "net/ng_pktbuf.h" +#include "net/ng_protnum.h" +#include "thread.h" +#include "utlist.h" + +#include "net/ng_ipv6/nc.h" +#include "net/ng_ipv6/netif.h" + +#include "net/ng_ipv6.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static char _ipv6_stack[NG_IPV6_STACK_SIZE]; +static kernel_pid_t _ipv6_pid = KERNEL_PID_UNDEF; + +#if ENABLE_DEBUG +static char addr_str[NG_IPV6_ADDR_MAX_STR_LEN]; +#endif + +#ifdef MODULE_NG_IPV6_ROUTER +static bool is_router = false; +#endif + +static inline bool _pkt_not_for_me(kernel_pid_t *netif, ng_ipv6_hdr_t *hdr) +{ + if (*netif == KERNEL_PID_UNDEF) { + *netif = ng_ipv6_netif_find_addr(NULL, &hdr->dest); + return (*netif != KERNEL_PID_UNDEF); + } + else { + return (ng_ipv6_netif_find_addr_local(*netif, &hdr->dest) != NULL); + } +} + +static void _dispatch_received(ng_nettype_t type, uint32_t demux_ctx, + ng_pktsnip_t *pkt) +{ + msg_t msg; + ng_netreg_entry_t *entry = ng_netreg_lookup(type, demux_ctx); + + msg.type = NG_NETAPI_MSG_TYPE_RCV; + msg.content.ptr = (char *)pkt; + + while (entry) { + DEBUG("ipv6: Send receive command for %p to %" PRIu16 "\n", (void *)pkt, + entry->pid); + msg_send(&msg, entry->pid); + entry = ng_netreg_getnext(entry); + } +} + +static void _handle_send(ng_pktsnip_t *pkt, bool set_src) +{ + kernel_pid_t iface = KERNEL_PID_UNDEF; + ng_pktsnip_t *ipv6 = NULL, *payload = pkt, *netif; + ng_ipv6_hdr_t *hdr; + ng_ipv6_addr_t *next_hop = NULL; + ng_ipv6_nc_t *nc_entry; + ng_netif_hdr_t netif_hdr; + uint16_t l2_src_len = 0; + + while (payload) { + if (payload->next->type == NG_NETTYPE_IPV6) { + ipv6 = payload->next; + break; + } + + payload = payload->next; + } + + if (ipv6 == NULL) { + DEBUG("ipv6: Sending packet has no IPv6 header\n"); + return; + } + + hdr = (ng_ipv6_hdr_t *)ipv6->data; + hdr->len = byteorder_htons(payload->size); + + if (hdr->nh == NG_PROTNUM_RESERVED) { + switch (payload->type) { +#ifdef MODULE_NG_ICMPV6 + + case NG_NETTYPE_ICMPV6: + DEBUG("ipv6: Set next header to ICMPv6.\n"); + hdr->nh = NG_PROTNUM_ICMPV6; + break; +#endif +#ifdef MODULE_NG_TCP + + case NG_NETTYPE_TCP: + DEBUG("ipv6: Set next header to TCP.\n"); + hdr->nh = NG_PROTNUM_TCP; + break; +#endif +#ifdef MODULE_NG_UDP + + case NG_NETTYPE_UDP: + DEBUG("ipv6: Set next header to UDP.\n"); + hdr->nh = NG_PROTNUM_UDP; + break; +#endif + + case NG_NETTYPE_IPV6: + DEBUG("ipv6: Set next header to IPv6.\n"); + hdr->nh = NG_PROTNUM_IPV6; + break; + + default: + break; + } + } + +#ifdef MODULE_NG_IPV6_ROUTER + + if (is_router) { + /* TODO: routing header */ + if (next_hop == NULL) { + size_t next_hop_size; + int res = 0; + + if ((res = fib_get_next_hop(&hdr->dest, sizeof(ng_ipv6_addr_t), + &iface, &next_hop, &next_hop_size)) != FIB_SUCCESS) { +#if ENABLE_DEBUG + + switch (res) { + case FIB_ERROR_DESTINATION_UNREACHABLE: + DEBUG("%s is unreachable\n", + ng_ipv6_addr_to_str(addr_str, && hdr->dest, + sizeof(addr_str))); + break; + + case FIB_ERROR_INSUFICCIENT_OUT_SIZE: + DEBUG("sizeof(next_hop) == %zu is of insuffient space.\n", + sizeof(next_hop)); + break; + + default: + DEBUG("An error occured while getting next hop.\n"); + break; + } + +#endif + return; + } + + if (next_hop_size != sizeof(ng_ipv6_addr_t)) { + DEBUG("next_hop is not an IPv6 address (size = %zu)\n", next_hop_size); + return; + } + } + } + else { + next_hop = &hdr->dest; + } + +#else + next_hop = &hdr->dest; +#endif + + if ((nc_entry = ng_ipv6_nc_get_reachable(iface, next_hop)) == NULL) { + DEBUG("ipv6: No link layer address for next_hop %s found.\n", + ng_ipv6_addr_to_str(addr_str, &next_hop, sizeof(addr_str))); + return; + } + else { + iface = nc_entry->iface; + } + + if (iface == KERNEL_PID_UNDEF) { + DEBUG("ipv6: No interface for %s found\n", + ng_ipv6_addr_to_str(addr_str, &next_hop, sizeof(addr_str))); + return; + } + + if (set_src) { + ng_ipv6_addr_t *src = ng_ipv6_netif_find_best_src_addr(iface, &hdr->dest); + + if (src == NULL) { + DEBUG("ipv6: No suitable source address for %s found on interface %" + PRIkernel_pid "\n", + ng_ipv6_addr_to_str(addr_str, &next_hop, sizeof(addr_str)), + iface); + } + + DEBUG("ipv6: set packet source to %s\n", + ng_ipv6_addr_to_str(addr_str, src, sizeof(addr_str))); + memcpy(&hdr->src, src, sizeof(ng_ipv6_addr_t)); + + DEBUG("ipv6: calculate checksum for upper header."); + ng_netreg_calculate_csum(NG_NETTYPE_IPV6, payload->type, pkt); + } + + /* XXX: this is ugly and requires context switches. Can we find a better + * solution? */ + ng_netapi_get(iface, NETCONF_OPT_ADDR_LEN, 0, &l2_src_len, sizeof(uint16_t)); + + ng_netif_hdr_init(&netif_hdr, l2_src_len, nc_entry->l2_addr_len); + netif = ng_pktbuf_add(NULL, &netif_hdr, ng_netif_hdr_sizeof(&netif_hdr), + NG_NETTYPE_UNDEF); + ng_netif_hdr_set_dst_addr(&netif_hdr, nc_entry->l2_addr, + nc_entry->l2_addr_len); + ipv6->next = netif; + ng_netapi_send(iface, pkt); +} + +static void _handle_receive(kernel_pid_t netif, ng_pktsnip_t *pkt) +{ + ng_pktsnip_t *ipv6, *payload; + ng_ipv6_hdr_t *hdr; + ng_nettype_t receiver_type = NG_NETTYPE_UNDEF; + + LL_SEARCH_SCALAR(pkt, payload, type, NG_NETTYPE_UNDEF); + + if (payload == NULL) { + DEBUG("ipv6: Received packet has no payload\n"); + return; + } + + payload = ng_pktbuf_start_write(payload); + + if (payload == NULL) { + DEBUG("ipv6: no space left in packet buffer\n"); +#if defined(DEVELHELP) && defined(ENABLE_DEBUG) + ng_pktbuf_stats(); +#endif + return; + } + + ipv6 = ng_pktbuf_add(payload, /* Mark IPv6 header and payload for next layer */ + payload->data, + sizeof(ng_ipv6_hdr_t), + NG_NETTYPE_IPV6); + + if (ipv6 == NULL) { + DEBUG("ipv6: no space left in packet buffer\n"); +#if defined(DEVELHELP) && defined(ENABLE_DEBUG) + ng_pktbuf_stats(); +#endif + return; + } + + /* extract header */ + hdr = (ng_ipv6_hdr_t *)ipv6->data; + + /* is it a valid IPv6 header? */ + if (!ng_ipv6_hdr_is_ipv6_hdr(hdr) && byteorder_ntohs(hdr->len) != payload->size) { + DEBUG("ipv6: Received packet was not an IPv6 packet or length field %" + PRIu16 " was different than actual payload length (%zu).\n", + byteorder_ntohs(hdr->len), payload->size); +#ifdef MODULE_OD + od_hex_dump(hdr, sizeof(ng_ipv6_hdr_t *)hdr, OD_WIDTH_DEFAULT); +#endif + return; + } + + DEBUG("ipv6: Received (src = %s, ", + ng_ipv6_addr_to_str(addr_str, &(hdr->src), sizeof(addr_str))); + DEBUG("dest = %s, next header = %" PRIu8 ", length = %" PRIu16 ")\n", + ng_ipv6_addr_to_str(addr_str, &(hdr->dest), sizeof(addr_str)), + hdr->nh, byteorder_ntohs(hdr->len)); + + if (_pkt_not_for_me(&netif, hdr)) { /* if packet is not for me */ + /* redirect to next hop */ + _handle_send(pkt, false); + } + + switch (hdr->nh) { +#ifdef MODULE_NG_ICMPV6 + + case NG_PROTNUM_ICMPV6: + DEBUG("ipv6: Received ICMPv6 packet.\n"); + receiver_type = NG_NETTYPE_ICMPV6; + /* TODO: handle ICMPv6 packet */ + break; +#endif +#ifdef MODULE_NG_TCP + + case NG_PROTNUM_TCP: + DEBUG("ipv6: Received TCP packet.\n"); + receiver_type = NG_NETTYPE_TCP; + break; +#endif +#ifdef MODULE_NG_UDP + + case NG_PROTNUM_UDP: + DEBUG("ipv6: Received UDP packet.\n"); + receiver_type = NG_NETTYPE_UDP; + break; +#endif + + case NG_PROTNUM_IPV6: + DEBUG("ipv6: Received encapsulated IPv6 packet.\n"); + receiver_type = NG_NETTYPE_IPV6; + _handle_receive(KERNEL_PID_UNDEF, payload); + break; + + default: + break; + } + + + ng_pktbuf_hold(pkt, ng_netreg_num(receiver_type, 0) + + ng_netreg_num(NG_NETTYPE_IPV6, hdr->nh) - 1); + /* IPv6 is not interested anymore so `- 1` */ + _dispatch_received(receiver_type, NG_NETREG_DEMUX_CTX_ALL, pkt); + _dispatch_received(NG_NETTYPE_IPV6, hdr->nh, pkt); +} + +static int _get_if_addresses(kernel_pid_t iface, ng_ipv6_addr_t *addrs, + uint16_t addrs_len) +{ + ng_ipv6_netif_t *entry; + + if (addrs_len < sizeof(ng_ipv6_addr_t)) { + DEBUG("Limit of conf->data_len exceeded (was %" PRIu16 ")\n", addrs_len); + return -EOVERFLOW; + } + + if ((entry = ng_ipv6_netif_get(iface)) == NULL) { + DEBUG("ipv6: interface %" PRIkernel_pid " does not exist\n", iface); + return -ENOENT; + } + + for (int i = 0; (i < NG_IPV6_NETIF_ADDR_NUMOF) && (addrs_len >= sizeof(ng_ipv6_addr_t)); i++) { + if (!ng_ipv6_addr_is_unspecified(&(entry->addrs[i].addr))) { + memcpy(addrs, &(entry->addrs[i].addr), sizeof(ng_ipv6_addr_t)); + DEBUG("ipv6: add %s to result\n", ng_ipv6_addr_to_str(addr_str, + addrs, sizeof(addr_str))); + addrs += sizeof(ng_ipv6_addr_t); + addrs_len -= sizeof(ng_ipv6_addr_t); + } + } + + return 0; +} + +static int _get_addresses(ng_netapi_opt_t *conf) +{ + ng_ipv6_addr_t *addrs = conf->data; + + if (conf->data_len < sizeof(ng_ipv6_addr_t)) { + DEBUG("ipv6: sizeof(address) (== %" PRIu16 ") must be " + + "sizeof(ng_ipv6_addr_t) (== %zu)\n", + conf->data_len, + sizeof(ng_ipv6_addr_t)); + return -EOVERFLOW; + } + + if (conf->context == KERNEL_PID_UNDEF) { + size_t iface_num; + uint16_t addr_num = 0; + kernel_pid_t *ifaces = ng_netif_get(&iface_num); + + DEBUG("Get addresses from all interfaces\n"); + + for (size_t i = 0; i < iface_num; i++) { + int res = _get_if_addresses(ifaces[i], addrs + addr_num, + conf->data_len - (addr_num * sizeof(ng_ipv6_addr_t))); + + if (res >= 0) { + addr_num += res; + } + else if (res == -EOVERFLOW) { + DEBUG("Stop copying\n"); + break; + } + + /* ignore non-existent interfaces */ + } + } + else if (_get_if_addresses((kernel_pid_t)conf->context, addrs, + conf->data_len) < 0) { + DEBUG("ipv6: interface %" PRIu16 " does not exist\n", conf->context); + return -ENOENT; + } + + return 0; +} + +static int _get_mtu(ng_netapi_opt_t *conf) +{ + uint16_t *mtu = conf->data; + ng_ipv6_netif_t *entry; + + if (conf->data_len != sizeof(uint16_t)) { + DEBUG("ipv6: sizeof(MTU) (== %" PRIu16 ") must be " + + "sizeof(uint16_t) (== %zu)\n", + conf->data_len, + sizeof(uint16_t)); + return -EOVERFLOW; + } + + if (conf->context == KERNEL_PID_UNDEF) { + size_t iface_num; + kernel_pid_t *ifaces = ng_netif_get(&iface_num); + + DEBUG("ipv6: get mimumum MTU of all interfaces\n"); + + *mtu = UINT16_MAX; + + for (size_t i = 0; i < iface_num; i++) { + if ((entry = ng_ipv6_netif_get(ifaces[i])) == NULL) { + DEBUG("ipv6: interface %" PRIu16 " does not exist\n", ifaces[i]); + continue; /* ignore non-existent interfaces */ + } + + if (entry->mtu < *mtu) { + *mtu = entry->mtu; + } + } + + } + else { + if ((entry = ng_ipv6_netif_get((kernel_pid_t)(conf->context))) == NULL) { + DEBUG("ipv6: interface %" PRIu16 " does not exist\n", conf->context); + return -ENOENT; + } + + *mtu = entry->mtu; + } + + DEBUG("ipv6: get MTU = %" PRIu16 "\n", *mtu); + + return 0; +} + +static int _toggle_address(ng_netapi_opt_t *conf) +{ + if (conf->data_len != sizeof(ng_ipv6_addr_t)) { + DEBUG("ipv6: sizeof(address) (== %" PRIu16 ") must be " + + "sizeof(ng_ipv6_addr_t) (== %zu)\n", + conf->data_len, + sizeof(ng_ipv6_addr_t)); + return -EOVERFLOW; + } + + if (ng_ipv6_netif_find_addr_local((kernel_pid_t)(conf->context), + (ng_ipv6_addr_t *)conf->data) == NULL) { + DEBUG("ipv6: add address %s to interface %" PRIu16 "\n", + ng_ipv6_addr_to_str(addr_str, (ng_ipv6_addr_t *)conf->data, + sizeof(addr_str)), + conf->context); + return ng_ipv6_netif_add_addr((kernel_pid_t)conf->context, + (ng_ipv6_addr_t *)conf->data, false); + } + else { + DEBUG("ipv6: remove address %s from interface %" PRIu16 "\n", + ng_ipv6_addr_to_str(addr_str, (ng_ipv6_addr_t *)conf->data, + sizeof(addr_str)), + conf->context); + ng_ipv6_netif_remove_addr((kernel_pid_t)conf->context, + (ng_ipv6_addr_t *)conf->data); + + } + + return 0; +} + +static int _set_mtu(ng_netapi_opt_t *conf) +{ + uint16_t *mtu = conf->data; + ng_ipv6_netif_t *entry; + + if (conf->data_len != sizeof(uint16_t)) { + DEBUG("ipv6: sizeof(MTU) (== %" PRIu16 ") must be " + + "sizeof(uint16_t) (== %zu)\n", + conf->data_len, + sizeof(uint16_t)); + return -EOVERFLOW; + } + + if ((entry = ng_ipv6_netif_get((kernel_pid_t)(conf->context))) == NULL) { + DEBUG("ipv6: interface %" PRIu16 " does not exist\n", conf->context); + return -ENOENT; + } + + entry->mtu = *mtu; + DEBUG("ipv6: set MTU = %" PRIu16 "\n", entry->mtu); + + return 0; +} + +static int _handle_get(ng_netapi_opt_t *conf) +{ + if (conf->data) { + return -EFAULT; + } + + switch (conf->opt) { + case NETCONF_OPT_ADDRESS: + DEBUG("ipv6: get IPv6 addresses for iface = %" PRIu16 " (0 == all)\n", + conf->context); + return _get_addresses(conf); + + case NETCONF_OPT_ADDR_LEN: + DEBUG("ipv6: get address length\n"); + + if (conf->data_len != sizeof(uint16_t)) { + return -EOVERFLOW; + } + else { + uint16_t addr_len = (uint16_t)sizeof(ng_ipv6_addr_t); + conf->data = &addr_len; + } + + break; + + case NETCONF_OPT_MIN_PACKET_SIZE: + DEBUG("ipv6: get MTU for iface = %" PRIu16 " (0 == all)\n", + conf->context); + return _get_mtu(conf); + + case NETCONF_OPT_PROTO: + DEBUG("ipv6: get protocol type\n"); + + if (conf->data_len != sizeof(ng_nettype_t)) { + return -EOVERFLOW; + } + else { + ng_nettype_t proto = NG_NETTYPE_IPV6; + conf->data = &proto; + } + + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +static int _handle_set(ng_netapi_opt_t *conf) +{ + if (conf->data) { + return -EFAULT; + } + + switch (conf->opt) { + case NETCONF_OPT_ADDRESS: + DEBUG("ipv6: toggle IPv6 address for iface = %" PRIu16 "\n", + conf->context); + return _toggle_address(conf); + + case NETCONF_OPT_MIN_PACKET_SIZE: + DEBUG("ipv6: set MTU for iface = %" PRIu16 "\n", conf->context); + return _set_mtu(conf); + + default: + return -ENOTSUP; + } +} + +static void *_ipv6_thread(void *args) +{ + msg_t msg, reply, msg_q[NG_IPV6_MSG_QUEUE_SIZE]; + ng_netreg_entry_t me_reg; + + (void)args; + msg_init_queue(msg_q, NG_IPV6_MSG_QUEUE_SIZE); + + me_reg.demux_ctx = NG_NETREG_DEMUX_CTX_ALL; + me_reg.pid = thread_getpid(); + + /* register interest in all IPv6 packets */ + ng_netreg_register(NG_NETTYPE_IPV6, &me_reg); + + /* preinitialize ACK */ + reply.type = NG_NETAPI_MSG_TYPE_ACK; + + /* start event loop */ + while (1) { + DEBUG("ipv6: waiting for incoming message.\n"); + msg_receive(&msg); + + switch (msg.type) { + case NG_NETAPI_MSG_TYPE_RCV: + DEBUG("ipv6: NG_NETDEV_MSG_TYPE_RCV received\n"); + _handle_receive(msg.sender_pid, (ng_pktsnip_t *)msg.content.ptr); + break; + + case NG_NETAPI_MSG_TYPE_SND: + DEBUG("ipv6: NG_NETDEV_MSG_TYPE_SND received\n"); + _handle_send((ng_pktsnip_t *)msg.content.ptr, true); + break; + + case NG_NETAPI_MSG_TYPE_GET: + DEBUG("ipv6: NG_NETDEV_MSG_TYPE_GET received\n"); + reply.content.value = _handle_get((ng_netapi_opt_t *)msg.content.ptr); + msg_reply(&msg, &reply); + break; + + case NG_NETAPI_MSG_TYPE_SET: + DEBUG("ipv6: NG_NETDEV_MSG_TYPE_SET received\n"); + reply.content.value = _handle_set((ng_netapi_opt_t *)msg.content.ptr); + msg_reply(&msg, &reply); + break; + + default: + break; + } + } + + return NULL; +} + +kernel_pid_t ng_ipv6_init(char priority) +{ + if (_ipv6_pid != KERNEL_PID_UNDEF) { + return -EEXIST; + } + + _ipv6_pid = thread_create(_ipv6_stack, NG_IPV6_STACK_SIZE, priority, + CREATE_STACKTEST, _ipv6_thread, NULL, + NG_IPV6_THREAD_NAME); + + if (_ipv6_pid <= 0) { + kernel_pid_t res = _ipv6_pid; + _ipv6_pid = KERNEL_PID_UNDEF; + return res; + } + + return _ipv6_pid; +} + +ng_pktsnip_t *ng_ipv6_hdr_build(ng_pktsnip_t *payload, + uint8_t *src, uint8_t src_len, + uint8_t *dest, uint8_t dest_len) +{ + ng_pktsnip_t *ipv6; + ng_ipv6_hdr_t *hdr; + + if ((src_len != 0) || (src_len != sizeof(ng_ipv6_addr_t)) || + (dest_len != 0) || (dest_len != sizeof(ng_ipv6_addr_t))) { + DEBUG("ipv6: Address length was not %zu byte.\n", + sizeof(ng_ipv6_addr_t)); + return NULL; + } + + ipv6 = ng_pktbuf_add(NULL, NULL, sizeof(ng_ipv6_hdr_t), + NG_NETTYPE_IPV6); + + if (ipv6 == NULL) { + DEBUG("ipv6: no space left in packet buffer\n"); + return NULL; + } + + hdr = (ng_ipv6_hdr_t *)ipv6->data; + + if ((src != NULL) && (src_len != 0)) { + DEBUG("ipv6: set packet source to %s\n", + ng_ipv6_addr_to_str(addr_str, (ng_ipv6_addr_t *)src, + sizeof(addr_str))); + memcpy(&hdr->src, src, src_len); + } + + if ((dest != NULL) && (dest_len != 0)) { + DEBUG("ipv6: set packet destination to %s\n", + ng_ipv6_addr_to_str(addr_str, (ng_ipv6_addr_t *)dest, + sizeof(addr_str))); + memcpy(&hdr->dest, dest, dest_len); + } + + hdr->nh = NG_PROTNUM_RESERVED; + + payload->next = ipv6; + + return ipv6; +} + + +/** @} */