/* * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH * * This file is part of Open5GS. * * 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 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 . */ #include "ogs-gtp.h" int ogs_gtp1_gsn_addr_to_sockaddr(const ogs_gtp1_gsn_addr_t *gsnaddr, uint16_t gsnaddr_len, uint16_t port, ogs_sockaddr_t **list) { ogs_sockaddr_t *addr = NULL, *addr6 = NULL; ogs_assert(gsnaddr); ogs_assert(list); switch (gsnaddr_len) { case OGS_GTP_GSN_ADDRESS_IPV4_LEN: addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); if (!addr) { ogs_error("ogs_calloc() failed"); return OGS_ERROR; } addr->ogs_sa_family = AF_INET; addr->ogs_sin_port = port; addr->sin.sin_addr.s_addr = gsnaddr->addr; *list = addr; break; case OGS_GTP_GSN_ADDRESS_IPV6_LEN: addr6 = ogs_calloc(1, sizeof(ogs_sockaddr_t)); if (!addr6) { ogs_error("ogs_calloc() failed"); return OGS_ERROR; } addr6->ogs_sa_family = AF_INET6; addr6->ogs_sin_port = port; memcpy(addr6->sin6.sin6_addr.s6_addr, gsnaddr->addr6, OGS_IPV6_LEN); *list = addr6; break; default: ogs_error("No IPv4 or IPv6"); return OGS_ERROR; } return OGS_OK; } int ogs_gtp1_sockaddr_to_gsn_addr(const ogs_sockaddr_t *addr, const ogs_sockaddr_t *addr6, ogs_gtp1_gsn_addr_t *gsnaddr, int *len) { ogs_assert(gsnaddr); if (addr && addr6) { ogs_error("GSN Address: Both IPv4 and IPv6 not supported"); return OGS_ERROR; } else if (addr) { gsnaddr->addr = addr->sin.sin_addr.s_addr; *len = OGS_GTP_GSN_ADDRESS_IPV4_LEN; } else if (addr6) { memcpy(gsnaddr->addr6, addr6->sin6.sin6_addr.s6_addr, OGS_IPV6_LEN); *len = OGS_GTP_GSN_ADDRESS_IPV6_LEN; } else { ogs_error("No IPv4 or IPv6"); return OGS_ERROR; } return OGS_OK; } int ogs_gtp1_gsn_addr_to_ip(const ogs_gtp1_gsn_addr_t *gsnaddr, uint16_t gsnaddr_len, ogs_ip_t *ip) { ogs_assert(ip); ogs_assert(gsnaddr); memset(ip, 0, sizeof(ogs_ip_t)); if (gsnaddr_len == OGS_GTP_GSN_ADDRESS_IPV4_LEN) { ip->ipv4 = 1; ip->ipv6 = 0; ip->addr = gsnaddr->addr; } else if (gsnaddr_len == OGS_GTP_GSN_ADDRESS_IPV6_LEN) { ip->ipv4 = 0; ip->ipv6 = 1; memcpy(ip->addr6, gsnaddr->addr6, OGS_IPV6_LEN); } else { ogs_error("No IPv4 or IPv6"); return OGS_ERROR; } return OGS_OK; } int ogs_gtp1_pdu_session_type_to_eua_ietf_type(uint8_t session_type) { switch (session_type) { case OGS_PDU_SESSION_TYPE_IPV4: return OGS_PDP_EUA_IETF_IPV4; case OGS_PDU_SESSION_TYPE_IPV6: return OGS_PDP_EUA_IETF_IPV6; case OGS_PDU_SESSION_TYPE_IPV4V6: return OGS_PDP_EUA_IETF_IPV4V6; default: return OGS_ERROR; } } int ogs_gtp1_eua_ietf_type_to_pdu_session_type(uint8_t eua_ietf_type) { switch (eua_ietf_type) { case OGS_PDP_EUA_IETF_IPV4: return OGS_PDU_SESSION_TYPE_IPV4; case OGS_PDP_EUA_IETF_IPV6: return OGS_PDU_SESSION_TYPE_IPV6; case OGS_PDP_EUA_IETF_IPV4V6: return OGS_PDU_SESSION_TYPE_IPV4V6; default: return OGS_ERROR; } } int ogs_gtp1_eua_to_ip(const ogs_eua_t *eua, uint16_t eua_len, ogs_ip_t *ip, uint8_t *pdu_session_type) { ogs_assert(eua); ogs_assert(ip); memset(ip, 0, sizeof *ip); switch (eua->organization) { case OGS_PDP_EUA_ORG_IETF: break; case OGS_PDP_EUA_ORG_ETSI: default: ogs_error("Unsupported EUA organization %u", eua->organization); return OGS_ERROR; } eua_len -= 2; switch (eua->type) { case OGS_PDP_EUA_IETF_IPV4: if (eua_len == OGS_IPV4_LEN) { ip->addr = eua->addr; } else if (eua_len != 0) { ogs_error("Wrong IPv4 EUA length %u", eua_len); return OGS_ERROR; } ip->ipv4 = 1; ip->ipv6 = 0; *pdu_session_type = OGS_PDU_SESSION_TYPE_IPV4; break; case OGS_PDP_EUA_IETF_IPV6: if (eua_len == OGS_IPV6_LEN) { memcpy(ip->addr6, eua->addr6, OGS_IPV6_LEN); } else if (eua_len != 0) { ogs_error("Wrong IPv6 EUA length %u", eua_len); return OGS_ERROR; } ip->ipv4 = 0; ip->ipv6 = 1; *pdu_session_type = OGS_PDU_SESSION_TYPE_IPV6; break; case OGS_PDP_EUA_IETF_IPV4V6: if (eua_len == OGS_IPV4_LEN) { ip->addr = eua->addr; } else if (eua_len == OGS_IPV6_LEN) { memcpy(ip->addr6, eua->addr6, OGS_IPV6_LEN); } else if (eua_len == OGS_IPV4_LEN + OGS_IPV6_LEN) { ip->addr = eua->both.addr; memcpy(ip->addr6, eua->both.addr6, OGS_IPV6_LEN); } else if (eua_len != 0) { ogs_error("Wrong IPv4v6 EUA length %u", eua_len); return OGS_ERROR; } ip->ipv4 = 1; ip->ipv6 = 1; *pdu_session_type = OGS_PDU_SESSION_TYPE_IPV4V6; break; default: ogs_error("No IPv4 or IPv6"); return OGS_ERROR; } return OGS_OK; } int ogs_gtp1_ip_to_eua(uint8_t pdu_session_type, const ogs_ip_t *ip, ogs_eua_t *eua, uint8_t *eua_len) { ogs_assert(eua); ogs_assert(ip); ogs_assert(eua_len); memset(eua, 0, sizeof *eua); eua->spare = 0xf; /* TS 29.060 Figure 35 */ eua->organization = OGS_PDP_EUA_ORG_IETF; switch (pdu_session_type) { case OGS_PDU_SESSION_TYPE_IPV4: if (!ip->ipv4) { ogs_error("EUA type IPv4 but no IPv4 address available"); return OGS_ERROR; } eua->addr = ip->addr; *eua_len = 2 + OGS_IPV4_LEN; eua->type = OGS_PDP_EUA_IETF_IPV4; break; case OGS_PDU_SESSION_TYPE_IPV6: if (!ip->ipv6) { ogs_error("EUA type IPv4 but no IPv6 address available"); return OGS_ERROR; } memcpy(eua->addr6, ip->addr6, OGS_IPV6_LEN); *eua_len = 2 + OGS_IPV6_LEN; eua->type = OGS_PDP_EUA_IETF_IPV6; break; case OGS_PDU_SESSION_TYPE_IPV4V6: if (ip->ipv4 && ip->ipv6) { eua->both.addr = ip->addr; memcpy(eua->both.addr6, ip->addr6, OGS_IPV6_LEN); *eua_len = 2 + OGS_IPV4_LEN + OGS_IPV6_LEN; } else if (ip->ipv4) { eua->addr = ip->addr; *eua_len = 2 + OGS_IPV4_LEN; } else if (ip->ipv6) { memcpy(eua->addr6, ip->addr6, OGS_IPV6_LEN); *eua_len = 2 + OGS_IPV6_LEN; } else { ogs_error("EUA type IPv4 but no IPv4 nor IPv6 address available"); return OGS_ERROR; } eua->type = OGS_PDP_EUA_IETF_IPV4V6; break; default: ogs_error("Unexpected session type"); return OGS_ERROR; } return OGS_OK; } /* 3GPP TS 23.401 Table E.3 */ int ogs_gtp1_qos_profile_to_qci(const ogs_gtp1_qos_profile_decoded_t *decoded, uint8_t *qci) { ogs_assert(decoded); ogs_assert(qci); if (!decoded->data_octet6_to_13_present) { /* traffic class not present, take QCI 9 as default */ *qci = 9; return OGS_OK; } switch (decoded->qos_profile.data.traffic_class) { case OGS_GTP1_QOS_TRAFFIC_CLASS_CONVERSATIONAL: if (decoded->qos_profile.data.source_statistics_descriptor == OGS_GTP1_QOS_SRC_STATS_DESC_SPEECH) *qci = 1; else if (decoded->dec_transfer_delay >= 150) *qci = 2; else *qci = 3; break; case OGS_GTP1_QOS_TRAFFIC_CLASS_STREAMING: *qci = 4; break; case OGS_GTP1_QOS_TRAFFIC_CLASS_INTERACTIVE: switch (decoded->qos_profile.data.traffic_handling_priority) { case 1: *qci = decoded->qos_profile.data.signalling_indication ? 5 : 6; break; case 2: *qci = 7; break; case 3: *qci = 8; break; default: *qci = 9; } break; case OGS_GTP1_QOS_TRAFFIC_CLASS_SUBSCRIBED: case OGS_GTP1_QOS_TRAFFIC_CLASS_BACKGROUND: default: *qci = 9; } return OGS_OK; }