/* ****************************************************************************** * 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 ****************************************************************************** */ /* 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 #include #include #include #include #include #include #include #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 **************************************************************************** */