diff options
Diffstat (limited to 'src/iu_helpers.c')
-rw-r--r-- | src/iu_helpers.c | 164 |
1 files changed, 163 insertions, 1 deletions
diff --git a/src/iu_helpers.c b/src/iu_helpers.c index bf82fde..5c7ff31 100644 --- a/src/iu_helpers.c +++ b/src/iu_helpers.c @@ -24,8 +24,12 @@ #include <arpa/inet.h> #include "asn1helpers.h" #include <osmocom/core/utils.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/bit16gen.h> + #include <osmocom/ranap/RANAP_IuTransportAssociation.h> #include <osmocom/ranap/RANAP_TransportLayerAddress.h> +#include <osmocom/ranap/RANAP_TransportLayerInformation.h> /* decode a BCD-string as used inside ASN.1 encoded Iu interface protocols */ int ranap_bcd_decode(char *out, size_t out_len, const uint8_t *in, size_t in_len) @@ -109,7 +113,7 @@ int ranap_transp_layer_addr_decode(char *addr, unsigned int addr_len, buf = trasp_layer_addr->buf; len = trasp_layer_addr->size; - if (buf[0] == 0x35 && len >= 7) + if (len >= 7 && buf[0] == 0x35) rc = inet_ntop(AF_INET, buf + 3, addr, addr_len); else if (len > 3) rc = inet_ntop(AF_INET, buf, addr, addr_len); @@ -121,3 +125,161 @@ int ranap_transp_layer_addr_decode(char *addr, unsigned int addr_len, return 0; } + +/* (same as ranap_transp_layer_addr_decode, but AF agnostic) */ +int ranap_transp_layer_addr_decode2(struct osmo_sockaddr *addr, bool *uses_x213_nsap, + const RANAP_TransportLayerAddress_t *trasp_layer_addr) +{ + unsigned char *buf; + int len; + bool x213_nsap = false; + uint16_t icp; + + buf = trasp_layer_addr->buf; + len = trasp_layer_addr->size; + + memset(addr, 0, sizeof(*addr)); + + if ((len == 7 || len == 20) && buf[0] == 0x35) { + /* ITU-T Rec. X.213 A.5.2.1.2.7, RFC 1888 section 6 + * For an X.213 NSAP encoded address we expect: + * 3 bytes IDP (first byte AFI = 0x35, which means that two byte IDI and an IP address follows) + * Either 4 or 17 bytes of DSP containing the IP address. + * (see also comments in function ranap_new_transp_layer_addr below) */ + x213_nsap = true; + icp = osmo_load16be(&buf[1]); + switch (icp) { + case 0x0000: + /* "RFC 1888 provides guidance on how to embed an IPv6 address within the DSP of an NSAP + * address. The IPv6 address is carried in the first 16 octets of the DSP. + * Octet 17 of the DSP is set to zero, but has no significance for IPv6." */ + if (len != 20) + return -EINVAL; + addr->u.sa.sa_family = AF_INET6; + memcpy(addr->u.sin6.sin6_addr.s6_addr, buf + 3, sizeof(addr->u.sin6.sin6_addr.s6_addr)); + break; + case 0x0001: + addr->u.sa.sa_family = AF_INET; + memcpy((uint8_t *) &addr->u.sin.sin_addr.s_addr, buf + 3, sizeof(addr->u.sin.sin_addr.s_addr)); + break; + default: + return -EINVAL; + } + } else if (len == 4) { + /* A non X.213 NSAP encoded IPv4 address is 4 bytes long */ + addr->u.sa.sa_family = AF_INET; + memcpy((uint8_t *) &addr->u.sin.sin_addr.s_addr, buf, sizeof(addr->u.sin.sin_addr.s_addr)); + } else if (len == 16) { + /* A non X.213 NSAP encoded IPv6 address is 16 bytes long */ + addr->u.sa.sa_family = AF_INET6; + memcpy(addr->u.sin6.sin6_addr.s6_addr, buf, sizeof(addr->u.sin6.sin6_addr.s6_addr)); + } else + return -EINVAL; + + /* In case the caller is interested in the encoding method that was used */ + if (uses_x213_nsap) + *uses_x213_nsap = x213_nsap; + + return 0; +} + +int ranap_new_transp_layer_addr(BIT_STRING_t *out, struct osmo_sockaddr *addr, bool use_x213_nsap) +{ + uint8_t *buf; + unsigned int len; + size_t ip_len; + uint8_t *ip_addr; + uint16_t icp; + + switch (addr->u.sa.sa_family) { + case AF_INET: + ip_len = sizeof(addr->u.sin.sin_addr.s_addr); + ip_addr = (uint8_t *) &addr->u.sin.sin_addr.s_addr; + icp = 0x0001; /* See X.213, section A.5.2.1.2.7 */ + break; + case AF_INET6: + ip_len = sizeof(addr->u.sin6.sin6_addr.s6_addr); + ip_addr = addr->u.sin6.sin6_addr.s6_addr; + icp = 0x0000; /* See X.213, section A.5.2.1.2.7 */ + break; + default: + return -EINVAL; + } + + if (use_x213_nsap) { + /* 3 bytes IDP (AFI+ICP) + 17 bytes DSP */ + len = 3 + 17; + buf = CALLOC(len, sizeof(uint8_t)); + + /* 1 byte AFI to announce IANA ICP, see also X.213, table A.4 */ + buf[0] = 0x35; + + /* 2 byte IANA ICP IDI, see also X.213, A.5.2.1.2.7 */ + osmo_store16be(icp, &buf[1]); + + /* 17 byte DSP, see also X.213, table A.5 and A.5.2.1.2.7 */ + memcpy(&buf[3], ip_addr, ip_len); + } else { + len = ip_len; + buf = CALLOC(len, sizeof(uint8_t)); + memcpy(buf, ip_addr, ip_len); + } + if (out->buf) + FREEMEM(out->buf); + out->buf = buf; + out->size = len; + out->bits_unused = 0; + + return 0; +} + +RANAP_TransportLayerInformation_t *ranap_new_transp_info_rtp(struct osmo_sockaddr *addr, bool use_x213_nsap) +{ + RANAP_TransportLayerInformation_t *tli; + uint8_t binding_id[4] = { 0 }; + int rc; + + switch (addr->u.sin.sin_family) { + case AF_INET: + osmo_store16be(ntohs(addr->u.sin.sin_port), binding_id); + break; + case AF_INET6: + osmo_store16be(ntohs(addr->u.sin6.sin6_port), binding_id); + break; + default: + return NULL; + } + + tli = CALLOC(1, sizeof(*tli)); + rc = ranap_new_transp_layer_addr(&tli->transportLayerAddress, addr, use_x213_nsap); + if (rc < 0) { + ASN_STRUCT_FREE(asn_DEF_RANAP_TransportLayerInformation, tli); + return NULL; + } + + tli->iuTransportAssociation.present = RANAP_IuTransportAssociation_PR_bindingID; + OCTET_STRING_fromBuf(&tli->iuTransportAssociation.choice.bindingID, + (const char *)binding_id, sizeof(binding_id)); + + return tli; +} + +RANAP_TransportLayerInformation_t *ranap_new_transp_info_gtp(struct osmo_sockaddr *addr, uint32_t tei, + bool use_x213_nsap) +{ + RANAP_TransportLayerInformation_t *tli = CALLOC(1, sizeof(*tli)); + uint32_t binding_buf = htonl(tei); + int rc; + + rc = ranap_new_transp_layer_addr(&tli->transportLayerAddress, addr, use_x213_nsap); + if (rc < 0) { + ASN_STRUCT_FREE(asn_DEF_RANAP_TransportLayerInformation, tli); + return NULL; + } + + tli->iuTransportAssociation.present = RANAP_IuTransportAssociation_PR_gTP_TEI; + OCTET_STRING_fromBuf(&tli->iuTransportAssociation.choice.gTP_TEI, + (const char *)&binding_buf, sizeof(binding_buf)); + + return tli; +} |