mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #8796 from miri64/dhcpv6c-pd/feat/initial
dhcpv6: Initial implementation of a client (with IA_PD support)
This commit is contained in:
commit
48fbd446fe
21
Makefile.dep
21
Makefile.dep
@ -32,6 +32,16 @@ ifneq (,$(filter csma_sender,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter dhcpv6_%,$(USEMODULE)))
|
||||
USEMODULE += dhcpv6
|
||||
endif
|
||||
|
||||
ifneq (,$(filter dhcpv6_client,$(USEMODULE)))
|
||||
USEMODULE += event
|
||||
USEMODULE += random
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_mac,$(USEMODULE)))
|
||||
USEMODULE += gnrc_priority_pktqueue
|
||||
USEMODULE += csma_sender
|
||||
@ -76,6 +86,17 @@ ifneq (,$(filter netdev_ieee802154,$(USEMODULE)))
|
||||
USEMODULE += random
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_dhcpv6_%, $(USEMODULE)))
|
||||
USEMODULE += gnrc_dhcpv6
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_dhcpv6_client,$(USEMODULE)))
|
||||
USEMODULE += dhcpv6_client
|
||||
USEMODULE += gnrc_ipv6_nib
|
||||
USEMODULE += gnrc_netif
|
||||
USEMODULE += gnrc_sock_udp
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_uhcpc,$(USEMODULE)))
|
||||
USEMODULE += uhcpc
|
||||
USEMODULE += gnrc_sock_udp
|
||||
|
@ -1,4 +1,5 @@
|
||||
PSEUDOMODULES += at_urc
|
||||
PSEUDOMODULES += auto_init_dhcpv6_client
|
||||
PSEUDOMODULES += auto_init_gnrc_rpl
|
||||
PSEUDOMODULES += can_mbox
|
||||
PSEUDOMODULES += can_pm
|
||||
@ -10,10 +11,12 @@ PSEUDOMODULES += core_%
|
||||
PSEUDOMODULES += cortexm_fpu
|
||||
PSEUDOMODULES += cpu_check_address
|
||||
PSEUDOMODULES += devfs_%
|
||||
PSEUDOMODULES += dhcpv6_%
|
||||
PSEUDOMODULES += ecc_%
|
||||
PSEUDOMODULES += emb6_router
|
||||
PSEUDOMODULES += event_%
|
||||
PSEUDOMODULES += fmt_%
|
||||
PSEUDOMODULES += gnrc_dhcpv6_%
|
||||
PSEUDOMODULES += gnrc_ipv6_default
|
||||
PSEUDOMODULES += gnrc_ipv6_router
|
||||
PSEUDOMODULES += gnrc_ipv6_router_default
|
||||
|
@ -13,6 +13,9 @@ endif
|
||||
ifneq (,$(filter posix_semaphore,$(USEMODULE)))
|
||||
DIRS += posix/semaphore
|
||||
endif
|
||||
ifneq (,$(filter dhcpv6,$(USEMODULE)))
|
||||
DIRS += net/application_layer/dhcpv6
|
||||
endif
|
||||
ifneq (,$(filter posix_sockets,$(USEMODULE)))
|
||||
DIRS += posix/sockets
|
||||
endif
|
||||
|
@ -624,4 +624,10 @@ void auto_init(void)
|
||||
test_utils_interactive_sync();
|
||||
#endif
|
||||
#endif /* MODULE_TEST_UTILS_INTERACTIVE_SYNC */
|
||||
|
||||
#ifdef MODULE_AUTO_INIT_DHCPV6_CLIENT
|
||||
DEBUG("auto_init DHCPv6 client");
|
||||
extern void dhcpv6_client_auto_init(void);
|
||||
dhcpv6_client_auto_init();
|
||||
#endif /* MODULE_AUTO_INIT_DHCPV6_CLIENT */
|
||||
}
|
||||
|
43
sys/include/net/arp.h
Normal file
43
sys/include/net/arp.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_arp Address resolution protocol (ARP)
|
||||
* @ingroup net_ipv4
|
||||
* @brief ARP definitions
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief ARP definitions
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef NET_ARP_H
|
||||
#define NET_ARP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Hardware types
|
||||
* @anchor net_arp_hwtype
|
||||
* @see [IANA ARP parameters]
|
||||
* (https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#table-arp-parameters-2)
|
||||
* @{
|
||||
*/
|
||||
#define ARP_HWTYPE_ETHERNET (1U) /**< Ethernet */
|
||||
#define ARP_HWTYPE_EUI64 (27U) /**< EUI-64 */
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_ARP_H */
|
||||
/** @} */
|
98
sys/include/net/dhcpv6.h
Normal file
98
sys/include/net/dhcpv6.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_dhcpv6 Dynamic Host Configuration Protocol for IPv6 (DHCPv6)
|
||||
* @ingroup net_ipv6
|
||||
* @brief DHCPv6 definitions
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief DHCPv6 definitions
|
||||
* @note This header is based on [RFC 8415](https://tools.ietf.org/html/rfc8415)
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef NET_DHCPV6_H
|
||||
#define NET_DHCPV6_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name DHCPv6 ports
|
||||
* @see [RFC 8415, section 7.2]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-7.2)
|
||||
* @{
|
||||
*/
|
||||
#define DHCPV6_CLIENT_PORT (546U) /**< client port */
|
||||
#ifndef DHCPV6_SERVER_PORT /* only reconfigure for testing!!1! */
|
||||
#define DHCPV6_SERVER_PORT (547U) /**< server and relay agent port */
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DHCPv6 message types
|
||||
* @anchor net_dhcp6_msg_types
|
||||
* @see [RFC 8415, section 7.3]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-7.3)
|
||||
* @{
|
||||
*/
|
||||
#define DHCPV6_SOLICIT (1U) /**< SOLICIT */
|
||||
#define DHCPV6_ADVERTISE (2U) /**< ADVERTISE */
|
||||
#define DHCPV6_REQUEST (3U) /**< REQUEST */
|
||||
#define DHCPV6_RENEW (5U) /**< RENEW */
|
||||
#define DHCPV6_REBIND (6U) /**< REBIND */
|
||||
#define DHCPV6_REPLY (7U) /**< REPLY */
|
||||
/** @ } */
|
||||
|
||||
/**
|
||||
* @name DHCPv6 option codes
|
||||
* @anchor net_dhcp6_opt_codes
|
||||
* @see [RFC 8415, section 21]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21)
|
||||
* @{
|
||||
*/
|
||||
#define DHCPV6_OPT_CID (1U) /**< client identifier option */
|
||||
#define DHCPV6_OPT_SID (2U) /**< server identifier option */
|
||||
#define DHCPV6_OPT_ORO (6U) /**< option request option */
|
||||
#define DHCPV6_OPT_PREF (7U) /**< preference option */
|
||||
#define DHCPV6_OPT_ELAPSED_TIME (8U) /**< elapsed time option */
|
||||
#define DHCPV6_OPT_STATUS (13U) /**< status code option */
|
||||
#define DHCPV6_OPT_IA_PD (25U) /**< identity association for prefix
|
||||
* delegation (IA_PD) option */
|
||||
#define DHCPV6_OPT_IAPFX (26U) /**< IA prefix option */
|
||||
#define DHCPV6_OPT_SMR (82U) /**< SOL_MAX_RT option */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DHCPv6 status codes
|
||||
* @anchor net_dhcp6_status_codes
|
||||
* @see [RFC 8415, section 21.13]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.13)
|
||||
* @{
|
||||
*/
|
||||
#define DHCPV6_STATUS_SUCCESS (0U) /**< Success */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DHCPv6 unique identifier (DUID) types
|
||||
* @see [RFC 8415, section 11.1]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-11.1)
|
||||
* @{
|
||||
*/
|
||||
#define DHCPV6_DUID_TYPE_L2 (3U) /**< Link-layer address */
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_DHCPV6_H */
|
||||
/** @} */
|
168
sys/include/net/dhcpv6/client.h
Normal file
168
sys/include/net/dhcpv6/client.h
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup net_dhcpv6_client DHCPv6 client
|
||||
* @ingroup net_dhcpv6
|
||||
* @brief DHCPv6 client implementation
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief DHCPv6 client definitions
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef NET_DHCPV6_CLIENT_H
|
||||
#define NET_DHCPV6_CLIENT_H
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "event.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Auto-initialization parameters
|
||||
*/
|
||||
#ifndef DHCPV6_CLIENT_STACK_SIZE
|
||||
#define DHCPV6_CLIENT_STACK_SIZE (THREAD_STACKSIZE_DEFAULT) /**< stack size */
|
||||
#endif
|
||||
|
||||
#ifndef DHCPV6_CLIENT_PRIORITY
|
||||
#define DHCPV6_CLIENT_PRIORITY (THREAD_PRIORITY_MAIN - 2) /**< priority */
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Static length of the DUID
|
||||
*/
|
||||
#define DHCPV6_CLIENT_DUID_LEN (sizeof(dhcpv6_duid_l2_t) + 8U)
|
||||
#define DHCPV6_CLIENT_BUFLEN (256) /**< length for send and receive buffer */
|
||||
#ifndef DHCPV6_CLIENT_SERVER_MAX
|
||||
#define DHCPV6_CLIENT_SERVER_MAX (1U) /**< maximum number of servers to store */
|
||||
#endif
|
||||
#ifndef DHCPV6_CLIENT_PFX_LEASE_MAX
|
||||
#define DHCPV6_CLIENT_PFX_LEASE_MAX (1U) /**< maximum number of prefix leases to store */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name DHCPv6 unique identifier (DUID) definitions
|
||||
* @see [RFC 8415, section 11](https://tools.ietf.org/html/rfc8415#section-11)
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief DUID based on link-layer address plus time
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_DUID_TYPE_L2 */
|
||||
network_uint16_t l2type; /**< [hardware type](@ref net_arp_hwtype)) */
|
||||
/* link-layer address follows this header */
|
||||
} dhcpv6_duid_l2_t;
|
||||
|
||||
#if defined(MODULE_AUTO_INIT_DHCPV6_CLIENT) || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Auto-initializes the client in its own thread
|
||||
*
|
||||
* @note Only available with (and called by) the `dhcpv6_client_auto_init`
|
||||
* module.
|
||||
*/
|
||||
void dhcpv6_client_auto_init(void);
|
||||
#endif /* MODULE_DHCPV6_CLIENT_AUTO_INIT */
|
||||
|
||||
/**
|
||||
* @brief Initializes the client
|
||||
*
|
||||
* @pre `event_queue->waiter != NULL`
|
||||
*
|
||||
* @param[in] event_queue Event queue to use with the client. Needs to be
|
||||
* initialized in the handler thread.
|
||||
* @param[in] netif The network interface the client should listen on.
|
||||
* SOCK_ADDR_ANY_NETIF for any interface
|
||||
*/
|
||||
void dhcpv6_client_init(event_queue_t *event_queue, uint16_t netif);
|
||||
|
||||
/**
|
||||
* @brief Let the server start listening
|
||||
*
|
||||
* This needs to be called *after* all desired [configuration functions]
|
||||
* (@ref net_dhcpv6_client_conf) where called.
|
||||
*/
|
||||
void dhcpv6_client_start(void);
|
||||
|
||||
/**
|
||||
* @name Configuration functions
|
||||
* @anchor net_dhcpv6_client_conf
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Configures the client to request prefix delegation for a network
|
||||
* interface from a server
|
||||
*
|
||||
* @pre `pfx_len <= 128`
|
||||
*
|
||||
* @param[in] netif The interface to request the prefix delegation for.
|
||||
* @param[in] pfx_len The desired length of the prefix (note that the server
|
||||
* might not consider this request). Must be <= 128
|
||||
*/
|
||||
void dhcpv6_client_req_ia_pd(unsigned netif, unsigned pfx_len);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Stack-specific functions
|
||||
*
|
||||
* These functions need to be provided by the network-stack implementation.
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Get the link-layer address DUID for the client
|
||||
*
|
||||
* @param[in] netif The network interface the client is bound to. May be
|
||||
* SOCK_ADDR_ANY_NETIF for any interface.
|
||||
* @param[out] duid The resulting DUID.
|
||||
*
|
||||
* @return length of the @p duid on success.
|
||||
* @return 0, on error.
|
||||
*/
|
||||
unsigned dhcpv6_client_get_duid_l2(unsigned netif, dhcpv6_duid_l2_t *duid);
|
||||
|
||||
/**
|
||||
* @brief Configures a prefix delegation lease that is provided by the server.
|
||||
*
|
||||
* @param[in] netif Network interface the prefix delegation was for.
|
||||
* @param[in] pfx Prefix for the prefix delegation.
|
||||
* @param[in] pfx_len Length of @p pfx in bits.
|
||||
* @param[in] valid Valid lifetime of the prefix delegation.
|
||||
* @param[in] pref Preferred lifetime of the prefix delegation.
|
||||
*/
|
||||
void dhcpv6_client_conf_prefix(unsigned netif, const ipv6_addr_t *pfx,
|
||||
unsigned pfx_len, uint32_t valid,
|
||||
uint32_t pref);
|
||||
|
||||
/**
|
||||
* @brief Determines how long the prefix delegation lease is still valid.
|
||||
*
|
||||
* @param[in] netif Network interface the prefix delegation was for.
|
||||
* @param[in] pfx Prefix of the prefix delegation
|
||||
* @param[in] pfx_len Length of @p pfx in bits.
|
||||
*
|
||||
* @return Remaining valid lifetime of the prefix delegation lease in seconds.
|
||||
*/
|
||||
uint32_t dhcpv6_client_prefix_valid_until(unsigned netif,
|
||||
const ipv6_addr_t *pfx,
|
||||
unsigned pfx_len);
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_DHCPV6_CLIENT_H */
|
||||
/** @} */
|
@ -53,6 +53,11 @@ extern "C" {
|
||||
*/
|
||||
#define US_PER_MS (1000U)
|
||||
|
||||
/**
|
||||
* @brief The number of microseconds per centisecond
|
||||
*/
|
||||
#define US_PER_CS (10000U)
|
||||
|
||||
/**
|
||||
* @brief The number of nanoseconds per microsecond
|
||||
*/
|
||||
|
3
sys/net/application_layer/dhcpv6/Makefile
Normal file
3
sys/net/application_layer/dhcpv6/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
SUBMODULES := 1
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
222
sys/net/application_layer/dhcpv6/_dhcpv6.h
Normal file
222
sys/net/application_layer/dhcpv6/_dhcpv6.h
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 net_dhcpv6
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Internal DHCPv6 definitions
|
||||
* @note This header is based on [RFC 8415](https://tools.ietf.org/html/rfc8415)
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef PRIV_DHCPV6_H
|
||||
#define PRIV_DHCPV6_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @name DHCPv6 multicast addresses
|
||||
* @see [RFC 8415, section 7.1]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-7.1)
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Multicast address used by clients to communicate with neighboring
|
||||
* relay agents and servers
|
||||
*
|
||||
* @note Corresponds with `All_DHCP_Relay_Agents_and_Servers` in the draft.
|
||||
*
|
||||
*/
|
||||
#define DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS { 0xff, 0x02, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x01, 0x00, 0x02 \
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DHCPv6 transmission and retransmission parameters
|
||||
* @see [RFC 8415, section 7.6]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-7.6)
|
||||
* @{
|
||||
*/
|
||||
#define DHCPV6_SOL_MAX_DELAY (1U) /**< SOL_MAX_DELAY (in sec) */
|
||||
#define DHCPV6_SOL_TIMEOUT (1U) /**< SOL_TIMEOUT (in sec) */
|
||||
#define DHCPV6_SOL_MAX_RT (3600U) /**< SOL_MAX_RT (in sec) */
|
||||
|
||||
#define DHCPV6_REQ_TIMEOUT (1U) /**< REQ_TIMEOUT (in sec) */
|
||||
#define DHCPV6_REQ_MAX_RT (30U) /**< REQ_MAX_RT (in sec) */
|
||||
#define DHCPV6_REQ_MAX_RC (10U) /**< REQ_MAX_RC */
|
||||
|
||||
#define DHCPV6_REN_TIMEOUT (10U) /**< REN_TIMEOUT (in sec) */
|
||||
#define DHCPV6_REN_MAX_RT (600U) /**< REN_MAX_RT (in sec) */
|
||||
|
||||
#define DHCPV6_REB_TIMEOUT (10U) /**< REB_TIMEOUT (in sec) */
|
||||
#define DHCPV6_REB_MAX_RT (600U) /**< REB_MAX_RT (in sec) */
|
||||
/** @} */
|
||||
|
||||
#define DHCPV6_DUID_MAX_LEN (128U) /**< maximum length of DUID */
|
||||
|
||||
/**
|
||||
* @name DHCPv6 message formats
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Client/Server message header
|
||||
* @see [RFC 8415, section 8]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-8)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t type; /**< message type (see [DHCPv6 messeg types ](@ref net_dhcp6_msg_types)) */
|
||||
uint8_t tid[3]; /**< transaction ID */
|
||||
} dhcpv6_msg_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Generic storage DUID
|
||||
*/
|
||||
typedef union {
|
||||
uint8_t u8[DHCPV6_DUID_MAX_LEN]; /**< array representation */
|
||||
dhcpv6_duid_l2_t duid_l2; /**< DUID-L2 type */
|
||||
} dhcpv6_duid_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DHCPv6 options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief General DHCPv6 option format
|
||||
* @see [RFC 8415, section 21.1]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.1)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< [option code](@ref net_dhcp6_opt_codes) */
|
||||
network_uint16_t len; /**< length of dhcpv6_opt_t::data in byte */
|
||||
uint8_t data[]; /**< option data */
|
||||
} dhcpv6_opt_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 client or server identifier option format
|
||||
* @see [RFC 8415, section 21.2]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.2)
|
||||
* @see [RFC 8415, section 21.3]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.3)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_CID or DHCPV6_OPT_SID */
|
||||
network_uint16_t len; /**< length of dhcpv6_opt_t::duid in byte */
|
||||
uint8_t duid[]; /**< the DUID of the client or server */
|
||||
} dhcpv6_opt_duid_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 option request option format
|
||||
* @see [RFC 8415, section 21.7]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.7)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_ORO */
|
||||
network_uint16_t len; /**< 2 * number of dhcpv6_opt_oro_t::opt_codes */
|
||||
network_uint16_t opt_codes[]; /**< option-code for an option requested by the client */
|
||||
} dhcpv6_opt_oro_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 preference option format
|
||||
* @see [RFC 8415, section 21.8]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.8)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_PREF */
|
||||
network_uint16_t len; /**< always 1 */
|
||||
uint8_t value; /**< preference value for the server */
|
||||
} dhcpv6_opt_pref_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 elapsed time option format
|
||||
* @see [RFC 8415, section 21.9]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.9)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_ELAPSED_TIME */
|
||||
network_uint16_t len; /**< always 2 */
|
||||
/**
|
||||
* @brief amount of time since client began current DHCPv6 transaction
|
||||
* (in cs) */
|
||||
network_uint16_t elapsed_time;
|
||||
} dhcpv6_opt_elapsed_time_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 status code option format
|
||||
* @see [RFC 8415, section 21.13]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.13)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_STATUS */
|
||||
network_uint16_t len; /**< 2 + length of dhcpv6_opt_status_t::msg in byte */
|
||||
network_uint16_t code; /**< [status code](@ref net_dhcp6_status_codes) */
|
||||
char msg[]; /**< UTF-8 encoded text string (not 0-terminated!) */
|
||||
} dhcpv6_opt_status_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 identity association for prefix delegation option (IA_PD)
|
||||
* format
|
||||
* @see [RFC 8415, section 21.21]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.21)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_IA_PD */
|
||||
network_uint16_t len; /**< 12 + length of dhcpv6_opt_ia_pd_t::opts in byte */
|
||||
network_uint32_t ia_id; /**< Unique ID for this IA_PD */
|
||||
network_uint32_t t1; /**< DHCPv6 T1 time (in sec) */
|
||||
network_uint32_t t2; /**< DHCPv6 T2 time (in sec) */
|
||||
uint8_t opts[]; /**< IA_PD options */
|
||||
} dhcpv6_opt_ia_pd_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 IA prefix option format
|
||||
* @see [RFC 8415, section 21.22]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.22)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_IAPFX */
|
||||
network_uint16_t len; /**< 25 + length of dhcpv6_opt_iapfx_t::opts in byte */
|
||||
network_uint32_t pref; /**< preferred lifetime (in sec) */
|
||||
network_uint32_t valid; /**< valid lifetime (in sec) */
|
||||
uint8_t pfx_len; /**< length of dhcpv6_opt_iapfx_t::pfx in bits */
|
||||
ipv6_addr_t pfx; /**< the prefix */
|
||||
uint8_t opts[]; /**< IAprefix options */
|
||||
} dhcpv6_opt_iapfx_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 SOL_MAX_RT option format
|
||||
* @see [RFC 8415, section 21.24]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.24)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_SMR */
|
||||
network_uint16_t len; /**< always 4 */
|
||||
network_uint32_t value; /**< overriding value for SOL_MAX_RT (in sec) */
|
||||
} dhcpv6_opt_smr_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PRIV_DHCPV6_H */
|
||||
/** @} */
|
847
sys/net/application_layer/dhcpv6/client.c
Normal file
847
sys/net/application_layer/dhcpv6/client.c
Normal file
@ -0,0 +1,847 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "kernel_defines.h"
|
||||
#include "net/dhcpv6/client.h"
|
||||
#include "net/dhcpv6.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "random.h"
|
||||
#include "timex.h"
|
||||
#include "xtimer.h"
|
||||
#include "xtimer/implementation.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#include "_dhcpv6.h"
|
||||
|
||||
/**
|
||||
* @brief Representation of a generic lease
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
uint32_t id;
|
||||
struct {
|
||||
uint16_t netif;
|
||||
uint16_t type;
|
||||
} info;
|
||||
} ia_id;
|
||||
} lease_t;
|
||||
|
||||
/**
|
||||
* @brief Representation of a DHCPv6 prefix deligation lease
|
||||
* @extends lease_t
|
||||
*/
|
||||
typedef struct {
|
||||
lease_t parent;
|
||||
ipv6_addr_t pfx;
|
||||
uint8_t pfx_len;
|
||||
uint8_t leased;
|
||||
} pfx_lease_t;
|
||||
|
||||
/**
|
||||
* @brief Client representation of a DHCPv6 server
|
||||
*/
|
||||
typedef struct {
|
||||
dhcpv6_duid_t duid;
|
||||
uint32_t t1;
|
||||
uint8_t pref;
|
||||
uint8_t duid_len;
|
||||
} server_t;
|
||||
|
||||
static uint8_t send_buf[DHCPV6_CLIENT_BUFLEN];
|
||||
static uint8_t recv_buf[DHCPV6_CLIENT_BUFLEN];
|
||||
static uint8_t best_adv[DHCPV6_CLIENT_BUFLEN];
|
||||
static uint8_t duid[DHCPV6_CLIENT_DUID_LEN];
|
||||
static pfx_lease_t pfx_leases[DHCPV6_CLIENT_PFX_LEASE_MAX];
|
||||
static server_t server;
|
||||
static xtimer_t timer, rebind_timer;
|
||||
static event_queue_t *event_queue;
|
||||
static sock_udp_t sock;
|
||||
static sock_udp_ep_t local = { .family = AF_INET6, .port = DHCPV6_CLIENT_PORT };
|
||||
static sock_udp_ep_t remote = { .family = AF_INET6, .port = DHCPV6_SERVER_PORT,
|
||||
.addr = {
|
||||
.ipv6 = DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS
|
||||
} };
|
||||
static uint32_t sol_max_rt = DHCPV6_SOL_MAX_RT;
|
||||
static uint32_t t2, rebind_time;
|
||||
static uint32_t transaction_start;
|
||||
static uint32_t transaction_id;
|
||||
static uint8_t duid_len = sizeof(dhcpv6_duid_l2_t);
|
||||
|
||||
static void _post_solicit_servers(void *args);
|
||||
static void _solicit_servers(event_t *event);
|
||||
static void _request(event_t *event);
|
||||
static void _renew(event_t *event);
|
||||
static void _rebind(event_t *event);
|
||||
|
||||
static event_t solicit_servers = { .handler = _solicit_servers };
|
||||
static event_t request = { .handler = _request };
|
||||
static event_t renew = { .handler = _renew };
|
||||
static event_t rebind = { .handler = _rebind };
|
||||
|
||||
#ifdef MODULE_AUTO_INIT_DHCPV6_CLIENT
|
||||
static char _thread_stack[DHCPV6_CLIENT_STACK_SIZE];
|
||||
static void *_thread(void *args);
|
||||
static kernel_pid_t _thread_pid;
|
||||
|
||||
void dhcpv6_client_auto_init(void)
|
||||
{
|
||||
if (_thread_pid <= 0) {
|
||||
_thread_pid = thread_create(_thread_stack, DHCPV6_CLIENT_STACK_SIZE,
|
||||
DHCPV6_CLIENT_PRIORITY,
|
||||
THREAD_CREATE_STACKTEST,
|
||||
_thread, NULL, "dhcpv6-client");
|
||||
}
|
||||
}
|
||||
|
||||
static void *_thread(void *args)
|
||||
{
|
||||
(void)args;
|
||||
event_queue_t event_queue;
|
||||
event_queue_init(&event_queue);
|
||||
dhcpv6_client_init(&event_queue, SOCK_ADDR_ANY_NETIF);
|
||||
/* TODO: add configuration for IA_NA here, when implemented */
|
||||
dhcpv6_client_start();
|
||||
event_loop(&event_queue); /* never returns */
|
||||
return NULL;
|
||||
}
|
||||
#endif /* MODULE_AUTO_INIT_DHCPV6_CLIENT */
|
||||
|
||||
void dhcpv6_client_init(event_queue_t *eq, uint16_t netif)
|
||||
{
|
||||
assert(eq->waiter != NULL);
|
||||
event_queue = eq;
|
||||
local.netif = netif;
|
||||
remote.netif = netif;
|
||||
}
|
||||
|
||||
void dhcpv6_client_start(void)
|
||||
{
|
||||
uint32_t delay = random_uint32_range(0, DHCPV6_SOL_MAX_DELAY * US_PER_SEC);
|
||||
|
||||
duid_len = dhcpv6_client_get_duid_l2(local.netif,
|
||||
(dhcpv6_duid_l2_t *)&duid);
|
||||
if (duid_len > 0) {
|
||||
sock_udp_create(&sock, &local, NULL, 0);
|
||||
timer.callback = _post_solicit_servers;
|
||||
xtimer_set(&timer, delay);
|
||||
}
|
||||
}
|
||||
|
||||
void dhcpv6_client_req_ia_pd(unsigned netif, unsigned pfx_len)
|
||||
{
|
||||
pfx_lease_t *lease = NULL;
|
||||
|
||||
assert(pfx_len <= 128);
|
||||
for (unsigned i = 0; i < DHCPV6_CLIENT_PFX_LEASE_MAX; i++) {
|
||||
if (pfx_leases[i].parent.ia_id.id == 0) {
|
||||
lease = &pfx_leases[i];
|
||||
lease->parent.ia_id.info.netif = netif;
|
||||
lease->parent.ia_id.info.type = DHCPV6_OPT_IA_PD;
|
||||
lease->pfx_len = pfx_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _post_solicit_servers(void *args)
|
||||
{
|
||||
(void)args;
|
||||
event_post(event_queue, &solicit_servers);
|
||||
}
|
||||
|
||||
static void _post_renew(void *args)
|
||||
{
|
||||
(void)args;
|
||||
event_post(event_queue, &renew);
|
||||
}
|
||||
|
||||
static void _post_rebind(void *args)
|
||||
{
|
||||
(void)args;
|
||||
event_post(event_queue, &rebind);
|
||||
}
|
||||
|
||||
static void _generate_tid(void)
|
||||
{
|
||||
transaction_id = random_uint32() & 0xffffff;
|
||||
}
|
||||
|
||||
static void _set_tid(uint8_t *tgt)
|
||||
{
|
||||
tgt[0] = (transaction_id & 0xff0000) >> 16;
|
||||
tgt[1] = (transaction_id & 0xff00) >> 8;
|
||||
tgt[2] = transaction_id & 0xff;
|
||||
}
|
||||
|
||||
static inline bool _is_tid(dhcpv6_msg_t *msg)
|
||||
{
|
||||
uint32_t tid = (((uint32_t)msg->tid[0]) << 16) |
|
||||
(((uint32_t)msg->tid[1]) << 8) |
|
||||
(msg->tid[2]);
|
||||
|
||||
return (transaction_id == (tid));
|
||||
}
|
||||
|
||||
static inline uint32_t _now_cs(void)
|
||||
{
|
||||
return (uint32_t)(xtimer_now_usec64() / US_PER_CS);
|
||||
}
|
||||
|
||||
static inline uint16_t _compose_cid_opt(dhcpv6_opt_duid_t *cid)
|
||||
{
|
||||
uint16_t len = duid_len;
|
||||
|
||||
cid->type = byteorder_htons(DHCPV6_OPT_CID);
|
||||
cid->len = byteorder_htons(len);
|
||||
memcpy(cid->duid, duid, duid_len);
|
||||
return len + sizeof(dhcpv6_opt_t);
|
||||
}
|
||||
|
||||
static inline uint16_t _compose_sid_opt(dhcpv6_opt_duid_t *sid)
|
||||
{
|
||||
uint16_t len = server.duid_len;
|
||||
|
||||
sid->type = byteorder_htons(DHCPV6_OPT_SID);
|
||||
sid->len = byteorder_htons(len);
|
||||
memcpy(sid->duid, server.duid.u8, server.duid_len);
|
||||
return len + sizeof(dhcpv6_opt_t);
|
||||
}
|
||||
|
||||
static inline uint16_t _get_elapsed_time(void)
|
||||
{
|
||||
uint32_t now = _now_cs();
|
||||
uint32_t elapsed_time = transaction_start - now;
|
||||
|
||||
if (elapsed_time > UINT16_MAX) {
|
||||
/* xtimer_now_usec64() overflowed since transaction_start */
|
||||
elapsed_time = (UINT32_MAX - transaction_start) + now + 1;
|
||||
}
|
||||
return elapsed_time;
|
||||
}
|
||||
|
||||
static inline size_t _compose_elapsed_time_opt(dhcpv6_opt_elapsed_time_t *time)
|
||||
{
|
||||
uint16_t len = 2U;
|
||||
|
||||
time->type = byteorder_htons(DHCPV6_OPT_ELAPSED_TIME);
|
||||
time->len = byteorder_htons(len);
|
||||
time->elapsed_time = byteorder_htons(_get_elapsed_time());
|
||||
return len + sizeof(dhcpv6_opt_t);
|
||||
}
|
||||
|
||||
static inline size_t _compose_oro_opt(dhcpv6_opt_oro_t *oro, uint16_t *opts,
|
||||
unsigned opts_num)
|
||||
{
|
||||
uint16_t len = 2U * opts_num;
|
||||
|
||||
oro->type = byteorder_htons(DHCPV6_OPT_ORO);
|
||||
oro->len = byteorder_htons(len);
|
||||
for (unsigned i = 0; i < opts_num; i++) {
|
||||
oro->opt_codes[i] = byteorder_htons(opts[i]);
|
||||
}
|
||||
return len + sizeof(dhcpv6_opt_t);
|
||||
}
|
||||
|
||||
static inline size_t _compose_ia_pd_opt(dhcpv6_opt_ia_pd_t *ia_pd,
|
||||
uint32_t ia_id, uint16_t opts_len)
|
||||
{
|
||||
uint16_t len = 12U + opts_len;
|
||||
|
||||
ia_pd->type = byteorder_htons(DHCPV6_OPT_IA_PD);
|
||||
ia_pd->len = byteorder_htons(len);
|
||||
ia_pd->ia_id = byteorder_htonl(ia_id);
|
||||
ia_pd->t1.u32 = 0;
|
||||
ia_pd->t2.u32 = 0;
|
||||
return len + sizeof(dhcpv6_opt_t);
|
||||
}
|
||||
|
||||
static inline size_t _add_ia_pd_from_config(uint8_t *buf)
|
||||
{
|
||||
size_t msg_len = 0;
|
||||
|
||||
for (unsigned i = 0; i < DHCPV6_CLIENT_PFX_LEASE_MAX; i++) {
|
||||
uint32_t ia_id = pfx_leases[i].parent.ia_id.id;
|
||||
if (ia_id != 0) {
|
||||
dhcpv6_opt_ia_pd_t *ia_pd = (dhcpv6_opt_ia_pd_t *)(&buf[msg_len]);
|
||||
|
||||
msg_len += _compose_ia_pd_opt(ia_pd, ia_id, 0U);
|
||||
}
|
||||
}
|
||||
return msg_len;
|
||||
}
|
||||
|
||||
static inline int32_t get_rand_us_factor(void)
|
||||
{
|
||||
int32_t res = ((int32_t)random_uint32_range(0, 200 * US_PER_MS));
|
||||
res -= 100 * US_PER_SEC;
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline uint32_t _irt_us(uint16_t irt, bool greater_irt)
|
||||
{
|
||||
uint32_t irt_us = (irt * US_PER_SEC);
|
||||
int32_t factor = get_rand_us_factor();
|
||||
|
||||
if (greater_irt && (factor < 0)) {
|
||||
factor = -factor;
|
||||
}
|
||||
irt_us += (factor * irt_us) / US_PER_SEC;
|
||||
return irt_us;
|
||||
}
|
||||
|
||||
static inline uint32_t _sub_rt_us(uint32_t rt_prev_us, uint16_t mrt)
|
||||
{
|
||||
uint32_t sub_rt_us = (2 * rt_prev_us) +
|
||||
((get_rand_us_factor() * rt_prev_us) / US_PER_SEC);
|
||||
|
||||
if (sub_rt_us > (mrt * US_PER_SEC)) {
|
||||
uint32_t mrt_us = mrt * US_PER_SEC;
|
||||
|
||||
sub_rt_us = mrt_us + ((get_rand_us_factor() * mrt_us) / US_PER_SEC);
|
||||
}
|
||||
return sub_rt_us;
|
||||
}
|
||||
|
||||
static inline size_t _opt_len(dhcpv6_opt_t *opt)
|
||||
{
|
||||
return sizeof(dhcpv6_opt_t) + byteorder_ntohs(opt->len);
|
||||
}
|
||||
|
||||
static inline dhcpv6_opt_t *_opt_next(dhcpv6_opt_t *opt)
|
||||
{
|
||||
return (dhcpv6_opt_t *)(((uint8_t *)opt) + _opt_len(opt));
|
||||
}
|
||||
|
||||
static bool _check_status_opt(dhcpv6_opt_status_t *status)
|
||||
{
|
||||
/* DHCPV6_STATUS_SUCCESS is 0, so we don't need to fix byte order */
|
||||
#if ENABLE_DEBUG
|
||||
if ((status != NULL) && (status->code.u16 != DHCPV6_STATUS_SUCCESS)) {
|
||||
size_t msg_len = byteorder_ntohs(status->len);
|
||||
char msg[msg_len - 1];
|
||||
|
||||
strncpy(msg, status->msg, msg_len - 2);
|
||||
DEBUG("DHCPv6 client: server returned error (%u) \"%s\"\n",
|
||||
byteorder_ntohs(status->code), msg);
|
||||
}
|
||||
#endif
|
||||
return (status == NULL) || (status->code.u16 == DHCPV6_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static bool _check_cid_opt(dhcpv6_opt_duid_t *cid)
|
||||
{
|
||||
#if ENABLE_DEBUG
|
||||
if ((byteorder_ntohs(cid->len) != duid_len) ||
|
||||
(memcmp(cid->duid, duid, duid_len) != 0)) {
|
||||
DEBUG("DHCPv6 client: message is not for me\n");
|
||||
}
|
||||
#endif
|
||||
return ((byteorder_ntohs(cid->len) == duid_len) &&
|
||||
(memcmp(cid->duid, duid, duid_len) == 0));
|
||||
}
|
||||
|
||||
static bool _check_sid_opt(dhcpv6_opt_duid_t *sid)
|
||||
{
|
||||
#if ENABLE_DEBUG
|
||||
if ((byteorder_ntohs(sid->len) != server.duid_len) ||
|
||||
(memcmp(sid->duid, server.duid.u8, server.duid_len) != 0)) {
|
||||
DEBUG("DHCPv6 client: message is not from my server\n");
|
||||
}
|
||||
#endif
|
||||
return ((byteorder_ntohs(sid->len) == server.duid_len) &&
|
||||
(memcmp(sid->duid, server.duid.u8, server.duid_len) == 0));
|
||||
}
|
||||
|
||||
static int _preparse_advertise(uint8_t *adv, size_t len, uint8_t **buf)
|
||||
{
|
||||
dhcpv6_opt_duid_t *cid = NULL, *sid = NULL;
|
||||
dhcpv6_opt_pref_t *pref = NULL;
|
||||
dhcpv6_opt_status_t *status = NULL;
|
||||
dhcpv6_opt_ia_pd_t *ia_pd = NULL;
|
||||
size_t orig_len = len;
|
||||
uint8_t pref_val = 0;
|
||||
|
||||
DEBUG("DHCPv6 client: received ADVERTISE\n");
|
||||
if ((len < sizeof(dhcpv6_msg_t)) || !_is_tid((dhcpv6_msg_t *)adv)) {
|
||||
DEBUG("DHCPv6 client: packet too small or transaction ID wrong\n");
|
||||
return -1;
|
||||
}
|
||||
for (dhcpv6_opt_t *opt = (dhcpv6_opt_t *)(&adv[sizeof(dhcpv6_msg_t)]);
|
||||
len > 0; len -= _opt_len(opt), opt = _opt_next(opt)) {
|
||||
if (len > orig_len) {
|
||||
DEBUG("DHCPv6 client: ADVERTISE options overflow packet boundaries\n");
|
||||
return -1;
|
||||
}
|
||||
switch (byteorder_ntohs(opt->type)) {
|
||||
case DHCPV6_OPT_CID:
|
||||
cid = (dhcpv6_opt_duid_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_SID:
|
||||
sid = (dhcpv6_opt_duid_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_STATUS:
|
||||
status = (dhcpv6_opt_status_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_IA_PD:
|
||||
ia_pd = (dhcpv6_opt_ia_pd_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_PREF:
|
||||
pref = (dhcpv6_opt_pref_t *)opt;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((cid == NULL) || (sid == NULL) || (ia_pd == NULL)) {
|
||||
DEBUG("DHCPv6 client: ADVERTISE does not contain either server ID, "
|
||||
"client ID or IA_PD option\n");
|
||||
return false;
|
||||
}
|
||||
if (!_check_status_opt(status) || !_check_cid_opt(cid)) {
|
||||
return -1;
|
||||
}
|
||||
if (pref != NULL) {
|
||||
pref_val = pref->value;
|
||||
}
|
||||
if ((server.duid_len == 0) || (pref_val > server.pref)) {
|
||||
memcpy(best_adv, recv_buf, orig_len);
|
||||
if (buf != NULL) {
|
||||
*buf = best_adv;
|
||||
}
|
||||
server.duid_len = byteorder_ntohs(sid->len);
|
||||
memcpy(server.duid.u8, sid->duid, server.duid_len);
|
||||
server.pref = pref_val;
|
||||
}
|
||||
return pref_val;
|
||||
}
|
||||
|
||||
static void _schedule_t2(void)
|
||||
{
|
||||
if (t2 < UINT32_MAX) {
|
||||
uint64_t t2_usec = t2 * US_PER_SEC;
|
||||
|
||||
rebind_time = (xtimer_now_usec64() / US_PER_SEC) + t2;
|
||||
xtimer_remove(&rebind_timer);
|
||||
rebind_timer.callback = _post_rebind;
|
||||
DEBUG("DHCPv6 client: scheduling REBIND in %lu sec\n",
|
||||
(unsigned long)t2);
|
||||
xtimer_set64(&rebind_timer, t2_usec);
|
||||
}
|
||||
}
|
||||
|
||||
static void _schedule_t1_t2(void)
|
||||
{
|
||||
if (server.t1 < UINT32_MAX) {
|
||||
uint64_t t1_usec = server.t1 * US_PER_SEC;
|
||||
|
||||
xtimer_remove(&timer);
|
||||
timer.callback = _post_renew;
|
||||
DEBUG("DHCPv6 client: scheduling RENEW in %lu sec\n",
|
||||
(unsigned long)server.t1);
|
||||
xtimer_set64(&timer, t1_usec);
|
||||
}
|
||||
_schedule_t2();
|
||||
}
|
||||
|
||||
static void _parse_advertise(uint8_t *adv, size_t len)
|
||||
{
|
||||
dhcpv6_opt_smr_t *smr = NULL;
|
||||
|
||||
/* might not have been executed when not received in first retransmission
|
||||
* window => redo even if already done */
|
||||
if (_preparse_advertise(adv, len, NULL) < 0) {
|
||||
return;
|
||||
}
|
||||
DEBUG("DHCPv6 client: scheduling REQUEST\n");
|
||||
event_post(event_queue, &request);
|
||||
for (dhcpv6_opt_t *opt = (dhcpv6_opt_t *)(&adv[sizeof(dhcpv6_msg_t)]);
|
||||
len > 0; len -= _opt_len(opt), opt = _opt_next(opt)) {
|
||||
switch (byteorder_ntohs(opt->type)) {
|
||||
case DHCPV6_OPT_IA_PD:
|
||||
for (unsigned i = 0; i < DHCPV6_CLIENT_PFX_LEASE_MAX; i++) {
|
||||
dhcpv6_opt_ia_pd_t *ia_pd = (dhcpv6_opt_ia_pd_t *)opt;
|
||||
unsigned pd_t1, pd_t2;
|
||||
uint32_t ia_id = byteorder_ntohl(ia_pd->ia_id);
|
||||
size_t ia_pd_len = byteorder_ntohs(ia_pd->len);
|
||||
size_t ia_pd_orig_len = ia_pd_len;
|
||||
|
||||
if (pfx_leases[i].parent.ia_id.id != ia_id) {
|
||||
continue;
|
||||
}
|
||||
/* check for status */
|
||||
for (dhcpv6_opt_t *ia_pd_opt = (dhcpv6_opt_t *)(ia_pd + 1);
|
||||
ia_pd_len > 0;
|
||||
ia_pd_len -= _opt_len(ia_pd_opt),
|
||||
ia_pd_opt = _opt_next(ia_pd_opt)) {
|
||||
if (ia_pd_len > ia_pd_orig_len) {
|
||||
DEBUG("DHCPv6 client: IA_PD options overflow option "
|
||||
"boundaries\n");
|
||||
return;
|
||||
}
|
||||
switch (byteorder_ntohs(ia_pd_opt->type)) {
|
||||
case DHCPV6_OPT_STATUS: {
|
||||
if (!_check_status_opt((dhcpv6_opt_status_t *)ia_pd_opt)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
pd_t1 = byteorder_ntohl(ia_pd->t1);
|
||||
pd_t2 = byteorder_ntohl(ia_pd->t2);
|
||||
if ((pd_t1 != 0) && (pd_t2 != 0) &&
|
||||
(server.t1 > pd_t1) && (t2 > pd_t2)) {
|
||||
server.t1 = pd_t1;
|
||||
t2 = pd_t2;
|
||||
_schedule_t2();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DHCPV6_OPT_SMR:
|
||||
smr = (dhcpv6_opt_smr_t *)opt;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (smr != NULL) {
|
||||
sol_max_rt = byteorder_ntohl(smr->value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static bool _parse_reply(uint8_t *rep, size_t len)
|
||||
{
|
||||
dhcpv6_opt_duid_t *cid = NULL, *sid = NULL;
|
||||
dhcpv6_opt_ia_pd_t *ia_pd = NULL;
|
||||
dhcpv6_opt_status_t *status = NULL;
|
||||
dhcpv6_opt_smr_t *smr = NULL;
|
||||
size_t orig_len = len;
|
||||
|
||||
DEBUG("DHCPv6 client: received REPLY\n");
|
||||
if ((len < sizeof(dhcpv6_msg_t)) || !_is_tid((dhcpv6_msg_t *)rep)) {
|
||||
DEBUG("DHCPv6 client: packet too small or transaction ID wrong\n");
|
||||
return false;
|
||||
}
|
||||
for (dhcpv6_opt_t *opt = (dhcpv6_opt_t *)(&rep[sizeof(dhcpv6_msg_t)]);
|
||||
len > 0; len -= _opt_len(opt), opt = _opt_next(opt)) {
|
||||
if (len > orig_len) {
|
||||
DEBUG("DHCPv6 client: ADVERTISE options overflow packet boundaries\n");
|
||||
return false;
|
||||
}
|
||||
switch (byteorder_ntohs(opt->type)) {
|
||||
case DHCPV6_OPT_CID:
|
||||
cid = (dhcpv6_opt_duid_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_SID:
|
||||
sid = (dhcpv6_opt_duid_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_STATUS:
|
||||
status = (dhcpv6_opt_status_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_IA_PD:
|
||||
ia_pd = (dhcpv6_opt_ia_pd_t *)opt;
|
||||
break;
|
||||
case DHCPV6_OPT_SMR:
|
||||
smr = (dhcpv6_opt_smr_t *)opt;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((cid == NULL) || (sid == NULL) || (ia_pd == NULL)) {
|
||||
DEBUG("DHCPv6 client: ADVERTISE does not contain either server ID, "
|
||||
"client ID or IA_PD option\n");
|
||||
return false;
|
||||
}
|
||||
if (!_check_cid_opt(cid) || !_check_sid_opt(sid)) {
|
||||
return false;
|
||||
}
|
||||
if (smr != NULL) {
|
||||
sol_max_rt = byteorder_ntohl(smr->value);
|
||||
}
|
||||
if (!_check_status_opt(status)) {
|
||||
return false;
|
||||
}
|
||||
len = orig_len;
|
||||
for (dhcpv6_opt_t *opt = (dhcpv6_opt_t *)(&rep[sizeof(dhcpv6_msg_t)]);
|
||||
len > 0; len -= _opt_len(opt), opt = _opt_next(opt)) {
|
||||
switch (byteorder_ntohs(opt->type)) {
|
||||
case DHCPV6_OPT_IA_PD:
|
||||
for (unsigned i = 0; i < DHCPV6_CLIENT_PFX_LEASE_MAX; i++) {
|
||||
dhcpv6_opt_iapfx_t *iapfx = NULL;
|
||||
pfx_lease_t *lease = &pfx_leases[i];
|
||||
ia_pd = (dhcpv6_opt_ia_pd_t *)opt;
|
||||
unsigned pd_t1, pd_t2;
|
||||
uint32_t ia_id = byteorder_ntohl(ia_pd->ia_id);
|
||||
size_t ia_pd_len = byteorder_ntohs(ia_pd->len);
|
||||
size_t ia_pd_orig_len = ia_pd_len;
|
||||
|
||||
if (lease->parent.ia_id.id != ia_id) {
|
||||
continue;
|
||||
}
|
||||
/* check for status */
|
||||
for (dhcpv6_opt_t *ia_pd_opt = (dhcpv6_opt_t *)(ia_pd + 1);
|
||||
ia_pd_len > 0;
|
||||
ia_pd_len -= _opt_len(ia_pd_opt),
|
||||
ia_pd_opt = _opt_next(ia_pd_opt)) {
|
||||
if (ia_pd_len > ia_pd_orig_len) {
|
||||
DEBUG("DHCPv6 client: IA_PD options overflow option "
|
||||
"boundaries\n");
|
||||
return false;
|
||||
}
|
||||
switch (byteorder_ntohs(ia_pd_opt->type)) {
|
||||
case DHCPV6_OPT_STATUS: {
|
||||
if (!_check_status_opt((dhcpv6_opt_status_t *)ia_pd_opt)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DHCPV6_OPT_IAPFX: {
|
||||
dhcpv6_opt_iapfx_t *this_iapfx = (dhcpv6_opt_iapfx_t *)ia_pd_opt;
|
||||
if ((!lease->leased) ||
|
||||
(iapfx == NULL) ||
|
||||
((this_iapfx->pfx_len == lease->pfx_len) &&
|
||||
ipv6_addr_match_prefix(&this_iapfx->pfx,
|
||||
&lease->pfx) >= lease->pfx_len)) {
|
||||
/* only take first prefix for now */
|
||||
iapfx = this_iapfx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
pd_t1 = byteorder_ntohl(ia_pd->t1);
|
||||
pd_t2 = byteorder_ntohl(ia_pd->t2);
|
||||
if ((pd_t1 != 0) && (pd_t2 != 0) &&
|
||||
((server.t1 == 0) || (server.t1 >= pd_t1)) &&
|
||||
((t2 == 0) || (t2 >= pd_t2))) {
|
||||
server.t1 = pd_t1;
|
||||
t2 = pd_t2;
|
||||
_schedule_t1_t2();
|
||||
}
|
||||
if ((iapfx != NULL)) {
|
||||
uint32_t valid = byteorder_ntohl(iapfx->valid);
|
||||
uint32_t pref = byteorder_ntohl(iapfx->pref);
|
||||
|
||||
lease->pfx_len = iapfx->pfx_len;
|
||||
lease->leased = 1U;
|
||||
ipv6_addr_init_prefix(&lease->pfx,
|
||||
&iapfx->pfx,
|
||||
iapfx->pfx_len);
|
||||
if (iapfx->pfx_len > 0) {
|
||||
dhcpv6_client_conf_prefix(
|
||||
lease->parent.ia_id.info.netif, &lease->pfx,
|
||||
lease->pfx_len, valid, pref
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _solicit_servers(event_t *event)
|
||||
{
|
||||
dhcpv6_msg_t *msg = (dhcpv6_msg_t *)&send_buf[0];
|
||||
dhcpv6_opt_elapsed_time_t *time;
|
||||
uint8_t *buf = NULL;
|
||||
uint32_t retrans_timeout = _irt_us(DHCPV6_SOL_TIMEOUT, true);
|
||||
size_t msg_len = sizeof(dhcpv6_msg_t);
|
||||
int res, best_res = 0;
|
||||
bool first_rt = true;
|
||||
uint16_t oro_opts[] = { DHCPV6_OPT_SMR };
|
||||
|
||||
(void)event;
|
||||
_generate_tid();
|
||||
msg->type = DHCPV6_SOLICIT;
|
||||
_set_tid(msg->tid);
|
||||
msg_len += _compose_cid_opt((dhcpv6_opt_duid_t *)&send_buf[msg_len]);
|
||||
transaction_start = _now_cs();
|
||||
time = (dhcpv6_opt_elapsed_time_t *)&send_buf[msg_len];
|
||||
msg_len += _compose_elapsed_time_opt(time);
|
||||
msg_len += _compose_oro_opt((dhcpv6_opt_oro_t *)&send_buf[msg_len], oro_opts,
|
||||
ARRAY_SIZE(oro_opts));
|
||||
msg_len += _add_ia_pd_from_config(&send_buf[msg_len]);
|
||||
DEBUG("DHCPv6 client: send SOLICIT\n");
|
||||
res = sock_udp_send(&sock, send_buf, msg_len, &remote);
|
||||
assert(res > 0); /* something went terribly wrong */
|
||||
while (((res = sock_udp_recv(&sock, recv_buf, sizeof(recv_buf),
|
||||
retrans_timeout, NULL)) <= 0) ||
|
||||
(first_rt && (res > 0)) ||
|
||||
((res > 0) && (recv_buf[0] != DHCPV6_ADVERTISE))) {
|
||||
if (first_rt && (res > 0) && (recv_buf[0] == DHCPV6_ADVERTISE)) {
|
||||
int parse_res;
|
||||
|
||||
DEBUG("DHCPv6 client: initial transmission, collect best advertise\n");
|
||||
retrans_timeout -= (_get_elapsed_time() * US_PER_CS);
|
||||
parse_res = _preparse_advertise(recv_buf, res, &buf);
|
||||
if (buf != NULL) {
|
||||
best_res = res;
|
||||
}
|
||||
if ((parse_res == UINT8_MAX) ||
|
||||
(retrans_timeout > (DHCPV6_SOL_MAX_RT * US_PER_SEC))) {
|
||||
/* retrans_timeout underflowed => don't retry to receive */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (buf == NULL) {
|
||||
DEBUG("DHCPv6 client: resend SOLICIT\n");
|
||||
first_rt = false;
|
||||
retrans_timeout = _sub_rt_us(retrans_timeout, DHCPV6_SOL_MAX_RT);
|
||||
_compose_elapsed_time_opt(time);
|
||||
res = sock_udp_send(&sock, send_buf, msg_len, &remote);
|
||||
assert(res > 0); /* something went terribly wrong */
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buf == NULL) {
|
||||
buf = recv_buf;
|
||||
best_res = res;
|
||||
}
|
||||
if (best_res > 0) {
|
||||
_parse_advertise(buf, best_res);
|
||||
}
|
||||
}
|
||||
|
||||
static void _request_renew_rebind(uint8_t type)
|
||||
{
|
||||
dhcpv6_msg_t *msg = (dhcpv6_msg_t *)&send_buf[0];
|
||||
dhcpv6_opt_elapsed_time_t *time;
|
||||
uint32_t retrans_timeout;
|
||||
size_t msg_len = sizeof(dhcpv6_msg_t);
|
||||
int res;
|
||||
uint16_t oro_opts[] = { DHCPV6_OPT_SMR };
|
||||
uint8_t retrans = 0;
|
||||
uint16_t irt;
|
||||
uint16_t mrt;
|
||||
uint16_t mrc = 0;
|
||||
uint32_t mrd = 0;
|
||||
|
||||
switch (type) {
|
||||
case DHCPV6_REQUEST:
|
||||
irt = DHCPV6_REQ_TIMEOUT;
|
||||
mrt = DHCPV6_REQ_MAX_RT;
|
||||
mrc = DHCPV6_REQ_MAX_RC;
|
||||
break;
|
||||
case DHCPV6_RENEW:
|
||||
irt = DHCPV6_REN_TIMEOUT;
|
||||
mrt = DHCPV6_REN_MAX_RT;
|
||||
mrd = rebind_time - t2;
|
||||
break;
|
||||
case DHCPV6_REBIND: {
|
||||
irt = DHCPV6_REB_TIMEOUT;
|
||||
mrt = DHCPV6_REB_MAX_RT;
|
||||
/* calculate MRD from prefix leases */
|
||||
for (unsigned i = 0; i < DHCPV6_CLIENT_PFX_LEASE_MAX; i++) {
|
||||
const pfx_lease_t *lease = &pfx_leases[i];
|
||||
uint32_t valid_until = dhcpv6_client_prefix_valid_until(
|
||||
lease->parent.ia_id.info.netif,
|
||||
&lease->pfx, lease->pfx_len
|
||||
);
|
||||
if (valid_until > mrd) {
|
||||
mrd = valid_until;
|
||||
}
|
||||
}
|
||||
if (mrd > 0) {
|
||||
/* all leases already expired, don't try to rebind and
|
||||
* solicit immediately */
|
||||
_post_solicit_servers(NULL);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
retrans_timeout = _irt_us(irt, false);
|
||||
_generate_tid();
|
||||
msg->type = type;
|
||||
_set_tid(msg->tid);
|
||||
msg_len += _compose_cid_opt((dhcpv6_opt_duid_t *)&send_buf[msg_len]);
|
||||
if (type != DHCPV6_REBIND) {
|
||||
msg_len += _compose_sid_opt((dhcpv6_opt_duid_t *)&send_buf[msg_len]);
|
||||
}
|
||||
transaction_start = _now_cs();
|
||||
time = (dhcpv6_opt_elapsed_time_t *)&send_buf[msg_len];
|
||||
msg_len += _compose_elapsed_time_opt(time);
|
||||
msg_len += _compose_oro_opt((dhcpv6_opt_oro_t *)&send_buf[msg_len], oro_opts,
|
||||
ARRAY_SIZE(oro_opts));
|
||||
msg_len += _add_ia_pd_from_config(&send_buf[msg_len]);
|
||||
while (sock_udp_send(&sock, send_buf, msg_len, &remote) <= 0) {}
|
||||
while (((res = sock_udp_recv(&sock, recv_buf, sizeof(recv_buf),
|
||||
retrans_timeout, NULL)) <= 0) ||
|
||||
((res > 0) && (recv_buf[0] != DHCPV6_REPLY))) {
|
||||
if ((mrd > 0) && (_get_elapsed_time() > (mrd * CS_PER_SEC))) {
|
||||
break;
|
||||
}
|
||||
retrans_timeout = _sub_rt_us(retrans_timeout, mrt);
|
||||
if ((mrc > 0) && (++retrans) >= mrc) {
|
||||
break;
|
||||
}
|
||||
_compose_elapsed_time_opt(time);
|
||||
DEBUG("DHCPv6 client: resend %s\n",
|
||||
(type == DHCPV6_REQUEST) ? "REQUEST" :
|
||||
(type == DHCPV6_RENEW) ? "RENEW": "REBIND");
|
||||
sock_udp_send(&sock, send_buf, msg_len, &remote);
|
||||
}
|
||||
if ((res > 0) && (recv_buf[0] == DHCPV6_REPLY)) {
|
||||
if (!_parse_reply(recv_buf, res)) {
|
||||
/* try again */
|
||||
event_post(event_queue, &request);
|
||||
}
|
||||
}
|
||||
else if (type == DHCPV6_REBIND) {
|
||||
_post_solicit_servers(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void _request(event_t *event)
|
||||
{
|
||||
(void)event;
|
||||
DEBUG("DHCPv6 client: send REQUEST\n");
|
||||
_request_renew_rebind(DHCPV6_REQUEST);
|
||||
}
|
||||
|
||||
static void _renew(event_t *event)
|
||||
{
|
||||
(void)event;
|
||||
DEBUG("DHCPv6 client: send RENEW\n");
|
||||
_request_renew_rebind(DHCPV6_RENEW);
|
||||
}
|
||||
|
||||
static void _rebind(event_t *event)
|
||||
{
|
||||
(void)event;
|
||||
DEBUG("DHCPv6 client: send REBIND\n");
|
||||
_request_renew_rebind(DHCPV6_REBIND);
|
||||
}
|
||||
|
||||
/** @} */
|
@ -1,3 +1,6 @@
|
||||
ifneq (,$(filter gnrc_dhcpv6,$(USEMODULE)))
|
||||
DIRS += application_layer/dhcpv6
|
||||
endif
|
||||
ifneq (,$(filter gnrc_icmpv6,$(USEMODULE)))
|
||||
DIRS += network_layer/icmpv6
|
||||
endif
|
||||
|
5
sys/net/gnrc/application_layer/dhcpv6/Makefile
Normal file
5
sys/net/gnrc/application_layer/dhcpv6/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
MODULE := gnrc_dhcpv6
|
||||
|
||||
SUBMODULES := 1
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
144
sys/net/gnrc/application_layer/dhcpv6/client.c
Normal file
144
sys/net/gnrc/application_layer/dhcpv6/client.c
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
#include "net/arp.h"
|
||||
#include "net/dhcpv6.h"
|
||||
#include "net/gnrc/ipv6/nib/pl.h"
|
||||
#include "net/gnrc/netif.h"
|
||||
#include "net/gnrc/rpl.h"
|
||||
#include "net/sock.h"
|
||||
#include "timex.h"
|
||||
|
||||
#include "net/dhcpv6/client.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||||
|
||||
unsigned dhcpv6_client_get_duid_l2(unsigned iface, dhcpv6_duid_l2_t *duid)
|
||||
{
|
||||
gnrc_netif_t *netif;
|
||||
uint8_t *l2addr = ((uint8_t *)(duid)) + sizeof(dhcpv6_duid_l2_t);
|
||||
int res;
|
||||
|
||||
duid->type = byteorder_htons(DHCPV6_DUID_TYPE_L2);
|
||||
/* TODO make GNRC-independent */
|
||||
if (iface == SOCK_ADDR_ANY_NETIF) {
|
||||
netif = gnrc_netif_iter(NULL);
|
||||
}
|
||||
else {
|
||||
netif = gnrc_netif_get_by_pid(iface);
|
||||
}
|
||||
assert(netif != NULL);
|
||||
if ((res = gnrc_netapi_get(netif->pid, NETOPT_ADDRESS_LONG, 0,
|
||||
l2addr, GNRC_NETIF_L2ADDR_MAXLEN)) > 0) {
|
||||
duid->l2type = byteorder_htons(ARP_HWTYPE_EUI64);
|
||||
}
|
||||
else {
|
||||
switch (netif->device_type) {
|
||||
case NETDEV_TYPE_ETHERNET:
|
||||
case NETDEV_TYPE_BLE:
|
||||
case NETDEV_TYPE_ESP_NOW:
|
||||
if ((res = gnrc_netapi_get(netif->pid,
|
||||
NETOPT_ADDRESS,
|
||||
0, l2addr,
|
||||
GNRC_NETIF_L2ADDR_MAXLEN)) > 0) {
|
||||
duid->l2type = byteorder_htons(ARP_HWTYPE_ETHERNET);
|
||||
break;
|
||||
}
|
||||
/* intentionally falls through */
|
||||
default:
|
||||
LOG_ERROR("DHCPv6 client: Link-layer type of interface %u not supported "
|
||||
"for DUID creation\n", netif->pid);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return (uint8_t)res + sizeof(dhcpv6_duid_l2_t);
|
||||
}
|
||||
|
||||
void dhcpv6_client_conf_prefix(unsigned iface, const ipv6_addr_t *pfx,
|
||||
unsigned pfx_len, uint32_t valid,
|
||||
uint32_t pref)
|
||||
{
|
||||
gnrc_netif_t *netif = gnrc_netif_get_by_pid(iface);
|
||||
eui64_t iid;
|
||||
ipv6_addr_t addr;
|
||||
|
||||
assert(netif != NULL);
|
||||
DEBUG("GNRC DHCPv6 client: (re-)configure prefix %s/%d\n",
|
||||
ipv6_addr_to_str(addr_str, pfx, sizeof(addr_str)), pfx_len);
|
||||
if (gnrc_netapi_get(netif->pid, NETOPT_IPV6_IID, 0, &iid,
|
||||
sizeof(eui64_t)) >= 0) {
|
||||
ipv6_addr_set_aiid(&addr, iid.uint8);
|
||||
}
|
||||
else {
|
||||
LOG_WARNING("GNRC DHCPv6 client: cannot get IID of netif %u\n", netif->pid);
|
||||
return;
|
||||
}
|
||||
ipv6_addr_init_prefix(&addr, pfx, pfx_len);
|
||||
/* add address as tentative */
|
||||
if (gnrc_netif_ipv6_addr_add(netif, &addr, pfx_len,
|
||||
GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_TENTATIVE & 0x1) > 0) {
|
||||
/* update lifetime */
|
||||
if (valid < UINT32_MAX) { /* UINT32_MAX means infinite lifetime */
|
||||
/* the valid lifetime is given in seconds, but the NIB's timers work
|
||||
* in microseconds, so we have to scale down to the smallest
|
||||
* possible value (UINT32_MAX - 1). */
|
||||
valid = (valid > (UINT32_MAX / MS_PER_SEC)) ?
|
||||
(UINT32_MAX - 1) : valid * MS_PER_SEC;
|
||||
}
|
||||
if (pref < UINT32_MAX) { /* UINT32_MAX means infinite lifetime */
|
||||
/* same treatment for pref */
|
||||
pref = (pref > (UINT32_MAX / MS_PER_SEC)) ?
|
||||
(UINT32_MAX - 1) : pref * MS_PER_SEC;
|
||||
}
|
||||
gnrc_ipv6_nib_pl_set(netif->pid, pfx, pfx_len, valid, pref);
|
||||
#if defined(MODULE_GNRC_IPV6_NIB) && GNRC_IPV6_NIB_CONF_6LBR && \
|
||||
GNRC_IPV6_NIB_CONF_MULTIHOP_P6C
|
||||
gnrc_ipv6_nib_abr_add(&addr);
|
||||
#endif
|
||||
#ifdef MODULE_GNRC_RPL
|
||||
gnrc_rpl_init(netif->pid);
|
||||
gnrc_rpl_instance_t *inst = gnrc_rpl_instance_get(GNRC_RPL_DEFAULT_INSTANCE);
|
||||
if (inst) {
|
||||
gnrc_rpl_instance_remove(inst);
|
||||
}
|
||||
gnrc_rpl_root_init(GNRC_RPL_DEFAULT_INSTANCE, &addr, false, false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t dhcpv6_client_prefix_valid_until(unsigned netif,
|
||||
const ipv6_addr_t *pfx,
|
||||
unsigned pfx_len)
|
||||
{
|
||||
gnrc_ipv6_nib_pl_t ple;
|
||||
void *state = NULL;
|
||||
uint32_t max_valid = 0;
|
||||
|
||||
while (gnrc_ipv6_nib_pl_iter(netif, &state, &ple)) {
|
||||
if ((ple.pfx_len == pfx_len) &&
|
||||
((ple.valid_until / MS_PER_SEC) > max_valid) &&
|
||||
(ipv6_addr_match_prefix(&ple.pfx,
|
||||
pfx) >= ple.pfx_len)) {
|
||||
max_valid = ple.valid_until / MS_PER_SEC;
|
||||
}
|
||||
}
|
||||
return max_valid;
|
||||
}
|
||||
|
||||
/** @} */
|
50
tests/gnrc_dhcpv6_client/Makefile
Normal file
50
tests/gnrc_dhcpv6_client/Makefile
Normal file
@ -0,0 +1,50 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
# generate random free port
|
||||
DHCPV6_SERVER_PORT := 61342
|
||||
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
# boards don't support ethos
|
||||
BOARD_BLACKLIST += mips-malta pic32-wifire pic32-clicker ruuvitag thingy52
|
||||
|
||||
USEMODULE += gnrc_dhcpv6_client
|
||||
USEMODULE += gnrc_ipv6_default
|
||||
USEMODULE += xtimer
|
||||
ifneq (,$(filter native,$(BOARD)))
|
||||
# Has to be provided here and not in Makefile.dep, so TERMFLAGS are properly
|
||||
# configured
|
||||
USEMODULE += netdev_default
|
||||
IFACE ?= tapbr0
|
||||
else
|
||||
IFACE ?= tap0
|
||||
ETHOS_BAUDRATE ?= 115200
|
||||
CFLAGS += -DETHOS_BAUDRATE=$(ETHOS_BAUDRATE)
|
||||
TERMPROG ?= sudo sh $(RIOTBASE)/dist/tools/ethos/ethos
|
||||
TERMFLAGS ?= $(IFACE) $(PORT) $(ETHOS_BAUDRATE)
|
||||
TERMDEPS += ethos
|
||||
endif
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
|
||||
USEMODULE += shell_commands
|
||||
|
||||
CFLAGS += -DDHCPV6_SERVER_PORT=$(DHCPV6_SERVER_PORT)
|
||||
|
||||
TEST_DEPS += dhcpv6_server
|
||||
|
||||
# The test requires to be run as root
|
||||
TEST_ON_CI_BLACKLIST += all
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
.PHONY: dhcpv6_server
|
||||
|
||||
dhcpv6_server:
|
||||
$(CURDIR)/dhcpv6_server.sh $(DHCPV6_SERVER_PORT) $(CURDIR)/kea-dhcp6.conf
|
||||
|
||||
ifeq (,$(filter native,$(BOARD)))
|
||||
.PHONY: ethos
|
||||
|
||||
ethos:
|
||||
$(Q)env -u CC -u CFLAGS make -C $(RIOTBASE)/dist/tools/ethos
|
||||
endif
|
3
tests/gnrc_dhcpv6_client/Makefile.board.dep
Normal file
3
tests/gnrc_dhcpv6_client/Makefile.board.dep
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq (native,$(BOARD))
|
||||
USEMODULE += stdio_ethos
|
||||
endif
|
36
tests/gnrc_dhcpv6_client/Makefile.ci
Normal file
36
tests/gnrc_dhcpv6_client/Makefile.ci
Normal file
@ -0,0 +1,36 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-mega2560 \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega1284p \
|
||||
atmega328p \
|
||||
derfmega128 \
|
||||
hifive1 \
|
||||
hifive1b \
|
||||
i-nucleo-lrwan1 \
|
||||
mega-xplained \
|
||||
microduino-corerf \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-f070rb \
|
||||
nucleo-f072rb \
|
||||
nucleo-f303k8 \
|
||||
nucleo-f334r8 \
|
||||
nucleo-l031k6 \
|
||||
nucleo-l053r8 \
|
||||
saml10-xpro \
|
||||
saml11-xpro \
|
||||
stm32f030f4-demo \
|
||||
stm32f0discovery \
|
||||
stm32l0538-disco \
|
||||
telosb \
|
||||
waspmote-pro \
|
||||
wsn430-v1_3b \
|
||||
wsn430-v1_4 \
|
||||
z1 \
|
||||
#
|
38
tests/gnrc_dhcpv6_client/README.md
Normal file
38
tests/gnrc_dhcpv6_client/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Overview
|
||||
|
||||
This folder contains a test application for RIOT's DHCPv6 client.
|
||||
|
||||
# How to test
|
||||
|
||||
The test script requires [Kea] as DHCPv6 server. It is available as
|
||||
`kea-dhcp6-server` on Ubuntu since Ubuntu 16.04:
|
||||
|
||||
```sh
|
||||
apt-get install kea-dhcp6-server
|
||||
```
|
||||
|
||||
On Arch Linux it is available in the `kea` package:
|
||||
|
||||
```sh
|
||||
pacman -Syu kea
|
||||
```
|
||||
|
||||
If you use any platform other than `native`, you need to use `ethos`, otherwise
|
||||
`netdev_tap` is chosen.
|
||||
|
||||
An instance of Kea that configured via [kea-dhcp6.conf](kea-dhcp6.conf) is
|
||||
started in parallel to `make term`/`make test`.
|
||||
|
||||
Read the [Kea documentation] on the configuration file for more information.
|
||||
|
||||
The default set-up is configured so a `2001:db8::/32` is delegated via the
|
||||
`tapbr0` bridge as created with the `dist/tools/tapsetup/tapsetup` script.
|
||||
If you created your interface and without the script, please reconfigure Kea by
|
||||
search & replacing "`tapbr0`" in the configuration file.
|
||||
|
||||
```sh
|
||||
BOARD=samr21-xpro make flash test
|
||||
```
|
||||
|
||||
[Kea]: http://kea.isc.org
|
||||
[Kea documentation]: https://kea.readthedocs.io/en/latest/arm/config.html
|
25
tests/gnrc_dhcpv6_client/dhcpv6_server.sh
Executable file
25
tests/gnrc_dhcpv6_client/dhcpv6_server.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#! /bin/bash
|
||||
#
|
||||
# random_port.sh
|
||||
# Copyright (C) 2018 Martine Lenders <m.lenders@fu-berlin.de>
|
||||
#
|
||||
# Distributed under terms of the MIT license.
|
||||
#
|
||||
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "\033[31;1mRequire root since kea uses some PID and Lock files in /var\033[0m" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v kea-dhcp6; then
|
||||
echo -e "\033[31;1mCommand kea-dhcp6 required\033[0m" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
_dhcpv6_server() {
|
||||
sleep 1 # sleep to let TAP become active
|
||||
sudo kea-dhcp6 -p $1 -c $2
|
||||
}
|
||||
|
||||
# no need to kill from external, kea handles double instances gracefully
|
||||
_dhcpv6_server $1 $2 &
|
45
tests/gnrc_dhcpv6_client/kea-dhcp6.conf
Normal file
45
tests/gnrc_dhcpv6_client/kea-dhcp6.conf
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"Dhcp6":
|
||||
{
|
||||
"interfaces-config": {
|
||||
"interfaces": [ "tapbr0" ]
|
||||
},
|
||||
"lease-database": {
|
||||
"type": "memfile"
|
||||
},
|
||||
"expired-leases-processing": {
|
||||
"reclaim-timer-wait-time": 10,
|
||||
"flush-reclaimed-timer-wait-time": 25,
|
||||
"hold-reclaimed-time": 3600,
|
||||
"max-reclaim-leases": 100,
|
||||
"max-reclaim-time": 250,
|
||||
"unwarned-reclaim-cycles": 5
|
||||
},
|
||||
"preferred-lifetime": 3000,
|
||||
"valid-lifetime": 4000,
|
||||
"renew-timer": 1000,
|
||||
"rebind-timer": 2000,
|
||||
"subnet6": [
|
||||
{ "interface": "tapbr0",
|
||||
"subnet": "2001:db8::/32",
|
||||
"pd-pools": [ { "prefix": "2001:db8::",
|
||||
"prefix-len": 32,
|
||||
"delegated-len": 64 } ] }
|
||||
]
|
||||
},
|
||||
"Logging":
|
||||
{
|
||||
"loggers": [
|
||||
{
|
||||
"name": "kea-dhcp6",
|
||||
"output_options": [
|
||||
{
|
||||
"output": "stderr"
|
||||
}
|
||||
],
|
||||
"severity": "INFO",
|
||||
"debuglevel": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
65
tests/gnrc_dhcpv6_client/main.c
Normal file
65
tests/gnrc_dhcpv6_client/main.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief DHCPv6 client test application
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "net/gnrc/netif.h"
|
||||
#include "net/dhcpv6/client.h"
|
||||
#include "net/sock.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
static char _dhcpv6_client_stack[DHCPV6_CLIENT_STACK_SIZE];
|
||||
|
||||
extern int _gnrc_netif_config(int argc, char **argv);
|
||||
extern int _gnrc_ipv6_nib(int argc, char **argv);
|
||||
|
||||
void *_dhcpv6_client_thread(void *args)
|
||||
{
|
||||
event_queue_t event_queue;
|
||||
gnrc_netif_t *netif = gnrc_netif_iter(NULL);
|
||||
|
||||
(void)args;
|
||||
/* initialize client event queue */
|
||||
event_queue_init(&event_queue);
|
||||
/* initialize DHCPv6 client on any interface */
|
||||
dhcpv6_client_init(&event_queue, SOCK_ADDR_ANY_NETIF);
|
||||
/* configure client to request prefix delegation of /64 subnet
|
||||
* interface netif */
|
||||
dhcpv6_client_req_ia_pd(netif->pid, 64U);
|
||||
/* start DHCPv6 client */
|
||||
dhcpv6_client_start();
|
||||
/* start event loop of DHCPv6 client */
|
||||
event_loop(&event_queue); /* never returns */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char *pl[] = { "nib", "prefix" };
|
||||
|
||||
_gnrc_netif_config(0, NULL);
|
||||
thread_create(_dhcpv6_client_stack, DHCPV6_CLIENT_STACK_SIZE,
|
||||
DHCPV6_CLIENT_PRIORITY, THREAD_CREATE_STACKTEST,
|
||||
_dhcpv6_client_thread, NULL, "dhcpv6-client");
|
||||
xtimer_sleep(5);
|
||||
/* global address should now be configured */
|
||||
_gnrc_netif_config(0, NULL);
|
||||
_gnrc_ipv6_nib(2, pl);
|
||||
return 0;
|
||||
}
|
32
tests/gnrc_dhcpv6_client/tests/01-run.py
Executable file
32
tests/gnrc_dhcpv6_client/tests/01-run.py
Executable file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2018 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.
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect(r"Iface\s+\d+")
|
||||
child.expect(r"inet6 addr:\sfe80:[0-9a-f:]+\s+scope: link")
|
||||
child.expect(r"Iface\s+\d+")
|
||||
child.expect(r"inet6 addr:\s+fe80:[0-9a-f:]+\s+scope: link")
|
||||
child.expect(r"inet6 addr:\s+(?P<global_addr>[0-9a-f:]+)\s+scope: global")
|
||||
global_addr = child.match.group("global_addr")
|
||||
child.expect(r"(?P<global_pfx>[0-9a-f:]+)/64\s+dev #\d\s+"
|
||||
r"expires \d+sec\s+"
|
||||
r"deprecates \d+sec")
|
||||
global_pfx = child.match.group("global_pfx")
|
||||
if global_pfx.endswith("::"):
|
||||
# remove one trailing : in case there are no 0s between prefix and
|
||||
# suffix
|
||||
global_pfx = global_pfx[0:-1]
|
||||
assert global_addr.startswith(global_pfx)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc, timeout=5))
|
Loading…
Reference in New Issue
Block a user