1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

sys: net: add UHCP protocol code and gnrc client implementation

This commit is contained in:
Kaspar Schleiser 2016-03-30 12:28:39 +02:00
parent 987f7191d1
commit 4fb07a852d
9 changed files with 522 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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");

179
sys/include/net/uhcp.h Normal file
View File

@ -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 */
/** @} */

View File

@ -0,0 +1,3 @@
MODULE=uhcpc
CFLAGS += -DUHCP_CLIENT
include $(RIOTBASE)/Makefile.base

View File

@ -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

View File

@ -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");
}
}
}

View File

@ -0,0 +1,3 @@
MODULE = gnrc_uhcpc
include $(RIOTBASE)/Makefile.base

View File

@ -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");
}