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.6
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  **/
58 
60 {
61  error_t error;
62 
63  //Open a UDP socket
64  context->clientSockets[index] = socketOpenEx(context->netContext,
66 
67  //Valid socket handle?
68  if(context->serverSocket != NULL)
69  {
70  //Explicitly associate the socket with the relevant interface
71  error = socketBindToInterface(context->clientSockets[index],
72  context->clientInterfaces[index]);
73 
74  //Check status code
75  if(!error)
76  {
77  //Relay agents listen for DHCPv6 messages on UDP port 547
78  error = socketBind(context->clientSockets[index], &IP_ADDR_ANY,
80  }
81 
82  //Check status code
83  if(!error)
84  {
86 
87  //The All_DHCP_Relay_Agents_and_Servers address (ff02::1:2) is a
88  //link-scoped multicast address used by a client to communicate
89  //with neighboring relay agents and servers
90  multicastAddr.length = sizeof(Ipv6Addr);
92 
93  //All servers and relay agents are members of this multicast group
94  //(refer to RFC 8415, section 7.1)
95  error = socketJoinMulticastGroup(context->clientSockets[index],
96  &multicastAddr);
97  }
98  }
99  else
100  {
101  //Report an error
102  error = ERROR_OPEN_FAILED;
103  }
104 
105  //Return status code
106  return error;
107 }
108 
109 
110 /**
111  * @brief Open server-facing socket
112  * @param[in] context Pointer to the DHCPv6 relay agent context
113  * @return Error code
114  **/
115 
117 {
118  error_t error;
119 
120  //Open a UDP socket
123 
124  //Valid socket handle?
125  if(context->serverSocket != NULL)
126  {
127  //Explicitly associate the socket with the relevant interface
128  error = socketBindToInterface(context->serverSocket,
129  context->serverInterface);
130 
131  //Check status code
132  if(!error)
133  {
134  //Relay agents listen for DHCPv6 messages on UDP port 547
135  error = socketBind(context->serverSocket, &IP_ADDR_ANY,
137  }
138 
139  //Check status code
140  if(!error)
141  {
142  //Only accept datagrams with source port number 547
143  error = socketConnect(context->serverSocket, &IP_ADDR_ANY,
145  }
146 
147  //Check status code
148  if(!error)
149  {
151 
152  //The All_DHCP_Relay_Agents_and_Servers address (ff02::1:2) is a
153  //link-scoped multicast address used by a client to communicate
154  //with neighboring relay agents and servers
155  multicastAddr.length = sizeof(Ipv6Addr);
157 
158  //All servers and relay agents are members of this multicast group
159  //(refer to RFC 8415, section 7.1)
160  error = socketJoinMulticastGroup(context->serverSocket,
161  &multicastAddr);
162  }
163 
164  //Check status code
165  if(!error)
166  {
167  //If the relay agent relays messages to the All_DHCP_Servers address
168  //or other multicast addresses, it sets the Hop Limit field to 8
169  //(refer to RFC 8415, section 19)
170  error = socketSetTtl(context->serverSocket,
172  }
173  }
174  else
175  {
176  //Report an error
177  error = ERROR_OPEN_FAILED;
178  }
179 
180  //Return status code
181  return error;
182 }
183 
184 
185 /**
186  * @brief Forward client message
187  * @param[in] context Pointer to the DHCPv6 relay agent context
188  * @param[in] index Index identifying the interface on which the message was received
189  * @return Error code
190  **/
191 
193 {
194  error_t error;
195  uint32_t interfaceId;
196  size_t inputMessageLen;
197  size_t outputMessageLen;
198  Dhcpv6RelayMessage *inputMessage;
199  Dhcpv6RelayMessage *outputMessage;
200  Dhcpv6Option *option;
201  IpAddr ipAddr;
202  uint16_t port;
203 
204  //Point to the buffer where to store the incoming DHCPv6 message
205  inputMessage = (Dhcpv6RelayMessage *) (context->buffer +
207 
208  //Message that will be forwarded by the DHCPv6 relay agent
209  outputMessage = (Dhcpv6RelayMessage *) context->buffer;
210 
211  //Read incoming message
212  error = socketReceiveFrom(context->clientSockets[index], &ipAddr, &port,
214  &inputMessageLen, 0);
215  //Any error to report?
216  if(error)
217  return error;
218 
219  //Debug message
220  TRACE_INFO("\r\nDHCPv6 message received on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
221  context->clientInterfaces[index]->name, inputMessageLen);
222 
223  //Dump the contents of the message for debugging purpose
224  dhcpv6DumpMessage(inputMessage, inputMessageLen);
225 
226  //The source address must be a valid IPv6 address
227  if(ipAddr.length != sizeof(Ipv6Addr))
228  return ERROR_INVALID_ADDRESS;
229 
230  //Check the length of the DHCPv6 message
231  if(inputMessageLen < sizeof(Dhcpv6Message))
232  return ERROR_INVALID_MESSAGE;
233 
234  //When the relay agent receives a valid message to be relayed, it constructs
235  //a new Relay-Forward message
236  outputMessage->msgType = DHCPV6_MSG_TYPE_RELAY_FORW;
237 
238  //Inspect message type
239  if(inputMessage->msgType == DHCPV6_MSG_TYPE_SOLICIT ||
240  inputMessage->msgType == DHCPV6_MSG_TYPE_REQUEST ||
241  inputMessage->msgType == DHCPV6_MSG_TYPE_CONFIRM ||
242  inputMessage->msgType == DHCPV6_MSG_TYPE_RENEW ||
243  inputMessage->msgType == DHCPV6_MSG_TYPE_REBIND ||
244  inputMessage->msgType == DHCPV6_MSG_TYPE_RELEASE ||
245  inputMessage->msgType == DHCPV6_MSG_TYPE_DECLINE ||
246  inputMessage->msgType == DHCPV6_MSG_TYPE_INFO_REQUEST)
247  {
248  //Clients use UDP source port 546
249  if(port != DHCPV6_CLIENT_PORT)
250  return ERROR_INVALID_PORT;
251 
252  //If the relay agent received the message to be relayed from a client,
253  //the hop-count in the Relay-Forward message is set to 0
254  outputMessage->hopCount = 0;
255  }
256  else if(inputMessage->msgType == DHCPV6_MSG_TYPE_RELAY_FORW)
257  {
258  //Relay agents use UDP source port 547
259  if(port != DHCPV6_SERVER_PORT)
260  return ERROR_INVALID_PORT;
261 
262  //If the message received by the relay agent is a Relay-Forward message
263  //and the hop-count in the message is greater than or equal to
264  //HOP_COUNT_LIMIT, the relay agent discards the received message
265  if(inputMessage->hopCount >= DHCPV6_RELAY_HOP_COUNT_LIMIT)
266  return ERROR_INVALID_MESSAGE;
267 
268  //Set the hop-count field to the value of the hop-count field in the
269  //received message incremented by 1
270  outputMessage->hopCount = inputMessage->hopCount + 1;
271  }
272  else
273  {
274  //Discard ADVERTISE, REPLY, RECONFIGURE and RELAY-REPL messages
275  return ERROR_INVALID_MESSAGE;
276  }
277 
278  //Set the link-address field to the unspecified address
279  outputMessage->linkAddress = IPV6_UNSPECIFIED_ADDR;
280 
281  //Copy the source address from the header of the IP datagram in which the
282  //message was received to the peer-address field
283  outputMessage->peerAddress = ipAddr.ipv6Addr;
284 
285  //Size of the Relay-Forward message
286  outputMessageLen = sizeof(Dhcpv6RelayMessage);
287 
288  //Get the interface identifier
289  interfaceId = context->clientInterfaces[index]->id;
290  //Convert the 32-bit integer to network byte order
292 
293  //If the relay agent cannot use the address in the link-address field
294  //to identify the interface through which the response to the client
295  //will be relayed, the relay agent must include an Interface ID option
296  dhcpv6AddOption(outputMessage, &outputMessageLen, DHCPV6_OPT_INTERFACE_ID,
297  &interfaceId, sizeof(interfaceId));
298 
299  //The relay agent copies the received DHCPv6 message into a Relay Message
300  //option in the new message (refer to RFC 8415, section 19.1)
301  option = dhcpv6AddOption(outputMessage, &outputMessageLen,
302  DHCPV6_OPT_RELAY_MSG, NULL, 0);
303 
304  //Set the appropriate length of the option
305  option->length = htons(inputMessageLen);
306  //Adjust the length of the Relay-Forward message
307  outputMessageLen += inputMessageLen;
308 
309  //Debug message
310  TRACE_INFO("Forwarding DHCPv6 message on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
311  context->serverInterface->name, outputMessageLen);
312 
313  //Dump the contents of the message for debugging purpose
314  dhcpv6DumpMessage(outputMessage, outputMessageLen);
315 
316  //The destination address is selected by the network administrator
317  ipAddr.length = sizeof(Ipv6Addr);
318  ipAddr.ipv6Addr = context->serverIpAddr;
319 
320  //Relay the client message to the server
322  outputMessage, outputMessageLen, NULL, 0);
323 }
324 
325 
326 /**
327  * @brief Forward Relay-Reply message
328  * @param[in] context Pointer to the DHCPv6 relay agent context
329  * @return Error code
330  **/
331 
333 {
334  error_t error;
335  uint_t i;
336  uint32_t interfaceId;
337  size_t inputMessageLen;
338  size_t outputMessageLen;
339  Dhcpv6RelayMessage *inputMessage;
340  Dhcpv6Message *outputMessage;
341  Dhcpv6Option *option;
342  IpAddr ipAddr;
343  uint16_t port;
344 
345  //Point to the buffer where to store the incoming DHCPv6 message
346  inputMessage = (Dhcpv6RelayMessage *) context->buffer;
347 
348  //Read incoming message
349  error = socketReceiveFrom(context->serverSocket, &ipAddr, &port,
350  inputMessage, DHCPV6_MAX_MSG_SIZE, &inputMessageLen, 0);
351  //Any error to report?
352  if(error)
353  return error;
354 
355  //Debug message
356  TRACE_INFO("\r\nDHCPv6 message received on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
357  context->serverInterface->name, inputMessageLen);
358 
359  //Dump the contents of the message for debugging purpose
360  dhcpv6DumpMessage(inputMessage, inputMessageLen);
361 
362  //Check the length of the DHCPv6 message
363  if(inputMessageLen < sizeof(Dhcpv6RelayMessage))
364  return ERROR_INVALID_MESSAGE;
365 
366  //Inspect the message type and only forward Relay-Reply messages. Other
367  //DHCPv6 message types must be silently discarded
368  if(inputMessage->msgType != DHCPV6_MSG_TYPE_RELAY_REPL)
369  return ERROR_INVALID_MESSAGE;
370 
371  //Get the length of the Options field
372  inputMessageLen -= sizeof(Dhcpv6Message);
373 
374  //The Relay-Reply message must include a Relay Message option
375  option = dhcpv6GetOption(inputMessage->options, inputMessageLen,
377  //Failed to retrieve specified option?
378  if(option == NULL || ntohs(option->length) < sizeof(Dhcpv6Message))
379  return ERROR_INVALID_MESSAGE;
380 
381  //The relay agent extracts the message from the Relay Message option. Relay
382  //agents must not modify the message (refer to RFC 8415, section 19.2)
383  outputMessage = (Dhcpv6Message *) option->value;
384 
385  //Save the length of the message
386  outputMessageLen = ntohs(option->length);
387 
388  //Check whether an Interface ID option is included in the Relay-Reply
389  option = dhcpv6GetOption(inputMessage->options, inputMessageLen,
391  //Failed to retrieve specified option?
392  if(option == NULL || ntohs(option->length) != sizeof(interfaceId))
393  return ERROR_INVALID_MESSAGE;
394 
395  //Read the Interface ID option contents
396  osMemcpy(&interfaceId, option->value, sizeof(interfaceId));
397  //Convert the 32-bit integer from network byte order
399 
400  //Loop through client-facing interfaces
401  for(i = 0; i < context->numClientInterfaces; i++)
402  {
403  //Check whether the current interface matches the Interface ID option
404  if(context->clientInterfaces[i]->id == interfaceId)
405  {
406  break;
407  }
408  }
409 
410  //Unknown interface identifier?
411  if(i >= context->numClientInterfaces)
412  return ERROR_WRONG_IDENTIFIER;
413 
414  //Debug message
415  TRACE_INFO("Forwarding DHCPv6 message on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
416  context->clientInterfaces[i]->name, outputMessageLen);
417 
418  //Dump the contents of the message for debugging purpose
419  dhcpv6DumpMessage(outputMessage, outputMessageLen);
420 
421  //Relay the message to the address contained in the peer-address field of
422  //the Relay-reply message
423  ipAddr.length = sizeof(Ipv6Addr);
424  ipAddr.ipv6Addr = inputMessage->peerAddress;
425 
426  //Select the destination port number to use
427  if(outputMessage->msgType == DHCPV6_MSG_TYPE_RELAY_REPL)
428  {
429  //The destination port number is set to 547 if the Relay-reply message is
430  //sent to other relay agents
432  }
433  else
434  {
435  //The destination port number is set to 546 if the message extracted from
436  //the Relay-reply message is sent to the client
438  }
439 
440  //Relay the DHCPv6 message from the server to the client on the link
441  //identified by the Interface ID option
442  return socketSendTo(context->clientSockets[i], &ipAddr, port, outputMessage,
443  outputMessageLen, NULL, 0);
444 }
445 
446 #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:1341
@ 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:94
#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:444
@ DHCPV6_MSG_TYPE_REBIND
Definition: dhcpv6_common.h:95
@ DHCPV6_MSG_TYPE_RELAY_FORW
Ipv6Addr
Definition: ipv6.h:282
@ 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:147
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:1743
error_t socketSetTtl(Socket *socket, uint8_t ttl)
Set TTL value for unicast datagrams.
Definition: socket.c:191
@ 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:1374
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:272
#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:143
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:1532
@ 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