dexter has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-hnbgw/+/26792 )
Change subject: ranap_rab_ass: add decoder and rewrite functions for RAB-AssignmentRequest/Response ......................................................................
ranap_rab_ass: add decoder and rewrite functions for RAB-AssignmentRequest/Response
The RANAP RAB AssignmentRequest and AssignmentResponse contains the IP-Address and the IP-Port for the RTP voice stream. In the comming MGCP implementation we will have to extract and replace this information. Lets add functions that do that in a convinient way.
Change-Id: I58b542bf23ff5e1db2ccf6833fec91d9ba332837 Depends: osmo-iuh.git Id7293c1035cf6de34b7a416fa9265a5a32ed3216 Related: OS#5152 --- M configure.ac A include/osmocom/hnbgw/ranap_rab_ass.h M src/osmo-hnbgw/Makefile.am A src/osmo-hnbgw/ranap_rab_ass.c M tests/Makefile.am A tests/test_common.c A tests/test_common.h M tests/testsuite.at 8 files changed, 699 insertions(+), 5 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-hnbgw refs/changes/92/26792/1
diff --git a/configure.ac b/configure.ac index 3e02bd9..4dc8087 100644 --- a/configure.ac +++ b/configure.ac @@ -233,6 +233,7 @@ src/osmo-hnbgw/Makefile tests/Makefile tests/atlocal + tests/ranap_rab_ass/Makefile doc/Makefile doc/examples/Makefile doc/manuals/Makefile diff --git a/include/osmocom/hnbgw/ranap_rab_ass.h b/include/osmocom/hnbgw/ranap_rab_ass.h new file mode 100644 index 0000000..94caa43 --- /dev/null +++ b/include/osmocom/hnbgw/ranap_rab_ass.h @@ -0,0 +1,18 @@ +#pragma once + +int ranap_rab_ass_req_decode(RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies, const uint8_t *data, + unsigned int len); +int ranap_rab_ass_req_encode(uint8_t *data, unsigned int len, + RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies); + +int ranap_rab_ass_resp_decode(RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies, const uint8_t *data, + unsigned int len); +int ranap_rab_ass_resp_encode(uint8_t *data, unsigned int len, + RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies); + +int ranap_rab_ass_req_extract_inet_addr(char *addr, uint16_t *port, uint8_t *rab_id, const uint8_t *data, + unsigned int data_len); +int ranap_rab_ass_resp_extract_inet_addr(char *addr, uint16_t *port, const uint8_t *data, unsigned int data_len); + +int ranap_rab_ass_req_replace_inet_addr(uint8_t *data, unsigned int data_len, const char *addr, uint32_t port); +int ranap_rab_ass_resp_replace_inet_addr(uint8_t *data, unsigned int data_len, const char *addr, uint32_t port); diff --git a/src/osmo-hnbgw/Makefile.am b/src/osmo-hnbgw/Makefile.am index bda8633..0948170 100644 --- a/src/osmo-hnbgw/Makefile.am +++ b/src/osmo-hnbgw/Makefile.am @@ -37,6 +37,7 @@ hnbgw_vty.c \ context_map.c \ hnbgw_cn.c \ + ranap_rab_ass.c \ $(NULL)
osmo_hnbgw_LDADD = \ diff --git a/src/osmo-hnbgw/ranap_rab_ass.c b/src/osmo-hnbgw/ranap_rab_ass.c new file mode 100644 index 0000000..c8fd3fe --- /dev/null +++ b/src/osmo-hnbgw/ranap_rab_ass.c @@ -0,0 +1,557 @@ +/* (C) 2021 by sysmocom s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * Author: Philipp Maier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +/* Note: This files contains tools to decode and re-encode the RAB-AssignmentRequest. This set of tools is used by + * mgcp_fsm.c to extract and manipulate the transportLayerAddress. */ + +#include <errno.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/sockaddr_str.h> +#include <osmocom/hnbgw/hnbgw.h> +#include <osmocom/ranap/ranap_common.h> +#include <osmocom/ranap/iu_helpers.h> +#include <asn1c/asn1helpers.h> + +/* Decode a RAB-AssignmentRequest message down to request IEs. */ +int ranap_rab_ass_req_decode(RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies, const uint8_t *data, + unsigned int len) +{ + int rc; + asn_dec_rval_t dec_ret; + RANAP_RANAP_PDU_t _ranap_pdu; + RANAP_RANAP_PDU_t *ranap_pdu = &_ranap_pdu; + + /* NOTE: The caller must take care of freeing the raB_SetupOrModifyList that is contained in the result using: + * ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyList, decoded.raB_SetupOrModifyList); */ + + memset(ranap_pdu, 0, sizeof(*ranap_pdu)); + memset(rab_assignment_request_ies, 0, sizeof(*rab_assignment_request_ies)); + + /* Decode the RANAP PDU (outer shell) */ + dec_ret = aper_decode(NULL, &asn_DEF_RANAP_RANAP_PDU, (void **)&ranap_pdu, data, len, 0, 0); + if (dec_ret.code != RC_OK) { + RANAP_DEBUG("Error in RANAP ASN.1 decode\n"); + return -EINVAL; + } + + /* We expect this to be an initiating message */ + if (ranap_pdu->present != RANAP_RANAP_PDU_PR_initiatingMessage) { + RANAP_DEBUG("RAB Assignment request does not contain an initiating message\n"); + rc = -EINVAL; + goto error; + } + + /* We expect the procedure code to match, this should always be the case since the procedure code is already + * matched by the code pathes before and we won't even attempt to enter this code path with a procedure code + * other then RANAP_ProcedureCode_id_RAB_Assignment */ + if (ranap_pdu->choice.initiatingMessage.procedureCode != RANAP_ProcedureCode_id_RAB_Assignment) { + RANAP_DEBUG("Procedure code indicates that this message is not a RAB Assignment!\n"); + rc = -EINVAL; + goto error; + } + + /* Decode RAB assignment request IEs (inner shell) */ + rc = ranap_decode_rab_assignmentrequesties(rab_assignment_request_ies, + &ranap_pdu->choice.initiatingMessage.value); + if (rc != 0) { + RANAP_DEBUG("Unable to decode RAB Assignment Request IEs!\n"); + rc = -EINVAL; + goto error; + } + + rc = 0; +error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RANAP_PDU, ranap_pdu); + return rc; +} + +/* Encode a RAB-AssignmentRequest message from given request IEs. */ +int ranap_rab_ass_req_encode(uint8_t *data, unsigned int len, + RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies) +{ + int rc; + struct msgb *msg; + + RANAP_RAB_AssignmentRequest_t _rab_assignment_request; + RANAP_RAB_AssignmentRequest_t *rab_assignment_request = &_rab_assignment_request; + memset(rab_assignment_request, 0, sizeof(*rab_assignment_request)); + + rc = ranap_encode_rab_assignmentrequesties(rab_assignment_request, rab_assignment_request_ies); + if (rc < 0) + return -EINVAL; + + /* generate an Initiating Mesasage: out -> msg */ + msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_RAB_Assignment, + RANAP_Criticality_reject, + &asn_DEF_RANAP_RAB_AssignmentRequest, rab_assignment_request); + + /* 'msg' has been generated, we cann now release the input 'out' */ + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_AssignmentRequest, rab_assignment_request); + + if (!msg) + return -EINVAL; + if (msg->len > len) + return -EINVAL; + + memcpy(data, msg->data, msg->len); + rc = msg->len; + msgb_free(msg); + return rc; +} + +/* Decode a RAB-AssignmentResponse message down to request IEs. */ +int ranap_rab_ass_resp_decode(RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies, const uint8_t *data, + unsigned int len) +{ + int rc; + asn_dec_rval_t dec_ret; + RANAP_RANAP_PDU_t _ranap_pdu; + RANAP_RANAP_PDU_t *ranap_pdu = &_ranap_pdu; + + /* NOTE: The caller must take care of freeing the raB_SetupOrModifyList that is contained in the result using: + * ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedList, decoded.raB_SetupOrModifiedList); */ + + memset(ranap_pdu, 0, sizeof(*ranap_pdu)); + memset(rab_assignment_response_ies, 0, sizeof(*rab_assignment_response_ies)); + + /* Decode the RANAP PDU (outer shell) */ + dec_ret = aper_decode(NULL, &asn_DEF_RANAP_RANAP_PDU, (void **)&ranap_pdu, data, len, 0, 0); + if (dec_ret.code != RC_OK) { + RANAP_DEBUG("Error in RANAP ASN.1 decode\n"); + return -EINVAL; + } + + /* We expect this to be an outcome message */ + if (ranap_pdu->present != RANAP_RANAP_PDU_PR_outcome) { + RANAP_DEBUG("RAB AssignmentResponse does not contain an outcome message\n"); + rc = -EINVAL; + goto error; + } + + /* We expect the procedure code to match, this should always be the case since the procedure code is already + * matched by the code pathes before and we won't even attempt to enter this code path with a procedure code + * other then RANAP_ProcedureCode_id_RAB_Assignment */ + if (ranap_pdu->choice.initiatingMessage.procedureCode != RANAP_ProcedureCode_id_RAB_Assignment) { + RANAP_DEBUG("Procedure code indicates that this message is not a RAB Assignment!\n"); + rc = -EINVAL; + goto error; + } + + /* Decode RAB assignment response IEs (inner shell) */ + rc = ranap_decode_rab_assignmentresponseies(rab_assignment_response_ies, &ranap_pdu->choice.outcome.value); + if (rc != 0) { + RANAP_DEBUG("Unable to decode RAB AssignmentResponse IEs!\n"); + rc = -EINVAL; + goto error; + } + + rc = 0; +error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RANAP_PDU, ranap_pdu); + return rc; +} + +/* Encode a RAB-AssignmentResponse message from given request IEs. */ +int ranap_rab_ass_resp_encode(uint8_t *data, unsigned int len, + RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies) +{ + int rc; + struct msgb *msg; + + RANAP_RAB_AssignmentResponse_t _rab_assignment_response; + RANAP_RAB_AssignmentResponse_t *rab_assignment_response = &_rab_assignment_response; + memset(rab_assignment_response, 0, sizeof(*rab_assignment_response)); + + rc = ranap_encode_rab_assignmentresponseies(rab_assignment_response, rab_assignment_response_ies); + if (rc < 0) + return -EINVAL; + + /* generate an outcome mesasage: out -> msg */ + msg = ranap_generate_outcome(RANAP_ProcedureCode_id_RAB_Assignment, + RANAP_Criticality_reject, + &asn_DEF_RANAP_RAB_AssignmentResponse, rab_assignment_response); + + /* 'msg' has been generated, we cann now release the input 'out' */ + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_AssignmentResponse, rab_assignment_response); + + if (!msg) + return -EINVAL; + if (msg->len > len) + return -EINVAL; + + memcpy(data, msg->data, msg->len); + rc = msg->len; + msgb_free(msg); + return rc; +} + +/* Pick the first item from the RAB setup-or-modify list and return the first protocol-ie-field-pair. This is based on + * the assumption that a PS call will only assign a signle RAB. This could be different for video calls and IMS but + * those are in practice a corner case, so we go for this simplified assumption for now. */ +static RANAP_ProtocolIE_FieldPair_t *prot_ie_field_pair_from_ass_req_ies(const RANAP_RAB_AssignmentRequestIEs_t *ies) +{ + RANAP_ProtocolIE_ContainerPair_t *protocol_ie_container_pair; + RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair; + + /* Make sure we indeed deal with a setup-or-modify list */ + if (!(ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT)) { + RANAP_DEBUG + ("Decoding failed, the RANAP RAB AssignmentRequest did not contain a setup-or-modify list!\n"); + return NULL; + } + + protocol_ie_container_pair = ies->raB_SetupOrModifyList.list.array[0]; + protocol_ie_field_pair = protocol_ie_container_pair->list.array[0]; + + return protocol_ie_field_pair; +} + +/* See also comment above prot_ie_field_pair_from_ass_req_ies */ +static RANAP_IE_t *setup_or_modif_item_from_rab_ass_resp(const RANAP_RAB_AssignmentResponseIEs_t *ies) +{ + /* Make sure we indeed deal with a setup-or-modified list */ + if (!(ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT)) { + RANAP_DEBUG + ("Decoding failed, the RANAP RAB AssignmentResponse did not contain a setup-or-modified list!\n"); + return NULL; + } + + return ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[0]; +} + +/*! Extract IP address and port from the transport-layer-address that is contained inside RAB AssignmentRequest. + * \ptmap[out] addr buffer to store extracted RTP stream IP address (length at least INET6_ADDRSTRLEN). + * \ptmap[out] port pointer to store extracted RTP stream IP port number (host byte order). + * \ptmap[out] rab_id pointer to store RAB-ID (optional, can be NULL). + * \ptmap[in] data buffer with ASN.1 encoded message. + * \ptmap[in] data_len length of buffer with ASN.1 encoded message. + * \returns 0 on success; negative on error. */ +int ranap_rab_ass_req_extract_inet_addr(char *addr, uint16_t *port, uint8_t *rab_id, const uint8_t *data, + unsigned int data_len) +{ + RANAP_RAB_AssignmentRequestIEs_t _rab_assignment_request_ies; + RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies = &_rab_assignment_request_ies; + RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair; + RANAP_RAB_SetupOrModifyItemFirst_t _rab_setup_or_modify_item_first; + RANAP_RAB_SetupOrModifyItemFirst_t *rab_setup_or_modify_item_first = &_rab_setup_or_modify_item_first; + int rc; + + rc = ranap_rab_ass_req_decode(rab_assignment_request_ies, data, data_len); + if (rc < 0) + return -EINVAL; + + protocol_ie_field_pair = prot_ie_field_pair_from_ass_req_ies(rab_assignment_request_ies); + if (!protocol_ie_field_pair) { + rc = -EINVAL; + goto early_error; + } + + if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem) { + RANAP_DEBUG + ("Decoding failed, the protocol IE field-pair is not of type RANAP RAB setup-or-modify-item!\n"); + rc = -EINVAL; + goto early_error; + } + + rc = ranap_decode_rab_setupormodifyitemfirst(rab_setup_or_modify_item_first, + &protocol_ie_field_pair->firstValue); + if (rc < 0) { + rc = -EINVAL; + goto early_error; + } + + if (rab_id) { + /* The RAB-ID is defined as a bitstring with a size of 8 (1 byte), + * See also RANAP-IEs.asn, RAB-ID ::= BIT STRING (SIZE (8)) */ + *rab_id = rab_setup_or_modify_item_first->rAB_ID.buf[0]; + } + + rc = ranap_transp_layer_addr_decode(addr, INET6_ADDRSTRLEN, + &rab_setup_or_modify_item_first->transportLayerInformation-> + transportLayerAddress); + if (rc < 0) { + rc = -EINVAL; + goto error; + } + + rc = ranap_transp_assoc_decode(port, + &rab_setup_or_modify_item_first->transportLayerInformation-> + iuTransportAssociation); + if (rc < 0) { + rc = -EINVAL; + goto error; + } + + rc = 0; +error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, rab_setup_or_modify_item_first); +early_error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyList, + &rab_assignment_request_ies->raB_SetupOrModifyList); + return rc; +} + +/*! Extract IP address and port from the transport-layer-address that is contained inside RAB AssignmentResponse. + * \ptmap[out] addr buffer to store extracted RTP stream IP address (length at least INET6_ADDRSTRLEN). + * \ptmap[out] port pointer to store extracted RTP stream IP port number (host byte order). + * \ptmap[in] data buffer with ASN.1 encoded message. + * \ptmap[in] data_len length of buffer with ASN.1 encoded message. + * \returns 0 on success; negative on error. */ +int ranap_rab_ass_resp_extract_inet_addr(char *addr, uint16_t *port, const uint8_t *data, unsigned int data_len) +{ + RANAP_RAB_AssignmentResponseIEs_t _rab_assignment_response_ies; + RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies = &_rab_assignment_response_ies; + RANAP_IE_t *setup_or_modified_list_ie; + RANAP_RAB_SetupOrModifiedItemIEs_t _rab_setup_or_modified_items_ies; + RANAP_RAB_SetupOrModifiedItemIEs_t *rab_setup_or_modified_items_ies = &_rab_setup_or_modified_items_ies; + RANAP_RAB_SetupOrModifiedItem_t *rab_setup_or_modified_item; + int rc; + + rc = ranap_rab_ass_resp_decode(rab_assignment_response_ies, data, data_len); + if (rc < 0) + return -EINVAL; + + setup_or_modified_list_ie = setup_or_modif_item_from_rab_ass_resp(rab_assignment_response_ies); + if (!setup_or_modified_list_ie) { + rc = -EINVAL; + goto early_error; + } + + rc = ranap_decode_rab_setupormodifieditemies_fromlist(rab_setup_or_modified_items_ies, + &setup_or_modified_list_ie->value); + if (rc < 0) { + rc = -EINVAL; + goto early_error; + } + + rab_setup_or_modified_item = &rab_setup_or_modified_items_ies->raB_SetupOrModifiedItem; + + rc = ranap_transp_layer_addr_decode(addr, INET6_ADDRSTRLEN, rab_setup_or_modified_item->transportLayerAddress); + if (rc < 0) { + rc = -EINVAL; + goto error; + } + + rc = ranap_transp_assoc_decode(port, rab_setup_or_modified_item->iuTransportAssociation); + if (rc < 0) { + rc = -EINVAL; + goto error; + } + + rc = 0; +error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, rab_setup_or_modified_items_ies); +early_error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedList, + &rab_assignment_response_ies->raB_SetupOrModifiedList); + return rc; +} + +/*! Replace IP address and port in the transport-layer-address that is contained inside RAB AssignmentRequest. + * \ptmap[inout] data buffer with ASN.1 encoded message. + * \ptmap[in] data_len length of buffer with ASN.1 encoded message. + * \ptmap[in] addr buffer that contains the new RTP stream IP address. + * \ptmap[in] port new RTP stream IP port number (host byte order). + * \returns resulting message length on success; negative on error. */ +int ranap_rab_ass_req_replace_inet_addr(uint8_t *data, unsigned int data_len, const char *addr, uint32_t port) +{ + + /* FIXME: WHAT IF THE RESULTING LENGTH OF THE MESSAGE IS DIFFERENT!? */ + RANAP_RAB_AssignmentRequestIEs_t _rab_assignment_request_ies; + RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies = &_rab_assignment_request_ies; + RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair; + RANAP_RAB_SetupOrModifyItemFirst_t _rab_setup_or_modify_item_first; + RANAP_RAB_SetupOrModifyItemFirst_t *rab_setup_or_modify_item_first = &_rab_setup_or_modify_item_first; + struct osmo_sockaddr ip_addr; + struct osmo_sockaddr_str sockaddr_str; + RANAP_TransportLayerInformation_t *old_transport_layer_information = NULL; + RANAP_TransportLayerInformation_t *new_transport_layer_information = NULL; + int rc; + + rc = ranap_rab_ass_req_decode(rab_assignment_request_ies, data, data_len); + if (rc < 0) + return -EINVAL; + + protocol_ie_field_pair = prot_ie_field_pair_from_ass_req_ies(rab_assignment_request_ies); + if (!protocol_ie_field_pair) { + rc = -EINVAL; + goto early_error; + } + + if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem) { + RANAP_DEBUG("Rewriting transport layer information failed, unexpected IE field-pair type!\n"); + rc = -EINVAL; + goto early_error; + } + + /* Decode setup-or-modifiy item (first) */ + rc = ranap_decode_rab_setupormodifyitemfirst(rab_setup_or_modify_item_first, + &protocol_ie_field_pair->firstValue); + if (rc < 0) { + rc = -EINVAL; + goto early_error; + } + + /* Replace transport-layer-information */ + if (rab_setup_or_modify_item_first->transportLayerInformation->iuTransportAssociation.present == + RANAP_IuTransportAssociation_PR_bindingID) { + sockaddr_str = (struct osmo_sockaddr_str) { + .af = AF_INET, + .port = port, + }; + osmo_strlcpy(sockaddr_str.ip, addr, INET6_ADDRSTRLEN); + + rc = osmo_sockaddr_str_to_sockaddr(&sockaddr_str, &ip_addr.u.sas); + if (rc < 0) { + RANAP_DEBUG("Rewriting transport layer information failed, invalid address!\n"); + rc = -EINVAL; + goto error; + } + + old_transport_layer_information = rab_setup_or_modify_item_first->transportLayerInformation; + new_transport_layer_information = ranap_new_transp_info_rtp(&ip_addr, true); //<============= FIXME! + OSMO_ASSERT(new_transport_layer_information); + rab_setup_or_modify_item_first->transportLayerInformation = new_transport_layer_information; + } else { + RANAP_DEBUG("Rewriting transport layer information failed, no bindingID (port)!\n"); + rc = -EINVAL; + goto error; + } + + /* Reencode transport-layer-information */ + rc = ANY_fromType_aper(&protocol_ie_field_pair->firstValue, &asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, + rab_setup_or_modify_item_first); + if (rc < 0) { + RANAP_DEBUG("Rewriting transport layer information failed, could not reencode\n"); + rc = -EINVAL; + goto error; + } + + /* Do the final encoding */ + rc = ranap_rab_ass_req_encode(data, data_len, rab_assignment_request_ies); + if (rc < 0) + rc = -EINVAL; + +error: + /* Restore original state of the modified ASN.1 struct so that the asn1c free mechanisms can work properly */ + if (old_transport_layer_information) + rab_setup_or_modify_item_first->transportLayerInformation = old_transport_layer_information; + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, rab_setup_or_modify_item_first); + if (new_transport_layer_information) + ASN_STRUCT_FREE(asn_DEF_RANAP_TransportLayerInformation, new_transport_layer_information); +early_error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyList, + &rab_assignment_request_ies->raB_SetupOrModifyList); + return rc; +} + +/*! Replace IP address and port in the transport-layer-address that is contained inside RAB AssignmentResponse. + * \ptmap[inout] data buffer with ASN.1 encoded message. + * \ptmap[in] data_len length of buffer with ASN.1 encoded message. + * \ptmap[in] addr buffer that contains the new RTP stream IP address. + * \ptmap[in] port new RTP stream IP port number (host byte order). + * \returns resulting message length on success; negative on error. */ +int ranap_rab_ass_resp_replace_inet_addr(uint8_t *data, unsigned int data_len, const char *addr, uint32_t port) +{ + RANAP_RAB_AssignmentResponseIEs_t _rab_assignment_response_ies; + RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies = &_rab_assignment_response_ies; + RANAP_IE_t *setup_or_modified_list_ie; + RANAP_RAB_SetupOrModifiedItemIEs_t _rab_setup_or_modified_items_ies; + RANAP_RAB_SetupOrModifiedItemIEs_t *rab_setup_or_modified_items_ies = &_rab_setup_or_modified_items_ies; + RANAP_RAB_SetupOrModifiedItem_t *rab_setup_or_modified_item; + RANAP_TransportLayerInformation_t *temp_transport_layer_information = NULL; + RANAP_TransportLayerAddress_t *old_transport_layer_address = NULL; + RANAP_IuTransportAssociation_t *old_iu_transport_association = NULL; + struct osmo_sockaddr ip_addr; + struct osmo_sockaddr_str sockaddr_str; + int rc; + + rc = ranap_rab_ass_resp_decode(rab_assignment_response_ies, data, data_len); + if (rc < 0) + return -EINVAL; + + setup_or_modified_list_ie = setup_or_modif_item_from_rab_ass_resp(rab_assignment_response_ies); + if (!setup_or_modified_list_ie) { + rc = -EINVAL; + goto early_error; + } + + rc = ranap_decode_rab_setupormodifieditemies_fromlist(rab_setup_or_modified_items_ies, + &setup_or_modified_list_ie->value); + if (rc < 0) { + rc = -EINVAL; + goto early_error; + } + + rab_setup_or_modified_item = &rab_setup_or_modified_items_ies->raB_SetupOrModifiedItem; + + /* Generate a temporary transport layer information, from which we can use the transport layer address and + * the iu transport association to update the setup or modified item */ + sockaddr_str = (struct osmo_sockaddr_str) { + .af = AF_INET, + .port = port, + }; + osmo_strlcpy(sockaddr_str.ip, addr, INET6_ADDRSTRLEN); + rc = osmo_sockaddr_str_to_sockaddr(&sockaddr_str, &ip_addr.u.sas); + if (rc < 0) { + RANAP_DEBUG("Rewriting transport layer address failed, invalid address!\n"); + rc = -EINVAL; + goto error; + } + temp_transport_layer_information = ranap_new_transp_info_rtp(&ip_addr, true); //<============= FIXME! + OSMO_ASSERT(temp_transport_layer_information); + + /* Replace transport layer address and iu transport association */ + old_transport_layer_address = rab_setup_or_modified_item->transportLayerAddress; + old_iu_transport_association = rab_setup_or_modified_item->iuTransportAssociation; + rab_setup_or_modified_item->transportLayerAddress = &temp_transport_layer_information->transportLayerAddress; + rab_setup_or_modified_item->iuTransportAssociation = &temp_transport_layer_information->iuTransportAssociation; + + /* Reencode modified setup or modified list */ + rc = ANY_fromType_aper(&setup_or_modified_list_ie->value, &asn_DEF_RANAP_RAB_SetupOrModifiedItem, + rab_setup_or_modified_items_ies); + if (rc < 0) { + RANAP_DEBUG("Rewriting transport layer address failed, could not reencode\n"); + rc = -EINVAL; + goto error; + } + + /* Do the final encoding */ + rc = ranap_rab_ass_resp_encode(data, data_len, rab_assignment_response_ies); + if (rc < 0) + rc = -EINVAL; + +error: + /* Restore original state of the modified ASN.1 struct so that the asn1c free mechanisms can work properly */ + if (old_transport_layer_address) + rab_setup_or_modified_item->transportLayerAddress = old_transport_layer_address; + if (old_iu_transport_association) + rab_setup_or_modified_item->iuTransportAssociation = old_iu_transport_association; + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, rab_setup_or_modified_items_ies); + if (temp_transport_layer_information) + ASN_STRUCT_FREE(asn_DEF_RANAP_TransportLayerInformation, temp_transport_layer_information); +early_error: + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedList, + &rab_assignment_response_ies->raB_SetupOrModifiedList); + return rc; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 899e436..f9da51f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,5 @@ SUBDIRS = \ + ranap_rab_ass \ $(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable. diff --git a/tests/test_common.c b/tests/test_common.c new file mode 100644 index 0000000..b9527c1 --- /dev/null +++ b/tests/test_common.c @@ -0,0 +1,111 @@ +/* Common osmo-iuh test stub code */ + +/* (C) 2015 by Sysmocom s.f.m.c. GmbH + * Author: Daniel Willmann dwillmann@sysmocom.de + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/sctp.h> +#include <arpa/inet.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> + +#include <osmocom/ranap/ranap_common.h> + +#include <osmocom/hnbgw/hnbgw.h> + +static const struct log_info_cat log_cat[] = { + [DMAIN] = { + .name = "DMAIN", .loglevel = LOGL_INFO, .enabled = 1, + .color = "", + .description = "Main program", + }, + [DHNBAP] = { + .name = "DHNBAP", .loglevel = LOGL_DEBUG, .enabled = 1, + .color = "", + .description = "Home Node B Application Part", + }, + [DRANAP] = { + .name = "RANAP", .loglevel = LOGL_DEBUG, .enabled = 1, + .color = "", + .description = "RAN Application Part", + }, + [DRUA] = { + .name = "RUA", .loglevel = LOGL_DEBUG, .enabled = 1, + .color = "", + .description = "RANAP User Adaptation", + }, +}; + +static const struct log_info test_log_info = { + .cat = log_cat, + .num_cat = ARRAY_SIZE(log_cat), +}; + +static void *msgb_ctx; + +int test_common_init(void) +{ + int rc; + + msgb_ctx = msgb_talloc_ctx_init(NULL, 0); + talloc_asn1_ctx = talloc_named_const(NULL, 0, "asn1_context"); + + rc = osmo_init_logging(&test_log_info); + if (rc < 0) + exit(1); + + ranap_set_log_area(DRANAP); + + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 0); + log_set_print_category_hex(osmo_stderr_target, 0); + return rc; +} + +void test_common_cleanup(void) +{ + if (talloc_total_blocks(msgb_ctx) != 1 + || talloc_total_size(msgb_ctx) != 0) + talloc_report_full(msgb_ctx, stderr); + + OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); + OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0); + talloc_free(msgb_ctx); + + if (talloc_total_blocks(talloc_asn1_ctx) != 1 + || talloc_total_size(talloc_asn1_ctx) != 0) + talloc_report_full(talloc_asn1_ctx, stderr); + + OSMO_ASSERT(talloc_total_blocks(talloc_asn1_ctx) == 1); + OSMO_ASSERT(talloc_total_size(talloc_asn1_ctx) == 0); + talloc_free(talloc_asn1_ctx); +} diff --git a/tests/test_common.h b/tests/test_common.h new file mode 100644 index 0000000..836d999 --- /dev/null +++ b/tests/test_common.h @@ -0,0 +1,4 @@ +#pragma once + +int test_common_init(void); +void test_common_cleanup(void); diff --git a/tests/testsuite.at b/tests/testsuite.at index 65d1ca0..41d0449 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -1,8 +1,9 @@ AT_INIT AT_BANNER([Regression tests.])
-#AT_SETUP([foobar]) -#AT_KEYWORDS([foobar]) -#cat $abs_srcdir/foobar/foobar_test.ok > expout -#AT_CHECK([$abs_top_builddir/tests/foobar/foobar_test], [], [expout], [ignore]) -#AT_CLEANUP +AT_SETUP([ranap-rab-ass]) +AT_KEYWORDS([ranap-rab-ass]) +cat $abs_srcdir/ranap_rab_ass/ranap_rab_ass_test.ok > expout +cat $abs_srcdir/ranap_rab_ass/ranap_rab_ass_test.err > experr +AT_CHECK([$abs_top_builddir/tests/ranap_rab_ass/ranap_rab_ass_test], [0], [expout], [experr]) +AT_CLEANUP \ No newline at end of file