/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_payload_nat.c,v $ $Revision: 1.14.10.4 $ * $Source: /home/interpeak/CVSRoot/ipike/src/ipike_payload_nat.c,v $ * $Author: hxie $ $Date: 2012-09-07 00:16:46 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Magnus Alstrom ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* NAT Discovery payload * NAT Original Address payload * * Payloads for discovering and handling of NAT traversal (RFC 3947) */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipike_h.h" #include "ipike_config.h" #include "ipike_payload_nat.h" #include #include #include #include /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 5 DEFINES **************************************************************************** */ /* **************************************************************************** * 6 TYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* *=========================================================================== * ipike_nat_discovery_data *=========================================================================== * Description: Returns a pointer to the nat discovery data in the payload. * Parameters: nat_discovery - a nat discovery payload. * Returns: A pointer to the nat discovery data. * */ IP_STATIC Ip_u8* ipike_nat_discovery_data(Ipike_nat_discovery_payload* nat_discovery); /* *=========================================================================== * ipike_nat_discovery_data_length *=========================================================================== * Description: Returns the length of the nat discovery data. * Parameters: nat_discovery - nat discovery payload. * Returns: The length of the nat discovery data (in bytes). * * */ IP_STATIC Ip_u16 ipike_nat_discovery_data_length(Ipike_nat_discovery_payload* nat_discovery); /* *=========================================================================== * ipike_nat_original_address_data *=========================================================================== * Description: Returns a pointer to the nat original data in the payload. * Parameters: nat_org_addr - a nat original address payload. * Returns: A pointer to the nat original data. * */ IP_STATIC Ip_u8* ipike_nat_original_address_data(Ipike_nat_org_addr_payload* nat_org_addr); /* *=========================================================================== * ipike_nat_original_address_data_length *=========================================================================== * Description: Returns the length of the nat original data in the payload. * Parameters: nat_org_addr - a nat original address payload. * Returns: The length of the nat original address data (in bytes). * * */ IP_STATIC Ip_u16 ipike_nat_original_address_data_length(Ipike_nat_org_addr_payload* nat_org_addr); /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_nat_discovery_data *=========================================================================== */ IP_STATIC Ip_u8* ipike_nat_discovery_data(Ipike_nat_discovery_payload* nat_discovery) { return (Ip_u8*) nat_discovery + IPIKE_NAT_DISCOVERY_HEADER_LENGTH; } /* *=========================================================================== * ipike_nat_discovery_data_length *=========================================================================== */ IP_STATIC Ip_u16 ipike_nat_discovery_data_length(Ipike_nat_discovery_payload* nat_discovery) { return IPIKE_PAYLOAD_DATA_LENGTH(nat_discovery->header.length, IPIKE_NAT_DISCOVERY_HEADER_LENGTH); } /* *=========================================================================== * ipike_nat_original_address_data *=========================================================================== */ IP_STATIC Ip_u8* ipike_nat_original_address_data(Ipike_nat_org_addr_payload* nat_org_addr) { return (Ip_u8*) nat_org_addr + IPIKE_NAT_ORG_ADDR_HEADER_LENGTH; } /* *=========================================================================== * ipike_nat_original_address_data_length *=========================================================================== */ IP_STATIC Ip_u16 ipike_nat_original_address_data_length(Ipike_nat_org_addr_payload* nat_org_addr) { return IPIKE_PAYLOAD_DATA_LENGTH(nat_org_addr->header.length, IPIKE_NAT_ORG_ADDR_HEADER_LENGTH); } /* *=========================================================================== * ipike_nat_original_address_new *=========================================================================== */ IP_STATIC Ipike_nat_org_addr_payload* ipike_nat_original_address_new( union Ip_sockaddr_union* address) { Ip_size_t len = 0; Ipike_nat_org_addr_payload* ret = IP_NULL; len = IPIKE_NAT_ORG_ADDR_HEADER_LENGTH + ipike_addr_get_len(address); if (IP_NULL == (ret = ipcom_calloc(1, len))) { IPCOM_LOG0(CRIT, "ipike_nat_original_address_new() :: out of memory"); return IP_NULL; } ret->header.length = ip_htons(len); ret->header.next_payload = IPIKE_PAYLOAD_NAT_ORIGINAL_ADDRESS; if (ipike_addr_get_len(address) == 4) ret->type = IPIKE_ID_IPV4_ADDR; else ret->type = IPIKE_ID_IPV6_ADDR; ipcom_memcpy(ipike_nat_original_address_data(ret), ipike_addr_get_ptr(address), ipike_addr_get_len(address)); return ret; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_nat_discovery_new *=========================================================================== */ IP_GLOBAL Ipike_nat_discovery_payload* ipike_nat_discovery_new( const Ip_u8* nat_discovery_data, Ip_u16 nat_discovery_len) { Ipike_nat_discovery_payload* ret = IP_NULL; if (IP_NULL == (ret = ipcom_calloc(1, IPIKE_NAT_DISCOVERY_HEADER_LENGTH + nat_discovery_len))) { IPCOM_LOG0(CRIT, "ipike_nat_discovery_new: out of memory"); return IP_NULL; } ret->header.length = ip_htons(IPIKE_NAT_DISCOVERY_HEADER_LENGTH + nat_discovery_len); ret->header.next_payload = IPIKE_PAYLOAD_NAT_DISCOVERY; ipcom_memcpy(ipike_nat_discovery_data(ret), nat_discovery_data, nat_discovery_len); return ret; } /* *=========================================================================== * ipike_nat_discovery_create *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_discovery_create( union Ip_sockaddr_union* address, Ip_u8* i_cookie, Ip_u8* r_cookie, Ip_u16 hash_alg, Ipike_nat_discovery_payload** nat_discovery) { Ipike_error err; Ipike_value n; ipcom_memset(&n, 0, sizeof(n)); err = ipike_nat_discovery_generate(address, i_cookie, r_cookie, hash_alg, &n); if (err != IPIKE_SUCCESS) return err; *nat_discovery = ipike_nat_discovery_new(n.value, (Ip_u16)n.len); ipcom_free(n.value); if (IP_NULL == *nat_discovery) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_nat_discovery_check_payload *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_discovery_check_payload(Ipike_nat_discovery_payload* nat_discovery) { size_t len = ipike_nat_discovery_data_length(nat_discovery); if (len < IPIKE_NAT_DISCOVERY_MIN_LEN || len > IPIKE_NAT_DISCOVERY_MAX_LEN) { IPCOM_LOG3(ERR, "ipike_nat_discovery_check_payload() :: the NAT "\ "discovery size must be between %u and %u, "\ "this nat_discovery was %u", IPIKE_NAT_DISCOVERY_MIN_LEN, IPIKE_NAT_DISCOVERY_MAX_LEN, len); return IPIKE_ATTRIBUTE_INVALID_VALUE; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_nat_discovery_generate_pair *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_discovery_generate_pair(Ipike_sadb_entry* ike_sa, Ip_u16 hash_alg) { Ipike_error err = IPIKE_SUCCESS; /* Is NAT discovery disabled? */ if (ipike.port_nat != 0) { IPIKE_FREEP(ike_sa->local_nat_data.value); IPIKE_FREEP(ike_sa->peer_nat_data.value); ike_sa->local_nat_data.value = IP_NULL; ike_sa->peer_nat_data.value = IP_NULL; err = ipike_nat_discovery_generate(&ike_sa->local_addr, ike_sa->initiator_cookie, ike_sa->responder_cookie, hash_alg, &ike_sa->local_nat_data); if (err != IPIKE_SUCCESS) { IPCOM_LOG0(ERR, "Failed to generate local NAT data"); } else { err = ipike_nat_discovery_generate(&ike_sa->peer_addr, ike_sa->initiator_cookie, ike_sa->responder_cookie, hash_alg, &ike_sa->peer_nat_data); if (err != IPIKE_SUCCESS) { IPCOM_LOG0(ERR, "Failed to generate peer NAT data"); } } } return err; } /* *=========================================================================== * ipike_nat_discovery_start *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_discovery_start(Ipike_sadb_entry* ike_sa, Ip_u16 hash_alg) { Ipike_error err = IPIKE_SUCCESS; /* Is NAT discovery disabled? */ if (ipike.port_nat == 0) { ike_sa->local_behind_nat = IP_FALSE; ike_sa->peer_behind_nat = IP_FALSE; } /* Always consider behind NAT and let the discover payloads tell differently */ else { ike_sa->local_behind_nat = IP_TRUE; ike_sa->peer_behind_nat = IP_TRUE; ike_sa->has_got_nat_data = IP_FALSE; err = ipike_nat_discovery_generate_pair(ike_sa, hash_alg); } return err; } /* *=========================================================================== * ipike_nat_discovery_end *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_discovery_end(Ipike_isakmp_exchange* ie) { Ipike_error err = IPIKE_SUCCESS; Ipike_sadb_entry* ike_sa = ie->ike_sa; if (!ike_sa->has_got_nat_data) { ike_sa->local_behind_nat = IP_FALSE; ike_sa->peer_behind_nat = IP_FALSE; } if (ike_sa->local_behind_nat || ike_sa->peer_behind_nat || (ipcom_sysvar_get_as_int("ipike.always_nat", 0) != 0) || (ike_sa->has_got_nat_data && (ipcom_sysvar_get_as_int("ipike.mobike", 0) != 0))) { if (ike_sa->local_behind_nat) IPCOM_LOG0(DEBUG, "Local is behind a NAT"); if (ike_sa->peer_behind_nat) IPCOM_LOG0(DEBUG, "Peer is behind a NAT"); /* In case of reauthentication we might already have updated * the IKE SA with correct port */ if (ipike_addr_get_port(&ike_sa->local_addr) == ipike.port || ipike_addr_get_port(&ike_sa->peer_addr) == ipike.port) { /* Switch to NAT port (default 4500) */ ipike_sadb_update_port(ike_sa, ipike.port_nat, ike_sa->is_initiator ? ipike.port_nat : ipike_addr_get_port(&ie->msg.peer_addr)); } (void)ipike_addr_set_port(&ie->msg.local_addr, ipike_addr_get_port(&ike_sa->local_addr)); (void)ipike_addr_set_port(&ie->msg.peer_addr, ipike_addr_get_port(&ike_sa->peer_addr)); } return err; } /* *=========================================================================== * ipike_nat_discovery_process *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_discovery_process( Ipike_sadb_entry* ike_sa, Ipike_nat_discovery_payload* payload) { ike_sa->has_got_nat_data = IP_TRUE; /* Is the payload for the local address? */ if ((ipike_nat_discovery_data_length(payload) == ike_sa->local_nat_data.len) && !ipcom_memcmp(ipike_nat_discovery_data(payload), ike_sa->local_nat_data.value, ike_sa->local_nat_data.len)) { ike_sa->local_behind_nat = IP_FALSE; } /* Is the payload for the peer address? */ else if ((ipike_nat_discovery_data_length(payload) == ike_sa->peer_nat_data.len) && !ipcom_memcmp(ipike_nat_discovery_data(payload), ike_sa->peer_nat_data.value, ike_sa->peer_nat_data.len)) { ike_sa->peer_behind_nat = IP_FALSE; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_nat_discovery_generate *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_discovery_generate( union Ip_sockaddr_union* addr, Ip_u8* i_cookie, Ip_u8* r_cookie, Ip_u16 hash_alg, Ipike_value* nat_discovery) { const EVP_MD* evp_md; EVP_MD_CTX ctx; Ip_u16 port; IPCOM_LOG2(DEBUG2, "NAT-Traversal for %s[%d]", ipike_addr_to_str(addr), ipike_addr_get_port(addr)); IPCOM_LOG1(DEBUG2, " Initiator cookie %s", ipike_cookie_to_string(i_cookie)); IPCOM_LOG1(DEBUG2, " Responder cookie %s", ipike_cookie_to_string(r_cookie)); IPCOM_LOG1(DEBUG2, " Hash Algorithm %s", ipike_attribute_hash_alg_to_string(hash_alg)); if (IP_NULL == (evp_md = ipike_attribute_get_evp_hash(hash_alg))) return IPIKE_SA_HASH_ALGORITHM_NOT_SUPPORTED; nat_discovery->len = (Ip_size_t) EVP_MD_size(evp_md); nat_discovery->value = ipcom_malloc(nat_discovery->len); if (IP_NULL == nat_discovery->value) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } (void)EVP_DigestInit(&ctx, evp_md); /* digest cookies */ (void)EVP_DigestUpdate(&ctx, i_cookie, IPIKE_ISAKMP_COOKIE_SIZE); (void)EVP_DigestUpdate(&ctx, r_cookie, IPIKE_ISAKMP_COOKIE_SIZE); /* digest address */ (void)EVP_DigestUpdate(&ctx, ipike_addr_get_ptr(addr), ipike_addr_get_len(addr)); /* digest port */ port = ipike_addr_get_port(addr); port = ip_ntohs(port); (void)EVP_DigestUpdate(&ctx, &port, 2); (void)EVP_DigestFinal(&ctx, nat_discovery->value, (unsigned int*)&nat_discovery->len); (void)ipike_print_memory(IPCOM_LOG_DEBUG2, "Hash: ", nat_discovery->value, nat_discovery->len); return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_nat_discovery_print_payload *=========================================================================== */ IP_GLOBAL void ipike_nat_discovery_print_payload(Ipike_nat_discovery_payload* nat_discovery) { char* hex = IP_NULL; hex = ipike_memory_to_string(ipike_nat_discovery_data(nat_discovery), ipike_nat_discovery_data_length(nat_discovery), 4); if (IP_NULL == hex) { IPCOM_LOG0(CRIT, "ipike_print_nat_discovery_payload: out of memory"); return; } IPCOM_LOG1(DEBUG2, " nat discovery: %s", hex); ipcom_free(hex); return; } /* *=========================================================================== * ipike_nat_original_address_create *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_original_address_create( union Ip_sockaddr_union* address, Ipike_nat_org_addr_payload** nat_org_addr) { *nat_org_addr = ipike_nat_original_address_new(address); if (IP_NULL == *nat_org_addr) { IP_PANIC(); return IPIKE_OUT_OF_MEMORY; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_nat_original_address_check_payload *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_original_address_check_payload( Ipike_nat_org_addr_payload* nat_org_addr) { if ( nat_org_addr->type == IPIKE_ID_IPV4_ADDR ) { if ( ipike_nat_original_address_data_length(nat_org_addr) != IPIKE_NAT_ORG_ADDR_MIN_LEN) { IPCOM_LOG2(ERR, "ipike_nat_original_address_check_payload() :: " " expected length %u, got %u", IPIKE_NAT_DISCOVERY_MIN_LEN, ipike_nat_original_address_data_length(nat_org_addr)); return IPIKE_ATTRIBUTE_INVALID_VALUE; } } else if ( nat_org_addr->type == IPIKE_ID_IPV6_ADDR ) { if ( ipike_nat_original_address_data_length(nat_org_addr) != IPIKE_NAT_ORG_ADDR_MAX_LEN) { IPCOM_LOG2(ERR, "ipike_nat_original_address_check_payload() :: " " expected length %u, got %u", IPIKE_NAT_ORG_ADDR_MAX_LEN, ipike_nat_original_address_data_length(nat_org_addr)); return IPIKE_ATTRIBUTE_INVALID_VALUE; } } else { IPCOM_LOG1(ERR, "ipike_nat_original_address_check_payload() :: " "invalid ID type %u", nat_org_addr->type); return IPIKE_ATTRIBUTE_INVALID_TYPE; } return IPIKE_SUCCESS; } /* *=========================================================================== * ipike_nat_original_address_extract *=========================================================================== */ IP_GLOBAL Ipike_error ipike_nat_original_address_extract( Ipike_nat_org_addr_payload* payload, union Ip_sockaddr_union* nat_org_addr) { return ipike_addr_set_from_data(ipike_nat_original_address_data(payload), ipike_nat_original_address_data_length(payload), nat_org_addr); } /* *=========================================================================== * ipike_nat_original_address_print_payload *=========================================================================== */ IP_GLOBAL void ipike_nat_original_address_print_payload( Ipike_nat_org_addr_payload* nat_org_addr) { char* hex = IP_NULL; hex = ipike_memory_to_string(ipike_nat_original_address_data(nat_org_addr), ipike_nat_original_address_data_length(nat_org_addr), 4); if (IP_NULL == hex) { IPCOM_LOG0(CRIT, "ipike_nat_original_address_print_payload()"\ " :: out of memory"); return; } IPCOM_LOG1(DEBUG2, " ID type: %s", ipike_attribute_id_type_to_string(nat_org_addr->type)); IPCOM_LOG1(DEBUG2, " NAT-OA: %s", hex); ipcom_free(hex); return; } /* **************************************************************************** * END OF FILE **************************************************************************** */