diff --git a/Makefile.dep b/Makefile.dep index 2eb1d993dab40183a5d21f90261bbb2208fca0f4..baacd4f358c739d8161ec8a63b037c7f2f3371e4 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -19,6 +19,11 @@ ifneq (,$(filter nhdp,$(USEMODULE))) USEMODULE += oonf_rfc5444 endif +ifneq (,$(filter sntp,$(USEMODULE))) + USEMODULE += gnrc_sock_udp + USEMODULE += xtimer +endif + ifneq (,$(filter gnrc_netdev_default,$(USEMODULE))) USEMODULE += gnrc_netif USEMODULE += gnrc_netdev2 diff --git a/sys/Makefile b/sys/Makefile index 6f53b2bdaa05811b6b37a44e293c4aa90193a992..99ddbaba88a1cd50617eb7713f82896563591420 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -89,6 +89,10 @@ ifneq (,$(filter gnrc_uhcpc,$(USEMODULE))) DIRS += net/gnrc/application_layer/uhcpc endif +ifneq (,$(filter sntp,$(USEMODULE))) + DIRS += net/application_layer/sntp +endif + ifneq (,$(filter netopt,$(USEMODULE))) DIRS += net/crosslayer/netopt endif diff --git a/sys/include/net/ntp_packet.h b/sys/include/net/ntp_packet.h new file mode 100644 index 0000000000000000000000000000000000000000..c9e84b18e3775b3c67b6cbed7b6302afc09f1286 --- /dev/null +++ b/sys/include/net/ntp_packet.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016 Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + * + * 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_ntp_packet NTP Packet + * @ingroup net + * @brief The NTP packet module provides functionality to manipulate the NTP header + * @{ + * + * @file + * @brief NTP packet definitions + * + * @author Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ + +#ifndef NTP_PACKET_H_ +#define NTP_PACKET_H_ + +#include <stdint.h> +#include "byteorder.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @{ + * @brief Bit positions and masks for ntp_packet_t::li_vn_mode + */ +/** + * @brief Leap indicator position + */ +#define NTP_PACKET_LI_POS (6U) +#define NTP_PACKET_LI_MASK (0xc0) /**< leap indicator mask */ +#define NTP_PACKET_VN_POS (3U) /**< version position */ +#define NTP_PACKET_VN_MASK (0x38) /**< version mask */ +#define NTP_PACKET_MODE_MASK (0x07) /**< mode mask */ +/** @} */ + +#define NTP_VERSION (4U) /**< NTP version */ +#define NTP_PORT (123U) /**< NTP port number */ + +/** + * @brief NTP modes + */ +typedef enum { + NTP_MODE_RESERVED = 0, /**< reserved */ + NTP_MODE_SYM_ACTIVE, /**< symmetric active */ + NTP_MODE_SYM_PASSIVE, /**< symmetric passive */ + NTP_MODE_CLIENT, /**< client */ + NTP_MODE_SERVER, /**< server */ + NTP_MODE_BROADCAST, /**< broadcast */ + NTP_MODE_PRIV /**< reserved for private use */ +} ntp_mode_t; + +/** + * @brief NTP timestamp + * + * @see [RFC 5905, Section 6](https://tools.ietf.org/html/rfc5905#section-6) + */ +typedef struct __attribute__((packed)) { + network_uint32_t seconds; /**< seconds since 1 January 1900 00:00 UTC */ + network_uint32_t fraction; /**< fraction of seconds in 232 picoseconds */ +} ntp_timestamp_t; + +/** + * @brief NTP packet + * + * @see [RFC 5905, Section 7.3](https://tools.ietf.org/html/rfc5905#section-7.3) + */ +typedef struct __attribute__((packed)) { + uint8_t li_vn_mode; /**< leap indicator, version and mode */ + uint8_t stratum; /**< stratum */ + uint8_t poll; /**< poll in log2 seconds */ + uint8_t precision; /**< precision in log2 seconds */ + network_uint32_t root_delay; /**< root delay in NTP short format */ + network_uint32_t root_dispersion; /**< root dispersion in NTP short format */ + network_uint32_t reference_id; /**< reference ID */ + ntp_timestamp_t reference; /**< reference timestamp */ + ntp_timestamp_t origin; /**< origin timesptamp */ + ntp_timestamp_t receive; /**< receive timestamp */ + ntp_timestamp_t transmit; /**< transmit timestamp */ +} ntp_packet_t; + +/** + * @brief Set leap indicator in a NTP packet + * + * @param[in] packet The NTP packet + * @param[in] li Leap indicator + */ +static inline void ntp_packet_set_li(ntp_packet_t *packet, uint8_t li) +{ + packet->li_vn_mode &= ~NTP_PACKET_LI_MASK; + packet->li_vn_mode |= li << NTP_PACKET_LI_POS; +} + +/** + * @brief Set version in a NTP packet + * + * @param[in] packet The NTP packet + */ +static inline void ntp_packet_set_vn(ntp_packet_t *packet) +{ + packet->li_vn_mode &= ~NTP_PACKET_VN_MASK; + packet->li_vn_mode |= (NTP_VERSION << NTP_PACKET_VN_POS) & NTP_PACKET_VN_MASK; +} + +/** + * @brief Set mode in a NTP packet + * + * @param[in] packet The NTP packet + * @param[in] mode Mode + */ +static inline void ntp_packet_set_mode(ntp_packet_t *packet, ntp_mode_t mode) +{ + packet->li_vn_mode &= ~NTP_PACKET_MODE_MASK; + packet->li_vn_mode |= mode & NTP_PACKET_MODE_MASK; +} + +/** + * @brief Get leap indicator from a NTP packet + * + * @param[in] packet The NTP packet + * + * @return The leap indicator of @p packet + */ +static inline uint8_t ntp_packet_get_li(ntp_packet_t *packet) +{ + return (packet->li_vn_mode & NTP_PACKET_LI_MASK) >> NTP_PACKET_LI_POS; +} + +/** + * @brief Get version from a NTP packet + * + * @param[in] packet The NTP packet + * + * @return The version of @p packet + */ +static inline uint8_t ntp_packet_get_vn(ntp_packet_t *packet) +{ + return (packet->li_vn_mode & NTP_PACKET_VN_MASK) >> NTP_PACKET_VN_POS; +} + +/** + * @brief Get mode from a NTP packet + * + * @param[in] packet The NTP packet + * + * @return The version of @p packet + */ +static inline ntp_mode_t ntp_packet_get_mode(ntp_packet_t *packet) +{ + return (packet->li_vn_mode & NTP_PACKET_MODE_MASK); +} + +#ifdef __cplusplus +} +#endif + +#endif /* NTP_PACKET_H_ */ +/** @} */ diff --git a/sys/include/net/sntp.h b/sys/include/net/sntp.h new file mode 100644 index 0000000000000000000000000000000000000000..7390438fcc227cabfdb33ad99e085a08d2fdc27b --- /dev/null +++ b/sys/include/net/sntp.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + * + * 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_sntp Simple Network Time Protocol + * @ingroup net + * @brief Simple Network Time Protocol (SNTP) implementation + * @{ + * + * @file + * @brief SNTP definitions + * + * @author Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ + +#ifndef SNTP_H_ +#define SNTP_H_ + +#include <stdint.h> +#include "net/sock/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Synchronize with time server + * + * @param[in] server The time server + * @param[in] timeout Timeout for the server response + * + * @return 0 on success + * @return Negative number on error + */ +int sntp_sync(sock_udp_ep_t *server, uint32_t timeout); + +/** + * @brief Get real time offset from system time as returned by @ref xtimer_now64() + * + * @return Real time offset in microseconds relative to 1900-01-01 00:00 UTC + */ +int64_t sntp_get_offset(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SNTP_H_ */ +/** @} */ diff --git a/sys/net/application_layer/sntp/Makefile b/sys/net/application_layer/sntp/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b0ece6866351af60f7f3e227a9bd6386e09bd791 --- /dev/null +++ b/sys/net/application_layer/sntp/Makefile @@ -0,0 +1,2 @@ +MODULE = sntp +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/sntp/sntp.c b/sys/net/application_layer/sntp/sntp.c new file mode 100644 index 0000000000000000000000000000000000000000..c8fbb443bc306ac6184539ef582c9c68415487e7 --- /dev/null +++ b/sys/net/application_layer/sntp/sntp.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + * + * 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 + * @brief SNTP implementation + * + * @author Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + * @author Martine Lenders <m.lenders@fu-berlin.de> + * + * @} + */ + +#include <string.h> +#include "net/sntp.h" +#include "net/ntp_packet.h" +#include "net/sock/udp.h" +#include "xtimer.h" +#include "mutex.h" +#include "byteorder.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static sock_udp_t _sntp_sock; +static int64_t _sntp_offset = 0; +static mutex_t _sntp_mutex = MUTEX_INIT; +static ntp_packet_t _sntp_packet; + +int sntp_sync(sock_udp_ep_t *server, uint32_t timeout) +{ + int result; + + mutex_lock(&_sntp_mutex); + if ((result = sock_udp_create(&_sntp_sock, + NULL, + server, + 0)) < 0) { + DEBUG("Error creating UDP sock\n"); + mutex_unlock(&_sntp_mutex); + return result; + } + memset(&_sntp_packet, 0, sizeof(_sntp_packet)); + ntp_packet_set_vn(&_sntp_packet); + ntp_packet_set_mode(&_sntp_packet, NTP_MODE_CLIENT); + + if ((result = (int)sock_udp_send(&_sntp_sock, + &_sntp_packet, + sizeof(_sntp_packet), + NULL)) < 0) { + DEBUG("Error sending message\n"); + sock_udp_close(&_sntp_sock); + mutex_unlock(&_sntp_mutex); + return result; + } + if ((result = (int)sock_udp_recv(&_sntp_sock, + &_sntp_packet, + sizeof(_sntp_packet), + timeout, + NULL)) < 0) { + DEBUG("Error receiving message\n"); + sock_udp_close(&_sntp_sock); + mutex_unlock(&_sntp_mutex); + return result; + } + sock_udp_close(&_sntp_sock); + _sntp_offset = (byteorder_ntohl(_sntp_packet.transmit.seconds) * SEC_IN_USEC) + + ((byteorder_ntohl(_sntp_packet.transmit.fraction) * 232) + / 1000000) - xtimer_now64(); + mutex_unlock(&_sntp_mutex); + return 0; +} + +int64_t sntp_get_offset(void) +{ + int64_t result; + + mutex_lock(&_sntp_mutex); + result = _sntp_offset; + mutex_unlock(&_sntp_mutex); + return result; +} diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index b0c4baa2bb9e92dd592b1785ef3c76808fb777b7..0e0dc877b6d670f203fdcd443b87e83d09980dc8 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -66,6 +66,9 @@ endif ifneq (,$(filter ccn-lite-utils,$(USEMODULE))) SRC += sc_ccnl.c endif +ifneq (,$(filter sntp,$(USEMODULE))) + SRC += sc_sntp.c +endif # TODO # Conditional building not possible at the moment due to diff --git a/sys/shell/commands/sc_sntp.c b/sys/shell/commands/sc_sntp.c new file mode 100644 index 0000000000000000000000000000000000000000..87cab4abc0a38dba68927e5a88fcbcb74beb8cca --- /dev/null +++ b/sys/shell/commands/sc_sntp.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + * + * 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 sys_shell_commands + * @{ + * + * @file + * @brief Prints the real time offset from the system time + * + * @author Luminița Lăzărescu <cluminita.lazarescu@gmail.com> + */ + + +#include "net/sntp.h" +#include "net/ntp_packet.h" +#include "net/af.h" +#include "net/ipv6/addr.h" + +#define _DEFAULT_TIMEOUT (5000U); + +static void _usage(char *cmd) +{ + printf("Usage: %s <server addr> [<timeout>]\n", cmd); + puts("default: timeout = 5000"); +} + +int _ntpdate(int argc, char **argv) +{ + uint32_t timeout = _DEFAULT_TIMEOUT; + + if (argc < 2) { + _usage(argv[0]); + return 1; + } + sock_udp_ep_t server = { .port = NTP_PORT, .family = AF_INET6 }; + ipv6_addr_from_str((ipv6_addr_t *)&server.addr, argv[1]); + if (argc > 2) { + timeout = atoi(argv[2]); + } + if (sntp_sync(&server, timeout) < 0) { + puts("Error in synchronization"); + return 1; + } + printf("Offset: %i\n", (int)sntp_get_offset()); + return 0; +} diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index 39125e34afa0628c6ddd5aa9160f9a29de8f7e63..c6a2d1f83fe8b64e249f7344ccee8d26b6637fb9 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -129,6 +129,10 @@ extern int _ccnl_interest(int argc, char **argv); extern int _ccnl_fib(int argc, char **argv); #endif +#ifdef MODULE_SNTP +extern int _ntpdate(int argc, char **argv); +#endif + const shell_command_t _shell_command_list[] = { {"reboot", "Reboot the node", _reboot_handler}, #ifdef MODULE_CONFIG @@ -209,10 +213,13 @@ const shell_command_t _shell_command_list[] = { {"saul", "interact with sensors and actuators using SAUL", _saul }, #endif #ifdef MODULE_CCN_LITE_UTILS - { "ccnl_open", "opens an interface or socket", _ccnl_open}, - { "ccnl_int", "sends an interest", _ccnl_interest}, - { "ccnl_cont", "create content and populated it", _ccnl_content}, - { "ccnl_fib", "shows or modifies the CCN-Lite FIB", _ccnl_fib}, + { "ccnl_open", "opens an interface or socket", _ccnl_open }, + { "ccnl_int", "sends an interest", _ccnl_interest }, + { "ccnl_cont", "create content and populated it", _ccnl_content }, + { "ccnl_fib", "shows or modifies the CCN-Lite FIB", _ccnl_fib }, +#endif +#ifdef MODULE_SNTP + { "ntpdate", "synchronizes with a remote time server", _ntpdate }, #endif {NULL, NULL, NULL} };