Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers dhcpv6_relay.c Source File

dhcpv6_relay.c

Go to the documentation of this file.
00001 /**
00002  * @file dhcpv6_relay.c
00003  * @brief DHCPv6 relay agent (Dynamic Host Configuration Protocol for IPv6)
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @section Description
00026  *
00027  * DHCPv6 Relay-Agents are deployed to forward DHCPv6 messages between clients
00028  * and servers when they are not on the same IPv6 link and are often implemented
00029  * alongside a routing function in a common node. Refer to RFC 3315
00030  *
00031  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00032  * @version 1.7.6
00033  **/
00034 
00035 //Switch to the appropriate trace level
00036 #define TRACE_LEVEL DHCPV6_TRACE_LEVEL
00037 
00038 //Dependencies
00039 #include "core/net.h"
00040 #include "dhcpv6_relay.h"
00041 #include "dhcpv6/dhcpv6_common.h"
00042 #include "dhcpv6/dhcpv6_debug.h"
00043 #include "debug.h"
00044 
00045 //Check TCP/IP stack configuration
00046 #if (IPV6_SUPPORT == ENABLED && DHCPV6_RELAY_SUPPORT == ENABLED)
00047 
00048 
00049 /**
00050  * @brief Start DHCPv6 relay agent
00051  * @param[in] context Pointer to the DHCPv6 relay agent context
00052  * @param[in] settings DHCPv6 relay agent specific settings
00053  * @return Error code
00054  **/
00055 
00056 error_t dhcpv6RelayStart(Dhcpv6RelayContext *context, const Dhcpv6RelaySettings *settings)
00057 {
00058    error_t error;
00059    uint_t i;
00060    OsTask *task;
00061 
00062    //Debug message
00063    TRACE_INFO("Starting DHCPv6 relay agent...\r\n");
00064 
00065    //Ensure the parameters are valid
00066    if(!context || !settings)
00067       return ERROR_INVALID_PARAMETER;
00068    //The pointer to the network-facing interface shall be valid
00069    if(!settings->serverInterface)
00070       return ERROR_INVALID_INTERFACE;
00071    //Check the number of client-facing interfaces
00072    if(!settings->clientInterfaceCount)
00073       return ERROR_INVALID_PARAMETER;
00074    if(settings->clientInterfaceCount >= DHCPV6_RELAY_MAX_CLIENT_IF)
00075       return ERROR_INVALID_PARAMETER;
00076 
00077    //Loop through the client-facing interfaces
00078    for(i = 0; i < settings->clientInterfaceCount; i++)
00079    {
00080       //A valid pointer is required for each interface
00081       if(!settings->clientInterface[i])
00082          return ERROR_INVALID_INTERFACE;
00083    }
00084 
00085    //Check the address to be used when forwarding messages to the server
00086    if(ipv6CompAddr(&settings->serverAddress, &IPV6_UNSPECIFIED_ADDR))
00087       return ERROR_INVALID_ADDRESS;
00088 
00089    //Clear the DHCPv6 relay agent context
00090    memset(context, 0, sizeof(Dhcpv6RelayContext));
00091 
00092    //Save the network-facing interface
00093    context->serverInterface = settings->serverInterface;
00094    //Save the number of client-facing interfaces
00095    context->clientInterfaceCount = settings->clientInterfaceCount;
00096 
00097    //Save all the client-facing interfaces
00098    for(i = 0; i < context->clientInterfaceCount; i++)
00099       context->clientInterface[i] = settings->clientInterface[i];
00100 
00101    //Save the address to be used when relaying client messages to the server
00102    context->serverAddress = settings->serverAddress;
00103 
00104    //Join the All_DHCP_Relay_Agents_and_Servers multicast group
00105    //for each client-facing interface
00106    error = dhcpv6RelayJoinMulticastGroup(context);
00107    //Any error to report?
00108    if(error)
00109       return error;
00110 
00111    //Start of exception handling block
00112    do
00113    {
00114       //Open a UDP socket to handle the network-facing interface
00115       context->serverSocket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
00116       //Failed to open socket?
00117       if(!context->serverSocket)
00118       {
00119          //Report an error
00120          error = ERROR_OPEN_FAILED;
00121          //Stop processing
00122          break;
00123       }
00124 
00125       //Explicitly associate the socket with the relevant interface
00126       error = socketBindToInterface(context->serverSocket, context->serverInterface);
00127       //Unable to bind the socket to the desired interface?
00128       if(error)
00129          break;
00130 
00131       //Relay agents listen for DHCPv6 messages on UDP port 547
00132       error = socketBind(context->serverSocket, &IP_ADDR_ANY, DHCPV6_SERVER_PORT);
00133       //Unable to bind the socket to the desired port?
00134       if(error)
00135          break;
00136 
00137       //Only accept datagrams with source port number 547
00138       error = socketConnect(context->serverSocket, &IP_ADDR_ANY, DHCPV6_SERVER_PORT);
00139       //Any error to report?
00140       if(error)
00141          break;
00142 
00143       //If the relay agent relays messages to the All_DHCP_Servers address
00144       //or other multicast addresses, it sets the Hop Limit field to 32
00145 
00146       //Loop through the client-facing interfaces
00147       for(i = 0; i < context->clientInterfaceCount; i++)
00148       {
00149          //Open a UDP socket to handle the current interface
00150          context->clientSocket[i] = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
00151          //Failed to open socket?
00152          if(!context->clientSocket[i])
00153          {
00154             //Report an error
00155             error = ERROR_OPEN_FAILED;
00156             //Stop processing
00157             break;
00158          }
00159 
00160          //Explicitly associate the socket with the relevant interface
00161          error = socketBindToInterface(context->clientSocket[i], context->clientInterface[i]);
00162          //Unable to bind the socket to the desired interface?
00163          if(error)
00164             break;
00165 
00166          //Relay agents listen for DHCPv6 messages on UDP port 547
00167          error = socketBind(context->clientSocket[i], &IP_ADDR_ANY, DHCPV6_SERVER_PORT);
00168          //Unable to bind the socket to the desired port?
00169          if(error)
00170             break;
00171 
00172          //Only accept datagrams with source port number 546
00173          error = socketConnect(context->clientSocket[i], &IP_ADDR_ANY, DHCPV6_CLIENT_PORT);
00174          //Any error to report?
00175          if(error)
00176             break;
00177       }
00178 
00179       //Propagate exception if necessary...
00180       if(error)
00181          break;
00182 
00183       //Initialize event object
00184       if(!osCreateEvent(&context->event))
00185       {
00186          //Failed to create event
00187          error = ERROR_OUT_OF_RESOURCES;
00188          //Stop processing
00189          break;
00190       }
00191 
00192       //Initialize ACK event object
00193       if(!osCreateEvent(&context->ackEvent))
00194       {
00195          //Failed to create event
00196          error = ERROR_OUT_OF_RESOURCES;
00197          //Stop processing
00198          break;
00199       }
00200 
00201       //The DHCPv6 relay agent is now running
00202       context->running = TRUE;
00203 
00204       //Start the DHCPv6 relay agent service
00205       task = osCreateTask("DHCPv6 Relay", dhcpv6RelayTask,
00206          context, DHCPV6_RELAY_STACK_SIZE, DHCPV6_RELAY_PRIORITY);
00207 
00208       //Unable to create the task?
00209       if(task == OS_INVALID_HANDLE)
00210          error = ERROR_OUT_OF_RESOURCES;
00211 
00212       //End of exception handling block
00213    } while(0);
00214 
00215    //Did we encounter an error?
00216    if(error)
00217    {
00218       //Close the socket associated with the network-facing interface
00219       socketClose(context->serverSocket);
00220 
00221       //Close the socket associated with each client-facing interface
00222       for(i = 0; i < context->clientInterfaceCount; i++)
00223          socketClose(context->clientSocket[i]);
00224 
00225       //Leave the All_DHCP_Relay_Agents_and_Servers multicast group
00226       //for each client-facing interface
00227       dhcpv6RelayLeaveMulticastGroup(context);
00228 
00229       //Delete event objects
00230       osDeleteEvent(&context->event);
00231       osDeleteEvent(&context->ackEvent);
00232    }
00233 
00234    //Return status code
00235    return error;
00236 }
00237 
00238 
00239 /**
00240  * @brief Stop DHCPv6 relay agent
00241  * @param[in] context Pointer to the DHCPv6 relay agent context
00242  * @return Error code
00243  **/
00244 
00245 error_t dhcpv6RelayStop(Dhcpv6RelayContext *context)
00246 {
00247    uint_t i;
00248 
00249    //Debug message
00250    TRACE_INFO("Stopping DHCPv6 relay agent...\r\n");
00251 
00252    //Ensure the specified pointer is valid
00253    if(context == NULL)
00254       return ERROR_INVALID_PARAMETER;
00255    //Check DHCPv6 relay agent state
00256    if(!context->running)
00257       return ERROR_WRONG_STATE;
00258 
00259    //Reset ACK event before sending the kill signal
00260    osResetEvent(&context->ackEvent);
00261    //Stop the DHCPv6 relay agent task
00262    context->stopRequest = TRUE;
00263    //Send a signal to the task in order to abort any blocking operation
00264    osSetEvent(&context->event);
00265 
00266    //Wait for the process to terminate...
00267    osWaitForEvent(&context->ackEvent, INFINITE_DELAY);
00268 
00269    //Leave the All_DHCP_Relay_Agents_and_Servers multicast group
00270    //for each client-facing interface
00271    dhcpv6RelayLeaveMulticastGroup(context);
00272 
00273    //Close the socket that carries traffic towards the DHCPv6 server
00274    socketClose(context->serverSocket);
00275 
00276    //Properly dispose the sockets that carry traffic towards the DHCPv6 clients
00277    for(i = 0; i < context->clientInterfaceCount; i++)
00278       socketClose(context->clientSocket[i]);
00279 
00280    //Delete event objects
00281    osDeleteEvent(&context->event);
00282    osDeleteEvent(&context->ackEvent);
00283 
00284    //Successful processing
00285    return NO_ERROR;
00286 }
00287 
00288 
00289 /**
00290  * @brief Join All_DHCP_Relay_Agents_and_Servers multicast group
00291  * @param[in] context Pointer to the DHCPv6 relay agent context
00292  **/
00293 
00294 error_t dhcpv6RelayJoinMulticastGroup(Dhcpv6RelayContext *context)
00295 {
00296    uint_t i;
00297    uint_t j;
00298 
00299    //Initialize status code
00300    error_t error = NO_ERROR;
00301 
00302    //Loop through the client-facing interfaces
00303    for(i = 0; i < context->clientInterfaceCount; i++)
00304    {
00305       //Join the All_DHCP_Relay_Agents_and_Servers multicast
00306       //group for each interface
00307       error = ipv6JoinMulticastGroup(context->clientInterface[i],
00308          &DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR);
00309       //Unable to join the specified multicast group?
00310       if(error)
00311          break;
00312    }
00313 
00314    //Did we encounter an error?
00315    if(error)
00316    {
00317       //Clean up side effects before returning...
00318       for(j = 0; j < i; j++)
00319       {
00320          //Leave the multicast group for each interface
00321          ipv6LeaveMulticastGroup(context->clientInterface[j],
00322             &DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR);
00323       }
00324    }
00325 
00326    //Return status code
00327    return error;
00328 }
00329 
00330 
00331 /**
00332  * @brief Leave All_DHCP_Relay_Agents_and_Servers multicast group
00333  * @param[in] context Pointer to the DHCPv6 relay agent context
00334  **/
00335 
00336 error_t dhcpv6RelayLeaveMulticastGroup(Dhcpv6RelayContext *context)
00337 {
00338    uint_t i;
00339 
00340    //Loop through the client-facing interfaces
00341    for(i = 0; i < context->clientInterfaceCount; i++)
00342    {
00343       //Leave the All_DHCP_Relay_Agents_and_Servers multicast
00344       //group for each interface
00345       ipv6LeaveMulticastGroup(context->clientInterface[i],
00346          &DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR);
00347    }
00348 
00349    //Successsful processing
00350    return NO_ERROR;
00351 }
00352 
00353 
00354 /**
00355  * @brief DHCPv6 relay agent task
00356  * @param[in] param Pointer to the DHCPv6 relay agent context
00357  **/
00358 
00359 void dhcpv6RelayTask(void *param)
00360 {
00361    error_t error;
00362    uint_t i;
00363 
00364    //Point to the DHCPv6 relay agent context
00365    Dhcpv6RelayContext *context = (Dhcpv6RelayContext *) param;
00366 
00367    //Specify the events the application is interested in for
00368    //each client-facing sockets
00369    for(i = 0; i < context->clientInterfaceCount; i++)
00370    {
00371       context->eventDesc[i].socket = context->clientSocket[i];
00372       context->eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
00373    }
00374 
00375    //Specify the events the application is interested in for
00376    //the network-facing socket
00377    context->eventDesc[i].socket = context->serverSocket;
00378    context->eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
00379 
00380    //Main loop
00381    while(1)
00382    {
00383       //Wait for incoming packets on network-facing or client-facing interfaces
00384       error = socketPoll(context->eventDesc, context->clientInterfaceCount + 1,
00385          &context->event, INFINITE_DELAY);
00386 
00387       //Stop DHCPv6 relay agent?
00388       if(context->stopRequest)
00389       {
00390          //The DHCPv6 relay agent is about to stop
00391          context->stopRequest = FALSE;
00392          context->running = FALSE;
00393          //Acknowledge the reception of the user request
00394          osSetEvent(&context->ackEvent);
00395          //Kill ourselves
00396          osDeleteTask(NULL);
00397       }
00398 
00399       //Verify status code
00400       if(!error)
00401       {
00402          //Check the state of each client-facing socket
00403          for(i = 0; i < context->clientInterfaceCount; i++)
00404          {
00405             //Relay client messages if applicable
00406             if(context->eventDesc[i].eventFlags & SOCKET_EVENT_RX_READY)
00407                dhcpv6ForwardClientMessage(context, i);
00408          }
00409 
00410          //Check the state of the network-facing socket
00411          if(context->eventDesc[i].eventFlags & SOCKET_EVENT_RX_READY)
00412          {
00413             //Forward Relay-Reply messages from the network
00414             dhcpv6ForwardRelayReplyMessage(context);
00415          }
00416       }
00417    }
00418 }
00419 
00420 
00421 /**
00422  * @brief Forward client message
00423  * @param[in] context Pointer to the DHCPv6 relay agent context
00424  * @param[in] index Index identifying the interface on which the message was received
00425  * @return Error code
00426  **/
00427 
00428 error_t dhcpv6ForwardClientMessage(Dhcpv6RelayContext *context, uint_t index)
00429 {
00430    error_t error;
00431    uint32_t interfaceId;
00432    size_t inputMessageLen;
00433    size_t outputMessageLen;
00434    Dhcpv6RelayMessage *inputMessage;
00435    Dhcpv6RelayMessage *outputMessage;
00436    Dhcpv6Option *option;
00437    IpAddr ipAddr;
00438 
00439    //Point to the buffer where to store the incoming DHCPv6 message
00440    inputMessage = (Dhcpv6RelayMessage *) (context->buffer + DHCPV6_RELAY_FORW_OVERHEAD);
00441    //Message that will be forwarded by the DHCPv6 relay agent
00442    outputMessage = (Dhcpv6RelayMessage *) context->buffer;
00443 
00444    //Read incoming message
00445    error = socketReceiveFrom(context->clientSocket[index], &ipAddr, NULL, inputMessage,
00446       DHCPV6_MAX_MSG_SIZE - DHCPV6_RELAY_FORW_OVERHEAD, &inputMessageLen, 0);
00447    //Any error to report?
00448    if(error)
00449       return error;
00450 
00451    //Debug message
00452    TRACE_INFO("\r\nDHCPv6 message received on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
00453       context->clientInterface[index]->name, inputMessageLen);
00454 
00455    //Dump the contents of the message for debugging purpose
00456    dhcpv6DumpMessage(inputMessage, inputMessageLen);
00457 
00458    //The source address must be a valid IPv6 address
00459    if(ipAddr.length != sizeof(Ipv6Addr))
00460       return ERROR_INVALID_ADDRESS;
00461    //Check the length of the DHCPv6 message
00462    if(inputMessageLen < sizeof(Dhcpv6Message))
00463       return ERROR_INVALID_MESSAGE;
00464 
00465    //When the relay agent receives a valid message to be relayed,
00466    //it constructs a new Relay-Forward message
00467    outputMessage->msgType = DHCPV6_MSG_TYPE_RELAY_FORW;
00468 
00469    //Inspect the message type
00470    switch(inputMessage->msgType)
00471    {
00472    //Message received from a client?
00473    case DHCPV6_MSG_TYPE_SOLICIT:
00474    case DHCPV6_MSG_TYPE_REQUEST:
00475    case DHCPV6_MSG_TYPE_CONFIRM:
00476    case DHCPV6_MSG_TYPE_RENEW:
00477    case DHCPV6_MSG_TYPE_REBIND:
00478    case DHCPV6_MSG_TYPE_RELEASE:
00479    case DHCPV6_MSG_TYPE_DECLINE:
00480    case DHCPV6_MSG_TYPE_INFO_REQUEST:
00481       //If the relay agent received the message to be relayed from a client
00482       //the hop-count in the Relay-Forward message is set to 0
00483       outputMessage->hopCount = 0;
00484       //Continue processing
00485       break;
00486 
00487    //Message received from another relay agent?
00488    case DHCPV6_MSG_TYPE_RELAY_FORW:
00489       //If the message received by the relay agent is a Relay-Forward message
00490       //and the hop-count in the message is greater than or equal to 32, the
00491       //relay agent discards the received message
00492       if(inputMessage->hopCount >= DHCPV6_HOP_COUNT_LIMIT)
00493          return ERROR_INVALID_MESSAGE;
00494       //Set the hop-count field to the value of the hop-count field in
00495       //the received message incremented by 1
00496       outputMessage->hopCount = inputMessage->hopCount + 1;
00497       //Continue processing
00498       break;
00499 
00500    //Message received from a server?
00501    default:
00502       //Discard ADVERTISE, REPLY, RECONFIGURE and RELAY-REPL messages
00503       return ERROR_INVALID_MESSAGE;
00504    }
00505 
00506    //Set the link-address field to the unspecified address
00507    outputMessage->linkAddress = IPV6_UNSPECIFIED_ADDR;
00508    //Copy the source address from the header of the IP datagram in
00509    //which the message was received to the peer-address field
00510    outputMessage->peerAddress = ipAddr.ipv6Addr;
00511    //Size of the Relay-Forward message
00512    outputMessageLen = sizeof(Dhcpv6RelayMessage);
00513 
00514    //Get the interface identifier
00515    interfaceId = context->clientInterface[index]->id;
00516    //Convert the 32-bit integer to network byte order
00517    interfaceId = htonl(interfaceId);
00518 
00519    //If the relay agent cannot use the address in the link-address field
00520    //to identify the interface through which the response to the client
00521    //will be relayed, the relay agent must include an Interface ID option
00522    dhcpv6AddOption(outputMessage, &outputMessageLen,
00523       DHCPV6_OPTION_INTERFACE_ID, &interfaceId, sizeof(interfaceId));
00524 
00525    //Copy the received DHCPv6 message into a Relay Message option
00526    option = dhcpv6AddOption(outputMessage, &outputMessageLen,
00527       DHCPV6_OPTION_RELAY_MSG, NULL, 0);
00528 
00529    //Set the appropriate length of the option
00530    option->length = htons(inputMessageLen);
00531    //Adjust the length of the Relay-Forward message
00532    outputMessageLen += inputMessageLen;
00533 
00534    //Debug message
00535    TRACE_INFO("Forwarding DHCPv6 message on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
00536       context->serverInterface->name, outputMessageLen);
00537 
00538    //Dump the contents of the message for debugging purpose
00539    dhcpv6DumpMessage(outputMessage, outputMessageLen);
00540 
00541    //Destination address to be used when relaying the client message
00542    ipAddr.length = sizeof(Ipv6Addr);
00543    ipAddr.ipv6Addr = context->serverAddress;
00544 
00545    //Relay the client message to the server
00546    return socketSendTo(context->serverSocket, &ipAddr,
00547       DHCPV6_SERVER_PORT, outputMessage, outputMessageLen, NULL, 0);
00548 }
00549 
00550 
00551 /**
00552  * @brief Forward Relay-Reply message
00553  * @param[in] context Pointer to the DHCPv6 relay agent context
00554  * @return Error code
00555  **/
00556 
00557 error_t dhcpv6ForwardRelayReplyMessage(Dhcpv6RelayContext *context)
00558 {
00559    error_t error;
00560    uint_t i;
00561    uint32_t interfaceId;
00562    size_t inputMessageLen;
00563    size_t outputMessageLen;
00564    Dhcpv6RelayMessage *inputMessage;
00565    Dhcpv6Message *outputMessage;
00566    Dhcpv6Option *option;
00567    IpAddr ipAddr;
00568    uint16_t port;
00569 
00570    //Point to the buffer where to store the incoming DHCPv6 message
00571    inputMessage = (Dhcpv6RelayMessage *) context->buffer;
00572 
00573    //Read incoming message
00574    error = socketReceiveFrom(context->serverSocket, &ipAddr, &port,
00575       inputMessage, DHCPV6_MAX_MSG_SIZE, &inputMessageLen, 0);
00576    //Any error to report?
00577    if(error)
00578       return error;
00579 
00580    //Debug message
00581    TRACE_INFO("\r\nDHCPv6 message received on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
00582       context->serverInterface->name, inputMessageLen);
00583 
00584    //Dump the contents of the message for debugging purpose
00585    dhcpv6DumpMessage(inputMessage, inputMessageLen);
00586 
00587    //Check the length of the DHCPv6 message
00588    if(inputMessageLen < sizeof(Dhcpv6RelayMessage))
00589       return ERROR_INVALID_MESSAGE;
00590 
00591    //Inspect the message type and only forward Relay-Reply messages.
00592    //Other DHCPv6 message types must be silently discarded
00593    if(inputMessage->msgType != DHCPV6_MSG_TYPE_RELAY_REPL)
00594       return ERROR_INVALID_MESSAGE;
00595 
00596    //Get the length of the Options field
00597    inputMessageLen -= sizeof(Dhcpv6Message);
00598 
00599    //Check whether an Interface ID option is included in the Relay-Reply
00600    option = dhcpv6GetOption(inputMessage->options, inputMessageLen, DHCPV6_OPTION_INTERFACE_ID);
00601    //Failed to retrieve specified option?
00602    if(option == NULL || ntohs(option->length) != sizeof(interfaceId))
00603       return ERROR_INVALID_MESSAGE;
00604 
00605    //Read the Interface ID option contents
00606    memcpy(&interfaceId, option->value, sizeof(interfaceId));
00607    //Convert the 32-bit integer from network byte order
00608    interfaceId = ntohl(interfaceId);
00609 
00610    //The Relay-Reply message must include a Relay Message option
00611    option = dhcpv6GetOption(inputMessage->options, inputMessageLen, DHCPV6_OPTION_RELAY_MSG);
00612    //Failed to retrieve specified option?
00613    if(option == NULL || ntohs(option->length) < sizeof(Dhcpv6Message))
00614       return ERROR_INVALID_MESSAGE;
00615 
00616    //Extract the message from the Relay Message option
00617    outputMessage = (Dhcpv6Message *) option->value;
00618    //Save the length of the message
00619    outputMessageLen = ntohs(option->length);
00620 
00621    //Loop through client-facing interfaces
00622    for(i = 0; i < context->clientInterfaceCount; i++)
00623    {
00624       //Check whether the current interface matches the Interface ID option
00625       if(context->clientInterface[i]->id == interfaceId)
00626       {
00627          //Debug message
00628          TRACE_INFO("Forwarding DHCPv6 message on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
00629             context->clientInterface[i]->name, outputMessageLen);
00630 
00631          //Dump the contents of the message for debugging purpose
00632          dhcpv6DumpMessage(outputMessage, outputMessageLen);
00633 
00634          //Copy the peer-address into the destination IP address
00635          ipAddr.length = sizeof(Ipv6Addr);
00636          ipAddr.ipv6Addr = inputMessage->peerAddress;
00637 
00638          //Select the relevant port number to use
00639           if(outputMessage->msgType == DHCPV6_MSG_TYPE_RELAY_REPL)
00640             port = DHCPV6_SERVER_PORT;
00641          else
00642             port = DHCPV6_CLIENT_PORT;
00643 
00644          //Relay the DHCPv6 message to the client on the link
00645          //identified by the Interface ID option
00646          return socketSendTo(context->clientSocket[i], &ipAddr,
00647             port, outputMessage, outputMessageLen, NULL, 0);
00648       }
00649    }
00650 
00651    //Unknown interface identifier...
00652    return ERROR_INVALID_OPTION;
00653 }
00654 
00655 #endif
00656