/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipdhcps6_prefix.c,v $ $Revision: 1.25.12.2 $ * $Source: /home/interpeak/CVSRoot/ipdhcps6/src/ipdhcps6_prefix.c,v $ * $Author: jchang1 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 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_host.h" #include "ipdhcps6_prefix.h" #include "ipdhcps6_subnet.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct Ipdhcps6_prefix_list_data_struct { Ipdhcps6_prefix *prefix; int index; } Ipdhcps6_prefix_list_data; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdhcps6_prefix_obj_hash *=========================================================================== * Description: Returns a hash value based on a DHCP option. * Parameters: opt - The option to return a hash value for. * Returns: * */ IP_STATIC unsigned ipdhcps6_prefix_obj_hash(Ipdhcps6_prefix *prefix) { return ipcom_hash_update(prefix->key, ipcom_strlen(prefix->key), 0); } /* *=========================================================================== * ipdhcps6_prefix_key_hash *=========================================================================== * Description: Returns a hash value for a prefix key. * Parameters: key - The prefix ID number. * Returns: * */ IP_STATIC unsigned ipdhcps6_prefix_key_hash(char *key) { return ipcom_hash_update(key, ipcom_strlen(key), 0); } /* *=========================================================================== * ipdhcps6_prefix_hash_cmp *=========================================================================== * Description: Compares a hash object and a hash key for equality. * Parameters: prefix - A prefix structure. * key - A pointer to the search key. * Returns: IP_TRUE if the search key matches the prefix entry. * */ IP_STATIC Ip_bool ipdhcps6_prefix_hash_cmp(Ipdhcps6_prefix *prefix, char *key) { return ipcom_strcmp(prefix->key, key) == 0; } /* *=========================================================================== * ipdhcps6_prefix_free_cb *=========================================================================== * Description: Frees the specified prefix. * Parameters: prefix - A prefix structure. * Returns: * */ IP_STATIC void ipdhcps6_prefix_free_cb(Ipdhcps6_prefix *prefix, void *unused) { (void) unused; ipcom_free(prefix); } /* *=========================================================================== * ipdhcps6_prefix_list_cb *=========================================================================== * Description: Makes a deep copy of all prefix entries. * Parameters: prefix - A prefix structure. * Returns: * */ IP_STATIC void ipdhcps6_prefix_list_cb(Ipdhcps6_prefix *prefix, Ipdhcps6_prefix_list_data *list_data) { ipcom_memcpy(&list_data->prefix[list_data->index++], prefix, sizeof(Ipdhcps6_prefix)); } /* *=========================================================================== * ipdhcps6_prefix_get_ptr *=========================================================================== * Description: Returns the prefix entry corrensponding to the specified key. * Parameters: key - The ID number for the prefix to return * Returns: The matching prefix entry or IP_NULL if no match was found. * */ IP_STATIC Ipdhcps6_prefix * ipdhcps6_prefix_get_ptr(IP_CONST char *key) { return ipcom_hash_get(ipdhcps6->db.prefixes, (void *) key); } /* *=========================================================================== * ipdhcps6_prefix_get_ptr_from_host *=========================================================================== * Description: Returns the prefix entry on a specific host. * Parameters: host - The host to which this prefix must be assigned. * prefix - The address of the prefix to find. * prefix_len - The length of the prefix to find. * Returns: The matching prefix entry or IP_NULL if no match was found. * */ IP_STATIC Ipdhcps6_prefix * ipdhcps6_prefix_get_ptr_from_host(Ipdhcps6_host *host, struct Ip_in6_addr *prefix, int prefix_len) { Ipdhcps6_prefix *p; int i; for (i = 0; i < IPDHCPS6_PREFIX_PER_USER; i++) { if (host->user.prefix_ids[i][0] == '\0') continue; p = ipdhcps6_prefix_get_ptr(host->user.prefix_ids[i]); if (p == IP_NULL) continue; if (prefix_len == p->prefix_len && IP_IN6_ARE_ADDR_EQUAL(prefix, &p->prefix)) return p; } return IP_NULL; } /* *=========================================================================== * ipdhcps6_prefix_add_del_rt *=========================================================================== * Description: Adds/deletes a route to the prefix that is beeing handed out. * Parameters: ifindex - The interface that have to be used to reach this prefix. * prefix - The prefix that is beeing handed out. * prefixlen - The prefix length. * add - IP_TRUE if the route should be added, it will be deleted * otherwise. * Returns: IPCOM_SUCCESS - operation was successful. * IPCOM_ERR_NO_MEMORY - could not allocate enough memory. * */ IP_STATIC Ip_err ipdhcps6_prefix_add_del_rt(int ifindex, struct Ip_in6_addr *prefix, int prefixlen , Ip_bool add) { struct Ip_sockaddr_in6 *gw; struct Ip_sockaddr_in6 *net; struct Ip_sockaddr_in6 *mask; struct Ipnet_rt_msghdr *rtm = IP_NULL; Ip_u16 rtm_len; int rt_sock; struct Ip_in6_ifreq ifreq6; Ip_err err; rt_sock = ipcom_socket(IP_AF_ROUTE, IP_SOCK_RAW, 0); if (rt_sock == IP_INVALID_SOCKET) { IPCOM_LOG0(ERR, "Failed to open routing socket"); err = IPCOM_ERR_SOCKCALLFAILED; /* Coverity defect fix for NEGATIVE_RETURNS */ return err; } (void)ipcom_if_indextoname((Ip_u32)ifindex, ifreq6.ifr_name); if (ipcom_socketioctl(rt_sock, IP_SIOCGIFDSTADDR_IN6, &ifreq6) < 0) /* Not a point-to-point interface or no peer address set, ignore t */ return IPCOM_SUCCESS; /* Peer address present, route any packets destinated to the network defined by the prefix to the PPP peer */ rtm_len = sizeof(struct Ipnet_rt_msghdr) + sizeof(struct Ip_sockaddr_in6) * 3; rtm = ipcom_calloc(1, rtm_len); if (rtm == IP_NULL) { IPCOM_LOG0(ERR, "Out of memory while adding route."); err = IPCOM_ERR_NO_MEMORY; goto cleanup; } net = (struct Ip_sockaddr_in6 *) (rtm + 1); gw = (struct Ip_sockaddr_in6 *) (net + 1); mask = (struct Ip_sockaddr_in6 *) (gw + 1); rtm->rtm_msglen = rtm_len; rtm->rtm_version = IPNET_RTM_VERSION; rtm->rtm_type = (Ip_u8)(add ? IPNET_RTM_ADD : IPNET_RTM_DELETE); rtm->rtm_index = (Ip_u32)ifindex; rtm->rtm_flags = IPNET_RTF_UP | IPNET_RTF_GATEWAY | IPNET_RTF_DONE | IPNET_RTF_MASK | IPNET_RTF_DYNAMIC; rtm->rtm_addrs = IPNET_RTA_DST | IPNET_RTA_GATEWAY | IPNET_RTA_NETMASK; rtm->rtm_pid = ipcom_getpid(); rtm->rtm_rmx.rmx_expire = IPCOM_ADDR_INFINITE; /* Network */ IPCOM_SA_LEN_SET(net, sizeof(*net)); net->sin6_family = IP_AF_INET6; IPDHCPS6_SET_IN6_ADDR(&net->sin6_addr, prefix); /* Mask */ IPCOM_SA_LEN_SET(mask, sizeof(*mask)); mask->sin6_family = IP_AF_INET6; ipdhcps6_create_mask(&mask->sin6_addr, prefixlen); /* Gateway */ ipcom_memcpy(gw, &ifreq6.ifr_ifru.ifru_dstaddr, IPCOM_SA_LEN_GET(&ifreq6.ifr_ifru.ifru_dstaddr)); if (ipcom_send(rt_sock, rtm, rtm->rtm_msglen, 0) < 0) { IPCOM_LOG1(ERR, "Failed to send route socket message: %s", ipcom_strerror(ipcom_errno)); } err = IPCOM_SUCCESS; cleanup: ipcom_socketclose(rt_sock); ipcom_free(rtm); return err; } /* *=========================================================================== * ipdhcps6_prefix_add_del_prefixes *=========================================================================== * Description: Adds or deletes all prefixes that this host should get. * Parameters: subnet - The subnet the requesting router is located at. * host - The host entry for the requesting router. * response_opt - Buffer to where all OPTION_IAPREFIX options * will be copied. * response_optlen - Length of the 'response_opt' buffer. * bytes_written - Number of bytes written to 'opt' or the requeried * length of 'opt' if failed with IPCOM_ERR_NO_MEMORY. * Returns: IPCOM_SUCCESS - operation was successful. * IPCOM_ERR_NO_MEMORY - the 'response_opt' buffer was not large enough. * */ IP_STATIC Ip_err ipdhcps6_prefix_add_del_prefixes(Ipdhcps6_subnet_entry *subnet, Ipdhcps6_host *host, Ipdhcps6_option_iaprefix *response_opt, Ip_u16 response_optlen, Ip_u16 *bytes_written, Ip_bool add) { Ipdhcps6_prefix *prefix; Ip_u16 written; Ip_err err; int i; (void) subnet; *bytes_written = 0; for (i = 0; i < IPDHCPS6_PREFIX_PER_USER; i++) { if (host->user.prefix_ids[i][0] == '\0') continue; prefix = ipdhcps6_prefix_get_ptr(host->user.prefix_ids[i]); if (prefix == IP_NULL) { IPCOM_LOG2(WARNING, "Prefix %s was specified for user %s but not found in the prefix database", host->user.prefix_ids[i], host->user.name); continue; } if (add) { err = ipdhcps6_option_add_iaprefix(response_opt, (Ip_u16) (response_optlen - *bytes_written), &written, prefix); if (err != IPCOM_SUCCESS) return err; *bytes_written += written; response_opt = (Ipdhcps6_option_iaprefix *) ((Ip_u8 *) response_opt + written); } err = ipdhcps6_prefix_add_del_rt(host->ifindex, &prefix->prefix, prefix->prefix_len, add); } return IPCOM_SUCCESS; } /* *=========================================================================== * ipdhcps6_prefix_renew_prefixes *=========================================================================== * Description: * Parameters: * Returns: IPCOM_SUCCESS - operation was successful. * IPCOM_ERR_NO_MEMORY - the 'response_opt' buffer was not large enough. * */ IP_STATIC Ip_err ipdhcps6_prefix_renew_prefixes(Ipdhcps6_subnet_entry *subnet, Ipdhcps6_host *host, Ipdhcps6_option *request_ia_pd_opt, Ipdhcps6_option_iaprefix *response_opt, Ip_u16 response_optlen, Ip_u16 *bytes_written) { Ipdhcps6_prefix *prefix; Ipdhcps6_option_iaprefix *response_ptr; Ipdhcps6_option_iaprefix *request_ptr; Ipdhcps6_option_iaprefix iaprefix; const Ip_u16 iaprefix_len = 25; Ip_u16 id_pd_opt_len; Ip_u16 request_offset; Ip_u16 request_iaprefix_len; (void)subnet; id_pd_opt_len = (Ip_u16) (ipdhcps6_get_u16(&request_ia_pd_opt->len) - ip_offsetof(struct Ipdhcps6_option_prefix_delegation_data, options) - IPDHCPS6_OPTION_HDR_LEN); ipcom_memset(&prefix, 0, sizeof(prefix)); ipdhcps6_set_u16(&iaprefix.code, IPDHCPS6_OPTION_IAPREFIX); ipdhcps6_set_u16(&iaprefix.len, iaprefix_len); *bytes_written = 0; for (request_offset = 0; request_offset < id_pd_opt_len;) { /* Point to the IAPREFIX option to process in the reqeust */ request_ptr = (Ipdhcps6_option_iaprefix *) ((Ip_u8 *) request_ia_pd_opt->data.ia_pd.options + request_offset); if (response_optlen < *bytes_written + iaprefix_len + IPDHCPS6_OPTION_HDR_LEN) /* The OPTION_IAPREFIX does not fit into the response buffer */ return IPCOM_ERR_NO_MEMORY; ipcom_memcpy(IPDHCPS6_OPTION_IAPREFIX_GET_PREFIX_PTR(&iaprefix), IPDHCPS6_OPTION_IAPREFIX_GET_PREFIX_PTR(request_ptr), sizeof(struct Ip_in6_addr)); IPDHCPS6_OPTION_IAPREFIX_SET_LEN(&iaprefix, IPDHCPS6_OPTION_IAPREFIX_GET_LEN(request_ptr)); prefix = ipdhcps6_prefix_get_ptr_from_host(host, IPDHCPS6_OPTION_IAPREFIX_GET_PREFIX_PTR(&iaprefix), IPDHCPS6_OPTION_IAPREFIX_GET_LEN(&iaprefix)); if (prefix == IP_NULL) { /* This prefix is no longer used by the subnet */ iaprefix.preferred_lifetime = 0; iaprefix.valid_lifetime = 0; } else { /* Extend the lifetime another period */ ipdhcps6_set_u32(&iaprefix.preferred_lifetime, prefix->preferred); ipdhcps6_set_u32(&iaprefix.valid_lifetime, prefix->valid); } /* Point to where the this IAPREFIX should be written into the response buffer */ response_ptr = (Ipdhcps6_option_iaprefix *) ((Ip_u8 *) response_opt + *bytes_written); /* Add the option to the response */ ipcom_memcpy(response_ptr, &iaprefix, iaprefix_len + IPDHCPS6_OPTION_HDR_LEN); *bytes_written += iaprefix_len + IPDHCPS6_OPTION_HDR_LEN; /* Move to the next IAPREFIX in the reqeust buffer */ request_iaprefix_len = ipdhcps6_get_u16(&request_ptr->len); request_offset += request_iaprefix_len + IPDHCPS6_OPTION_HDR_LEN; } return IPCOM_SUCCESS; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdhcps6_prefix_process_request *=========================================================================== * Description: Adds prefix to the requesting router (using OPTION_IA_PD). * Parameters: msg - The message type in the request. * host - The entry for the requesting router. * opt - The OPTION_IA_PD option sent by the requesting router. * response_opt - Pointer into the response buffer where the * OPTION_IA_PD options containing the prefixes should be written. * response_optlen - The length of the 'reponse_opt' * bytes_written - Number of bytes written to 'opt' or the requeried * length of 'opt' if failed with IPCOM_ERR_NO_MEMORY. * Returns: IPCOM_SUCCESS - operation was successful. * IPCOM_ERR_NO_MEMORY - the 'response_opt'. was to small. * */ IP_GLOBAL Ip_err ipdhcps6_prefix_process_request(Ip_u16 msg_type, Ipdhcps6_subnet_entry *subnet, Ipdhcps6_host *host, Ipdhcps6_option *opt, Ipdhcps6_option *response_opt, Ip_u16 response_optlen, Ip_u16 *bytes_written) { Ip_u16 ia_pd_opt_len; Ipdhcps6_option_iaprefix *iaprefix; Ip_err err; Ip_u16 hdr_len; *bytes_written = 0; ia_pd_opt_len = ipdhcps6_get_u16(&opt->len); hdr_len = ip_offsetof(struct Ipdhcps6_option_prefix_delegation_data, options); if (ia_pd_opt_len != hdr_len) { if (ia_pd_opt_len < hdr_len || ipdhcps6_option_check((Ipdhcps6_option *) opt->data.ia_pd.options, (Ip_u16) (ia_pd_opt_len - hdr_len))) { /* Error in length field in the OPTION_IA_PD option, drop the message */ return IPCOM_ERR_FAILED; } } if (subnet == IP_NULL) { /* No configuration for the subnet the requesting router is located at */ return ipdhcps6_option_add_status_code(response_opt, response_optlen, bytes_written, IPDHCPS6_CODE_NO_PREFIX_AVAIL, "No configuration create for the subnet from where the request originates."); } if (ipdhcps6_host_has_prefix(host) == IP_FALSE) { return ipdhcps6_option_add_status_code(response_opt, response_optlen, bytes_written, IPDHCPS6_CODE_NO_PREFIX_AVAIL, "The host has not been given any prefixes."); } ia_pd_opt_len = ip_offsetof(struct Ipdhcps6_option_prefix_delegation_data, options); if (response_optlen < ia_pd_opt_len + IPDHCPS6_OPTION_HDR_LEN) /* Not the response buffer is not big enough to hold the IPDHCPS6_OPTION_IA_PD header */ return IPCOM_ERR_NO_MEMORY; /* Add the IPDHCPS6_OPTION_IA_PD header */ ipdhcps6_set_u16(&response_opt->code, IPDHCPS6_OPTION_IA_PD); ipcom_memcpy(&response_opt->data.ia_pd.iaid, opt->data.ia_pd.iaid, sizeof(response_opt->data.ia_pd.iaid)); /* Let the client calculate the T1 and T2 times */ ipdhcps6_set_u32(&response_opt->data.ia_pd.t1, 0); ipdhcps6_set_u32(&response_opt->data.ia_pd.t2, 0); response_optlen -= ia_pd_opt_len + IPDHCPS6_OPTION_HDR_LEN; /* Pointer to where the first IPDHCPS6_OPTION_IAPREFIX should be written */ iaprefix = response_opt->data.ia_pd.options; switch (msg_type) { case IPDHCPS6_MSG_SOLICIT: case IPDHCPS6_MSG_REQUEST: err = ipdhcps6_prefix_add_del_prefixes(subnet, host, iaprefix, response_optlen, bytes_written, IP_TRUE); break; case IPDHCPS6_MSG_RELEASE: err = ipdhcps6_prefix_add_del_prefixes(subnet, host, iaprefix, response_optlen, bytes_written, IP_FALSE); break; case IPDHCPS6_MSG_RENEW: case IPDHCPS6_MSG_REBIND: err = ipdhcps6_prefix_renew_prefixes(subnet, host, opt, iaprefix, response_optlen, bytes_written); break; default: /* This message type should not forward here */ return IPCOM_ERR_INVALID_ARG; } *bytes_written += ia_pd_opt_len; ipdhcps6_set_u16(&response_opt->len, *bytes_written); *bytes_written += IPDHCPS6_OPTION_HDR_LEN; return err; } /* *=========================================================================== * ipdhcps6_prefix_new_db *=========================================================================== * Description: Deletes all elements in the subnet database, memory for * the route table holding the entries is also deleted. * Parameters: subnets - The subnet database to delete. * Returns: * */ IP_GLOBAL Ipcom_hash * ipdhcps6_prefix_new_db(void) { return ipcom_hash_new((Ipcom_hash_obj_func) ipdhcps6_prefix_obj_hash, (Ipcom_hash_key_func) ipdhcps6_prefix_key_hash, (Ipcom_hash_cmp_func) ipdhcps6_prefix_hash_cmp); } /* *=========================================================================== * ipdhcps6_prefix_delete_db *=========================================================================== * Description: Frees all prefix entries in the list. * Parameters: * Returns: * */ IP_GLOBAL void ipdhcps6_prefix_delete_db(Ipcom_hash *prefix_db) { if(prefix_db != IP_NULL) { ipcom_hash_for_each(prefix_db, (Ipcom_hash_foreach_cb_func) ipdhcps6_prefix_free_cb, IP_NULL); ipcom_hash_delete(prefix_db); } } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipdhcps6_prefix_add *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdhcps6_prefix_add(Ipdhcps6_prefix *prefix) { Ipdhcps6_prefix *p = IP_NULL; Ip_err err; if (ipdhcps6 == IP_NULL || IP_BIT_ISFALSE(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN)) return IPCOM_ERR_NOT_OPENED; ipcom_mutex_lock(ipdhcps6->db.mutex); if (ipdhcps6_prefix_get_ptr(prefix->key) != IP_NULL) { err = IPCOM_ERR_DUPLICATE; goto errout; } p = ipcom_malloc(sizeof(Ipdhcps6_prefix)); if (p == IP_NULL) { err = IPCOM_ERR_NO_MEMORY; goto errout; } ipcom_memcpy(p, prefix, sizeof(Ipdhcps6_prefix)); if ((err = ipcom_hash_add(ipdhcps6->db.prefixes, p)) != IPCOM_SUCCESS) goto errout; ipcom_mutex_unlock(ipdhcps6->db.mutex); return IPCOM_SUCCESS; errout: ipcom_free(p); ipcom_mutex_unlock(ipdhcps6->db.mutex); return err; } /* *=========================================================================== * ipdhcps6_subnet_delete *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdhcps6_prefix_delete(Ipdhcps6_prefix *prefix) { Ipdhcps6_prefix *p; Ip_err err = IPCOM_SUCCESS; if (ipdhcps6 == IP_NULL || IP_BIT_ISFALSE(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN)) return IPCOM_ERR_NOT_OPENED; ipcom_mutex_lock(ipdhcps6->db.mutex); if ((p = ipdhcps6_prefix_get_ptr(prefix->key)) == IP_NULL) { err = IPCOM_ERR_NOT_FOUND; goto cleanup; } (void) ipcom_hash_remove(ipdhcps6->db.prefixes, p); ipcom_free(p); cleanup: ipcom_mutex_unlock(ipdhcps6->db.mutex); return err; } /* *=========================================================================== * ipdhcps6_subnet_list *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipdhcps6_prefix_list(Ipdhcps6_prefix prefix[], int *num_elem) { Ipdhcps6_prefix_list_data cb_data; if (ipdhcps6 == IP_NULL || IP_BIT_ISFALSE(ipdhcps6->flags, IPDHCPS6_FLAG_OPEN)) return IPCOM_ERR_NOT_OPENED; ipcom_mutex_lock(ipdhcps6->db.mutex); if (prefix == IP_NULL) { *num_elem = (int)ipdhcps6->db.prefixes->elem; ipcom_mutex_unlock(ipdhcps6->db.mutex); return IPCOM_SUCCESS; } if (*num_elem < (int) ipdhcps6->db.prefixes->elem) { ipcom_mutex_unlock(ipdhcps6->db.mutex); return IPCOM_ERR_NO_MEMORY; } *num_elem = (int)ipdhcps6->db.prefixes->elem; cb_data.prefix = prefix; cb_data.index = 0; ipcom_hash_for_each(ipdhcps6->db.prefixes, (Ipcom_hash_foreach_cb_func) ipdhcps6_prefix_list_cb, &cb_data); ipcom_mutex_unlock(ipdhcps6->db.mutex); return IPCOM_SUCCESS; } /* **************************************************************************** * END OF FILE **************************************************************************** */