dhcpv6_relay_misc.c
Go to the documentation of this file.
1 /**
2  * @file dhcpv6_relay_misc.
3  * @brief Helper functions for DHCPv6 relay agent
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * DHCPv6 Relay-Agents are deployed to forward DHCPv6 messages between clients
30  * and servers when they are not on the same IPv6 link and are often implemented
31  * alongside a routing function in a common node. Refer to RFC 3315
32  *
33  * @author Oryx Embedded SARL (www.oryx-embedded.com)
34  * @version 2.6.0
35  **/
36 
37 //Switch to the appropriate trace level
38 #define TRACE_LEVEL DHCPV6_TRACE_LEVEL
39 
40 //Dependencies
41 #include "core/net.h"
42 #include "ipv6/ipv6_multicast.h"
43 #include "dhcpv6/dhcpv6_relay.h"
45 #include "dhcpv6/dhcpv6_debug.h"
46 #include "debug.h"
47 
48 //Check TCP/IP stack configuration
49 #if (IPV6_SUPPORT == ENABLED && DHCPV6_RELAY_SUPPORT == ENABLED)
50 
51 
52 /**
53  * @brief Open client-facing socket
54  * @param[in] context Pointer to the DHCPv6 relay agent context
55  * @param[in] index Zero-based index
56  * @return Error code
57  * @return
58  **/
59 
61 {
62  error_t error;
63 
64  //Open a UDP socket
65  context->clientSockets[index] = socketOpenEx(context->netContext,
67 
68  //Valid socket handle?
69  if(context->serverSocket != NULL)
70  {
71  //Explicitly associate the socket with the relevant interface
72  error = socketBindToInterface(context->clientSockets[index],
73  context->clientInterfaces[index]);
74 
75  //Check status code
76  if(!error)
77  {
78  //Relay agents listen for DHCPv6 messages on UDP port 547
79  error = socketBind(context->clientSockets[index], &IP_ADDR_ANY,
81  }
82 
83  //Check status code
84  if(!error)
85  {
87 
88  //The All_DHCP_Relay_Agents_and_Servers address (ff02::1:2) is a
89  //link-scoped multicast address used by a client to communicate
90  //with neighboring relay agents and servers
91  multicastAddr.length = sizeof(Ipv6Addr);
93 
94  //All servers and relay agents are members of this multicast group
95  //(refer to RFC 8415, section 7.1)
96  error = socketJoinMulticastGroup(context->clientSockets[index],
97  &multicastAddr);
98  }
99  }
100  else
101  {
102  //Report an error
103  error = ERROR_OPEN_FAILED;
104  }
105 
106  //Return status code
107  return error;
108 }
109 
110 
111 /**
112  * @brief Open server-facing socket
113  * @param[in] context Pointer to the DHCPv6 relay agent context
114  * @return Error code
115  **/
116 
118 {
119  error_t error;
120 
121  //Open a UDP socket
124 
125  //Valid socket handle?
126  if(context->serverSocket != NULL)
127  {
128  //Explicitly associate the socket with the relevant interface
129  error = socketBindToInterface(context->serverSocket,
130  context->serverInterface);
131 
132  //Check status code
133  if(!error)
134  {
135  //Relay agents listen for DHCPv6 messages on UDP port 547
136  error = socketBind(context->serverSocket, &IP_ADDR_ANY,
138  }
139 
140  //Check status code
141  if(!error)
142  {
143  //Only accept datagrams with source port number 547
144  error = socketConnect(context->serverSocket, &IP_ADDR_ANY,
146  }
147 
148  //Check status code
149  if(!error)
150  {
152 
153  //The All_DHCP_Relay_Agents_and_Servers address (ff02::1:2) is a
154  //link-scoped multicast address used by a client to communicate
155  //with neighboring relay agents and servers
156  multicastAddr.length = sizeof(Ipv6Addr);
158 
159  //All servers and relay agents are members of this multicast group
160  //(refer to RFC 8415, section 7.1)
161  error = socketJoinMulticastGroup(context->serverSocket,
162  &multicastAddr);
163  }
164 
165  //Check status code
166  if(!error)
167  {
168  //If the relay agent relays messages to the All_DHCP_Servers address
169  //or other multicast addresses, it sets the Hop Limit field to 8
170  //(refer to RFC 8415, section 19)
171  error = socketSetTtl(context->serverSocket,
173  }
174  }
175  else
176  {
177  //Report an error
178  error = ERROR_OPEN_FAILED;
179  }
180 
181  //Return status code
182  return error;
183 }
184 
185 
186 /**
187  * @brief Forward client message
188  * @param[in] context Pointer to the DHCPv6 relay agent context
189  * @param[in] index Index identifying the interface on which the message was received
190  * @return Error code
191  **/
192 
194 {
195  error_t error;
196  uint32_t interfaceId;
197  size_t inputMessageLen;
198  size_t outputMessageLen;
199  Dhcpv6RelayMessage *inputMessage;
200  Dhcpv6RelayMessage *outputMessage;
201  Dhcpv6Option *option;
202  IpAddr ipAddr;
203  uint16_t port;
204 
205  //Point to the buffer where to store the incoming DHCPv6 message
206  inputMessage = (Dhcpv6RelayMessage *) (context->buffer +
208 
209  //Message that will be forwarded by the DHCPv6 relay agent
210  outputMessage = (Dhcpv6RelayMessage *) context->buffer;
211 
212  //Read incoming message
213  error = socketReceiveFrom(context->clientSockets[index], &ipAddr, &port,
215  &inputMessageLen, 0);
216  //Any error to report?
217  if(error)
218  return error;
219 
220  //Debug message
221  TRACE_INFO("\r\nDHCPv6 message received on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
222  context->clientInterfaces[index]->name, inputMessageLen);
223 
224  //Dump the contents of the message for debugging purpose
225  dhcpv6DumpMessage(inputMessage, inputMessageLen);
226 
227  //The source address must be a valid IPv6 address
228  if(ipAddr.length != sizeof(Ipv6Addr))
229  return ERROR_INVALID_ADDRESS;
230 
231  //Check the length of the DHCPv6 message
232  if(inputMessageLen < sizeof(Dhcpv6Message))
233  return ERROR_INVALID_MESSAGE;
234 
235  //When the relay agent receives a valid message to be relayed, it constructs
236  //a new Relay-Forward message
237  outputMessage->msgType = DHCPV6_MSG_TYPE_RELAY_FORW;
238 
239  //Inspect message type
240  if(inputMessage->msgType == DHCPV6_MSG_TYPE_SOLICIT ||
241  inputMessage->msgType == DHCPV6_MSG_TYPE_REQUEST ||
242  inputMessage->msgType == DHCPV6_MSG_TYPE_CONFIRM ||
243  inputMessage->msgType == DHCPV6_MSG_TYPE_RENEW ||
244  inputMessage->msgType == DHCPV6_MSG_TYPE_REBIND ||
245  inputMessage->msgType == DHCPV6_MSG_TYPE_RELEASE ||
246  inputMessage->msgType == DHCPV6_MSG_TYPE_DECLINE ||
247  inputMessage->msgType == DHCPV6_MSG_TYPE_INFO_REQUEST)
248  {
249  //Clients use UDP source port 546
250  if(port != DHCPV6_CLIENT_PORT)
251  return ERROR_INVALID_PORT;
252 
253  //If the relay agent received the message to be relayed from a client,
254  //the hop-count in the Relay-Forward message is set to 0
255  outputMessage->hopCount = 0;
256  }
257  else if(inputMessage->msgType == DHCPV6_MSG_TYPE_RELAY_FORW)
258  {
259  //Relay agents use UDP source port 547
260  if(port != DHCPV6_SERVER_PORT)
261  return ERROR_INVALID_PORT;
262 
263  //If the message received by the relay agent is a Relay-Forward message
264  //and the hop-count in the message is greater than or equal to
265  //HOP_COUNT_LIMIT, the relay agent discards the received message
266  if(inputMessage->hopCount >= DHCPV6_RELAY_HOP_COUNT_LIMIT)
267  return ERROR_INVALID_MESSAGE;
268 
269  //Set the hop-count field to the value of the hop-count field in the
270  //received message incremented by 1
271  outputMessage->hopCount = inputMessage->hopCount + 1;
272  }
273  else
274  {
275  //Discard ADVERTISE, REPLY, RECONFIGURE and RELAY-REPL messages
276  return ERROR_INVALID_MESSAGE;
277  }
278 
279  //Set the link-address field to the unspecified address
280  outputMessage->linkAddress = IPV6_UNSPECIFIED_ADDR;
281 
282  //Copy the source address from the header of the IP datagram in which the
283  //message was received to the peer-address field
284  outputMessage->peerAddress = ipAddr.ipv6Addr;
285 
286  //Size of the Relay-Forward message
287  outputMessageLen = sizeof(Dhcpv6RelayMessage);
288 
289  //Get the interface identifier
290  interfaceId = context->clientInterfaces[index]->id;
291  //Convert the 32-bit integer to network byte order
293 
294  //If the relay agent cannot use the address in the link-address field
295  //to identify the interface through which the response to the client
296  //will be relayed, the relay agent must include an Interface ID option
297  dhcpv6AddOption(outputMessage, &outputMessageLen, DHCPV6_OPT_INTERFACE_ID,
298  &interfaceId, sizeof(interfaceId));
299 
300  //The relay agent copies the received DHCPv6 message into a Relay Message
301  //option in the new message (refer to RFC 8415, section 19.1)
302  option = dhcpv6AddOption(outputMessage, &outputMessageLen,
303  DHCPV6_OPT_RELAY_MSG, NULL, 0);
304 
305  //Set the appropriate length of the option
306  option->length = htons(inputMessageLen);
307  //Adjust the length of the Relay-Forward message
308  outputMessageLen += inputMessageLen;
309 
310  //Debug message
311  TRACE_INFO("Forwarding DHCPv6 message on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
312  context->serverInterface->name, outputMessageLen);
313 
314  //Dump the contents of the message for debugging purpose
315  dhcpv6DumpMessage(outputMessage, outputMessageLen);
316 
317  //The destination address is selected by the network administrator
318  ipAddr.length = sizeof(Ipv6Addr);
319  ipAddr.ipv6Addr = context->serverIpAddr;
320 
321  //Relay the client message to the server
323  outputMessage, outputMessageLen, NULL, 0);
324 }
325 
326 
327 /**
328  * @brief Forward Relay-Reply message
329  * @param[in] context Pointer to the DHCPv6 relay agent context
330  * @return Error code
331  **/
332 
334 {
335  error_t error;
336  uint_t i;
337  uint32_t interfaceId;
338  size_t inputMessageLen;
339  size_t outputMessageLen;
340  Dhcpv6RelayMessage *inputMessage;
341  Dhcpv6Message *outputMessage;
342  Dhcpv6Option *option;
343  IpAddr ipAddr;
344  uint16_t port;
345 
346  //Point to the buffer where to store the incoming DHCPv6 message
347  inputMessage = (Dhcpv6RelayMessage *) context->buffer;
348 
349  //Read incoming message
350  error = socketReceiveFrom(context->serverSocket, &ipAddr, &port,
351  inputMessage, DHCPV6_MAX_MSG_SIZE, &inputMessageLen, 0);
352  //Any error to report?
353  if(error)
354  return error;
355 
356  //Debug message
357  TRACE_INFO("\r\nDHCPv6 message received on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
358  context->serverInterface->name, inputMessageLen);
359 
360  //Dump the contents of the message for debugging purpose
361  dhcpv6DumpMessage(inputMessage, inputMessageLen);
362 
363  //Check the length of the DHCPv6 message
364  if(inputMessageLen < sizeof(Dhcpv6RelayMessage))
365  return ERROR_INVALID_MESSAGE;
366 
367  //Inspect the message type and only forward Relay-Reply messages. Other
368  //DHCPv6 message types must be silently discarded
369  if(inputMessage->msgType != DHCPV6_MSG_TYPE_RELAY_REPL)
370  return ERROR_INVALID_MESSAGE;
371 
372  //Get the length of the Options field
373  inputMessageLen -= sizeof(Dhcpv6Message);
374 
375  //The Relay-Reply message must include a Relay Message option
376  option = dhcpv6GetOption(inputMessage->options, inputMessageLen,
378  //Failed to retrieve specified option?
379  if(option == NULL || ntohs(option->length) < sizeof(Dhcpv6Message))
380  return ERROR_INVALID_MESSAGE;
381 
382  //The relay agent extracts the message from the Relay Message option. Relay
383  //agents must not modify the message (refer to RFC 8415, section 19.2)
384  outputMessage = (Dhcpv6Message *) option->value;
385 
386  //Save the length of the message
387  outputMessageLen = ntohs(option->length);
388 
389  //Check whether an Interface ID option is included in the Relay-Reply
390  option = dhcpv6GetOption(inputMessage->options, inputMessageLen,
392  //Failed to retrieve specified option?
393  if(option == NULL || ntohs(option->length) != sizeof(interfaceId))
394  return ERROR_INVALID_MESSAGE;
395 
396  //Read the Interface ID option contents
397  osMemcpy(&interfaceId, option->value, sizeof(interfaceId));
398  //Convert the 32-bit integer from network byte order
400 
401  //Loop through client-facing interfaces
402  for(i = 0; i < context->numClientInterfaces; i++)
403  {
404  //Check whether the current interface matches the Interface ID option
405  if(context->clientInterfaces[i]->id == interfaceId)
406  {
407  break;
408  }
409  }
410 
411  //Unknown interface identifier?
412  if(i >= context->numClientInterfaces)
413  return ERROR_WRONG_IDENTIFIER;
414 
415  //Debug message
416  TRACE_INFO("Forwarding DHCPv6 message on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
417  context->clientInterfaces[i]->name, outputMessageLen);
418 
419  //Dump the contents of the message for debugging purpose
420  dhcpv6DumpMessage(outputMessage, outputMessageLen);
421 
422  //Relay the message to the address contained in the peer-address field of
423  //the Relay-reply message
424  ipAddr.length = sizeof(Ipv6Addr);
425  ipAddr.ipv6Addr = inputMessage->peerAddress;
426 
427  //Select the destination port number to use
428  if(outputMessage->msgType == DHCPV6_MSG_TYPE_RELAY_REPL)
429  {
430  //The destination port number is set to 547 if the Relay-reply message is
431  //sent to other relay agents
433  }
434  else
435  {
436  //The destination port number is set to 546 if the message extracted from
437  //the Relay-reply message is sent to the client
439  }
440 
441  //Relay the DHCPv6 message from the server to the client on the link
442  //identified by the Interface ID option
443  return socketSendTo(context->clientSockets[i], &ipAddr, port, outputMessage,
444  outputMessageLen, NULL, 0);
445 }
446 
447 #endif
#define htons(value)
Definition: cpu_endian.h:413
@ SOCKET_IP_PROTO_UDP
Definition: socket.h:108
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:1344
@ DHCPV6_MSG_TYPE_DECLINE
Definition: dhcpv6_common.h:98
error_t dhcpv6ForwardClientMessage(Dhcpv6RelayContext *context, uint_t index)
Forward client message.
error_t dhcpv6ForwardRelayReplyMessage(Dhcpv6RelayContext *context)
Forward Relay-Reply message.
DHCPv6 relay agent (Dynamic Host Configuration Protocol for IPv6)
@ DHCPV6_MSG_TYPE_SOLICIT
Definition: dhcpv6_common.h:90
IP network address.
Definition: ip.h:90
#define DHCPV6_CLIENT_PORT
Definition: dhcpv6_common.h:40
error_t dhcpv6RelayOpenServerSocket(Dhcpv6RelayContext *context)
Open server-facing socket.
error_t socketJoinMulticastGroup(Socket *socket, const IpAddr *groupAddr)
Join the specified host group.
Definition: socket.c:447
@ DHCPV6_MSG_TYPE_REBIND
Definition: dhcpv6_common.h:95
@ DHCPV6_MSG_TYPE_RELAY_FORW
Ipv6Addr
Definition: ipv6.h:280
@ SOCKET_TYPE_DGRAM
Definition: socket.h:93
const Ipv6Addr DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR
Definition: dhcpv6_common.c:54
Dhcpv6Message
@ ERROR_INVALID_PORT
Definition: error.h:104
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
Dhcpv6Option * dhcpv6GetOption(const uint8_t *options, size_t optionsLength, uint16_t optionCode)
Search a DHCPv6 message for a given option.
#define DHCPV6_RELAY_FORWARDING_OVERHEAD
Definition: dhcpv6_relay.h:65
Dhcpv6RelayMessage
@ ERROR_OPEN_FAILED
Definition: error.h:75
const IpAddr IP_ADDR_ANY
Definition: ip.c:53
Helper functions for DHCPv6 relay agent.
IPv6 multicast filtering.
NetInterface * serverInterface
Network-facing interface.
Definition: dhcpv6_relay.h:95
#define htonl(value)
Definition: cpu_endian.h:414
#define DHCPV6_MAX_MSG_SIZE
Definition: dhcpv6_common.h:44
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
uint_t numClientInterfaces
Number of client-facing interfaces.
Definition: dhcpv6_relay.h:96
error_t
Error codes.
Definition: error.h:43
NetInterface * clientInterfaces[DHCPV6_RELAY_MAX_CLIENT_INTERFACES]
Client-facing interfaces.
Definition: dhcpv6_relay.h:97
Dhcpv6Option
@ ERROR_INVALID_ADDRESS
Definition: error.h:103
@ DHCPV6_MSG_TYPE_RELEASE
Definition: dhcpv6_common.h:97
NetContext * netContext
TCP/IP stack context.
Definition: dhcpv6_relay.h:94
Socket * clientSockets[DHCPV6_RELAY_MAX_CLIENT_INTERFACES]
Sockets that handle client-facing interfaces.
Definition: dhcpv6_relay.h:100
error_t socketReceiveFrom(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, void *data, size_t size, size_t *received, uint_t flags)
Receive a datagram from a connectionless socket.
Definition: socket.c:1746
error_t socketSetTtl(Socket *socket, uint8_t ttl)
Set TTL value for unicast datagrams.
Definition: socket.c:194
@ DHCPV6_OPT_INTERFACE_ID
error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
Establish a connection to a specified socket.
Definition: socket.c:1377
const Ipv6Addr IPV6_UNSPECIFIED_ADDR
Definition: ipv6.c:65
@ DHCPV6_MSG_TYPE_RENEW
Definition: dhcpv6_common.h:94
Eui64 interfaceId
Definition: ipv6cp.h:71
#define TRACE_INFO(...)
Definition: debug.h:105
error_t dhcpv6RelayOpenClientSocket(Dhcpv6RelayContext *context, uint_t index)
Open client-facing socket.
uint8_t buffer[DHCPV6_MAX_MSG_SIZE]
Scratch buffer to store DHCPv6 messages.
Definition: dhcpv6_relay.h:107
#define socketBindToInterface
Definition: net_legacy.h:193
@ DHCPV6_MSG_TYPE_INFO_REQUEST
@ DHCPV6_MSG_TYPE_RELAY_REPL
uint16_t port
Definition: dns_common.h:270
#define ntohs(value)
Definition: cpu_endian.h:421
#define DHCPV6_SERVER_PORT
Definition: dhcpv6_common.h:41
@ DHCPV6_OPT_RELAY_MSG
DHCPv6 relay agent context.
Definition: dhcpv6_relay.h:93
error_t dhcpv6DumpMessage(const void *message, size_t length)
Dump DHCPv6 message for debugging purpose.
Definition: dhcpv6_debug.c:123
Socket * socketOpenEx(NetContext *context, uint_t type, uint_t protocol)
Create a socket.
Definition: socket.c:146
Dhcpv6Option * dhcpv6AddOption(void *message, size_t *messageLen, uint16_t optionCode, const void *optionValue, size_t optionLen)
Add an option to a DHCPv6 message.
#define DHCPV6_RELAY_HOP_COUNT_LIMIT
Definition: dhcpv6_common.h:49
@ ERROR_WRONG_IDENTIFIER
Definition: error.h:89
error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, const void *data, size_t length, size_t *written, uint_t flags)
Send a datagram to a specific destination.
Definition: socket.c:1535
@ DHCPV6_MSG_TYPE_CONFIRM
Definition: dhcpv6_common.h:93
Ipv4Addr ipAddr
Definition: ipcp.h:105
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:57
TCP/IP stack core.
Ipv6Addr serverIpAddr
Address to be used when relaying messages to the server.
Definition: dhcpv6_relay.h:98
#define ntohl(value)
Definition: cpu_endian.h:422
Ipv4Addr multicastAddr
Definition: igmp_common.h:267
Debugging facilities.
Socket * serverSocket
Socket that handles the network-facing interface.
Definition: dhcpv6_relay.h:99
Data logging functions for debugging purpose (DHCPv6)
@ DHCPV6_MSG_TYPE_REQUEST
Definition: dhcpv6_common.h:92