From 4fb07a852dd32fb578850ab672de899e46536267 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser <kaspar@schleiser.de> Date: Wed, 30 Mar 2016 12:28:39 +0200 Subject: [PATCH] sys: net: add UHCP protocol code and gnrc client implementation --- Makefile.dep | 6 + sys/Makefile | 8 + sys/auto_init/auto_init.c | 5 + sys/include/net/uhcp.h | 179 ++++++++++++++++++ sys/net/application_layer/uhcp/Makefile | 3 + sys/net/application_layer/uhcp/uhcp.c | 111 +++++++++++ sys/net/application_layer/uhcp/uhcpc.c | 75 ++++++++ sys/net/gnrc/application_layer/uhcpc/Makefile | 3 + .../gnrc/application_layer/uhcpc/gnrc_uhcpc.c | 132 +++++++++++++ 9 files changed, 522 insertions(+) create mode 100644 sys/include/net/uhcp.h create mode 100644 sys/net/application_layer/uhcp/Makefile create mode 100644 sys/net/application_layer/uhcp/uhcp.c create mode 100644 sys/net/application_layer/uhcp/uhcpc.c create mode 100644 sys/net/gnrc/application_layer/uhcpc/Makefile create mode 100644 sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c diff --git a/Makefile.dep b/Makefile.dep index 283a83df2d..d2b09b295e 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -25,6 +25,12 @@ ifneq (,$(filter netdev2_ieee802154,$(USEMODULE))) USEMODULE += ieee802154 endif +ifneq (,$(filter gnrc_uhcpc,$(USEMODULE))) + USEMODULE += uhcpc + USEMODULE += gnrc_conn_udp + USEMODULE += fmt +endif + ifneq (,$(filter gnrc_%,$(filter-out gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pktbuf,$(USEMODULE)))) USEMODULE += gnrc endif diff --git a/sys/Makefile b/sys/Makefile index eaea7e3815..c6ffeb132a 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -78,6 +78,14 @@ ifneq (,$(filter hamming256,$(USEMODULE))) DIRS += ecc/hamming256 endif +ifneq (,$(filter uhcpc,$(USEMODULE))) + DIRS += net/application_layer/uhcp +endif + +ifneq (,$(filter gnrc_uhcpc,$(USEMODULE))) + DIRS += net/gnrc/application_layer/uhcpc +endif + ifneq (,$(filter netopt,$(USEMODULE))) DIRS += net/crosslayer/netopt endif diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 9981271385..d9ca369481 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -213,6 +213,11 @@ void auto_init(void) gnrc_ipv6_netif_init_by_dev(); #endif +#ifdef MODULE_GNRC_UHCPC + extern void auto_init_gnrc_uhcpc(void); + auto_init_gnrc_uhcpc(); +#endif + /* initialize sensors and actuators */ #ifdef MODULE_AUTO_INIT_SAUL DEBUG("auto_init SAUL\n"); diff --git a/sys/include/net/uhcp.h b/sys/include/net/uhcp.h new file mode 100644 index 0000000000..1f8c21d358 --- /dev/null +++ b/sys/include/net/uhcp.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de> + * + * 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_uhcp UHCP + * @ingroup net + * @brief Provides UHCP (micro host configuration protocol) + * @{ + * + * @file + * @brief UHCP header + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + */ + +#ifndef UHCP_H +#define UHCP_H + +#include <stdint.h> +#include <stddef.h> +#include <arpa/inet.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief UHCP magic number */ +#define UHCP_MAGIC (0x55484350) /* "UHCP" in hex */ + +/** @brief UHCP version of this header */ +#define UHCP_VER (0) + +/** @brief UHCP port number */ +#define UHCP_PORT (12345U) + +/** @brief UHCP port number (as string for e.g., getaddrinfo() service arg */ +#define UHCP_PORT_STR "12345" + +/** @brief Enum containing possible UHCP packet types */ +typedef enum { + UHCP_REQ, /**< packet is a request packet */ + UHCP_PUSH /**< packet is a push / answer packet */ +} uhcp_type_t; + +/** + * @brief UHCP packet header struct + */ +typedef struct __attribute__((packed)) { + uint32_t uhcp_magic; /**< always contains UHCP in hex */ + uint8_t ver_type; /**< four bits version number, four bits + packet type (see uchp_type_t) */ +} uhcp_hdr_t; + +/** + * @brief struct for request packets + * + * @extends uhcp_hdr_t + */ +typedef struct __attribute__((packed)) { + uhcp_hdr_t hdr; /**< member holding parent type */ + uint8_t prefix_len; /**< contains the requested prefix length */ +} uhcp_req_t; + +/** + * @brief struct for push packets + * + * @extends uhcp_hdr_t + */ +typedef struct __attribute__((packed)) { + uhcp_hdr_t hdr; /**< member holding parent type */ + uint8_t prefix_len; /**< contains the prefix length of assigned + prefix */ + uint8_t prefix[]; /**< contains the assigned prefix */ +} uhcp_push_t; + +/** @brief typedef for interface handle */ +typedef unsigned uhcp_iface_t; + +/** + * @brief handle incoming UDP packet + * + * This function should be called by UHCP server/client network code for every + * incoming UDP packet destined to UCHP_PORT. + * + * @param[in] buf buffer containing UDP packet + * @param[in] len length of @c buf + * @param[in] src ptr to IPv6 source address + * @param[in] port source port of packet + * @param[in] iface interface number of incoming packet + */ +void uhcp_handle_udp(uint8_t *buf, size_t len, uint8_t *src, uint16_t port, uhcp_iface_t iface); + +/** + * @brief handle incoming UHCP request packet + * + * This function will be called by uhcp_handle_udp() for incoming request + * packet. + * + * @internal + * + * @param[in] req ptr to UHCP request header + * @param[in] src ptr to IPv6 source address + * @param[in] port source port of packet + * @param[in] iface number of interface the packet came in + */ +void uhcp_handle_req(uhcp_req_t *req, uint8_t *src, uint16_t port, uhcp_iface_t iface); + +/** + * @brief handle incoming UHCP push packet + * + * This function will be called by uhcp_handle_udp() for incoming push + * packet. + * + * @internal + * + * @param[in] req ptr to UHCP push header + * @param[in] src ptr to IPv6 source address + * @param[in] port source port of packet + * @param[in] iface number of interface the packet came in + */ +void uhcp_handle_push(uhcp_push_t *req, uint8_t *src, uint16_t port, uhcp_iface_t iface); + +/** + * @brief handle incoming prefix (as parsed from push packet) + * + * Supposed to be implemented by UHCP client implementations. + * + * The function might be called with an already configured prefix. In that + * case, the lifetime *MUST* be updated. + * + * If the function is called with a different prefix than before, the old + * prefix *MUST* be considered obsolete. + * + * @param[in] prefix ptr to assigned prefix + * @param[in] prefix_len length of assigned prefix + * @param[in] lifetime lifetime of prefix + * @param[in] src ptr to IPv6 source address + * @param[in] iface number of interface the packet came in + */ +void uhcp_handle_prefix(uint8_t *prefix, uint8_t prefix_len, uint16_t lifetime, uint8_t *src, uhcp_iface_t iface); + +/** + * @brief function to set constant values in UHCP header + * + * @internal + * + * @param[out] hdr hdr to set up + * @param[in] type type of packet (request or push) + */ +static inline void uhcp_hdr_set(uhcp_hdr_t *hdr, uhcp_type_t type) +{ + hdr->uhcp_magic = htonl(UHCP_MAGIC); + hdr->ver_type = (UHCP_VER << 4) | (type & 0xF); +} + +/** + * @brief UDP send function used by UHCP client / server + * + * Supposed to be implemented by UHCP clients. + * + * @param[in] buf buffer to send + * @param[in] len length of buf + * @param[in] dst ptr to IPv6 destination address + * @param[in] dst_port destination port + * @param[in] dst_iface interface number of destination interface + */ +int udp_sendto(uint8_t *buf, size_t len, uint8_t *dst, uint16_t dst_port, uhcp_iface_t dst_iface); + +#ifdef __cplusplus +} +#endif + +#endif /* UHCP_H */ +/** @} */ diff --git a/sys/net/application_layer/uhcp/Makefile b/sys/net/application_layer/uhcp/Makefile new file mode 100644 index 0000000000..5bf81d28db --- /dev/null +++ b/sys/net/application_layer/uhcp/Makefile @@ -0,0 +1,3 @@ +MODULE=uhcpc +CFLAGS += -DUHCP_CLIENT +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/application_layer/uhcp/uhcp.c b/sys/net/application_layer/uhcp/uhcp.c new file mode 100644 index 0000000000..6f4ea7b162 --- /dev/null +++ b/sys/net/application_layer/uhcp/uhcp.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de> + * + * 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. + */ + +#include <stdio.h> + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <string.h> + +#include "net/uhcp.h" + +void uhcp_handle_udp(uint8_t *buf, size_t len, uint8_t *src, uint16_t port, uhcp_iface_t iface) +{ + char addr_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, src, addr_str, INET6_ADDRSTRLEN); + printf("got packet from %s port %u\n", addr_str, (unsigned)port); + + if (len < sizeof(uhcp_req_t)) { + puts("error: packet too small."); + return; + } + + uhcp_hdr_t *hdr = (uhcp_hdr_t *)buf; + + if (! (ntohl(hdr->uhcp_magic) == UHCP_MAGIC)) { + puts("error: wrong magic number."); + return; + } + + unsigned ver, type; + ver = hdr->ver_type >> 4; + type = hdr->ver_type & 0xF; + + if (ver != UHCP_VER) { + puts("error: wrong protocol version."); + } + + switch(type) { +#ifdef UHCP_SERVER + case UHCP_REQ: + if (len < sizeof(uhcp_req_t)) { + puts("error: request too small\n"); + } + else { + uhcp_handle_req((uhcp_req_t*)hdr, src, port, iface); + } + break; +#endif +#ifdef UHCP_CLIENT + case UHCP_PUSH: + { + uhcp_push_t *push = (uhcp_push_t*)hdr; + if ((len < sizeof(uhcp_push_t)) + || (len < (sizeof(uhcp_push_t) + (push->prefix_len >> 3))) + ) { + puts("error: request too small\n"); + } + else { + uhcp_handle_push(push, src, port, iface); + } + break; + } +#endif + default: + puts("error: unexpected type\n"); + } +} + +#ifdef UHCP_SERVER +extern char _prefix[16]; +extern unsigned _prefix_len; +void uhcp_handle_req(uhcp_req_t *req, uint8_t *src, uint16_t port, uhcp_iface_t iface) +{ + size_t prefix_bytes = (_prefix_len + 7)>>3; + uint8_t packet[sizeof(uhcp_push_t) + prefix_bytes]; + + uhcp_push_t *reply = (uhcp_push_t *)packet; + uhcp_hdr_set(&reply->hdr, UHCP_PUSH); + + reply->prefix_len = _prefix_len; + memcpy(reply->prefix, _prefix, prefix_bytes); + + int res = udp_sendto(packet, sizeof(packet), src, port, iface); + if (res == -1) { + printf("uhcp_handle_req(): udp_sendto() res=%i\n", res); + } +} +#endif /* UHCP_SERVER */ + +#ifdef UHCP_CLIENT +void uhcp_handle_push(uhcp_push_t *req, uint8_t *src, uint16_t port, uhcp_iface_t iface) +{ + char addr_str[INET6_ADDRSTRLEN]; + char prefix_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, src, addr_str, INET6_ADDRSTRLEN); + uint8_t prefix[16]; + size_t prefix_bytes = (req->prefix_len + 7)>>3; + memset(prefix + 16 - prefix_bytes, '\0', 16 - prefix_bytes); + memcpy(prefix, req->prefix, prefix_bytes); + + inet_ntop(AF_INET6, prefix, prefix_str, INET6_ADDRSTRLEN); + + printf("uhcp: push from %s:%u prefix=%s/%u\n", addr_str, (unsigned)port, prefix_str, req->prefix_len); + uhcp_handle_prefix(prefix, req->prefix_len, 0xFFFF, src, iface); +} +#endif diff --git a/sys/net/application_layer/uhcp/uhcpc.c b/sys/net/application_layer/uhcp/uhcpc.c new file mode 100644 index 0000000000..bc1b32652d --- /dev/null +++ b/sys/net/application_layer/uhcp/uhcpc.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de> + * + * 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. + */ + +#include "net/uhcp.h" + +#include "net/af.h" +#include "net/conn/udp.h" +#include "net/ipv6/addr.h" +#include "xtimer.h" + +static void _timeout(void *arg) { + kernel_pid_t pid = *(kernel_pid_t*)arg; + msg_t msg; + msg_send_int(&msg, pid); + msg_send_int(&msg, pid); + msg_send_int(&msg, pid); +} + +/** + * @brief Request prefix from uhcp server + * + * Never returns. + * Calls @c uhcp_handle_prefix() when a prefix or prefix change is received. + * + * @param[in] iface interface to request prefix on + */ +void uhcp_client(uhcp_iface_t iface) +{ + ipv6_addr_t target; + ipv6_addr_from_str(&target, "ff15::abcd"); + + /* prepare UHCP header */ + uhcp_req_t req; + uhcp_hdr_set(&req.hdr, UHCP_REQ); + req.prefix_len = 64; + + /* create listening socket */ + ipv6_addr_t zero = {{0}}; + conn_udp_t conn; + int res = conn_udp_create(&conn, &zero, 16, AF_INET6, UHCP_PORT); + + uint8_t srv_addr[16]; + size_t srv_addr_len; + uint16_t srv_port; + uint8_t buf[sizeof(uhcp_push_t) + 16]; + + kernel_pid_t pid = thread_getpid(); + xtimer_t timeout; + timeout.callback = _timeout; + timeout.arg = &pid; + + while(1) { + xtimer_set(&timeout, 10U*SEC_IN_USEC); + puts("uhcp_client(): sending REQ..."); + conn_udp_sendto(&req, sizeof(uhcp_req_t), NULL, 0, &target, 16, AF_INET6 , 12345, 12345); + res = conn_udp_recvfrom(&conn, buf, sizeof(buf), srv_addr, &srv_addr_len, &srv_port); + if (res > 0) { + xtimer_remove(&timeout); + uhcp_handle_udp(buf, res, srv_addr, srv_port, iface); + xtimer_sleep(60); + } + else { + msg_t msg; + msg_try_receive(&msg); + msg_try_receive(&msg); + msg_try_receive(&msg); + puts("uhcp_client(): timeout waiting for reply"); + } + } +} diff --git a/sys/net/gnrc/application_layer/uhcpc/Makefile b/sys/net/gnrc/application_layer/uhcpc/Makefile new file mode 100644 index 0000000000..8f29e62611 --- /dev/null +++ b/sys/net/gnrc/application_layer/uhcpc/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_uhcpc + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c b/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c new file mode 100644 index 0000000000..bd4c016be8 --- /dev/null +++ b/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de> + * + * 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. + */ + +#include "net/fib.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/ipv6/nc.h" +#include "net/gnrc/ipv6/netif.h" +#include "net/gnrc/netapi.h" +#include "net/gnrc/netif.h" +#include "net/ipv6/addr.h" +#include "net/netdev2.h" +#include "net/netopt.h" + +#include "net/uhcp.h" +#include "log.h" +#include "fmt.h" + +static kernel_pid_t gnrc_border_interface; +static kernel_pid_t gnrc_wireless_interface; + +static void set_interface_roles(void) +{ + kernel_pid_t ifs[GNRC_NETIF_NUMOF]; + size_t numof = gnrc_netif_get(ifs); + uint16_t dev_type; + + for (size_t i = 0; i < numof && i < GNRC_NETIF_NUMOF; i++) { + kernel_pid_t dev = ifs[i]; + int res = gnrc_netapi_get(dev, NETOPT_DEVICE_TYPE, 0, &dev_type, sizeof(dev_type)); + if (res <= 0) { + dev_type = NETDEV2_TYPE_UNKNOWN; + } + if ((!gnrc_border_interface) && (dev_type == NETDEV2_TYPE_ETHERNET)) { + ipv6_addr_t addr, defroute; + gnrc_border_interface = dev; + + ipv6_addr_from_str(&addr, "fe80::2"); + gnrc_ipv6_netif_add_addr(dev, &addr, 64, + GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST); + + ipv6_addr_from_str(&defroute, "::"); + ipv6_addr_from_str(&addr, "fe80::1"); + fib_add_entry(&gnrc_ipv6_fib_table, dev, defroute.u8, 16, + 0x00, addr.u8, 16, 0, + (uint32_t)FIB_LIFETIME_NO_EXPIRE); + } + else if ((!gnrc_wireless_interface) && (dev_type == NETDEV2_TYPE_UNKNOWN)) { + gnrc_wireless_interface = dev; + } + + if (gnrc_border_interface && gnrc_wireless_interface) { + break; + } + } + + LOG_INFO("uhcpc: Using %u as border interface and %u as wireless interface.\n", gnrc_border_interface, gnrc_wireless_interface); +} + +static ipv6_addr_t _prefix; + +void uhcp_handle_prefix(uint8_t *prefix, uint8_t prefix_len, uint16_t lifetime, uint8_t *src, uhcp_iface_t iface) +{ + eui64_t iid; + if (!gnrc_wireless_interface) { + LOG_WARNING("uhcpc: uhcp_handle_prefix(): received prefix, but don't know any wireless interface\n"); + return; + } + + if (iface != gnrc_border_interface) { + LOG_WARNING("uhcpc: uhcp_handle_prefix(): received prefix from unexpected interface\n"); + return; + } + + if (gnrc_netapi_get(gnrc_wireless_interface, NETOPT_IPV6_IID, 0, &iid, + sizeof(eui64_t)) >= 0) { + ipv6_addr_set_aiid((ipv6_addr_t*)prefix, iid.uint8); + } + else { + LOG_WARNING("uhcpc: uhcp_handle_prefix(): cannot get IID of wireless interface\n"); + return; + } + + if (ipv6_addr_equal(&_prefix, (ipv6_addr_t*)prefix)) { + LOG_WARNING("uhcpc: uhcp_handle_prefix(): got same prefix again\n"); + return; + } + + gnrc_ipv6_netif_add_addr(gnrc_wireless_interface, (ipv6_addr_t*)prefix, 64, + GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST | + GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_AUTO); + + gnrc_ipv6_netif_remove_addr(gnrc_wireless_interface, &_prefix); + print_str("uhcpc: uhcp_handle_prefix(): configured new prefix "); + ipv6_addr_print((ipv6_addr_t*)prefix); + puts("/64"); + + if (!ipv6_addr_is_unspecified(&_prefix)) { + gnrc_ipv6_netif_remove_addr(gnrc_wireless_interface, &_prefix); + print_str("uhcpc: uhcp_handle_prefix(): removed old prefix "); + ipv6_addr_print(&_prefix); + puts("/64"); + } + + memcpy(&_prefix, prefix, 16); +} + +extern void uhcp_client(uhcp_iface_t iface); + +static char _uhcp_client_stack[THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF]; +static msg_t _uhcp_msg_queue[4]; + +static void* uhcp_client_thread(void *arg) +{ + msg_init_queue(_uhcp_msg_queue, sizeof(_uhcp_msg_queue)/sizeof(msg_t)); + uhcp_client(gnrc_border_interface); + return NULL; +} + +void auto_init_gnrc_uhcpc(void) +{ + set_interface_roles(); + + /* initiate uhcp client */ + thread_create(_uhcp_client_stack, sizeof(_uhcp_client_stack), + THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, + uhcp_client_thread, NULL, "uhcp"); +} -- GitLab