Files
m241Neelima/components/ip_net2-6.9/ipdhcps6/src/ipdhcps6_msg.c
T
2026-07-30 11:19:44 +05:30

1426 lines
53 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipdhcps6_msg.c,v $ $Revision: 1.40.12.2 $
* $Source: /home/interpeak/CVSRoot/ipdhcps6/src/ipdhcps6_msg.c,v $
* $Author: svc-cmnet $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/* Copyright (C) 2007 Wind River Systems */
/*
modification history
--------------------
01a,17jul07,ps initial checkin of dhcpv6 authentication key database module
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipdhcps6_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_clib.h>
#include <ipcom_err.h>
#include <ipcom_syslog.h>
#include <ipcom_sysvar.h>
#include <ipcom_sock.h>
#include <ipnet_ctrl.h>
#include "ipdhcps6.h"
#include "ipdhcps6_h.h"
#include "ipdhcps6_msg.h"
#include "ipdhcps6_host.h"
#include "ipdhcps6_option.h"
#include "ipdhcps6_prefix.h"
#include "ipdhcps6_subnet.h"
#include "ipdhcps6_authhash.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
#define IPDHCPS6_CPY_TRANSACTION_ID(dst, src) \
do { \
dst->transaction_id_1 = src->transaction_id_1; \
dst->transaction_id_2 = src->transaction_id_2; \
dst->transaction_id_3 = src->transaction_id_3; \
} while (0)
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
struct Ipdhcps6_msg_add_opt
{
/* The buffer to where the options will be copied */
Ipdhcps6_option *opts;
/* The length left in the 'opts' buffer */
Ip_u16 optslen;
/* Number of bytes written to the 'opts' buffer */
Ip_u16 total_written;
/* Result code of the operation */
Ip_err err;
};
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcps6_msg_contains_local_server_id
*===========================================================================
* Description: Returns IP_TRUE if the options array contains the server ID
* for this host.
* Parameters: options - An array of options.
* optslen - The length of the 'options' buffer.
* Returns: IP_TRUE if the server ID of this host is included.
*
*/
IP_STATIC Ip_bool
ipdhcps6_msg_contains_local_server_id(Ipdhcps6_option *options, Ip_u16 optslen)
{
Ipdhcps6_option *server_id;
Ip_u16 server_id_len;
server_id = ipdhcps6_option_get_ptr(options, optslen, IPDHCPS6_OPTION_SERVERID);
if (server_id == IP_NULL)
return IP_FALSE;
server_id_len = (Ip_u16) (ipdhcps6_get_u16(&server_id->len) + IPDHCPS6_OPTION_HDR_LEN);
if (server_id_len != ipdhcps6_get_u16(&ipdhcps6->duid->len) + IPDHCPS6_OPTION_HDR_LEN)
/* Not the same length */
return IP_FALSE;
return (ipcom_memcmp(server_id, ipdhcps6->duid, server_id_len) == 0);
}
/*
*===========================================================================
* ipdhcps6_msg_add_requsted_opt
*===========================================================================
* Description: Adds all options requested by the client and is configured
* at the server.
* Parameters: host - The host entry for the requestor.
* subnet - The matching subnet (can be IP_NULL).
* oro_opt - The option request option included in the client
* message.
* opt - The buffer where the options will be written.
* optlen - The length of the option buffer, will be updated
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - the 'opt' was not large enough.
*
*/
IP_STATIC Ip_err
ipdhcps6_msg_add_requsted_opt(Ipdhcps6_host *host,
Ipdhcps6_subnet_entry *subnet,
Ipdhcps6_option *oro_opt,
Ipdhcps6_option *opt,
Ip_u16 optlen,
Ip_u16 *bytes_written)
{
Ip_u16 oro_code;
Ip_u16 oro_len;
Ip_u16 written;
Ip_u16 resp_buf_len;
Ipdhcps6_option *resp_opt;
Ipdhcps6_option *server_opt = IP_NULL;
int i;
ip_assert(host != IP_NULL);
ip_assert(oro_opt != IP_NULL);
ip_assert(opt != IP_NULL);
ip_assert(optlen != IP_NULL);
oro_len = ipdhcps6_get_u16(&oro_opt->len);
ip_assert((oro_len & 1) == 0);
oro_len /= 2;
*bytes_written = 0;
for (i = 0; i < oro_len; i++)
{
oro_code = ipdhcps6_get_u16(&oro_opt->data.request_oro[i]);
resp_opt = (Ipdhcps6_option *) ((Ip_u8 *) opt + *bytes_written);
resp_buf_len = (Ip_u16) (optlen - *bytes_written);
/*Find FQDN in oro option*/
if (oro_code == IPDHCPS6_OPTION_FQDN)
{
host->fqdn.oro = IP_TRUE;
continue;
}
/* First try host specific options */
if (host->options != IP_NULL)
server_opt = ipcom_hash_get(host->options, &oro_code);
if (server_opt != IP_NULL)
goto option_found;
/* Next try subnet specific options */
if (subnet != IP_NULL && subnet->options != IP_NULL)
server_opt = ipcom_hash_get(subnet->options, &oro_code);
if (server_opt != IP_NULL)
goto option_found;
/* Last, check in global options */
server_opt = ipcom_hash_get(ipdhcps6->db.options, &oro_code);
if (server_opt == IP_NULL)
/* The server does not have a value for this option */
continue;
option_found:
/* Add the option to the message */
written = (Ip_u16) (ipdhcps6_get_u16(&server_opt->len) + IPDHCPS6_OPTION_HDR_LEN);
if (resp_buf_len < written)
return IPCOM_ERR_NO_MEMORY;
ipcom_memcpy(resp_opt, server_opt, written);
*bytes_written += written;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcps6_msg_add_opt_cb
*===========================================================================
* Description: Adds each option this functions is called with.
* Parameters: server_opt - A server option.
* mao - Callback paramters.
* Returns:
*
*/
IP_STATIC void
ipdhcps6_msg_add_opt_cb(Ipdhcps6_option *server_opt, struct Ipdhcps6_msg_add_opt *mao)
{
Ip_u16 bytes_written;
if (mao->err != IPCOM_SUCCESS)
return;
bytes_written = (Ip_u16) (ipdhcps6_get_u16(&server_opt->len) + IPDHCPS6_OPTION_HDR_LEN);
if (mao->optslen < bytes_written)
{
mao->err = IPCOM_ERR_NO_MEMORY;
return;
}
ipcom_memcpy(mao->opts, server_opt, bytes_written);
mao->opts = (Ipdhcps6_option *) ((Ip_u8 *) mao->opts + bytes_written);
mao->optslen -= bytes_written;
mao->total_written += bytes_written;
}
/*
*===========================================================================
* ipdhcps6_msg_add_all_opt
*===========================================================================
* Description: Adds all options configured at the server.
* Parameters: host - The host entry for the requestor.
* subnet - The matching subnet (can be IP_NULL).
* opt - The buffer where the options will be written.
* optlen - The length of the option buffer
* bytes_written - Number of bytes written to the 'opt' buffer.
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - the 'opt' was not large enough.
*
*/
IP_STATIC Ip_err
ipdhcps6_msg_add_all_opt(Ipdhcps6_host *host, Ipdhcps6_subnet_entry *subnet,
Ipdhcps6_option *opt, Ip_u16 optlen, Ip_u16 *bytes_written)
{
Ipcom_hash *server_opts;
Ip_err err;
struct Ipdhcps6_msg_add_opt mao;
ip_assert(host != IP_NULL);
ip_assert(opt != IP_NULL);
ip_assert(optlen != IP_NULL);
server_opts = ipdhcps6_option_new_db();
if (server_opts == IP_NULL)
return IPCOM_ERR_NO_MEMORY;
/* Merge host specific options */
if (host->options != IP_NULL)
{
err = ipdhcps6_option_merge(server_opts, host->options);
if (err != IPCOM_SUCCESS)
return IPCOM_ERR_NO_MEMORY;
}
/* Merge subnet specific options */
if (subnet != IP_NULL && subnet->options != IP_NULL)
{
err = ipdhcps6_option_merge(server_opts, subnet->options);
if (err != IPCOM_SUCCESS)
return IPCOM_ERR_NO_MEMORY;
}
/* Merge global options */
err = ipdhcps6_option_merge(server_opts, ipdhcps6->db.options);
if (err != IPCOM_SUCCESS)
return IPCOM_ERR_NO_MEMORY;
mao.opts = opt;
mao.optslen = optlen;
mao.err = IPCOM_SUCCESS;
mao.total_written = 0;
ipcom_hash_for_each(server_opts, (void(*)(void *, void *)) ipdhcps6_msg_add_opt_cb, &mao);
*bytes_written = mao.total_written;
ipcom_hash_delete(server_opts);
return mao.err;
}
/*
*===========================================================================
* ipdhcps6_msg_add_server_options
*===========================================================================
* Description: Returns IP_TRUE if the options array contains the server ID
* for this host.
* Parameters: subnet - The subnet associated with this request.
* host - The host associated with this request.
* request_opts - Pointer to the first option in the request.
* request_optslen - The length of the request option.
* response_opt - Pointer to where the first response option
* should be written.
* response_optlen - The maximum size of the 'response_opt' buffer.
* written - Will contain the number of bytes written to 'response_opt'
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - The 'response_opt' was not large enough.
*
*/
IP_STATIC Ip_err
ipdhcps6_msg_add_server_options(Ipdhcps6_subnet_entry *subnet,
Ipdhcps6_host *host,
Ipdhcps6_option *request_opts, Ip_u16 request_optslen,
Ipdhcps6_option *response_opt, Ip_u16 response_optlen,
Ip_u16 *written)
{
Ip_err err;
Ipdhcps6_option *oro_opt;
*written = 0;
if (ipdhcps6_option_exist(request_opts, request_optslen, IPDHCPS6_OPTION_ORO))
{
/* Only include options asked for by the client */
oro_opt = ipdhcps6_option_get_ptr(request_opts, request_optslen, IPDHCPS6_OPTION_ORO);
err = ipdhcps6_msg_add_requsted_opt(host, subnet, oro_opt, response_opt,
response_optlen, written);
}
else
{
/* Include all options appropriate for this subnet/host */
err = ipdhcps6_msg_add_all_opt(host, subnet, response_opt, response_optlen, written);
}
return err;
}
/*
*===========================================================================
* ipdhcps6_msg_process_fqdn
*===========================================================================
* Description: Processes the fqdn option.
* Parameters: host -where the message comes from
* opt - all in options
* request_type - The message type sent by the client.
* out_opt - Pointer to where the next response option should be written
* written - Will contain the number of bytes added to 'out_opt'
* Returns: IPCOM_SUCCESS - operation was successful.
*
*/
IP_STATIC Ip_err
ipdhcps6_msg_process_fqdn(Ipdhcps6_host *host,
Ipdhcps6_option* opt,
Ip_u8 request_type,
Ipdhcps6_option *out_opt,
Ip_u16 *written)
{
/*Host is not NULL here*/
/*Need to update flags here*/
/*Here just add fqdn oro*/
/*Flags will be sent*/
Ip_u8 sflag = 0;
/*Get the flags from packet received*/
Ip_u8 flag = opt->data.generic[0];
char *domain = (char*)opt->data.generic+1;
(void)request_type;
*written = 0;
if(ipcom_ntohs(opt->len)<=2)
{
/*No name provided from client, at present, log error*/
IPCOM_LOG0(WARNING, "client must provide a name at fqdn option");
return IPCOM_ERR_FAILED;
}
host->fqdn.agent = 0;
if (IP_BIT_ISSET(flag, IPDHCPS6_FQDN_FLAG_N))
/*Server does none update*/
/*Fqdn needs return back*/
IP_BIT_SET(sflag, IPDHCPS6_FQDN_FLAG_N);
else
{
if (IP_BIT_ISSET(flag, IPDHCPS6_FQDN_FLAG_S))
{
if(ipcom_sysvar_get_as_int("ipdhcps6.fqdn.allow.update", 1))
{
host->fqdn.agent = 1;
IP_BIT_SET(sflag, IPDHCPS6_FQDN_FLAG_S);
}
else
IP_BIT_SET(sflag, IPDHCPS6_FQDN_FLAG_O);
}
}
if (host->fqdn.oro)
{
Ip_u8 dc_len = 0;
char *payload =(char*) out_opt->data.generic ;
char *dc_len_ptr = IP_NULL;
char domainname[128];
out_opt->code = ipcom_htons(IPDHCPS6_OPTION_FQDN);
*payload = sflag;
payload++;
ipcom_strncpy(payload+1, domain+1,*domain);
*(payload+1+*domain) = '\0';
/*End with '\0'*/
ipcom_strcat(payload+1,".");
if(ipdhcps6_dns_get_domain(domainname))
ipcom_strcat(payload+1,domainname);
else
{
IPCOM_LOG0(WARNING, "no domainname found at fqdn option");
return IPCOM_ERR_FAILED;
}
out_opt->len = ipcom_htons(1+1+ipcom_strlen(payload+1)+1);
/*1--flag,1--first len--1---'\0'*/
do
{
dc_len = 0;
dc_len_ptr = payload++;
while (*payload != '.' && *payload != '\0')
{
++dc_len;
++payload;
}
*dc_len_ptr = dc_len;
}
while (*payload != '\0');
/*Add the fqdn to response message*/
*written = ipcom_ntohs(out_opt->len)+IPDHCPS6_OPTION_HDR_LEN;
}
host->fqdn.oro = IP_FALSE;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcps6_msg_process_rapid_commit
*===========================================================================
* Description: Processes the rapid commit option.
* Parameters: request_type - The message type sent by the client.
* out_opt - Pointer to where the next response option should
* be written
* written - Will contain the number of bytes added to 'out_opt'
* Returns: IPCOM_SUCCESS - operation was successful.
*
*/
IP_STATIC Ip_err
ipdhcps6_msg_process_rapid_commit(Ip_u8 request_type,
Ipdhcps6_option *out_opt,
Ip_u16 *written)
{
char str[4];
Ip_size_t str_len = sizeof(str);
*written = 0;
if (request_type != IPDHCPS6_MSG_SOLICIT)
IPCOM_LOG0(NOTICE, "Ignoring rapid commit option on non-solicit message");
else
{
char *cfg = ipcom_sysvar_get("ipdhcps6.server.allow_rapid_commit",
str, &str_len);
if (cfg && ipcom_strcmp("yes", cfg) != 0)
{
IPCOM_LOG0(NOTICE, "Ignoring rapid commit option since it is administratively disabled");
}
else
{
ipdhcps6_set_u16(&out_opt->code, IPDHCPS6_OPTION_RAPID_COMMIT);
ipdhcps6_set_u16(&out_opt->len, 0);
*written = IPDHCPS6_OPTION_HDR_LEN;
}
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcps6_msg_process_options
*===========================================================================
* Description: Returns IP_TRUE if the options array contains the server ID
* for this host.
* Parameters: options - An array of options.
* optslen - The length of the 'options' buffer.
* Returns: IPCOM_SUCCESS - operation was successful.
* IPCOM_ERR_NO_MEMORY - out of memory.
*
*/
IP_STATIC Ip_err
ipdhcps6_msg_process_options(Ipdhcps6_message *msg, Ipdhcps6_option *options, Ip_u16 optslen)
{
Ipdhcps6_option *opt;
Ipdhcps6_msg_hdr *request;
Ipdhcps6_msg_hdr *response;
Ipdhcps6_option *response_opt;
Ip_u16 response_optlen;
Ip_u16 opt_code;
Ip_u16 written;
Ipdhcps6_subnet_entry *subnet;
Ipdhcps6_host *host;
Ipdhcps6_option *clientid;
Ip_err err = 0;
Ipcom_netif *netif = IP_NULL;
request = (Ipdhcps6_msg_hdr *) msg->recv_buf;
response = (Ipdhcps6_msg_hdr *) msg->send_buf;
/* Write the message header to the respose */
response->type = IPDHCPS6_MSG_REPLY;
IPDHCPS6_CPY_TRANSACTION_ID(response, request);
ipdhcps6_msg_first_response_opt(msg, &response_opt, &response_optlen);
/* Add the server ID */
if (ipdhcps6_option_add_server_id(response_opt, response_optlen, &written) != IPCOM_SUCCESS)
{
err = IPCOM_ERR_FAILED;
goto done;
}
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
/* Add the supplied client ID to the reponse */
if (ipdhcps6_option_get(options, optslen, IPDHCPS6_OPTION_CLIENTID,
response_opt, response_optlen, &written) != IPCOM_SUCCESS)
{
/* Client ID must be present before calling this function. */
err = IPCOM_ERR_FAILED;
goto done;
}
clientid = response_opt;
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
/* Find the host and subnet entries matching this request */
subnet = ipdhcps6_subnet_get_from_addr(&msg->peer_addr.sin6_addr, (int)msg->ifindex);
host = ipdhcps6_host_get(clientid);
if (host == IP_NULL)
{
if (subnet == IP_NULL)
{
/* Unknown host and using a unconfigured subnet, drop the message */
IPCOM_LOG1(DEBUG,
"Sending error response to %s, no configuration found for that subnet",
ipcom_inet_ntop(IP_AF_INET6,
&msg->peer_addr.sin6_addr,
ipdhcps6->addr_str,
sizeof(ipdhcps6->addr_str)));
err = ipdhcps6_option_add_status_code(response_opt,
response_optlen,
&written,
IPDHCPS6_CODE_NO_BINDING,
"No configuration for this subnet or host.");
if (err != IPCOM_SUCCESS)
goto done;
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
goto done_send_response;
}
/* Create a new host structure */
host = ipdhcps6_host_new(clientid, &msg->peer_addr);
if (host == IP_NULL)
{
IPCOM_LOG1(DEBUG, "Dropping message from %s, failed to create host entry (out of memory)",
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr, ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
goto done;
}
}
netif = ipdhcps6_get_netifbyindex(msg->ifindex);
ip_assert(IP_NULL != netif);
ipdhcps6_host_try_get_user(host, netif);
ip_assert(host != IP_NULL);
host->last_servered = ipcom_time(IP_NULL);
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_IAADDR)
|| ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_IAPREFIX))
{
/* RFC 3315 chapter 22.6
The IA Address option is used to specify IPv6 addresses associated
with an IA_NA or an IA_TA.
*/
err = ipdhcps6_option_add_status_code(response_opt,
response_optlen,
&written,
IPDHCPS6_CODE_UNSPEC_FAIL,
"Invalid option in request");
/* start for defect WIND00228550 */
if (err != IPCOM_SUCCESS)
goto done;
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
/* end for defect WIND00228550 */
goto done_send_response;
}
if (request->type == IPDHCPS6_MSG_INFORMATION_REQUEST)
{
/* RFC 3315 chapter 15
If a server receives a message that contains options it should not
contain (such as an Information-request message with an IA option),
is missing options that it should contain, or is otherwise not valid,
it MAY send a Reply (or Advertise as appropriate) with a Server
Identifier option, a Client Identifier option if one was included in
the message and a Status Code option with status UnSpecFail.*/
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_IA_TA)
|| ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_IA_NA))
{
err = ipdhcps6_option_add_status_code(response_opt,
response_optlen,
&written,
IPDHCPS6_CODE_UNSPEC_FAIL,
"Not allowed to request addresses in an information-request");
/* start for defect WIND00228550 */
if (err != IPCOM_SUCCESS)
goto done;
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
/* end for defect WIND00228550 */
goto done_send_response;
}
}
if (host->is_allowed_to_use_unicast == IP_FALSE &&
IP_IN6_IS_ADDR_MULTICAST(&msg->local_addr.sin6_addr) == IP_FALSE)
{
/* Coverity defect fix for DEADCODE */
err = ipdhcps6_option_add_status_code(response_opt, response_optlen, &written,
IPDHCPS6_CODE_USE_MULTICAST, "Must use multicast");
/* start for defect WIND00228550 */
if (err != IPCOM_SUCCESS)
goto done;
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
/* end for defect WIND00228550 */
goto done_send_response;
}
/* Add server options to the response */
err = ipdhcps6_msg_add_server_options(subnet, host, options, optslen,
response_opt, response_optlen, &written);
/*do nothing in this fucntion*/
if (err != IPCOM_SUCCESS)
{
IPCOM_LOG1(DEBUG, "Dropping message from %s, failed to create response (too small buffer)",
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr, ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
goto done;
}
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
/* Iterate through all options sent by the client */
for (opt = options; opt != IP_NULL; opt = ipdhcps6_option_next(opt, &optslen))
{
/* Cannot use 'normal' assignment since opt might be located on an odd address */
opt_code = ipdhcps6_get_u16(&opt->code);
switch (opt_code)
{
case IPDHCPS6_OPTION_FQDN:
err = ipdhcps6_msg_process_fqdn(host, opt, request->type, response_opt, &written);
break;
case IPDHCPS6_OPTION_RAPID_COMMIT:
err = ipdhcps6_msg_process_rapid_commit(request->type,
response_opt,
&written);
break;
case IPDHCPS6_OPTION_IA_NA:
err = ipdhcps6_pool_process_non_temporary_addr_request(request->type,
subnet,
host,
opt,
response_opt,
response_optlen,
&written);
break;
case IPDHCPS6_OPTION_IA_TA:
IPCOM_LOG0(WARNING, "Assigning of temporary addresses not implemented yet");
err = ipdhcps6_option_add_status_code(response_opt,
response_optlen,
&written,
IPDHCPS6_CODE_NO_ADDRS_AVAIL,
"Assigning of temporary addresses not implemented yet");
goto done_send_response;
case IPDHCPS6_OPTION_IA_PD:
switch (netif->type)
{
#ifdef IPPPP
case IP_IFT_PPP:
err = ipdhcps6_prefix_process_request(request->type,
subnet,
host,
opt,
response_opt,
response_optlen,
&written);
break;
#endif /* IPPPP */
/* more type if needed */
default:
err = ipdhcps6_pool_process_prefix_delegation_request(request->type,
subnet,
host,
opt,
response_opt,
response_optlen,
&written);
break;
}
break;
default:
continue;
}
if (err != IPCOM_SUCCESS)
goto done;
ipdhcps6_msg_next_response_opt(written, msg, &response_opt, &response_optlen);
}
done_send_response:
msg->send_response = IP_TRUE;
done:
if (IPCOM_SUCCESS != err)
IPCOM_LOG2(ERR,
"Failed to create a response for %s, err = %d",
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr,
ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)),
err);
return err;
}
/*
*===========================================================================
* ipdhcps6_msg_check_client_and_server_id
*===========================================================================
* Description: Checks is server & client ID exists and that server ID
* matches this server.
* Parameters: msg - The request/response message structure.
* Returns: IP_TRUE if client & server ID exists and server ID matches
* this server.
*
*/
IP_STATIC Ip_bool
ipdhcps6_msg_check_client_and_server_id(Ipdhcps6_message *msg,
Ipdhcps6_option *options,
Ip_u16 optslen,
const char *msg_type)
{
/* Must contain Client Identifier and Server Identifier options */
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_CLIENTID) == IP_FALSE)
{
/* Discard the message since it is invalid */
IPCOM_LOG2(INFO, "Discarding %s message received from %s since client ID is missing",
msg_type,
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr, ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
return IP_FALSE;
}
if (ipdhcps6_msg_contains_local_server_id(options, optslen) == IP_FALSE)
{
/* Does not contain the server ID for this host, just drop the message */
IPCOM_LOG2(DEBUG, "Discarding %s message received from %s since "
"server ID is missing or does not match local server ID",
msg_type,
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr,
ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
return IP_FALSE;
}
return IP_TRUE;
}
/*
*===========================================================================
* ipdhcps6_msg_check_IA_infor
*===========================================================================
* Description: Checks if IA information exists.
* Parameters: msg - The request/response message structure.
* Returns: IP_TRUE if exists, else IP_FALSE.
*
*/
IP_STATIC Ip_bool
ipdhcps6_msg_check_IA_infor(Ipdhcps6_message *msg,
Ipdhcps6_option *options,
Ip_u16 optslen,
const char *msg_type)
{
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_IA_NA))
{
/* Discard the message since it is invalid */
IPCOM_LOG2(INFO, "Discarding %s message received from %s since including IA_NA.",
msg_type,
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr, ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
return IP_FALSE;
}
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_IA_TA))
{
/* Discard the message since it is invalid */
IPCOM_LOG2(INFO, "Discarding %s message received from %s since including IA_TA.",
msg_type,
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr, ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
return IP_FALSE;
}
/* XXX : IA_PD */
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_IA_PD))
{
/* Discard the message since it is invalid */
IPCOM_LOG2(INFO, "Discarding %s message received from %s since including IA_PD.",
msg_type,
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr, ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
return IP_FALSE;
}
return IP_TRUE;
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdhcps6_msg_get_first_option
*===========================================================================
* Description: Returns a pointer to the first option in the message.
* Parameters: msg - The DHCP message.
* msg_len - The length of the message.
* poptions - Will contain a pointer to the first message.
* poptslen - Will contain the total length of all options.
* Returns:
*
*/
IP_GLOBAL void
ipdhcps6_msg_get_first_option(Ipdhcps6_msg_hdr *msg,
Ip_u16 msg_len,
Ipdhcps6_option **poptions,
Ip_u16 *poptslen)
{
Ip_u16 header_len = IPDHCPS6_MSG_HDR_LEN;
if (msg->type == IPDHCPS6_MSG_RELAY_FORW
|| msg->type == IPDHCPS6_MSG_RELAY_REPL)
header_len = IPDHCP_RELAY_MSG_HDR_LEN;
ip_assert(msg_len > header_len);
*poptslen = msg_len - header_len;
*poptions = (Ipdhcps6_option *) ((Ip_u8 *) msg + header_len);
}
/*
*===========================================================================
* ipdhcps6_msg_first_response_opt
*===========================================================================
* Description: Moves the 'response_opt' pointer to the address where the
* next option can be written.
* Parameters: msg - The request/response message structure.
* reponse_opt - Will contain a pointer to where the next option
* should be located.
* optlen - Will contain the number of bytes left of the response
* buffer.
* Returns:
*
*/
IP_GLOBAL void
ipdhcps6_msg_first_response_opt(Ipdhcps6_message *msg,
Ipdhcps6_option **response_opt,
Ip_u16 *optlen)
{
Ip_u16 header_len = IPDHCPS6_MSG_HDR_LEN;
Ipdhcps6_msg_hdr *msg_header = (Ipdhcps6_msg_hdr *) msg->send_buf;
if (msg_header->type == IPDHCPS6_MSG_RELAY_FORW
|| msg_header->type == IPDHCPS6_MSG_RELAY_REPL)
header_len = IPDHCP_RELAY_MSG_HDR_LEN;
ip_assert(msg->send_buf_maxlen > header_len);
msg->send_buf_len = header_len;
*optlen = (Ip_u16) (msg->send_buf_maxlen - msg->send_buf_len);
*response_opt = (Ipdhcps6_option *) &msg->send_buf[msg->send_buf_len];
}
/*
*===========================================================================
* ipdhcps6_msg_next_response_opt
*===========================================================================
* Description: Moves the 'response_opt' pointer to the address where the
* next option can be written.
* Parameters: written - Number of bytes written to 'current'.
* msg - The request/response message structure.
* next_response_opt - Will contain a pointer to where the next option
* should be located.
* opt_buf_left - Will contain the number of bytes left of the response
* buffer.
* Returns:
*
*/
IP_GLOBAL void
ipdhcps6_msg_next_response_opt(Ip_u16 written,
Ipdhcps6_message *msg,
Ipdhcps6_option **next_response_opt,
Ip_u16 *opt_buf_left)
{
msg->send_buf_len += written;
ip_assert(msg->send_buf_maxlen >= msg->send_buf_len);
*opt_buf_left = (Ip_u16) (msg->send_buf_maxlen - msg->send_buf_len);
*next_response_opt = (Ipdhcps6_option *) &msg->send_buf[msg->send_buf_len];
}
/*
*===========================================================================
* ipdhcps6_msg_type_to_string
*===========================================================================
* Description: Returns a string representation of the message type.
* Parameters: type - The message type.
* Returns: A string representation.
*
*/
IP_GLOBAL const char *
ipdhcps6_msg_type_to_string(Ip_u8 type)
{
static char num_str[20];
switch (type)
{
case IPDHCPS6_MSG_SOLICIT:
return "MSG_SOLICIT";
case IPDHCPS6_MSG_ADVERTISE:
return "MSG_ADVERTISE";
case IPDHCPS6_MSG_REQUEST:
return "MSG_REQUEST";
case IPDHCPS6_MSG_CONFIRM:
return "MSG_CONFIRM";
case IPDHCPS6_MSG_RENEW:
return "MSG_RENEW";
case IPDHCPS6_MSG_REBIND:
return "MSG_REBIND";
case IPDHCPS6_MSG_REPLY:
return "MSG_REPLY";
case IPDHCPS6_MSG_RELEASE:
return "MSG_RELEASE";
case IPDHCPS6_MSG_DECLINE:
return "MSG_DECLINE";
case IPDHCPS6_MSG_RECONFIGURE:
return "MSG_RECONFIGURE";
case IPDHCPS6_MSG_INFORMATION_REQUEST:
return "MSG_INFORMATION_REQUEST";
case IPDHCPS6_MSG_RELAY_FORW:
return "MSG_RELAY_FORW";
case IPDHCPS6_MSG_RELAY_REPL:
return "MSG_RELAY_REPL";
}
ipcom_sprintf(num_str, "Unknown type: %d", type);
return num_str;
}
/*
*===========================================================================
* ipdhcps6_msg_process_solicit
*===========================================================================
* Description: Handles Solicit message.
* Parameters: msg - The received message, will contain the reply if such
* should be sent.
* options - Points to the first option included in the client
* message. All options are packed into this buffer without padding.
* optslen - The length of all options in the buffer pointer to
* by 'options'.
* Returns: IPCOM_SUCCESS - message was processed successfully (dropped
* messages also counts as successful processing).
* IPCOM_ERR_FAILED - critical error encountered.
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_solicit(Ipdhcps6_message *msg,
Ipdhcps6_option *options,
Ip_u16 optslen)
{
Ip_err err;
/* PSERVITA */
Ipdhcps6_option clientid_option;
union Ipdhcps6_duid_data *client_duid;
Ip_u16 wbytes;
Ip_err status;
status = ipdhcps6_option_get(options, optslen, IPDHCPS6_OPTION_CLIENTID,
&clientid_option, sizeof (clientid_option), &wbytes);
if (IPCOM_SUCCESS != status)
{ /*
* no client id! reject this packet.
*/
return IPCOM_ERR_FAILED;
}
clientid_option.len = ip_ntohs(clientid_option.len);
client_duid = (union Ipdhcps6_duid_data *) &(clientid_option.data);
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_AUTH) == IP_TRUE)
{
/*
* add client into the "wants auth" database,
* if not already there.
*/
if (IP_NULL == ipdhcps6_client_requested_auth (client_duid, clientid_option.len))
ipdhcps6_client_wants_auth (client_duid, clientid_option.len);
}
else
{
/*
*client has not requested AUTH.
* remove them from the authtrans db
*/
ipdhcps6_client_no_auth (client_duid, clientid_option.len);
}
/* PSERVITA */
err = ipdhcps6_msg_process_rebind(msg, options, optslen);
if (err != IPCOM_SUCCESS)
return err;
if (msg->send_response)
{
Ipdhcps6_msg_hdr *response= (Ipdhcps6_msg_hdr *) msg->send_buf;
ipdhcps6_msg_get_first_option((Ipdhcps6_msg_hdr *) msg->send_buf,
msg->send_buf_len,
&options,
&optslen);
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_RAPID_COMMIT) == IP_FALSE)
response->type = IPDHCPS6_MSG_ADVERTISE;
/* else: Handle this in the same way as a Request message */
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipdhcps6_msg_process_confirm
*===========================================================================
* Description: Handles Request message.
* Parameters: msg - The received message, will contain the reply if such
* should be sent.
* options - Points to the first option included in the client
* message. All options are packed into this buffer without padding.
* optslen - The length of all options in the buffer pointer to
* by 'options'.
* Returns: IPCOM_SUCCESS - message was processed successfully (dropped
* messages also counts as successful processing).
* IPCOM_ERR_FAILED - critical error encountered.
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_confirm(Ipdhcps6_message *msg,
Ipdhcps6_option *options,
Ip_u16 optslen)
{
/* The information-request specific processing is located in
ipdhcps6_msg_process_options() since they require a response
which in turn requires a valid response message */
return ipdhcps6_msg_process_rebind(msg, options, optslen);
}
/*
*===========================================================================
* ipdhcps6_msg_process_information_request
*===========================================================================
* Description: Handles Request message.
* Parameters: msg - The received message, will contain the reply if such
* should be sent.
* options - Points to the first option included in the client
* message. All options are packed into this buffer without padding.
* optslen - The length of all options in the buffer pointer to
* by 'options'.
* Returns: IPCOM_SUCCESS - message was processed successfully (dropped
* messages also counts as successful processing).
* IPCOM_ERR_FAILED - critical error encountered.
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_information_request(Ipdhcps6_message *msg,
Ipdhcps6_option *options,
Ip_u16 optslen)
{
/* For TAHI, but it is OK with the process in ipdhcps6_msg_process_options */
if (ipdhcps6_msg_check_IA_infor(msg, options, optslen, "information request"))
return ipdhcps6_msg_process_rebind(msg, options, optslen);
return IPCOM_ERR_IGNORED_PACKET;
}
/*
*===========================================================================
* ipdhcps6_msg_process_request
*===========================================================================
* Description: Handles Request message.
* Parameters: msg - The received message, will contain the reply if such
* should be sent.
* options - Points to the first option included in the client
* message. All options are packed into this buffer without padding.
* optslen - The length of all options in the buffer pointer to
* by 'options'.
* Returns: IPCOM_SUCCESS - message was processed successfully (dropped
* messages also counts as successful processing).
* IPCOM_ERR_FAILED - critical error encountered.
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_request(Ipdhcps6_message *msg,
Ipdhcps6_option *options, Ip_u16 optslen)
{
if (ipdhcps6_msg_check_client_and_server_id(msg, options, optslen, "request"))
return ipdhcps6_msg_process_options(msg, options, optslen);
return IPCOM_ERR_IGNORED_PACKET;
}
/*
*===========================================================================
* ipdhcps6_msg_process_renew
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_renew(Ipdhcps6_message *msg,
Ipdhcps6_option *options,
Ip_u16 optslen)
{
Ipdhcps6_option *client_id;
if (ipdhcps6_msg_check_client_and_server_id(msg, options, optslen, "renew") == IP_FALSE)
return IPCOM_SUCCESS;
client_id = ipdhcps6_option_get_ptr(options, optslen, IPDHCPS6_OPTION_CLIENTID);
ip_assert(client_id != IP_NULL);
if (ipdhcps6_host_get(client_id) == IP_NULL)
{
/* This host has not been served before, so RENEW is not possible
drop the message */
IPCOM_LOG1(DEBUG, "Discarding renew message received from %s since "
"this host has not been served since last restart of this server",
ipcom_inet_ntop(IP_AF_INET6, &msg->peer_addr.sin6_addr,
ipdhcps6->addr_str, sizeof(ipdhcps6->addr_str)));
return IPCOM_SUCCESS;
}
return ipdhcps6_msg_process_options(msg, options, optslen);
}
/*
*===========================================================================
* ipdhcps6_msg_process_rebind
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_rebind(Ipdhcps6_message * msg,
Ipdhcps6_option *options,
Ip_u16 optslen)
{
Ipdhcps6_msg_hdr *request = (Ipdhcps6_msg_hdr *) msg->recv_buf;
/* RFC 3315 chapter 15.
A server MUST discard any Solicit, Confirm, Rebind or
Information-request messages it receives with a unicast destination
address. */
if (IP_IN6_IS_ADDR_MULTICAST(&msg->local_addr.sin6_addr) == IP_FALSE)
{
IPCOM_LOG2(INFO,
"Discarding %s message received from %s since it was sent to a unicast address",
ipdhcps6_msg_type_to_string(request->type),
ipcom_inet_ntop(IP_AF_INET6,
&msg->peer_addr.sin6_addr,
ipdhcps6->addr_str,
sizeof(ipdhcps6->addr_str)));
return IPCOM_ERR_IGNORED_PACKET;
}
/* RFC 3315 chapter 15.7
Must contain Client Identifier option
and must NOT include Server Identifier option*/
if (ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_CLIENTID) == IP_FALSE
|| ipdhcps6_option_exist(options, optslen, IPDHCPS6_OPTION_SERVERID))
{
/* Discard the message */
IPCOM_LOG2(INFO,
"Discarding %s message received from %s since client ID is missing"
" and/or server ID is present",
ipdhcps6_msg_type_to_string(request->type),
ipcom_inet_ntop(IP_AF_INET6,
&msg->peer_addr.sin6_addr,
ipdhcps6->addr_str,
sizeof(ipdhcps6->addr_str)));
return IPCOM_ERR_IGNORED_PACKET;
}
return ipdhcps6_msg_process_options(msg, options, optslen);
}
/*
*===========================================================================
* ipdhcps6_msg_process_release
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_release(Ipdhcps6_message * msg,
Ipdhcps6_option *options,
Ip_u16 optslen)
{
if (ipdhcps6_msg_check_client_and_server_id(msg, options, optslen, "release"))
return ipdhcps6_msg_process_options(msg, options, optslen);
return IPCOM_ERR_IGNORED_PACKET;
}
/*
*===========================================================================
* ipdhcps6_msg_process_decline
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_decline(Ipdhcps6_message * msg,
Ipdhcps6_option *options,
Ip_u16 optslen)
{
/*we start to deal decline message*/
if (ipdhcps6_msg_check_client_and_server_id(msg, options, optslen, "decline"))
return ipdhcps6_msg_process_options(msg, options, optslen);
return IPCOM_ERR_IGNORED_PACKET;
}
/*
*===========================================================================
* ipdhcps6_msg_process_relay_forw
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipdhcps6_msg_process_relay_forw(Ipdhcps6_message *msg,
Ipdhcps6_option *in_opts,
Ip_u16 in_opts_len)
{
Ipdhcps6_option *reply_opt;
Ip_u16 reply_opt_len;
Ip_u16 written;
Ipdhcps6_option *relay_message_opt;
Ipdhcps6_option *interface_id_opt;
Ipdhcps6_relay_msg_hdr *response;
Ipdhcps6_relay_msg_hdr *request;
Ipdhcps6_message inner_msg = *msg;
Ip_err err;
response = (Ipdhcps6_relay_msg_hdr *) msg->send_buf;
request = (Ipdhcps6_relay_msg_hdr *) msg->recv_buf;
relay_message_opt = ipdhcps6_option_get_ptr(in_opts,
in_opts_len,
IPDHCPS6_OPTION_RELAY_MSG);
if (relay_message_opt == IP_NULL)
{
IPCOM_LOG0(INFO, "Ignoring relay-forward packet since it does not contain a relay message option");
return IPCOM_ERR_IGNORED_PACKET;
}
/* Create the relay-reply header */
IPDHCPS6_SET_IN6_ADDR(&inner_msg.peer_addr.sin6_addr, &request->link_addr);
/* Process the inner message as if it has been sent to a multicast address
since the relay agents is always allowed to use unicast */
IPDHCPS6_SET_IN6_ADDR(&inner_msg.local_addr.sin6_addr, &ipdhcps6_all_dhcp_servers);
inner_msg.local_addr.sin6_scope_id = 0;
inner_msg.recv_buf = (void*) &relay_message_opt->data;
inner_msg.recv_buf_len -= (Ip_u16) ((Ip_ptrdiff_t) inner_msg.recv_buf
- (Ip_ptrdiff_t) msg->recv_buf);
ipcom_memcpy(response, request, IPDHCP_RELAY_MSG_HDR_LEN);
response->type = IPDHCPS6_MSG_RELAY_REPL;
/* Add the Relay Message to the response */
ipdhcps6_msg_first_response_opt(msg,
&reply_opt,
&reply_opt_len);
/* The response of the inner message is the payload for this Relay Message Option */
inner_msg.send_buf = (void*) &reply_opt->data;
inner_msg.send_buf_maxlen -= (Ip_u16) ((Ip_ptrdiff_t) inner_msg.send_buf
- (Ip_ptrdiff_t) msg->send_buf);
/* Process the inner message */
err = ipdhcps6_process_message(&inner_msg);
if (err != IPCOM_SUCCESS)
return err;
err = ipdhcps6_option_add_relay_message(reply_opt,
reply_opt_len,
&written,
inner_msg.send_buf,
inner_msg.send_buf_len);
if (err != IP_NULL)
return err;
ipdhcps6_msg_next_response_opt(written,
msg,
&reply_opt,
&reply_opt_len);
interface_id_opt = ipdhcps6_option_get_ptr(in_opts,
in_opts_len,
IPDHCPS6_OPTION_INTERFACE_ID);
if (interface_id_opt != IP_NULL)
{
/* Copy the Interface Id Option verbatim */
written = (Ip_u16)(ipdhcps6_get_u16(&interface_id_opt->len) + IPDHCPS6_OPTION_HDR_LEN);
if (written > reply_opt_len)
{
IPCOM_LOG0(WARNING, "Could not create response since the response is too small");
return IPCOM_ERR_NO_MEMORY;
}
ipcom_memcpy(reply_opt, interface_id_opt, written);
ipdhcps6_msg_next_response_opt(written,
msg,
&reply_opt,
&reply_opt_len);
}
msg->send_response = IP_TRUE;
return IPCOM_SUCCESS;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/