diff --git a/Makefile.dep b/Makefile.dep index 60e251a279..3f6ef658a8 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -10,6 +10,14 @@ ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pk USEMODULE += gnrc endif +ifneq (,$(filter gnrc_conn_%,$(USEMODULE))) + USEMODULE += gnrc_conn +endif + +ifneq (,$(filter gnrc_conn_udp,$(USEMODULE))) + USEMODULE += gnrc_udp +endif + ifneq (,$(filter schedstatistics,$(USEMODULE))) USEMODULE += xtimer endif @@ -302,6 +310,22 @@ ifneq (,$(filter oonf_common,$(USEMODULE))) USEMODULE += socket_base endif +ifneq (,$(filter %_conn_ip,$(USEMODULE))) + USEMODULE += conn_ip +endif + +ifneq (,$(filter %_conn_tcp,$(USEMODULE))) + USEMODULE += conn_tcp +endif + +ifneq (,$(filter %_conn_udp,$(USEMODULE))) + USEMODULE += conn_udp +endif + +ifneq (,$(filter conn_%,$(USEMODULE))) + USEMODULE += conn +endif + # if any log_* is used, also use LOG pseudomodule ifneq (,$(filter log_%,$(USEMODULE))) USEMODULE += log diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules index 1efad80a02..86aca801eb 100644 --- a/Makefile.pseudomodules +++ b/Makefile.pseudomodules @@ -1,3 +1,7 @@ +PSEUDOMODULES += conn +PSEUDOMODULES += conn_ip +PSEUDOMODULES += conn_tcp +PSEUDOMODULES += conn_udp PSEUDOMODULES += gnrc_netif_default PSEUDOMODULES += gnrc_ipv6_default PSEUDOMODULES += gnrc_ipv6_router diff --git a/sys/include/net/conn.h b/sys/include/net/conn.h new file mode 100644 index 0000000000..bd03991973 --- /dev/null +++ b/sys/include/net/conn.h @@ -0,0 +1,51 @@ +/* + * 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_conn Application connection API + * @ingroup net + * @brief Provides a minimal common API for applications to connect to the + * different network stacks. + * + * This module provides a minimal common API for applications to connect over different network + * stacks. For each network stack there is supposed to be at least one connection type + * implementation. Note that this definition gives no restriction on how a connection type should be + * structured for simplicity and modularity. As a result, it can't give any guarantee that an + * implementation keeps them compatible to each other. For example, an implementation might allow, + * that a UDP receive function is called on a raw IPv6 connection object and even choose to do so + * for valid reasons (e.g. code size), but this definition does not impose this on the + * implementation. Currently there are the following option types defined: + * + * * @ref conn_ip_t (net/conn/ip.h): raw IP connections + * * @ref conn_tcp_t (net/conn/tcp.h): TCP connections + * * @ref conn_udp_t (net/conn/udp.h): UDP connections + * + * @{ + * + * @file + * @brief Application connection API definitions + * + * @author Martine Lenders + */ +#ifndef NET_CONN_H_ +#define NET_CONN_H_ + +#include "net/conn/ip.h" +#include "net/conn/tcp.h" +#include "net/conn/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NET_CONN_H_ */ +/** @} */ diff --git a/sys/include/net/conn/ip.h b/sys/include/net/conn/ip.h new file mode 100644 index 0000000000..ee7cf92081 --- /dev/null +++ b/sys/include/net/conn/ip.h @@ -0,0 +1,128 @@ +/* + * 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_conn_ip Raw IPv4/IPv6 connections + * @ingroup net_conn + * @brief Connection submodule for raw IPv4/IPv6 connections + * @{ + * + * @file + * @brief Raw IPv4/IPv6 connection definitions + * + * @author Martine Lenders + */ +#ifndef NET_CONN_IP_H_ +#define NET_CONN_IP_H_ + +#include +#include + +#ifdef MODULE_GNRC_CONN_IP +#include "net/gnrc/conn.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Forward declaration of @ref conn_ip_t to allow for external definition. + */ +struct conn_ip; + +/** + * @brief Implementation-specific type of a raw IPv4/IPv6 connection object + */ +typedef struct conn_ip conn_ip_t; + +/** + * @brief Creates a new raw IPv4/IPv6 connection object + * + * @param[out] conn Preallocated connection object. Must fill the size of the stack-specific + * connection desriptor. + * @param[in] addr The local IP address for @p conn. + * @param[in] addr_len Length of @p addr. Must be fitting for the @p family. + * @param[in] family The family of @p addr (see @ref net_af). + * @param[in] proto @ref net_protnum for the IPv6 packets to receive. + * + * @return 0 on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' bind() function specification. + */ +int conn_ip_create(conn_ip_t *conn, const void *addr, size_t addr_len, int family, int proto); + +/** + * @brief Closes a raw IPv4/IPv6 connection + * + * @param[in,out] conn A raw IPv4/IPv6 connection object. + */ +void conn_ip_close(conn_ip_t *conn); + +/** + * @brief Gets the local address of a raw IPv4/IPv6 connection + * + * @param[in] conn A raw IPv4/IPv6 connection object. + * @param[out] addr The local IP address. Must have space for any address of the connection's + * family. + * + * @return length of @p addr on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' getsockname() function + * specification. + */ +int conn_ip_getlocaladdr(conn_ip_t *conn, void *addr); + +/** + * @brief Receives a message over IPv4/IPv6 + * + * @param[in] conn A raw IPv4/IPv6 connection object. + * @param[out] data Pointer where the received data should be stored. + * @param[in] max_len Maximum space available at @p data. + * @param[out] addr NULL pointer or the sender's IP address. Must have space for any address + * of the connection's family. + * @param[out] addr_len Length of @p addr. Can be NULL if @p addr is NULL. + * + * @note Function may block. + * + * @return The number of bytes received on success. + * @return 0, if no received data is available, but everything is in order. + * @return any other negative number in case of an error. For portability, implementations should + * draw inspiration of the errno values from the POSIX' recv(), recvfrom(), or recvmsg() + * function specification. + */ +int conn_ip_recvfrom(conn_ip_t *conn, void *data, size_t max_len, void *addr, size_t *addr_len); + +/** + * @brief Sends a message over IPv4/IPv6 + * + * @param[in] data Pointer where the received data should be stored. + * @param[in] len Maximum space available at @p data. + * @param[in] src The source address. May be NULL for all any interface address. + * @param[in] src_len Length of @p src. + * @param[in] dst The receiver's network address. + * @param[in] dst_len Length of @p dst. + * @param[in] family The family of @p src and @p dst (see @ref net_af). + * @param[in] proto @ref net_protnum for the IPv6 packets to set. + * + * @note Function may block. + * + * @return The number of bytes send on success. + * @return any other negative number in case of an error. For portability, implementations should + * draw inspiration of the errno values from the POSIX' send(), sendfrom(), or sendmsg() + * function specification. + */ +int conn_ip_sendto(const void *data, size_t len, const void *src, size_t src_len, + void *dst, size_t dst_len, int family, int proto); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_CONN_IP_H_ */ +/** @} */ diff --git a/sys/include/net/conn/tcp.h b/sys/include/net/conn/tcp.h new file mode 100644 index 0000000000..aa02fa7e04 --- /dev/null +++ b/sys/include/net/conn/tcp.h @@ -0,0 +1,177 @@ +/* + * 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_conn_tcp TCP connections + * @ingroup net_conn + * @brief Connection submodule for TCP connections + * @{ + * + * @file + * @brief TCP connection definitions + * + * @author Martine Lenders + */ +#ifndef NET_CONN_TCP_H_ +#define NET_CONN_TCP_H_ + +#include +#include + +#ifdef MODULE_GNRC_CONN_TCP +#include "net/gnrc/conn.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Forward declaration of @ref conn_tcp_t to allow for external definition. + */ +struct conn_tcp; + +/** + * @brief Implementation-specific type of a TCP connection object + */ +typedef struct conn_tcp conn_tcp_t; + +/** + * @brief Creates a new TCP connection object + * + * @param[out] conn Preallocated connection object. Must fill the size of the stack-specific + * connection desriptor. + * @param[in] addr The local network layer address for @p conn. + * @param[in] addr_len The length of @p addr. Must be fitting for the @p family. + * @param[in] family The family of @p addr (see @ref net_af). + * @param[in] port The local TCP port for @p conn. + * + * @return 0 on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' bind() function specification. + */ +int conn_tcp_create(conn_tcp_t *conn, const void *addr, size_t addr_len, int family, + uint16_t port); + +/** + * @brief Closes a TCP connection + * + * @param[in,out] conn A TCP connection object. + */ +void conn_tcp_close(conn_tcp_t *conn); + +/** + * @brief Gets the local address of a TCP connection + * + * @param[in] conn A TCP connection object. + * @param[out] addr The local network layer address. Must have space for any address of + * the connection's family. + * @param[out] port The local TCP port. + * + * @return length of @p addr on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' getsockname() function + * specification. + */ +int conn_tcp_getlocaladdr(conn_tcp_t *conn, void *addr, uint16_t *port); + +/** + * @brief Gets the address of the connected peer of a TCP connection + * + * @param[in] conn A TCP connection object. + * @param[out] addr The network layer address of the connected peer. Must have space for any + * address of the connection's family. + * @param[out] port The TCP port of the connected peer. + * + * @return length of @p addr on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' getpeername() function + * specification. + */ +int conn_tcp_getpeeraddr(conn_tcp_t *conn, void *addr, uint16_t *port); + +/** + * @brief Connects to a remote TCP peer + * + * @param[in] conn A TCP connection object. + * @param[in] addr The remote network layer address for @p conn. + * @param[in] addr_len Length of @p addr. + * @param[in] port The remote TCP port for @p conn. + * + * @return 0 on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' connect() function specification. + */ +int conn_tcp_connect(conn_tcp_t *conn, const void *addr, size_t addr_len, uint16_t port); + +/** + * @brief Marks connection to listen for a connection request by a remote TCP peer + * + * @param[in] conn A TCP connection object. + * @param[in] queue_len Maximum length of the queue for connection requests. + * An implementation may choose to silently adapt this value to its needs + * (setting it to a minimum or maximum value). Any negative number must be + * set at least to 0. + * + * @return 0 on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' listen() function specification. + */ +int conn_tcp_listen(conn_tcp_t *conn, int queue_len); + +/** + * @brief Receives and handles TCP connection requests from other peers + * + * @param[in] conn A TCP connection object. + * @param[out] out_conn A new TCP connection object for the established connection. + * + * @return 0 on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' accept() function specification. + */ +int conn_tcp_accept(conn_tcp_t *conn, conn_tcp_t *out_conn); + +/** + * @brief Receives a TCP message + * + * @param[in] conn A TCP connection object. + * @param[out] data Pointer where the received data should be stored. + * @param[in] max_len Maximum space available at @p data. + * + * @note Function may block. + * + * @return The number of bytes received on success. + * @return 0, if no received data is available, but everything is in order. + * @return any other negative number in case of an error. For portability, implementations should + * draw inspiration of the errno values from the POSIX' recv(), recvfrom(), or recvmsg() + * function specification. + */ +int conn_tcp_recv(conn_tcp_t *conn, void *data, size_t max_len); + +/** + * @brief Sends a TCP message + * + * @param[in] conn A TCP connection object. + * @param[in] data Pointer where the received data should be stored. + * @param[in] len Maximum space available at @p data. + * + * @note Function may block. + * + * @return The number of bytes send on success. + * @return any other negative number in case of an error. For portability, implementations should + * draw inspiration of the errno values from the POSIX' send(), sendfrom(), or sendmsg() + * function specification. + */ +int conn_tcp_send(conn_tcp_t *conn, const void *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_CONN_TCP_H_ */ +/** @} */ diff --git a/sys/include/net/conn/udp.h b/sys/include/net/conn/udp.h new file mode 100644 index 0000000000..68b40323fa --- /dev/null +++ b/sys/include/net/conn/udp.h @@ -0,0 +1,134 @@ +/* + * 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_conn_udp UDP connections + * @ingroup net_conn + * @brief Connection submodule for UDP connections + * @{ + * + * @file + * @brief UDP connection definitions + * + * @author Martine Lenders + */ +#ifndef NET_CONN_UDP_H_ +#define NET_CONN_UDP_H_ + +#include +#include + +#ifdef MODULE_GNRC_CONN_UDP +#include "net/gnrc/conn.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Forward declaration of @ref conn_udp_t to allow for external definition. + */ +struct conn_udp; + +/** + * @brief Implementation-specific type of a UDP connection object + */ +typedef struct conn_udp conn_udp_t; + +/** + * @brief Creates a new UDP connection object + * + * @param[out] conn Preallocated connection object. Must fill the size of the stack-specific + * connection desriptor. + * @param[in] addr The local network layer address for @p conn. + * @param[in] addr_len The length of @p addr. Must be fitting for the @p family. + * @param[in] family The family of @p addr (see @ref net_af). + * @param[in] port The local UDP port for @p conn. + * + * @return 0 on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' bind() function specification. + */ +int conn_udp_create(conn_udp_t *conn, const void *addr, size_t addr_len, int family, + uint16_t port); + +/** + * @brief Closes a UDP connection + * + * @param[in,out] conn A UDP connection object. + */ +void conn_udp_close(conn_udp_t *conn); + +/** + * @brief Gets the local address of a UDP connection + * + * @param[in] conn A UDP connection object. + * @param[out] addr The local network layer address. Must have space for any address of + * the connection's family. + * @param[out] port The local UDP port. + * + * @return length of @p addr on success. + * @return any other negative number in case of an error. For portability implementations should + * draw inspiration of the errno values from the POSIX' getsockname() function + * specification. + */ +int conn_udp_getlocaladdr(conn_udp_t *conn, void *addr, uint16_t *port); + +/** + * @brief Receives a UDP message + * + * @param[in] conn A UDP connection object. + * @param[out] data Pointer where the received data should be stored. + * @param[in] max_len Maximum space available at @p data. + * @param[out] addr NULL pointer or the sender's network layer address. Must have space + * for any address of the connection's family. + * @param[out] addr_len Length of @p addr. Can be NULL if @p addr is NULL. + * @param[out] port NULL pointer or the sender's UDP port. + * + * @note Function may block. + * + * @return The number of bytes received on success. + * @return 0, if no received data is available, but everything is in order. + * @return any other negative number in case of an error. For portability, implementations should + * draw inspiration of the errno values from the POSIX' recv(), recvfrom(), or recvmsg() + * function specification. + */ +int conn_udp_recvfrom(conn_udp_t *conn, void *data, size_t max_len, void *addr, size_t *addr_len, + uint16_t *port); + +/** + * @brief Sends a UDP message + * + * @param[in] data Pointer where the received data should be stored. + * @param[in] len Maximum space available at @p data. + * @param[in] src The source address. May be NULL for all any interface address. + * @param[in] src_len Length of @p src. May be 0 if @p src is NULL + * @param[in] dst The receiver's network address. + * @param[in] dst_len Length of @p dst. + * @param[in] family The family of @p src and @p dst (see @ref net_af). + * @param[in] sport The source UDP port. + * @param[in] dport The receiver's UDP port. + * + * @note Function may block. + * + * @return The number of bytes send on success. + * @return any other negative number in case of an error. For portability, implementations should + * draw inspiration of the errno values from the POSIX' send(), sendfrom(), or sendmsg() + * function specification. + */ +int conn_udp_sendto(const void *data, size_t len, const void *src, size_t src_len, + const void *dst, size_t dst_len, int family, uint16_t sport, + uint16_t dport); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_CONN_UDP4_H_ */ +/** @} */ diff --git a/sys/include/net/gnrc/conn.h b/sys/include/net/gnrc/conn.h new file mode 100644 index 0000000000..f8e53efd0f --- /dev/null +++ b/sys/include/net/gnrc/conn.h @@ -0,0 +1,130 @@ +/* + * 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_gnrc_conn GNRC-specific @ref net_conn implementation + * @ingroup net_gnrc + * @brief GNRC-specific @ref net_conn implementation + * @{ + * + * @file + * @brief GNRC-specific types and function definitions + * + * @author Martine Lenders + */ +#ifndef GNRC_CONN_H_ +#define GNRC_CONN_H_ + +#include +#include +#include "net/ipv6/addr.h" +#include "net/gnrc.h" +#include "sched.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Connection base class + * @internal + */ +typedef struct { + gnrc_nettype_t l3_type; /**< Network layer type of the connection */ + gnrc_nettype_t l4_type; /**< Transport layer type of the connection */ + gnrc_netreg_entry_t netreg_entry; /**< @p net_ng_netreg entry for the connection */ +} conn_t; + +/** + * @brief Raw connection type + * @internal + * @extends conn_t + */ +struct conn_ip { + gnrc_nettype_t l3_type; /**< Network layer type of the connection. */ + gnrc_nettype_t l4_type; /**< Transport layer type of the connection. + * Always GNRC_NETTYPE_UNDEF */ + gnrc_netreg_entry_t netreg_entry; /**< @p net_ng_netreg entry for the connection */ + uint8_t local_addr[sizeof(ipv6_addr_t)]; /**< local IP address */ + size_t local_addr_len; /**< length of struct conn_ip::local_addr */ +}; + +/** + * @brief UDP connection type + * @internal + * @extends conn_t + */ +struct conn_udp { + gnrc_nettype_t l3_type; /**< Network layer type of the connection. + * Always GNRC_NETTYPE_IPV6 */ + gnrc_nettype_t l4_type; /**< Transport layer type of the connection. + * Always GNRC_NETTYPE_UDP */ + gnrc_netreg_entry_t netreg_entry; /**< @p net_ng_netreg entry for the connection */ + uint8_t local_addr[sizeof(ipv6_addr_t)]; /**< local IP address */ + size_t local_addr_len; /**< length of struct conn_ip::local_addr */ +}; + +/** + * @brief Bind connection to demux context + * + * @internal + * + * @param[out] entry @ref net_ng_netreg entry. + * @param[in] type @ref net_ng_nettype. + * @param[in] demux_ctx demux context (port or proto) for the connection. + */ +static inline void gnrc_conn_reg(gnrc_netreg_entry_t *entry, gnrc_nettype_t type, + uint32_t demux_ctx) +{ + entry->pid = sched_active_pid; + entry->demux_ctx = demux_ctx; + gnrc_netreg_register(type, entry); +} + +/** + * @brief Sets local address for a connection + * + * @internal + * + * @param[out] conn_addr Pointer to the local address on the connection. + * @param[in] addr An IPv6 address. + * + * @return true, if @p addr was a legal address (`::`, `::1` or an address assigned to any + * interface of this node) for the connection. + * @return false if @p addr was not a legal address for the connection. + */ +bool gnrc_conn6_set_local_addr(uint8_t *conn_addr, const ipv6_addr_t *addr); + +/** + * @brief Generic recvfrom + * + * @internal + * + * @param[in] conn Connection object. + * @param[out] data Pointer where the received data should be stored. + * @param[in] max_len Maximum space available at @p data. + * @param[out] addr NULL pointer or the sender's IP address. Must fit address of connection's + * family if not NULL. + * @param[out] addr_len Length of @p addr. May be NULL if @p addr is NULL. + * @param[out] port NULL pointer or the sender's port. + * + * @return The number of bytes received on success. + * @return 0, if no received data is available, but everything is in order. + * @return -ENOMEM, if received data was more than max_len. + * @returne -ETIMEDOUT, if more than 3 IPC messages were not @ref net_ng_netapi receive commands + * with the required headers in the packet + */ +int gnrc_conn_recvfrom(conn_t *conn, void *data, size_t max_len, void *addr, size_t *addr_len, + uint16_t *port); + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_CONN_H_ */ +/** @} */ diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 0b577bdd88..cc7f891353 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -1,3 +1,12 @@ +ifneq (,$(filter gnrc_conn,$(USEMODULE))) + DIRS += conn +endif +ifneq (,$(filter gnrc_conn_ip,$(USEMODULE))) + DIRS += conn/ip +endif +ifneq (,$(filter gnrc_conn_udp,$(USEMODULE))) + DIRS += conn/udp +endif ifneq (,$(filter gnrc_icmpv6,$(USEMODULE))) DIRS += network_layer/icmpv6 endif diff --git a/sys/net/gnrc/conn/Makefile b/sys/net/gnrc/conn/Makefile new file mode 100644 index 0000000000..a9b8d3687a --- /dev/null +++ b/sys/net/gnrc/conn/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_conn + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/conn/gnrc_conn.c b/sys/net/gnrc/conn/gnrc_conn.c new file mode 100644 index 0000000000..dfe0648327 --- /dev/null +++ b/sys/net/gnrc/conn/gnrc_conn.c @@ -0,0 +1,115 @@ +/* + * 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 + * @author Martine Lenders + */ + +#include "net/conn.h" +#include "net/ipv6/hdr.h" +#include "net/gnrc/conn.h" +#include "net/gnrc/ipv6/hdr.h" +#include "net/gnrc/ipv6/netif.h" +#include "net/udp.h" + +static inline size_t _srcaddr(void *addr, gnrc_pktsnip_t *hdr) +{ + switch (hdr->type) { +#ifdef MODULE_GNRC_IPV6 + case GNRC_NETTYPE_IPV6: + memcpy(addr, &((ipv6_hdr_t *)hdr->data)->src, sizeof(ipv6_addr_t)); + return sizeof(ipv6_addr_t); +#endif + default: + (void)addr; + return 0; + } +} + +static inline void _srcport(uint16_t *port, gnrc_pktsnip_t *hdr) +{ + switch (hdr->type) { +#ifdef MODULE_GNRC_UDP + case GNRC_NETTYPE_UDP: + memcpy(port, &((udp_hdr_t *)hdr->data)->src_port, sizeof(uint16_t)); + break; +#endif + default: + (void)port; + (void)hdr; + break; + } +} + +int gnrc_conn_recvfrom(conn_t *conn, void *data, size_t max_len, void *addr, size_t *addr_len, + uint16_t *port) +{ + msg_t msg; + int timeout = 3; + while ((timeout--) > 0) { + gnrc_pktsnip_t *pkt, *l3hdr; + msg_receive(&msg); + switch (msg.type) { + case GNRC_NETAPI_MSG_TYPE_RCV: + pkt = (gnrc_pktsnip_t *)msg.content.ptr; + if (pkt->size > max_len) { + return -ENOMEM; + } + LL_SEARCH_SCALAR(pkt, l3hdr, type, conn->l3_type); + if (l3hdr == NULL) { + msg_send_to_self(&msg); /* requeue invalid messages */ + continue; + } +#if defined(MODULE_CONN_UDP) || defined(MODULE_CONN_TCP) + if ((conn->l4_type != GNRC_NETTYPE_UNDEF) && (port != NULL)) { + gnrc_pktsnip_t *l4hdr; + LL_SEARCH_SCALAR(pkt, l4hdr, type, conn->l4_type); + if (l4hdr == NULL) { + msg_send_to_self(&msg); /* requeue invalid messages */ + continue; + } + _srcport(port, l4hdr); + } +#endif /* defined(MODULE_CONN_UDP) */ + if (addr != NULL) { + *addr_len = _srcaddr(addr, l3hdr); + } + memcpy(data, pkt->data, pkt->size); + return pkt->size; + default: + (void)port; + msg_send_to_self(&msg); /* requeue invalid messages */ + break; + } + } + return -ETIMEDOUT; +} + +#ifdef MODULE_GNRC_IPV6 +bool gnrc_conn6_set_local_addr(uint8_t *conn_addr, const ipv6_addr_t *addr) +{ + ipv6_addr_t *tmp; + if (!ipv6_addr_is_unspecified(addr) && + !ipv6_addr_is_loopback(addr) && + gnrc_ipv6_netif_find_by_addr(&tmp, addr) == KERNEL_PID_UNDEF) { + return false; + } + else if (ipv6_addr_is_loopback(addr) || ipv6_addr_is_unspecified(addr)) { + ipv6_addr_set_unspecified((ipv6_addr_t *)conn_addr); + } + else { + memcpy(conn_addr, addr, sizeof(ipv6_addr_t)); + } + return true; +} +#endif + +/** @} */ diff --git a/sys/net/gnrc/conn/ip/Makefile b/sys/net/gnrc/conn/ip/Makefile new file mode 100644 index 0000000000..bf3473a0b1 --- /dev/null +++ b/sys/net/gnrc/conn/ip/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_conn_ip + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/conn/ip/gnrc_conn_ip.c b/sys/net/gnrc/conn/ip/gnrc_conn_ip.c new file mode 100644 index 0000000000..52896703f4 --- /dev/null +++ b/sys/net/gnrc/conn/ip/gnrc_conn_ip.c @@ -0,0 +1,128 @@ +/* + * 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 + * @author Martine Lenders + */ + +#include +#include "net/af.h" +#include "net/gnrc/conn.h" +#include "net/gnrc/ipv6.h" + +#include "net/conn/ip.h" + +int conn_ip_create(conn_ip_t *conn, const void *addr, size_t addr_len, int family, int proto) +{ + switch (family) { +#ifdef MODULE_GNRC_IPV6 + case AF_INET6: + if (addr_len != sizeof(ipv6_addr_t)) { + return -EINVAL; + } + if (gnrc_conn6_set_local_addr(conn->local_addr, addr)) { + conn->l3_type = GNRC_NETTYPE_IPV6; + conn->local_addr_len = addr_len; + conn_ip_close(conn); /* unregister possibly registered netreg entry */ + gnrc_conn_reg(&conn->netreg_entry, conn->l3_type, (uint32_t)proto); + } + else { + return -EADDRNOTAVAIL; + } + break; +#endif + default: + (void)addr; + (void)addr_len; + (void)proto; + return -EAFNOSUPPORT; + } + conn->l4_type = GNRC_NETTYPE_UNDEF; + return 0; +} + +void conn_ip_close(conn_ip_t *conn) +{ + assert(conn->l4_type == GNRC_NETTYPE_UNDEF); + if (conn->netreg_entry.pid != KERNEL_PID_UNDEF) { + gnrc_netreg_unregister(conn->l3_type, &conn->netreg_entry); + } +} + +int conn_ip_getlocaladdr(conn_ip_t *conn, void *addr) +{ + assert(conn->l4_type == GNRC_NETTYPE_UNDEF); + memcpy(addr, &conn->local_addr, conn->local_addr_len); + return conn->local_addr_len; +} + +int conn_ip_recvfrom(conn_ip_t *conn, void *data, size_t max_len, void *addr, size_t *addr_len) +{ + assert(conn->l4_type == GNRC_NETTYPE_UNDEF); + switch (conn->l3_type) { +#ifdef MODULE_GNRC_IPV6 + case GNRC_NETTYPE_IPV6: + return gnrc_conn_recvfrom((conn_t *)conn, data, max_len, addr, addr_len, NULL); +#endif + default: + (void)data; + (void)max_len; + (void)addr; + (void)addr_len; + return -EBADF; + } +} + +int conn_ip_sendto(const void *data, size_t len, const void *src, size_t src_len, + void *dst, size_t dst_len, int family, int proto) +{ + gnrc_pktsnip_t *pkt, *hdr = NULL; + gnrc_nettype_t l3_type; + + pkt = gnrc_pktbuf_add(NULL, (void *)data, len, GNRC_NETTYPE_UNDEF); /* data will only be copied */ + + switch (family) { +#ifdef MODULE_GNRC_IPV6 + case AF_INET6: + if (((src != NULL) && (src_len != sizeof(ipv6_addr_t))) || + (dst_len != sizeof(ipv6_addr_t)) || + (((unsigned)proto) > 256U)) { + gnrc_pktbuf_release(pkt); + return -EINVAL; + } + /* addr will only be copied */ + hdr = gnrc_ipv6_hdr_build(pkt, (uint8_t *)src, src_len, (uint8_t *)dst, dst_len); + if (hdr == NULL) { + gnrc_pktbuf_release(pkt); + return -ENOMEM; + } + /* set next header to connection's proto */ + ipv6_hdr_t *ipv6_hdr = hdr->data; + ipv6_hdr->nh = (uint8_t)proto; + pkt = hdr; + l3_type = GNRC_NETTYPE_IPV6; + break; +#endif /* MODULE_GNRC_IPV6 */ + default: + (void)src; + (void)src_len; + (void)dst; + (void)dst_len; + (void)proto; + (void)hdr; + gnrc_pktbuf_release(pkt); + return -EAFNOSUPPORT; + } + + gnrc_netapi_dispatch_send(l3_type, GNRC_NETREG_DEMUX_CTX_ALL, pkt); + + return len; +} diff --git a/sys/net/gnrc/conn/udp/Makefile b/sys/net/gnrc/conn/udp/Makefile new file mode 100644 index 0000000000..ed44cc6afd --- /dev/null +++ b/sys/net/gnrc/conn/udp/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_conn_udp + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/conn/udp/gnrc_conn_udp.c b/sys/net/gnrc/conn/udp/gnrc_conn_udp.c new file mode 100644 index 0000000000..0aa9212173 --- /dev/null +++ b/sys/net/gnrc/conn/udp/gnrc_conn_udp.c @@ -0,0 +1,135 @@ +/* + * 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 + * @author Martine Lenders + */ + +#include +#include "net/af.h" +#include "net/gnrc/conn.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/udp.h" + +#include "net/conn/udp.h" + +int conn_udp_create(conn_udp_t *conn, const void *addr, size_t addr_len, + int family, uint16_t port) +{ + conn->l4_type = GNRC_NETTYPE_UDP; + switch (family) { +#ifdef MODULE_GNRC_IPV6 + case AF_INET6: + if (addr_len != sizeof(ipv6_addr_t)) { + return -EINVAL; + } + if (gnrc_conn6_set_local_addr(conn->local_addr, addr)) { + conn->l3_type = GNRC_NETTYPE_IPV6; + conn->local_addr_len = addr_len; + conn_udp_close(conn); /* unregister possibly registered netreg entry */ + gnrc_conn_reg(&conn->netreg_entry, conn->l4_type, (uint32_t)port); + } + else { + return -EADDRNOTAVAIL; + } + break; +#endif + default: + (void)addr; + (void)addr_len; + (void)port; + return -EAFNOSUPPORT; + } + return 0; +} + +void conn_udp_close(conn_udp_t *conn) +{ + assert(conn->l4_type == GNRC_NETTYPE_UDP); + if (conn->netreg_entry.pid != KERNEL_PID_UNDEF) { + gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &conn->netreg_entry); + } +} + +int conn_udp_getlocaladdr(conn_udp_t *conn, void *addr, uint16_t *port) +{ + assert(conn->l4_type == GNRC_NETTYPE_UDP); + memcpy(addr, &conn->local_addr, conn->local_addr_len); + *port = (uint16_t)conn->netreg_entry.demux_ctx; + return conn->local_addr_len; +} + +int conn_udp_recvfrom(conn_udp_t *conn, void *data, size_t max_len, void *addr, size_t *addr_len, + uint16_t *port) +{ + assert(conn->l4_type == GNRC_NETTYPE_UDP); + switch (conn->l3_type) { +#ifdef MODULE_GNRC_IPV6 + case GNRC_NETTYPE_IPV6: + return gnrc_conn_recvfrom((conn_t *)conn, data, max_len, addr, addr_len, port); +#endif + default: + (void)data; + (void)max_len; + (void)addr; + (void)addr_len; + (void)port; + return -EBADF; + } +} + +int conn_udp_sendto(const void *data, size_t len, const void *src, size_t src_len, + const void *dst, size_t dst_len, int family, uint16_t sport, + uint16_t dport) +{ + gnrc_pktsnip_t *pkt, *hdr = NULL; + + pkt = gnrc_pktbuf_add(NULL, (void *)data, len, GNRC_NETTYPE_UNDEF); /* data will only be copied */ + hdr = gnrc_udp_hdr_build(pkt, (uint8_t *)&sport, sizeof(uint16_t), (uint8_t *)&dport, + sizeof(uint16_t)); + if (hdr == NULL) { + gnrc_pktbuf_release(pkt); + return -ENOMEM; + } + pkt = hdr; + switch (family) { +#ifdef MODULE_GNRC_IPV6 + case AF_INET6: + if (((src != NULL) && (src_len != sizeof(ipv6_addr_t))) || + (dst_len != sizeof(ipv6_addr_t))) { + gnrc_pktbuf_release(pkt); + return -EINVAL; + } + /* addr will only be copied */ + hdr = gnrc_ipv6_hdr_build(pkt, (uint8_t *)src, src_len, (uint8_t *)dst, dst_len); + if (hdr == NULL) { + gnrc_pktbuf_release(pkt); + return -ENOMEM; + } + pkt = hdr; + break; +#endif /* MODULE_GNRC_IPV6 */ + default: + (void)hdr; + (void)src; + (void)src_len; + (void)dst; + (void)dst_len; + gnrc_pktbuf_release(pkt); + return -EAFNOSUPPORT; + } + + gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, pkt); + + return len; +} + +/** @} */ diff --git a/tests/conn_ip/Makefile b/tests/conn_ip/Makefile new file mode 100644 index 0000000000..6fb8b8001b --- /dev/null +++ b/tests/conn_ip/Makefile @@ -0,0 +1,25 @@ +APPLICATION = conn_ip + +BOARD ?= native + +RIOTBASE ?= $(CURDIR)/../.. + +BOARD_INSUFFICIENT_MEMORY := chronos msb-430 msb-430h nucleo-f334 stm32f0discovery telosb \ + wsn430-v1_3b wsn430-v1_4 z1 + +USEMODULE += gnrc_netif_default +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_icmpv6_echo +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_conn_ip +USEMODULE += od +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/conn_ip/ip.c b/tests/conn_ip/ip.c new file mode 100644 index 0000000000..fcbed6b91e --- /dev/null +++ b/tests/conn_ip/ip.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2015 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 examples + * @{ + * + * @file + * @brief Demonstrating the sending and receiving of UDP data over POSIX sockets. + * + * @author Martine Lenders + * + * @} + */ + +#include +#include +#include + +#include "od.h" +#include "net/af.h" +#include "net/conn/ip.h" +#include "net/ipv6.h" +#include "thread.h" +#include "xtimer.h" + +#define SERVER_MSG_QUEUE_SIZE (8) +#define SERVER_BUFFER_SIZE (64) + +static bool server_running; +static conn_ip_t server_conn; +static char server_buffer[SERVER_BUFFER_SIZE]; +static char server_stack[THREAD_STACKSIZE_DEFAULT]; +static msg_t server_msg_queue[SERVER_MSG_QUEUE_SIZE]; + +static void *_server_thread(void *args) +{ + ipv6_addr_t server_addr = IPV6_ADDR_UNSPECIFIED; + uint8_t protocol; + msg_init_queue(server_msg_queue, SERVER_MSG_QUEUE_SIZE); + /* parse protocol */ + protocol = (uint8_t)atoi((char *)args); + if (conn_ip_create(&server_conn, &server_addr, sizeof(server_addr), AF_INET6, protocol) < 0) { + return NULL; + } + server_running = true; + printf("Success: started IP server on protocol %" PRIu8 "\n", protocol); + while (1) { + int res; + ipv6_addr_t src; + size_t src_len = sizeof(ipv6_addr_t); + if ((res = conn_ip_recvfrom(&server_conn, server_buffer, sizeof(server_buffer), &src, + &src_len)) < 0) { + puts("Error on receive"); + } + else if (res == 0) { + puts("No data received"); + } + else { + od_hex_dump(server_buffer, res, 0); + } + } + return NULL; +} + + +static size_t _parse_data(uint8_t *out, const char *data) +{ + bool upper = true; + size_t out_size = 0; + while (*data != '\0') { + if ((*data >= '0') && (*data <= '9')) { + if (upper) { + *out = (char)(*data - '0') << 4; + } + else { + *out |= (char)(*data - '0'); + out++; + out_size++; + } + upper = !upper; + } + else if ((*data >= 'a') && (*data <= 'f')) { + if (upper) { + *out = (char)(*data - 'a' + 10) << 4; + } + else { + *out |= (char)(*data - 'a' + 10); + out++; + out_size++; + } + upper = !upper; + } + else if ((*data >= 'A') && (*data <= 'F')) { + if (upper) { + *out = (char)(*data - 'A' + 10) << 4; + } + else { + *out |= (char)(*data - 'A' + 10); + out++; + out_size++; + } + upper = !upper; + } + data++; + } + if (!upper) { + out_size++; + } + return out_size; +} + +static int ip_send(char *addr_str, char *port_str, char *data, unsigned int num, + unsigned int delay) +{ + ipv6_addr_t src = IPV6_ADDR_UNSPECIFIED, dst; + uint8_t protocol; + uint8_t byte_data[strlen(data) / 2]; + size_t data_len; + /* parse destination address */ + if (ipv6_addr_from_str(&dst, addr_str) == NULL) { + puts("Error: unable to parse destination address"); + return 1; + } + /* parse protocol */ + protocol = (uint8_t)atoi(port_str); + data_len = _parse_data(byte_data, data); + for (unsigned int i = 0; i < num; i++) { + if (conn_ip_sendto(byte_data, data_len, &src, sizeof(src), (struct sockaddr *)&dst, + sizeof(dst), AF_INET6, protocol) < 0) { + puts("could not send"); + } + else { + printf("Success: send %u byte to %s (next header: %" PRIu8 ")\n", + (unsigned)data_len, addr_str, protocol); + } + xtimer_usleep(delay); + } + return 0; +} + +static int ip_start_server(char *port_str) +{ + if (thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, + CREATE_STACKTEST, _server_thread, port_str, "IP server") <= KERNEL_PID_UNDEF) { + return 1; + } + return 0; +} + +int ip_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s [send|server]\n", argv[0]); + return 1; + } + + if (strcmp(argv[1], "send") == 0) { + uint32_t num = 1; + uint32_t delay = 1000000; + if (argc < 5) { + printf("usage: %s send [ []]\n", + argv[0]); + return 1; + } + if (argc > 5) { + num = (uint32_t)atoi(argv[5]); + } + if (argc > 6) { + delay = (uint32_t)atoi(argv[6]); + } + return ip_send(argv[2], argv[3], argv[4], num, delay); + } + else if (strcmp(argv[1], "server") == 0) { + if (argc < 3) { + printf("usage: %s server [start|stop]\n", argv[0]); + return 1; + } + if (strcmp(argv[2], "start") == 0) { + if (argc < 4) { + printf("usage %s server start \n", argv[0]); + return 1; + } + return ip_start_server(argv[3]); + } + else { + puts("error: invalid command"); + return 1; + } + } + else { + puts("error: invalid command"); + return 1; + } +} + +/** @} */ diff --git a/tests/conn_ip/main.c b/tests/conn_ip/main.c new file mode 100644 index 0000000000..2d3aa5fda7 --- /dev/null +++ b/tests/conn_ip/main.c @@ -0,0 +1,47 @@ +/* + * 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. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Test for raw IPv6 connections + * + * @author Martine Lenders + * + * This test application tests the gnrc_conn_ip module. If you select protocol 58 you can also + * test if gnrc is able to deal with multiple subscribers to ICMPv6 (gnrc_icmpv6 and this + * application). + * + * @} + */ + +#include + +#include "msg.h" +#include "shell.h" + +extern int ip_cmd(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + { "ip", "send hex over IP and listen for IP packets of certain type", ip_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("RIOT socket example application"); + /* start shell */ + puts("All up, running the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should be never reached */ + return 0; +}