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/cpu/native/netdev2_tap/netdev2_tap.c b/cpu/native/netdev2_tap/netdev2_tap.c index 6c1a39b833..43d24399ff 100644 --- a/cpu/native/netdev2_tap/netdev2_tap.c +++ b/cpu/native/netdev2_tap/netdev2_tap.c @@ -107,7 +107,7 @@ static inline void _isr(netdev2_t *netdev) #endif } -int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len) +static int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len) { if (dev != (netdev2_t *)&netdev2_tap) { return -ENODEV; @@ -137,7 +137,7 @@ int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len) return res; } -int _set(netdev2_t *dev, netopt_t opt, void *value, size_t value_len) +static int _set(netdev2_t *dev, netopt_t opt, void *value, size_t value_len) { (void)value_len; diff --git a/dist/tools/ethos/Makefile b/dist/tools/ethos/Makefile index 804f4aa4cc..f9862c0301 100644 --- a/dist/tools/ethos/Makefile +++ b/dist/tools/ethos/Makefile @@ -2,3 +2,6 @@ all: ethos ethos: ethos.c $(CC) -O3 -Wall ethos.c -o ethos + +clean: + rm -f ethos diff --git a/dist/tools/ethos/start_network.sh b/dist/tools/ethos/start_network.sh new file mode 100755 index 0000000000..c85e90c711 --- /dev/null +++ b/dist/tools/ethos/start_network.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +create_tap() { + ip tuntap add ${TAP} mode tap user ${USER} + sysctl -w net.ipv6.conf.${TAP}.forwarding=1 + sysctl -w net.ipv6.conf.${TAP}.accept_ra=0 + ip link set ${TAP} up + ip a a fe80::1/64 dev ${TAP} + ip a a fd00:dead:beef::1/128 dev lo + ip route add ${PREFIX} via fe80::2 dev ${TAP} +} + +remove_tap() { + ip tuntap del ${TAP} mode tap +} + +cleanup() { + echo "Cleaning up..." + remove_tap + ip a d fd00:dead:beef::1/128 dev lo + kill $UHCPD_PID + trap "" INT QUIT TERM EXIT +} + +start_uhcpd() { + ${UHCPD} ${TAP} ${PREFIX} > /dev/null & + UHCPD_PID=$! +} + +PORT=$1 +TAP=$2 +PREFIX=$3 +UHCPD=../uhcpd/bin/uhcpd + +[ -z "$PORT" -o -z "$TAP" -o -z "$PREFIX" ] && { + echo "usage: $0 " + exit 1 +} + +trap "cleanup" INT QUIT TERM EXIT + +create_tap && start_uhcpd && ./ethos $TAP $PORT diff --git a/dist/tools/uhcpd/.gitignore b/dist/tools/uhcpd/.gitignore new file mode 100644 index 0000000000..ba077a4031 --- /dev/null +++ b/dist/tools/uhcpd/.gitignore @@ -0,0 +1 @@ +bin diff --git a/dist/tools/uhcpd/Makefile b/dist/tools/uhcpd/Makefile new file mode 100644 index 0000000000..177b3434f1 --- /dev/null +++ b/dist/tools/uhcpd/Makefile @@ -0,0 +1,17 @@ +CFLAGS?=-g -O3 -Wall +CFLAGS_EXTRA=-DUHCP_SERVER +all: bin bin/uhcpd + +bin: + mkdir bin + +RIOTBASE:=../../.. +UHCP_DIR:=$(RIOTBASE)/sys/net/application_layer/uhcp +RIOT_INCLUDE=$(RIOTBASE)/sys/include +SRCS:=$(UHCP_DIR)/uhcp.c uhcpd.c +HDRS:=$(RIOT_INCLUDE)/net/uhcp.h +bin/uhcpd: $(SRCS) $(HDRS) + $(CC) $(CFLAGS) $(CFLAGS_EXTRA) -I$(RIOT_INCLUDE) $(SRCS) -o $@ + +clean: + rm -f bin/uhcpd diff --git a/dist/tools/uhcpd/project.py b/dist/tools/uhcpd/project.py new file mode 100644 index 0000000000..99fe1d986b --- /dev/null +++ b/dist/tools/uhcpd/project.py @@ -0,0 +1,5 @@ +# pyjam build file. See https://github.com/kaspar030/pyjam for info. + +default.CFLAGS = "-O3 -DUHCP_SYSTEM_LINUX -DUHCP_SERVER" + +Main("uhcpd") diff --git a/dist/tools/uhcpd/uhcpd.c b/dist/tools/uhcpd/uhcpd.c new file mode 100644 index 0000000000..18982511fc --- /dev/null +++ b/dist/tools/uhcpd/uhcpd.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file LICENSE for more details. + */ + +#define UHCP_MCAST_ADDR "ff15::ABCD" + +#include +#include +#include +#include +#include +#include +#include + +#include "net/uhcp.h" + +char _prefix[16]; +unsigned _prefix_len; + +int ipv6_addr_split(char *addr_str, char seperator, int _default) +{ + char *sep = addr_str; + while(*++sep) { + if (*sep == seperator) { + *sep++ = '\0'; + if (*sep) { + _default = atoi(sep); + } + break; + } + } + + return _default; +} + +int main(int argc, char *argv[]) +{ + static unsigned ifindex; + + if (argc < 3) { + fprintf(stderr, "usage: uhcpd \n"); + exit(1); + } + + ifindex = if_nametoindex(argv[1]); + if (!ifindex) { + fprintf(stderr, "error: invalid interface \"%s\"\n", argv[1]); + exit(1); + } + + _prefix_len = ipv6_addr_split(argv[2], '/', 64); + if ((!inet_pton(AF_INET6, argv[2], _prefix)) || (_prefix_len > 128)) { + fprintf(stderr, "error: cannot parse prefix\n"); + exit(1); + } + + char *addr_str = UHCP_MCAST_ADDR; + + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET6; + hint.ai_socktype = SOCK_DGRAM; + hint.ai_protocol = IPPROTO_UDP; + hint.ai_flags |= AI_NUMERICHOST; + + struct addrinfo *mcast_addr; + int res = getaddrinfo(addr_str, UHCP_PORT_STR, + &hint, &mcast_addr); + if (res != 0) { + perror("getaddrinfo()"); + exit(1); + } + + int sock = socket(mcast_addr->ai_family, mcast_addr->ai_socktype, + mcast_addr->ai_protocol); + if (sock < 0) { + perror("socket() failed"); + exit(1); + } + + if (bind(sock, mcast_addr->ai_addr, mcast_addr->ai_addrlen) < 0) { + perror("bind() failed"); + exit(1); + } + + /* join multicast group */ + struct ipv6_mreq mreq; + memcpy(&mreq.ipv6mr_multiaddr, + &((struct sockaddr_in6 *)mcast_addr->ai_addr)->sin6_addr, + sizeof(struct in6_addr)); + + mreq.ipv6mr_interface = ifindex; + + puts("Joining IPv6 multicast group..."); + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt(IPV6_JOIN_GROUP) failed"); + exit(1); + } + freeaddrinfo(mcast_addr); + + char buf[2048]; + struct sockaddr_in6 src_addr; + unsigned n = sizeof(src_addr);; + + puts("entering loop..."); + while(1) { + int nbytes = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*)&src_addr, &n); + if (nbytes < 0) { + perror("recvfrom() failed"); + continue; + } + uhcp_handle_udp((uint8_t *)buf, nbytes, (uint8_t *)&src_addr.sin6_addr, ntohs(src_addr.sin6_port), ifindex); + } + + close(sock); + exit(0); +} + +int udp_sendto(uint8_t *buf, size_t len, uint8_t *dst, uint16_t dst_port, uhcp_iface_t iface) +{ + struct sockaddr_in6 dst_addr; + memset(&dst_addr, '\0', sizeof(dst_addr)); + dst_addr.sin6_family = AF_INET6; + memcpy(&dst_addr.sin6_addr, dst, 16); + dst_addr.sin6_port = htons(dst_port); + dst_addr.sin6_scope_id = iface; + + int fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd == -1) { + perror("creating send socket"); + return -1; + } + + ssize_t res; + if ((res = sendto(fd, buf, len, 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr))) == -1) { + perror("udp_sendto(): sendto()"); + } + + close(fd); + + return res; +} diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 67b9c20942..a9801239f5 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -50,6 +50,7 @@ endif ifneq (,$(filter ethos,$(USEMODULE))) USEMODULE += netdev2_eth USEMODULE += random + USEMODULE += tsrb endif ifneq (,$(filter hih6130,$(USEMODULE))) diff --git a/drivers/ethos/ethos.c b/drivers/ethos/ethos.c index f625ed5126..28bcd1beed 100644 --- a/drivers/ethos/ethos.c +++ b/drivers/ethos/ethos.c @@ -26,6 +26,7 @@ #include "ethos.h" #include "periph/uart.h" #include "tsrb.h" +#include "irq.h" #include "net/netdev2.h" #include "net/netdev2/eth.h" @@ -41,7 +42,7 @@ static void _get_mac_addr(netdev2_t *dev, uint8_t* buf); static void ethos_isr(void *arg, uint8_t c); -const static netdev2_driver_t netdev2_driver_ethos; +static const netdev2_driver_t netdev2_driver_ethos; static const uint8_t _esc_esc[] = {ETHOS_ESC_CHAR, (ETHOS_ESC_CHAR ^ 0x20)}; static const uint8_t _esc_delim[] = {ETHOS_ESC_CHAR, (ETHOS_FRAME_DELIMITER ^ 0x20)}; @@ -297,12 +298,12 @@ static int _recv(netdev2_t *netdev, char* buf, int len, void* info) ethos_t * dev = (ethos_t *) netdev; if (buf) { - if (len < dev->last_framesize) { + if (len < (int)dev->last_framesize) { DEBUG("ethos _recv(): receive buffer too small."); return -1; } - len = dev->last_framesize; + len = (int)dev->last_framesize; dev->last_framesize = 0; if ((tsrb_get(&dev->inbuf, buf, len) != len)) { @@ -317,7 +318,7 @@ static int _recv(netdev2_t *netdev, char* buf, int len, void* info) } } -int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len) +static int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len) { int res = 0; @@ -340,7 +341,7 @@ int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len) } /* netdev2 interface */ -const static netdev2_driver_t netdev2_driver_ethos = { +static const netdev2_driver_t netdev2_driver_ethos = { .send = _send, .recv = _recv, .init = _init, diff --git a/examples/gnrc_border_router/Makefile b/examples/gnrc_border_router/Makefile index b4e1b4f343..6e17659aa9 100644 --- a/examples/gnrc_border_router/Makefile +++ b/examples/gnrc_border_router/Makefile @@ -12,26 +12,19 @@ BOARD_INSUFFICIENT_MEMORY := airfy-beacon msb-430 msb-430h pca10000 pca10005 \ spark-core stm32f0discovery telosb \ weio wsn430-v1_3b wsn430-v1_4 yunjia-nrf51822 z1 nucleo-f072 -ifeq (,$(SLIP_UART)) - # set default (last available UART) - SLIP_UART="UART_DEV(UART_NUMOF-1)" -endif -ifeq (,$(SLIP_BAUDRATE)) - # set default - SLIP_BAUDRATE=115200 -endif - +# use ethos (ethernet over serial) for network communication and stdio over +# UART, but not on native, as native has a tap interface towards the host. +ifeq (,$(filter native,$(BOARD))) GNRC_NETIF_NUMOF := 2 -INCLUDES += -I$(CURDIR) -CFLAGS += -DSLIP_UART=$(SLIP_UART) -CFLAGS += -DSLIP_BAUDRATE=$(SLIP_BAUDRATE) +USEMODULE += ethos gnrc_netdev2 +CFLAGS += '-DETHOS_UART=UART_DEV(0)' -DETHOS_BAUDRATE=115200 -DUSE_ETHOS_FOR_STDIO +FEATURES_REQUIRED += periph_uart +endif # Include packages that pull up and auto-init the link layer. # NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present USEMODULE += gnrc_netdev_default USEMODULE += auto_init_gnrc_netif -# Include SLIP package for IP over Serial communication -USEMODULE += gnrc_slip # Specify the mandatory networking modules for 6LoWPAN border router USEMODULE += gnrc_sixlowpan_border_router_default # Add forwarding table @@ -43,6 +36,9 @@ USEMODULE += shell USEMODULE += shell_commands USEMODULE += ps +# include UHCP client +USEMODULE += gnrc_uhcpc + # Comment this out to disable code in RIOT that does safety checking # which is not needed in a production environment but helps in the # development process: diff --git a/examples/gnrc_border_router/slip_params.h b/examples/gnrc_border_router/slip_params.h deleted file mode 100644 index 3978ef7b82..0000000000 --- a/examples/gnrc_border_router/slip_params.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 tests - * @{ - * - * @file - * @brief slip parameters example, used by auto_init_gnrc_netif - * - * @author Martine Lenders - */ - -#ifndef SLIP_PARAMS_H -#define SLIP_PARAMS_H - -#include "net/gnrc/slip.h" - -#ifdef __cplusplus -extern "C" { -#endif - -static gnrc_slip_params_t gnrc_slip_params[] = { - { - .uart = SLIP_UART, - .baudrate = SLIP_BAUDRATE, - }, -}; - -#ifdef __cplusplus -} -#endif -#endif /* SLIP_PARAMS_H */ -/** @} */ 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 + * + * 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 + */ + +#ifndef UHCP_H +#define UHCP_H + +#include +#include +#include + +#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 + * + * 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 + +#include +#include +#include + +#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 + * + * 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..a8ee6a9e64 --- /dev/null +++ b/sys/net/gnrc/application_layer/uhcpc/gnrc_uhcpc.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * 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); + + for (size_t i = 0; i < numof && i < GNRC_NETIF_NUMOF; i++) { + kernel_pid_t dev = ifs[i]; + int is_wired = gnrc_netapi_get(dev, NETOPT_IS_WIRED, 0, NULL, 0); + if ((!gnrc_border_interface) && (is_wired == 1)) { + 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) && (is_wired != 1)) { + gnrc_wireless_interface = dev; + } + + if (gnrc_border_interface && gnrc_wireless_interface) { + break; + } + } + + LOG_INFO("gnrc_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) +{ + (void)prefix_len; + (void)lifetime; + (void)src; + + eui64_t iid; + if (!gnrc_wireless_interface) { + LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): received prefix, but don't know any wireless interface\n"); + return; + } + + if ((kernel_pid_t)iface != gnrc_border_interface) { + LOG_WARNING("gnrc_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("gnrc_uhcpc: uhcp_handle_prefix(): cannot get IID of wireless interface\n"); + return; + } + + if (ipv6_addr_equal(&_prefix, (ipv6_addr_t*)prefix)) { + LOG_WARNING("gnrc_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("gnrc_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("gnrc_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) +{ + (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(); + + /* only start client if more than one interface is given */ + if (! (gnrc_border_interface && gnrc_wireless_interface)) { + LOG_WARNING("gnrc_uhcpc: only one interface found, skipping setup.\n"); + return; + } + + /* initiate uhcp client */ + thread_create(_uhcp_client_stack, sizeof(_uhcp_client_stack), + THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, + uhcp_client_thread, NULL, "uhcp"); +}