mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 04:52:59 +01:00
dhcpv6_relay: initial import of a lightweight DHCPv6 relay agent
This commit is contained in:
parent
6da2f0fab3
commit
4afc65688f
@ -27,6 +27,7 @@ PSEUDOMODULES += dhcpv6_%
|
||||
PSEUDOMODULES += dhcpv6_client_dns
|
||||
PSEUDOMODULES += dhcpv6_client_ia_pd
|
||||
PSEUDOMODULES += dhcpv6_client_mud_url
|
||||
PSEUDOMODULES += dhcpv6_relay
|
||||
PSEUDOMODULES += dns_msg
|
||||
PSEUDOMODULES += ecc_%
|
||||
PSEUDOMODULES += event_%
|
||||
|
@ -110,6 +110,16 @@ ifneq (,$(filter dhcpv6_client,$(USEMODULE)))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(filter dhcpv6_relay,$(USEMODULE)))
|
||||
USEMODULE += event
|
||||
USEMODULE += sock_async_event
|
||||
USEMODULE += sock_udp
|
||||
endif
|
||||
|
||||
ifneq (,$(filter auto_init_dhcpv6_relay,$(USEMODULE)))
|
||||
USEMODULE += dhcpv6_relay
|
||||
endif
|
||||
|
||||
ifneq (,$(filter dns_%,$(USEMODULE)))
|
||||
USEMODULE += dns
|
||||
endif
|
||||
|
@ -267,6 +267,12 @@ void auto_init(void)
|
||||
dhcpv6_client_auto_init();
|
||||
}
|
||||
|
||||
if (IS_USED(MODULE_AUTO_INIT_DHCPV6_RELAY)) {
|
||||
LOG_DEBUG("Auto init DHCPv6 relay agent.\n");
|
||||
extern void dhcpv6_relay_auto_init(void);
|
||||
dhcpv6_relay_auto_init();
|
||||
}
|
||||
|
||||
if (IS_USED(MODULE_GNRC_DHCPV6_CLIENT_SIMPLE_PD)) {
|
||||
LOG_DEBUG("Auto init DHCPv6 client for simple prefix delegation\n");
|
||||
extern void gnrc_dhcpv6_client_simple_pd_init(void);
|
||||
|
@ -47,9 +47,15 @@ extern "C" {
|
||||
#define DHCPV6_SOLICIT (1U) /**< SOLICIT */
|
||||
#define DHCPV6_ADVERTISE (2U) /**< ADVERTISE */
|
||||
#define DHCPV6_REQUEST (3U) /**< REQUEST */
|
||||
#define DHCPV6_CONFIRM (4U) /**< CONFIRM */
|
||||
#define DHCPV6_RENEW (5U) /**< RENEW */
|
||||
#define DHCPV6_REBIND (6U) /**< REBIND */
|
||||
#define DHCPV6_REPLY (7U) /**< REPLY */
|
||||
#define DHCPV6_RELEASE (8U) /**< RELEASE */
|
||||
#define DHCPV6_DECLINE (9U) /**< DECLINE */
|
||||
#define DHCPV6_INFO_REQUEST (11U) /**< INFORMATION-REQUEST */
|
||||
#define DHCPV6_RELAY_FORW (12U) /**< RELAY-FORW */
|
||||
#define DHCPV6_RELAY_REPL (13U) /**< RELAY-REPL */
|
||||
/** @ } */
|
||||
|
||||
/**
|
||||
@ -64,7 +70,9 @@ extern "C" {
|
||||
#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_RELAY_MSG (9U) /**< relay message option */
|
||||
#define DHCPV6_OPT_STATUS (13U) /**< status code option */
|
||||
#define DHCPV6_OPT_IID (18U) /**< interface-id option */
|
||||
#define DHCPV6_OPT_DNS_RNS (23U) /**< DNS recursive name server option */
|
||||
#define DHCPV6_OPT_IA_PD (25U) /**< identity association for prefix
|
||||
* delegation (IA_PD) option */
|
||||
|
53
sys/include/net/dhcpv6/relay.h
Normal file
53
sys/include/net/dhcpv6/relay.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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_relay DHCPv6 relay agent
|
||||
* @ingroup net_dhcpv6
|
||||
* @brief DHCPv6 relay agent implementation
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief DHCPv6 client definitions
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef NET_DHCPV6_RELAY_H
|
||||
#define NET_DHCPV6_RELAY_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "event.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum hop count in a relay-forward message (HOP_COUNT_LIMIT)
|
||||
*
|
||||
* @see [RFC 8415, section 7.6](https://tools.ietf.org/html/rfc8415#section-7.6)
|
||||
*/
|
||||
#ifndef CONFIG_DHCPV6_RELAY_HOP_LIMIT
|
||||
#define CONFIG_DHCPV6_RELAY_HOP_LIMIT (8U)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_DHCPV6_RELAY_BUFLEN
|
||||
#define CONFIG_DHCPV6_RELAY_BUFLEN (256U) /**< default length for send and receive buffer */
|
||||
#endif
|
||||
|
||||
void dhcpv6_relay_auto_init(void);
|
||||
void dhcpv6_relay_init(event_queue_t *eq, uint16_t listen_netif,
|
||||
uint16_t fwd_netif);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_DHCPV6_RELAY_H */
|
||||
/** @} */
|
@ -84,6 +84,26 @@ 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 Relay Agents/Server message format
|
||||
* @see [RFC 8415, section 9]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-9)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t type; /**< message type (see [DHCPv6 messeg types ](@ref net_dhcp6_msg_types)) */
|
||||
uint8_t hop_count; /**< number of relays that have already relayed the message */
|
||||
/**
|
||||
* @brief optional address to identify the link on which the client is
|
||||
* located.
|
||||
*/
|
||||
ipv6_addr_t link_address;
|
||||
/**
|
||||
* @brief The address of the client or relay agent from which the message
|
||||
* to be relayed was received.
|
||||
*/
|
||||
ipv6_addr_t peer_address;
|
||||
} dhcpv6_relay_msg_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -160,6 +180,17 @@ typedef struct __attribute__((packed)) {
|
||||
network_uint16_t elapsed_time;
|
||||
} dhcpv6_opt_elapsed_time_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 relay message option
|
||||
* @see [RFC 8415, section 21.10]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.10)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_RELAY_MSG */
|
||||
network_uint16_t len; /**< length of dhcpv6_opt_iid_t::msg in byte */
|
||||
uint16_t msg[]; /**< the relayed message */
|
||||
} dhcpv6_opt_relay_msg_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 status code option format
|
||||
* @see [RFC 8415, section 21.13]
|
||||
@ -172,6 +203,17 @@ typedef struct __attribute__((packed)) {
|
||||
char msg[]; /**< UTF-8 encoded text string (not 0-terminated!) */
|
||||
} dhcpv6_opt_status_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 interface-id option
|
||||
* @see [RFC 8415, section 21.18]
|
||||
* (https://tools.ietf.org/html/rfc8415#section-21.18)
|
||||
*/
|
||||
typedef struct __attribute__((packed)) {
|
||||
network_uint16_t type; /**< @ref DHCPV6_OPT_IID */
|
||||
network_uint16_t len; /**< length of dhcpv6_opt_iid_t::iid in byte */
|
||||
uint8_t iid[]; /**< opaque interface identifier */
|
||||
} dhcpv6_opt_iid_t;
|
||||
|
||||
/**
|
||||
* @brief DHCPv6 DNS recursive name server option
|
||||
* @see [RFC 3646, section 3]
|
||||
|
415
sys/net/application_layer/dhcpv6/relay.c
Normal file
415
sys/net/application_layer/dhcpv6/relay.c
Normal file
@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 <assert.h>
|
||||
|
||||
#include "event.h"
|
||||
#include "event/thread.h"
|
||||
#include "log.h"
|
||||
#include "net/dhcpv6.h"
|
||||
#include "net/dhcpv6/client.h" /* required for dhcpv6_duid_l2_t in _dhcpv6.h */
|
||||
#include "net/dhcpv6/relay.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/netif.h"
|
||||
#include "net/sock/async/event.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "net/sock/util.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include "_dhcpv6.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#define AUTO_INIT_PRIO (THREAD_PRIORITY_MAIN - 1)
|
||||
|
||||
static char _auto_init_stack[THREAD_STACKSIZE_DEFAULT];
|
||||
static struct {
|
||||
uint8_t inbuf[CONFIG_DHCPV6_RELAY_BUFLEN];
|
||||
uint8_t outbuf[CONFIG_DHCPV6_RELAY_BUFLEN];
|
||||
sock_udp_t *listen_sock;
|
||||
sock_udp_t *fwd_sock;
|
||||
uint16_t fwd_netif;
|
||||
} _relay_state;
|
||||
|
||||
static void *_dhcpv6_relay_auto_init_thread(void *);
|
||||
static void _udp_handler(sock_udp_t *sock, sock_async_flags_t type,
|
||||
void *arg);
|
||||
static void _dhcpv6_handler(const sock_udp_ep_t *remote, const uint8_t *msg,
|
||||
size_t msg_size);
|
||||
static void _forward_msg(const sock_udp_ep_t *remote, const uint8_t *msg, size_t
|
||||
msg_size, bool is_client_msg);
|
||||
static void _forward_reply(const uint8_t *in_msg, size_t in_msg_size);
|
||||
|
||||
static int16_t _only_one_netif(void)
|
||||
{
|
||||
if (IS_USED(MODULE_NETIF)) {
|
||||
netif_t *netif = netif_iter(NULL);
|
||||
|
||||
return (netif_iter(netif) == NULL) ? netif_get_id(netif) : -1;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void dhcpv6_relay_auto_init(void)
|
||||
{
|
||||
if (IS_USED(MODULE_AUTO_INIT_DHCPV6_RELAY)) {
|
||||
int16_t netif = _only_one_netif();
|
||||
if (netif > 0) {
|
||||
if (IS_USED(MODULE_EVENT_THREAD)) {
|
||||
dhcpv6_relay_init(EVENT_PRIO_LOWEST, netif, netif);
|
||||
}
|
||||
else {
|
||||
thread_create(_auto_init_stack, ARRAY_SIZE(_auto_init_stack),
|
||||
AUTO_INIT_PRIO, THREAD_CREATE_STACKTEST,
|
||||
_dhcpv6_relay_auto_init_thread,
|
||||
(void *)(intptr_t)netif, "dhcpv6_relay");
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG_WARNING("DHCPv6 relay: auto init failed, more than 1 interface\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int _join_all_relays_and_server(uint16_t netif_id)
|
||||
{
|
||||
netif_t *netif = netif_get_by_id(netif_id);
|
||||
ipv6_addr_t all_relays_and_server = {
|
||||
.u8 = DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS
|
||||
};
|
||||
assert(netif != NULL);
|
||||
|
||||
return netif_set_opt(netif, NETOPT_IPV6_GROUP, 0, &all_relays_and_server,
|
||||
sizeof(all_relays_and_server));
|
||||
}
|
||||
|
||||
void dhcpv6_relay_init(event_queue_t *eq, uint16_t listen_netif,
|
||||
uint16_t fwd_netif)
|
||||
{
|
||||
sock_udp_ep_t local = { .family = AF_INET6, .port = DHCPV6_SERVER_PORT,
|
||||
.netif = listen_netif };
|
||||
static sock_udp_t listen_sock;
|
||||
int res;
|
||||
|
||||
assert(eq->waiter != NULL);
|
||||
memset(&listen_sock, 0, sizeof(listen_sock));
|
||||
_relay_state.fwd_netif = fwd_netif;
|
||||
res = _join_all_relays_and_server(listen_netif);
|
||||
if (res < 0) {
|
||||
DEBUG("DHCPv6 relay: unable to join All_DHCP_Relay_Agents_and_Servers: "
|
||||
"%d\n", -res);
|
||||
return;
|
||||
}
|
||||
/* initialize client-listening sock */
|
||||
res = sock_udp_create(&listen_sock, &local, NULL, 0);
|
||||
if (res < 0) {
|
||||
DEBUG("DHCPv6 relay: unable to open listen sock: %d\n", -res);
|
||||
return;
|
||||
}
|
||||
_relay_state.listen_sock = &listen_sock;
|
||||
sock_udp_event_init(_relay_state.listen_sock, eq, _udp_handler, NULL);
|
||||
|
||||
if (listen_netif != fwd_netif) {
|
||||
static sock_udp_t fwd_sock;
|
||||
|
||||
memset(&fwd_sock, 0, sizeof(fwd_sock));
|
||||
/* initialize forwarding / reply-listening sock */
|
||||
local.netif = fwd_netif;
|
||||
res = sock_udp_create(&fwd_sock, &local, NULL, 0);
|
||||
if (res < 0) {
|
||||
DEBUG("DHCPv6 relay: unable to open fwd sock: %d\n", -res);
|
||||
return;
|
||||
}
|
||||
_relay_state.fwd_sock = &fwd_sock;
|
||||
sock_udp_event_init(_relay_state.fwd_sock, eq, _udp_handler, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void *_dhcpv6_relay_auto_init_thread(void *args)
|
||||
{
|
||||
event_queue_t queue;
|
||||
int16_t netif = (intptr_t)args;
|
||||
|
||||
event_queue_init(&queue);
|
||||
dhcpv6_relay_init(&queue, netif, netif);
|
||||
event_loop(&queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _udp_handler(sock_udp_t *sock, sock_async_flags_t type,
|
||||
void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
if (type == SOCK_ASYNC_MSG_RECV) {
|
||||
sock_udp_ep_t remote = { .family = AF_INET6 };
|
||||
ssize_t res = sock_udp_recv(sock, _relay_state.inbuf,
|
||||
sizeof(_relay_state.inbuf), 0, &remote);
|
||||
|
||||
if (res < 0) {
|
||||
DEBUG("DHCPv6 relay: Error receiving UDP message: %d\n", (int)-res);
|
||||
return;
|
||||
}
|
||||
_dhcpv6_handler(&remote, _relay_state.inbuf, res);
|
||||
}
|
||||
}
|
||||
|
||||
static void _dhcpv6_handler(const sock_udp_ep_t *remote, const uint8_t *msg,
|
||||
size_t msg_size)
|
||||
{
|
||||
bool is_client_msg = false;
|
||||
|
||||
if (msg_size == 0) {
|
||||
DEBUG("DHCPv6 relay: incoming message size 0\n");
|
||||
return;
|
||||
}
|
||||
if (remote->family != AF_INET6) {
|
||||
DEBUG("DHCPv6 relay: incoming message source not an IPv6 address\n");
|
||||
return;
|
||||
}
|
||||
switch (msg[0]) {
|
||||
case DHCPV6_SOLICIT:
|
||||
case DHCPV6_REQUEST:
|
||||
case DHCPV6_CONFIRM:
|
||||
case DHCPV6_RENEW:
|
||||
case DHCPV6_REBIND:
|
||||
case DHCPV6_RELEASE:
|
||||
case DHCPV6_DECLINE:
|
||||
case DHCPV6_INFO_REQUEST:
|
||||
is_client_msg = true;
|
||||
/* intentionally falls through */
|
||||
case DHCPV6_RELAY_FORW:
|
||||
_forward_msg(remote, msg, msg_size, is_client_msg);
|
||||
break;
|
||||
case DHCPV6_RELAY_REPL:
|
||||
_forward_reply(msg, msg_size);
|
||||
break;
|
||||
default:
|
||||
DEBUG("DHCPv6 relay: unexpected incoming message type %u\n",
|
||||
msg[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t _compose_iid_opt(dhcpv6_opt_iid_t *opt,
|
||||
const sock_udp_ep_t *remote)
|
||||
{
|
||||
opt->type = byteorder_htons(DHCPV6_OPT_IID);
|
||||
opt->len = byteorder_htons(sizeof(remote->netif));
|
||||
memcpy(opt->iid, &remote->netif, sizeof(remote->netif));
|
||||
return sizeof(remote->netif) + sizeof(dhcpv6_opt_iid_t);
|
||||
}
|
||||
|
||||
static uint16_t _compose_relay_msg_opt(dhcpv6_opt_relay_msg_t *opt,
|
||||
const uint8_t *in_msg,
|
||||
size_t in_msg_size)
|
||||
{
|
||||
opt->type = byteorder_htons(DHCPV6_OPT_RELAY_MSG);
|
||||
opt->len = byteorder_htons((uint16_t)in_msg_size);
|
||||
memcpy(opt->msg, in_msg, in_msg_size);
|
||||
return (uint16_t)in_msg_size + sizeof(dhcpv6_opt_relay_msg_t);
|
||||
}
|
||||
|
||||
static bool _addr_unspec(const uint8_t *addr, size_t addr_len)
|
||||
{
|
||||
for (unsigned i = 0; i < addr_len; i++) {
|
||||
if (addr[i] != 0U) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _remote_unspec(const sock_udp_ep_t *remote)
|
||||
{
|
||||
switch (remote->family) {
|
||||
case AF_INET6:
|
||||
return _addr_unspec(remote->addr.ipv6, sizeof(remote->addr.ipv6));
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void _forward_msg(const sock_udp_ep_t *remote, const uint8_t *in_msg,
|
||||
size_t in_msg_size, bool is_client_msg)
|
||||
{
|
||||
dhcpv6_relay_msg_t *out_fwd = (dhcpv6_relay_msg_t *)_relay_state.outbuf;
|
||||
int res;
|
||||
sock_udp_ep_t send_remote = {
|
||||
.family = AF_INET6,
|
||||
.netif = _relay_state.fwd_netif,
|
||||
.addr = { .ipv6 = DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS },
|
||||
.port = DHCPV6_SERVER_PORT,
|
||||
};
|
||||
uint16_t out_fwd_len = sizeof(dhcpv6_relay_msg_t);
|
||||
|
||||
assert(in_msg_size <= UINT16_MAX);
|
||||
|
||||
if (in_msg_size < sizeof(dhcpv6_msg_t)) {
|
||||
DEBUG("DHCPv6 relay: incoming message too small\n");
|
||||
return;
|
||||
}
|
||||
if (_remote_unspec(remote)) {
|
||||
DEBUG("DHCPv6 relay: incoming message came from unspecified remote\n");
|
||||
return;
|
||||
}
|
||||
if (is_client_msg) {
|
||||
out_fwd->hop_count = 0;
|
||||
}
|
||||
else {
|
||||
const dhcpv6_relay_msg_t *in_fwd = (dhcpv6_relay_msg_t *)in_msg;
|
||||
|
||||
if (in_fwd->hop_count > CONFIG_DHCPV6_RELAY_HOP_LIMIT) {
|
||||
DEBUG("DHCPv6 relay: incoming message exceeded hop limit\n");
|
||||
return;
|
||||
}
|
||||
if (in_msg_size < sizeof(dhcpv6_relay_msg_t)) {
|
||||
DEBUG("DHCPv6 relay: incoming forward message too small\n");
|
||||
return;
|
||||
}
|
||||
/* TODO: check if peer-address is myself to prevent network spam when
|
||||
* fwd_netif == listen_netif */
|
||||
out_fwd->hop_count = in_fwd->hop_count + 1;
|
||||
}
|
||||
|
||||
out_fwd->type = DHCPV6_RELAY_FORW;
|
||||
/* set link-address to unspecified address, we will provide an Interface-ID
|
||||
* option instead */
|
||||
memset(&out_fwd->link_address, 0, sizeof(out_fwd->link_address));
|
||||
assert(sizeof(out_fwd->peer_address) == sizeof(remote->addr.ipv6));
|
||||
memcpy(&out_fwd->peer_address, &remote->addr.ipv6,
|
||||
sizeof(out_fwd->peer_address));
|
||||
|
||||
/* set mandatory options */
|
||||
out_fwd_len += _compose_iid_opt(
|
||||
(dhcpv6_opt_iid_t *)&_relay_state.outbuf[out_fwd_len], remote
|
||||
);
|
||||
if ((out_fwd_len + in_msg_size + sizeof(dhcpv6_opt_relay_msg_t)) >
|
||||
sizeof(_relay_state.outbuf)) {
|
||||
DEBUG("DHCPv6 relay: output buffer too small to relay message\n");
|
||||
return;
|
||||
}
|
||||
out_fwd_len += _compose_relay_msg_opt(
|
||||
(dhcpv6_opt_relay_msg_t *)&_relay_state.outbuf[out_fwd_len],
|
||||
in_msg, in_msg_size
|
||||
);
|
||||
res = sock_udp_send(_relay_state.fwd_sock, out_fwd, out_fwd_len,
|
||||
&send_remote);
|
||||
if (res < 0) {
|
||||
DEBUG("DHCPv6 relay: sending forward message failed: %d\n", -res);
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t _get_iid(dhcpv6_opt_iid_t *opt)
|
||||
{
|
||||
return (opt->iid[1] << 8) | (opt->iid[0] & 0xff);
|
||||
}
|
||||
|
||||
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 void _forward_reply(const uint8_t *in_msg, size_t in_msg_size)
|
||||
{
|
||||
const dhcpv6_relay_msg_t *in_reply = (const dhcpv6_relay_msg_t *)in_msg;
|
||||
const uint8_t *out_msg = NULL;
|
||||
size_t out_msg_len = 0;
|
||||
int res;
|
||||
sock_udp_ep_t target = { .family = AF_INET6 };
|
||||
|
||||
if (in_msg_size < sizeof(dhcpv6_relay_msg_t)) {
|
||||
DEBUG("DHCPv6 relay: incoming reply message too small\n");
|
||||
return;
|
||||
}
|
||||
if (_addr_unspec(in_reply->peer_address.u8,
|
||||
sizeof(in_reply->peer_address.u8))) {
|
||||
DEBUG("DHCPv6 relay: incoming reply message has unspecified peer "
|
||||
"address\n");
|
||||
return;
|
||||
}
|
||||
in_msg_size -= sizeof(dhcpv6_relay_msg_t);
|
||||
for (dhcpv6_opt_t *opt = (dhcpv6_opt_t *)(&in_msg[sizeof(dhcpv6_relay_msg_t)]);
|
||||
in_msg_size > 0; in_msg_size -= _opt_len(opt), opt = _opt_next(opt)) {
|
||||
|
||||
uint16_t opt_len = byteorder_ntohs(opt->len);
|
||||
|
||||
if (opt_len > in_msg_size) {
|
||||
DEBUG("DHCPv6 relay: invalid option size\n");
|
||||
return;
|
||||
}
|
||||
switch (byteorder_ntohs(opt->type)) {
|
||||
case DHCPV6_OPT_IID:
|
||||
if (opt_len != sizeof(uint16_t)) {
|
||||
DEBUG("DHCPv6 relay: unexpected interface-ID length\n");
|
||||
return;
|
||||
}
|
||||
target.netif = _get_iid((dhcpv6_opt_iid_t *)opt);
|
||||
break;
|
||||
case DHCPV6_OPT_RELAY_MSG: {
|
||||
out_msg = ((uint8_t *)opt) + sizeof(dhcpv6_opt_relay_msg_t);
|
||||
out_msg_len = opt_len;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DEBUG("DHCPv6 relay: ignoring unknown option %u\n",
|
||||
byteorder_ntohs(opt->type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.netif == 0) {
|
||||
DEBUG("DHCPv6 relay: no interface ID option found\n");
|
||||
return;
|
||||
}
|
||||
if ((out_msg == NULL) || (out_msg_len == 0)) {
|
||||
DEBUG("DHCPv6 relay: no reply to forward found\n");
|
||||
return;
|
||||
}
|
||||
if (out_msg[0] == DHCPV6_RELAY_REPL) {
|
||||
/* out message is heading for the next relay */
|
||||
target.port = DHCPV6_SERVER_PORT;
|
||||
}
|
||||
else {
|
||||
/* out message is heading for the client it is destined to */
|
||||
target.port = DHCPV6_CLIENT_PORT;
|
||||
}
|
||||
assert(sizeof(in_reply->peer_address) == sizeof(target.addr.ipv6));
|
||||
|
||||
memcpy(&target.addr.ipv6, &in_reply->peer_address, sizeof(target.addr.ipv6));
|
||||
if (IS_USED(MODULE_SOCK_UTIL) && ENABLE_DEBUG) {
|
||||
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
|
||||
uint16_t port;
|
||||
|
||||
if (sock_udp_ep_fmt(&target, addr_str, &port) > 0) {
|
||||
DEBUG("DHCPv6 relay: forwarding reply towards target [%s]:%u\n",
|
||||
addr_str, port);
|
||||
}
|
||||
}
|
||||
res = sock_udp_send(NULL, out_msg, out_msg_len, &target);
|
||||
if (res < 0) {
|
||||
DEBUG("DHCPv6 relay: forwarding reply towards target failed: %d\n",
|
||||
-res);
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user