1057 lines
36 KiB
C
1057 lines
36 KiB
C
/* Decoded PFCP IEs, to be used by the auto-generated pfcp_ies_auto.c. */
|
|
/*
|
|
* (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
|
* All Rights Reserved.
|
|
*
|
|
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*
|
|
* 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/>.
|
|
*
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <osmocom/core/socket.h>
|
|
#include <osmocom/core/logging.h>
|
|
#include <osmocom/core/utils.h>
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/gtlv/gtlv.h>
|
|
|
|
#include <osmocom/pfcp/pfcp_ies_custom.h>
|
|
#include <osmocom/pfcp/pfcp_strs.h>
|
|
#include <osmocom/pfcp/pfcp_msg.h>
|
|
|
|
/* Assumes presence of local variable osmo_pfcp_msg *m. m->log_ctx may be NULL. */
|
|
#define RETURN_ERROR(RC, FMT, ARGS...) \
|
|
do {\
|
|
OSMO_ASSERT(decoded_struct); \
|
|
OSMO_LOG_PFCP_MSG(OSMO_PFCP_MSG_FOR_IES(decoded_struct), LOGL_ERROR, FMT " (%d: %s)\n", ##ARGS, RC, \
|
|
strerror((RC) > 0 ? (RC) : -(RC))); \
|
|
return RC; \
|
|
} while (0)
|
|
|
|
/* Assumes presence of local variable osmo_gtlv_load *tlv. Usage:
|
|
* ENSURE_LENGTH_IS_EXACTLY(2);
|
|
*/
|
|
#define ENSURE_LENGTH_IS_EXACTLY(VAL) \
|
|
do { \
|
|
if (!(tlv->len == VAL)) \
|
|
RETURN_ERROR(-EINVAL, "IE has length = %zu, expected length == " #VAL, tlv->len); \
|
|
} while (0)
|
|
|
|
/* Assumes presence of local variable osmo_gtlv_load *tlv. Usage:
|
|
* ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
*/
|
|
#define ENSURE_LENGTH_IS_AT_LEAST(VAL) \
|
|
do { \
|
|
if (!(tlv->len >= VAL)) \
|
|
RETURN_ERROR(-EINVAL, "IE has length = %zu, expected length >= " #VAL, tlv->len); \
|
|
} while (0)
|
|
|
|
/* Assumes presence of local variable osmo_gtlv_load *tlv. Usage:
|
|
* const uint8_t *pos = tlv->val;
|
|
* ENSURE_REMAINING_LENGTH_IS_AT_LEAST("first part", pos, 23);
|
|
* <parse first part>
|
|
* pos += 23;
|
|
* ENSURE_REMAINING_LENGTH_IS_AT_LEAST("very long part", pos, 235);
|
|
* <parse very long part>
|
|
* pos += 235;
|
|
*/
|
|
#define ENSURE_REMAINING_LENGTH_IS_AT_LEAST(NAME, POS, MIN_VAL) \
|
|
do { \
|
|
if (!((tlv->len - ((POS) - tlv->val)) >= MIN_VAL)) \
|
|
RETURN_ERROR(-EINVAL, \
|
|
"at value octet %d: %zu octets remaining, but " #NAME " requires length >= " #MIN_VAL, \
|
|
(int)((POS) - tlv->val), \
|
|
tlv->len - ((POS) - tlv->val)); \
|
|
} while (0)
|
|
|
|
void osmo_pfcp_ie_f_seid_set(struct osmo_pfcp_ie_f_seid *f_seid, uint64_t seid, const struct osmo_sockaddr *remote_addr)
|
|
{
|
|
*f_seid = (struct osmo_pfcp_ie_f_seid) {
|
|
.seid = seid,
|
|
};
|
|
osmo_pfcp_ip_addrs_set(&f_seid->ip_addr, remote_addr);
|
|
}
|
|
|
|
int osmo_pfcp_ie_f_seid_cmp(const struct osmo_pfcp_ie_f_seid *a, const struct osmo_pfcp_ie_f_seid *b)
|
|
{
|
|
int rc;
|
|
if (a == b)
|
|
return 0;
|
|
if (!a)
|
|
return -1;
|
|
if (!b)
|
|
return 1;
|
|
/* It suffices if one of the IP addresses match */
|
|
if (a->ip_addr.v4_present && b->ip_addr.v4_present)
|
|
rc = osmo_sockaddr_cmp(&a->ip_addr.v4, &b->ip_addr.v4);
|
|
else if (a->ip_addr.v6_present && b->ip_addr.v6_present)
|
|
rc = osmo_sockaddr_cmp(&a->ip_addr.v6, &b->ip_addr.v6);
|
|
else
|
|
rc = (a->ip_addr.v4_present ? -1 : 1);
|
|
if (rc)
|
|
return rc;
|
|
if (a->seid < b->seid)
|
|
return -1;
|
|
if (a->seid > b->seid)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_dec_cause(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
enum osmo_pfcp_cause *cause = decode_to;
|
|
ENSURE_LENGTH_IS_EXACTLY(1);
|
|
*cause = *tlv->val;
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_cause(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_cause *cause = encode_from;
|
|
msgb_put_u8(tlv->dst, *cause);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_cause(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_cause *cause = encode_from;
|
|
return snprintf(buf, buflen, "%s", osmo_pfcp_cause_str(*cause));
|
|
}
|
|
|
|
int osmo_pfcp_dec_offending_ie(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
enum osmo_pfcp_iei *offending_ie = decode_to;
|
|
ENSURE_LENGTH_IS_EXACTLY(2);
|
|
*offending_ie = osmo_load16be(tlv->val);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_offending_ie(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_iei *offending_ie = encode_from;
|
|
msgb_put_u16(tlv->dst, *offending_ie);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_offending_ie(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_iei *offending_ie = encode_from;
|
|
return snprintf(buf, buflen, "%s", osmo_pfcp_iei_str(*offending_ie));
|
|
}
|
|
|
|
int osmo_pfcp_dec_8(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
uint8_t *u8 = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
*u8 = tlv->val[0];
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_8(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const uint8_t *u8 = encode_from;
|
|
msgb_put_u8(tlv->dst, *u8);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_8(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const uint8_t *u8 = encode_from;
|
|
return snprintf(buf, buflen, "%u", *u8);
|
|
}
|
|
|
|
int osmo_pfcp_dec_16be(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
uint16_t *u16 = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(2);
|
|
*u16 = osmo_load16be(tlv->val);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_16be(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const uint16_t *u16 = encode_from;
|
|
msgb_put_u16(tlv->dst, *u16);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_16be(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const uint16_t *u16 = encode_from;
|
|
return snprintf(buf, buflen, "%u", *u16);
|
|
}
|
|
|
|
int osmo_pfcp_dec_32be(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
uint32_t *u32 = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(4);
|
|
*u32 = osmo_load32be(tlv->val);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_32be(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const uint32_t *u32 = encode_from;
|
|
msgb_put_u32(tlv->dst, *u32);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_32be(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const uint32_t *u32 = encode_from;
|
|
return snprintf(buf, buflen, "%u", *u32);
|
|
}
|
|
|
|
int osmo_pfcp_dec_3gpp_iface_type(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
enum osmo_pfcp_3gpp_iface_type *_3gpp_iface_type = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
*_3gpp_iface_type = tlv->val[0] & 0x3f;
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_3gpp_iface_type(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_3gpp_iface_type *_3gpp_iface_type = encode_from;
|
|
msgb_put_u8(tlv->dst, (uint8_t)(*_3gpp_iface_type) & 0x3f);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_3gpp_iface_type(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_3gpp_iface_type *_3gpp_iface_type = encode_from;
|
|
return snprintf(buf, buflen, "%s", osmo_pfcp_3gpp_iface_type_str(*_3gpp_iface_type));
|
|
}
|
|
|
|
int osmo_pfcp_dec_source_iface(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
enum osmo_pfcp_source_iface *source_iface = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
*source_iface = tlv->val[0] & 0xf;
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_source_iface(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_source_iface *source_iface = encode_from;
|
|
msgb_put_u8(tlv->dst, (uint8_t)(*source_iface) & 0xf);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_source_iface(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_source_iface *source_iface = encode_from;
|
|
return snprintf(buf, buflen, "%s", osmo_pfcp_source_iface_str(*source_iface));
|
|
}
|
|
|
|
int osmo_pfcp_dec_dest_iface(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
enum osmo_pfcp_dest_iface *dest_interface = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
*dest_interface = tlv->val[0] & 0xf;
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_dest_iface(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_dest_iface *dest_interface = encode_from;
|
|
msgb_put_u8(tlv->dst, (uint8_t)(*dest_interface) & 0xf);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_dest_iface(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const enum osmo_pfcp_dest_iface *dest_iface = encode_from;
|
|
return snprintf(buf, buflen, "%s", osmo_pfcp_dest_iface_str(*dest_iface));
|
|
}
|
|
|
|
int osmo_pfcp_dec_node_id(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_node_id *node_id = decode_to;
|
|
const void *ip;
|
|
unsigned int ip_len;
|
|
unsigned int want_len;
|
|
ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
node_id->type = *(uint8_t *)tlv->val;
|
|
ip = &tlv->val[1];
|
|
ip_len = tlv->len - 1;
|
|
|
|
switch (node_id->type) {
|
|
case OSMO_PFCP_NODE_ID_T_IPV4:
|
|
want_len = sizeof(node_id->ip.u.sin.sin_addr);
|
|
if (ip_len != want_len)
|
|
RETURN_ERROR(-EINVAL, "Node ID: wrong IPv4 address value length %u, expected %u",
|
|
ip_len, want_len);
|
|
osmo_sockaddr_from_octets(&node_id->ip, ip, ip_len);
|
|
break;
|
|
case OSMO_PFCP_NODE_ID_T_IPV6:
|
|
want_len = sizeof(node_id->ip.u.sin6.sin6_addr);
|
|
if (ip_len != want_len)
|
|
RETURN_ERROR(-EINVAL, "Node ID: wrong IPv6 address value length %u, expected %u",
|
|
ip_len, want_len);
|
|
osmo_sockaddr_from_octets(&node_id->ip, ip, ip_len);
|
|
break;
|
|
case OSMO_PFCP_NODE_ID_T_FQDN:
|
|
/* Copy and add a trailing nul */
|
|
OSMO_STRLCPY_ARRAY(node_id->fqdn, ip);
|
|
break;
|
|
default:
|
|
RETURN_ERROR(-EINVAL, "Invalid Node ID Type: %d", node_id->type);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_node_id(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
unsigned int l;
|
|
const struct osmo_pfcp_ie_node_id *node_id = encode_from;
|
|
msgb_put_u8(tlv->dst, node_id->type);
|
|
switch (node_id->type) {
|
|
case OSMO_PFCP_NODE_ID_T_IPV4:
|
|
l = sizeof(node_id->ip.u.sin.sin_addr);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &node_id->ip);
|
|
break;
|
|
case OSMO_PFCP_NODE_ID_T_IPV6:
|
|
l = sizeof(node_id->ip.u.sin6.sin6_addr);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &node_id->ip);
|
|
break;
|
|
case OSMO_PFCP_NODE_ID_T_FQDN:
|
|
l = strnlen(node_id->fqdn, sizeof(node_id->fqdn));
|
|
/* Copy without trailing nul */
|
|
memcpy((char *)msgb_put(tlv->dst, l), node_id->fqdn, l);
|
|
break;
|
|
default:
|
|
RETURN_ERROR(-EINVAL, "Invalid Node ID Type: %d", node_id->type);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_ie_node_id_to_str_buf(char *buf, size_t buflen, const struct osmo_pfcp_ie_node_id *node_id)
|
|
{
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
|
|
switch (node_id->type) {
|
|
case OSMO_PFCP_NODE_ID_T_IPV4:
|
|
OSMO_STRBUF_PRINTF(sb, "v4:");
|
|
break;
|
|
case OSMO_PFCP_NODE_ID_T_IPV6:
|
|
OSMO_STRBUF_PRINTF(sb, "v6:");
|
|
break;
|
|
case OSMO_PFCP_NODE_ID_T_FQDN:
|
|
OSMO_STRBUF_PRINTF(sb, "fqdn:");
|
|
OSMO_STRBUF_APPEND(sb, osmo_quote_str_buf3,
|
|
node_id->fqdn, strnlen(node_id->fqdn, sizeof(node_id->fqdn)));
|
|
return sb.chars_needed;
|
|
default:
|
|
OSMO_STRBUF_PRINTF(sb, "unknown-node-id-type-%u", node_id->type);
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &node_id->ip);
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
char *osmo_pfcp_ie_node_id_to_str_c(void *ctx, const struct osmo_pfcp_ie_node_id *node_id)
|
|
{
|
|
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_pfcp_ie_node_id_to_str_buf, node_id)
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_node_id(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
return osmo_pfcp_ie_node_id_to_str_buf(buf, buflen, encode_from);
|
|
}
|
|
|
|
bool osmo_pfcp_bits_get(const uint8_t *bits, unsigned int bitpos)
|
|
{
|
|
unsigned int bytenum = bitpos / 8;
|
|
unsigned int bitmask = 1 << (bitpos % 8);
|
|
|
|
return (bool)(bits[bytenum] & bitmask);
|
|
}
|
|
|
|
void osmo_pfcp_bits_set(uint8_t *bits, unsigned int bitpos, bool val)
|
|
{
|
|
unsigned int bytenum = bitpos / 8;
|
|
unsigned int bitmask = 1 << (bitpos % 8);
|
|
|
|
if (val)
|
|
bits[bytenum] |= bitmask;
|
|
else
|
|
bits[bytenum] &= ~bitmask;
|
|
}
|
|
|
|
int osmo_pfcp_bits_to_str_buf(char *buf, size_t buflen, const uint8_t *bits, const struct value_string *bit_strs)
|
|
{
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
OSMO_STRBUF_PRINTF(sb, "(");
|
|
for (; bit_strs->str; bit_strs++) {
|
|
if (osmo_pfcp_bits_get(bits, bit_strs->value)) {
|
|
OSMO_STRBUF_PRINTF(sb, " %s", bit_strs->str);
|
|
}
|
|
}
|
|
OSMO_STRBUF_PRINTF(sb, " )");
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
char *osmo_pfcp_bits_to_str_c(void *ctx, const uint8_t *bits, const struct value_string *bit_str)
|
|
{
|
|
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_pfcp_bits_to_str_buf, bits, bit_str)
|
|
}
|
|
|
|
int osmo_pfcp_dec_up_function_features(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_up_function_features *up_function_features = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(6);
|
|
memcpy(up_function_features->bits, tlv->val, 6);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_up_function_features(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_up_function_features *up_function_features = encode_from;
|
|
memcpy(msgb_put(tlv->dst, 6), up_function_features->bits, 6);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_up_function_features(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_up_function_features *up_function_features = encode_from;
|
|
return osmo_pfcp_bits_to_str_buf(buf, buflen, up_function_features->bits, osmo_pfcp_up_feature_strs);
|
|
}
|
|
|
|
int osmo_pfcp_dec_cp_function_features(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_cp_function_features *cp_function_features = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(sizeof(cp_function_features->bits));
|
|
memcpy(cp_function_features->bits, tlv->val, sizeof(cp_function_features->bits));
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_cp_function_features(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_cp_function_features *cp_function_features = encode_from;
|
|
memcpy(msgb_put(tlv->dst, sizeof(cp_function_features->bits)),
|
|
cp_function_features->bits, sizeof(cp_function_features->bits));
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_cp_function_features(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_cp_function_features *cp_function_features = encode_from;
|
|
return osmo_pfcp_bits_to_str_buf(buf, buflen, cp_function_features->bits, osmo_pfcp_cp_feature_strs);
|
|
}
|
|
|
|
int osmo_pfcp_dec_f_seid(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_f_seid *f_seid = decode_to;
|
|
uint8_t flags;
|
|
uint8_t pos;
|
|
unsigned int l;
|
|
/* flags and 8 octet seid */
|
|
ENSURE_LENGTH_IS_AT_LEAST(9);
|
|
flags = tlv->val[0];
|
|
f_seid->ip_addr.v6_present = flags & 1;
|
|
f_seid->ip_addr.v4_present = flags & 2;
|
|
f_seid->seid = osmo_load64be(&tlv->val[1]);
|
|
pos = 9;
|
|
if (f_seid->ip_addr.v4_present) {
|
|
l = sizeof(f_seid->ip_addr.v4.u.sin.sin_addr);
|
|
if (pos + l > tlv->len)
|
|
RETURN_ERROR(-EINVAL, "F-SEID IE is too short for the IPv4 address: %zu", tlv->len);
|
|
osmo_sockaddr_from_octets(&f_seid->ip_addr.v4, &tlv->val[pos], l);
|
|
pos += l;
|
|
}
|
|
if (f_seid->ip_addr.v6_present) {
|
|
l = sizeof(f_seid->ip_addr.v4.u.sin6.sin6_addr);
|
|
if (pos + l > tlv->len)
|
|
RETURN_ERROR(-EINVAL, "F-SEID IE is too short for the IPv6 address: %zu", tlv->len);
|
|
osmo_sockaddr_from_octets(&f_seid->ip_addr.v6, &tlv->val[pos], l);
|
|
pos += l;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_f_seid(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_f_seid *f_seid = encode_from;
|
|
unsigned int l;
|
|
uint8_t flags = (f_seid->ip_addr.v6_present ? 1 : 0) + (f_seid->ip_addr.v4_present ? 2 : 0);
|
|
/* flags and 8 octet seid */
|
|
msgb_put_u8(tlv->dst, flags);
|
|
osmo_store64be(f_seid->seid, msgb_put(tlv->dst, 8));
|
|
|
|
if (f_seid->ip_addr.v4_present) {
|
|
if (f_seid->ip_addr.v4.u.sin.sin_family != AF_INET)
|
|
RETURN_ERROR(-EINVAL,
|
|
"f_seid IE indicates IPv4 address, but there is no ipv4_addr");
|
|
l = sizeof(f_seid->ip_addr.v4.u.sin.sin_addr);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &f_seid->ip_addr.v4);
|
|
}
|
|
if (f_seid->ip_addr.v6_present) {
|
|
if (f_seid->ip_addr.v6.u.sin6.sin6_family != AF_INET6)
|
|
RETURN_ERROR(-EINVAL,
|
|
"f_seid IE indicates IPv6 address, but there is no ipv6_addr");
|
|
l = sizeof(f_seid->ip_addr.v6.u.sin6.sin6_addr);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &f_seid->ip_addr.v6);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_ip_addrs_to_str_buf(char *buf, size_t buflen, const struct osmo_pfcp_ip_addrs *addrs)
|
|
{
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
if (!addrs) {
|
|
OSMO_STRBUF_PRINTF(sb, "NULL-addr");
|
|
return sb.chars_needed;
|
|
}
|
|
if (!(addrs->v4_present || addrs->v6_present)) {
|
|
OSMO_STRBUF_PRINTF(sb, "empty:-addr");
|
|
return sb.chars_needed;
|
|
}
|
|
if (addrs->v4_present) {
|
|
OSMO_STRBUF_PRINTF(sb, "v4:");
|
|
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &addrs->v4);
|
|
}
|
|
if (addrs->v4_present && addrs->v6_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",");
|
|
if (addrs->v6_present) {
|
|
OSMO_STRBUF_PRINTF(sb, "v6:");
|
|
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &addrs->v6);
|
|
}
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
char *osmo_pfcp_ip_addrs_to_str_c(void *ctx, const struct osmo_pfcp_ip_addrs *addrs)
|
|
{
|
|
OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_pfcp_ip_addrs_to_str_buf, addrs)
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_f_seid(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_f_seid *f_seid = encode_from;
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
OSMO_STRBUF_PRINTF(sb, "0x%"PRIx64",", f_seid->seid);
|
|
OSMO_STRBUF_APPEND(sb, osmo_pfcp_ip_addrs_to_str_buf, &f_seid->ip_addr);
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
int osmo_pfcp_dec_f_teid(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_f_teid *f_teid = decode_to;
|
|
uint8_t flags;
|
|
const uint8_t *pos;
|
|
|
|
*f_teid = (struct osmo_pfcp_ie_f_teid){};
|
|
|
|
pos = tlv->val;
|
|
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("flags", pos, 1);
|
|
flags = *pos;
|
|
pos++;
|
|
f_teid->choose_flag = flags & 4;
|
|
|
|
if (!f_teid->choose_flag) {
|
|
/* A fixed TEID and address are provided */
|
|
f_teid->fixed.ip_addr.v4_present = flags & 1;
|
|
f_teid->fixed.ip_addr.v6_present = flags & 2;
|
|
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("TEID", pos, 4);
|
|
f_teid->fixed.teid = osmo_load32be(pos);
|
|
pos += 4;
|
|
|
|
if (f_teid->fixed.ip_addr.v4_present) {
|
|
osmo_static_assert(sizeof(f_teid->fixed.ip_addr.v4.u.sin.sin_addr) == 4, sin_addr_size_is_4);
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv4 address", pos, 4);
|
|
osmo_sockaddr_from_octets(&f_teid->fixed.ip_addr.v4, pos, 4);
|
|
pos += 4;
|
|
}
|
|
if (f_teid->fixed.ip_addr.v6_present) {
|
|
osmo_static_assert(sizeof(f_teid->fixed.ip_addr.v6.u.sin6.sin6_addr) == 16, sin6_addr_size_is_16);
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 address", pos, 16);
|
|
osmo_sockaddr_from_octets(&f_teid->fixed.ip_addr.v6, pos, 16);
|
|
pos += 16;
|
|
}
|
|
} else {
|
|
/* CH flag is 1, choose an F-TEID. */
|
|
f_teid->choose.ipv4_addr = flags & 1;
|
|
f_teid->choose.ipv6_addr = flags & 2;
|
|
f_teid->choose.choose_id_present = flags & 8;
|
|
|
|
if (f_teid->choose.choose_id_present) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("CHOOSE ID", pos, 1);
|
|
f_teid->choose.choose_id = *pos;
|
|
pos++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_f_teid(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_f_teid *f_teid = encode_from;
|
|
uint8_t flags;
|
|
|
|
flags = (f_teid->choose_flag ? 4 : 0);
|
|
|
|
if (!f_teid->choose_flag) {
|
|
/* A fixed TEID and address are provided */
|
|
flags |= (f_teid->fixed.ip_addr.v4_present ? 1 : 0)
|
|
+ (f_teid->fixed.ip_addr.v6_present ? 2 : 0);
|
|
|
|
msgb_put_u8(tlv->dst, flags);
|
|
msgb_put_u32(tlv->dst, f_teid->fixed.teid);
|
|
|
|
if (f_teid->fixed.ip_addr.v4_present) {
|
|
if (f_teid->fixed.ip_addr.v4.u.sin.sin_family != AF_INET)
|
|
RETURN_ERROR(-EINVAL,
|
|
"f_teid IE indicates IPv4 address, but there is no ipv4_addr"
|
|
" (sin_family = %d != AF_INET)", f_teid->fixed.ip_addr.v4.u.sin.sin_family);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 4), 4, &f_teid->fixed.ip_addr.v4);
|
|
}
|
|
if (f_teid->fixed.ip_addr.v6_present) {
|
|
if (f_teid->fixed.ip_addr.v6.u.sin6.sin6_family != AF_INET6)
|
|
RETURN_ERROR(-EINVAL,
|
|
"f_teid IE indicates IPv6 address, but there is no ipv6_addr"
|
|
" (sin6_family = %d != AF_INET6)", f_teid->fixed.ip_addr.v6.u.sin6.sin6_family);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 16), 16, &f_teid->fixed.ip_addr.v6);
|
|
}
|
|
} else {
|
|
flags |= (f_teid->choose.ipv4_addr ? 1 : 0)
|
|
+ (f_teid->choose.ipv6_addr ? 2 : 0)
|
|
+ (f_teid->choose.choose_id_present ? 8 : 0);
|
|
msgb_put_u8(tlv->dst, flags);
|
|
if (f_teid->choose.choose_id_present)
|
|
msgb_put_u8(tlv->dst, f_teid->choose.choose_id);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_ie_f_teid_to_str_buf(char *buf, size_t buflen, const struct osmo_pfcp_ie_f_teid *ft)
|
|
{
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
if (ft->choose_flag) {
|
|
OSMO_STRBUF_PRINTF(sb, "CHOOSE");
|
|
if (ft->choose.ipv4_addr)
|
|
OSMO_STRBUF_PRINTF(sb, "-v4");
|
|
if (ft->choose.ipv6_addr)
|
|
OSMO_STRBUF_PRINTF(sb, "-v6");
|
|
if (ft->choose.choose_id_present)
|
|
OSMO_STRBUF_PRINTF(sb, "-id%u", ft->choose.choose_id);
|
|
} else {
|
|
OSMO_STRBUF_PRINTF(sb, "TEID-0x%x,", ft->fixed.teid);
|
|
OSMO_STRBUF_APPEND(sb, osmo_pfcp_ip_addrs_to_str_buf, &ft->fixed.ip_addr);
|
|
}
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
char *osmo_pfcp_ie_f_teid_to_str_c(void *ctx, const struct osmo_pfcp_ie_f_teid *ft)
|
|
{
|
|
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_pfcp_ie_f_teid_to_str_buf, ft)
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_f_teid(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_f_teid *f_teid = encode_from;
|
|
return osmo_pfcp_ie_f_teid_to_str_buf(buf, buflen, f_teid);
|
|
}
|
|
|
|
int osmo_pfcp_dec_apply_action(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_apply_action *apply_action = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
*apply_action = (struct osmo_pfcp_ie_apply_action){};
|
|
memcpy(apply_action->bits, tlv->val, OSMO_MIN(tlv->len, sizeof(apply_action->bits)));
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_apply_action(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_apply_action *apply_action = encode_from;
|
|
memcpy(msgb_put(tlv->dst, sizeof(apply_action->bits)),
|
|
apply_action->bits, sizeof(apply_action->bits));
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_apply_action(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_apply_action *apply_action = encode_from;
|
|
return osmo_pfcp_bits_to_str_buf(buf, buflen, apply_action->bits, osmo_pfcp_apply_action_strs);
|
|
}
|
|
|
|
int osmo_pfcp_dec_network_inst(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_network_inst *network_inst = decode_to;
|
|
osmo_strlcpy(network_inst->str, (const char *)tlv->val, OSMO_MIN(sizeof(network_inst->str), tlv->len+1));
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_network_inst(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_network_inst *network_inst = encode_from;
|
|
unsigned int l = strlen(network_inst->str);
|
|
if (l)
|
|
memcpy(msgb_put(tlv->dst, l), network_inst->str, l);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_network_inst(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_network_inst *network_inst = encode_from;
|
|
return osmo_quote_str_buf3(buf, buflen, network_inst->str,
|
|
strnlen(network_inst->str, sizeof(network_inst->str)));
|
|
}
|
|
|
|
int osmo_pfcp_dec_outer_header_creation(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_outer_header_creation *ohc = decode_to;
|
|
const uint8_t *pos;
|
|
bool gtp_u_udp_ipv4;
|
|
bool gtp_u_udp_ipv6;
|
|
bool udp_ipv4;
|
|
bool udp_ipv6;
|
|
bool ipv4;
|
|
bool ipv6;
|
|
bool c_tag;
|
|
bool s_tag;
|
|
|
|
*ohc = (struct osmo_pfcp_ie_outer_header_creation){};
|
|
|
|
ENSURE_LENGTH_IS_AT_LEAST(2);
|
|
|
|
memcpy(ohc->desc_bits, tlv->val, 2);
|
|
|
|
gtp_u_udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4);
|
|
udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV4);
|
|
ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV4);
|
|
gtp_u_udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV6);
|
|
udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV6);
|
|
ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV6);
|
|
c_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_C_TAG);
|
|
s_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_S_TAG);
|
|
|
|
pos = tlv->val + 2;
|
|
if (gtp_u_udp_ipv4 || gtp_u_udp_ipv6) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("TEID", pos, 4);
|
|
ohc->teid_present = true;
|
|
ohc->teid = osmo_load32be(pos);
|
|
pos += 4;
|
|
}
|
|
if (gtp_u_udp_ipv4 || udp_ipv4 || ipv4) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv4 address", pos, 4);
|
|
ohc->ip_addr.v4_present = true;
|
|
osmo_sockaddr_from_octets(&ohc->ip_addr.v4, pos, 4);
|
|
pos += 4;
|
|
}
|
|
if (gtp_u_udp_ipv6 || udp_ipv6 || ipv6) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 address", pos, 16);
|
|
ohc->ip_addr.v6_present = true;
|
|
osmo_sockaddr_from_octets(&ohc->ip_addr.v6, pos, 16);
|
|
pos += 16;
|
|
}
|
|
if (udp_ipv4 || udp_ipv6) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("UDP port number", pos, 2);
|
|
ohc->port_number_present = true;
|
|
ohc->port_number = osmo_load16be(pos);
|
|
pos += 2;
|
|
}
|
|
if (c_tag) {
|
|
ohc->c_tag_present = true;
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("C-TAG", pos, 3);
|
|
ohc->c_tag_present = true;
|
|
ohc->c_tag = osmo_load32be_ext_2(pos, 3);
|
|
pos += 3;
|
|
}
|
|
if (s_tag) {
|
|
ohc->s_tag_present = true;
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("S-TAG", pos, 3);
|
|
ohc->s_tag_present = true;
|
|
ohc->s_tag = osmo_load32be_ext_2(pos, 3);
|
|
pos += 3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_outer_header_creation(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_outer_header_creation *ohc = encode_from;
|
|
bool gtp_u_udp_ipv4;
|
|
bool gtp_u_udp_ipv6;
|
|
bool udp_ipv4;
|
|
bool udp_ipv6;
|
|
bool ipv4;
|
|
bool ipv6;
|
|
bool c_tag;
|
|
bool s_tag;
|
|
|
|
memcpy(msgb_put(tlv->dst, sizeof(ohc->desc_bits)), ohc->desc_bits, sizeof(ohc->desc_bits));
|
|
|
|
gtp_u_udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4);
|
|
udp_ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV4);
|
|
ipv4 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV4);
|
|
gtp_u_udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV6);
|
|
udp_ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_UDP_IPV6);
|
|
ipv6 = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_IPV6);
|
|
c_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_C_TAG);
|
|
s_tag = osmo_pfcp_bits_get(ohc->desc_bits, OSMO_PFCP_OUTER_HEADER_CREATION_S_TAG);
|
|
|
|
if ((gtp_u_udp_ipv4 || gtp_u_udp_ipv6) != (ohc->teid_present))
|
|
RETURN_ERROR(-EINVAL, "teid_present = %s does not match the description bits 0x%02x\n",
|
|
ohc->teid_present ? "true" : "false",
|
|
ohc->desc_bits[0]);
|
|
if (ohc->teid_present)
|
|
msgb_put_u32(tlv->dst, ohc->teid);
|
|
|
|
if ((gtp_u_udp_ipv4 || udp_ipv4 || ipv4) != ohc->ip_addr.v4_present)
|
|
RETURN_ERROR(-EINVAL, "ipv4_addr_present = %s does not match the description bits 0x%02x\n",
|
|
ohc->ip_addr.v4_present ? "true" : "false",
|
|
ohc->desc_bits[0]);
|
|
if (ohc->ip_addr.v4_present)
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 4), 4, &ohc->ip_addr.v4);
|
|
|
|
if ((gtp_u_udp_ipv6 || udp_ipv6 || ipv6) != ohc->ip_addr.v6_present)
|
|
RETURN_ERROR(-EINVAL, "ipv6_addr_present = %s does not match the description bits 0x%02x\n",
|
|
ohc->ip_addr.v6_present ? "true" : "false",
|
|
ohc->desc_bits[0]);
|
|
if (ohc->ip_addr.v6_present)
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, 16), 16, &ohc->ip_addr.v6);
|
|
|
|
if ((udp_ipv4 || udp_ipv6) != ohc->port_number_present)
|
|
RETURN_ERROR(-EINVAL, "port_number_present = %s does not match the description bits 0x%02x\n",
|
|
ohc->port_number_present ? "true" : "false",
|
|
ohc->desc_bits[0]);
|
|
if (ohc->port_number_present)
|
|
msgb_put_u16(tlv->dst, ohc->port_number);
|
|
|
|
if (c_tag != ohc->c_tag_present)
|
|
RETURN_ERROR(-EINVAL, "c_tag_present = %s does not match the description bits 0x%02x%02x\n",
|
|
ohc->c_tag_present ? "true" : "false",
|
|
ohc->desc_bits[1], ohc->desc_bits[0]);
|
|
if (ohc->c_tag_present)
|
|
osmo_store32be_ext(ohc->c_tag, msgb_put(tlv->dst, 3), 3);
|
|
|
|
if (s_tag != ohc->s_tag_present)
|
|
RETURN_ERROR(-EINVAL, "s_tag_present = %s does not match the description bits 0x%02x%02x\n",
|
|
ohc->s_tag_present ? "true" : "false",
|
|
ohc->desc_bits[1], ohc->desc_bits[0]);
|
|
if (ohc->s_tag_present)
|
|
osmo_store32be_ext(ohc->s_tag, msgb_put(tlv->dst, 3), 3);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_ie_outer_header_creation_to_str_buf(char *buf, size_t buflen,
|
|
const struct osmo_pfcp_ie_outer_header_creation *ohc)
|
|
{
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
OSMO_STRBUF_APPEND(sb, osmo_pfcp_bits_to_str_buf, ohc->desc_bits, osmo_pfcp_outer_header_creation_strs);
|
|
if (ohc->teid_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",TEID:0x%x", ohc->teid);
|
|
|
|
OSMO_STRBUF_PRINTF(sb, ",");
|
|
OSMO_STRBUF_APPEND(sb, osmo_pfcp_ip_addrs_to_str_buf, &ohc->ip_addr);
|
|
|
|
if (ohc->port_number_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",port:%u", ohc->port_number);
|
|
if (ohc->c_tag_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",c-tag:%u", ohc->c_tag);
|
|
if (ohc->s_tag_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",s-tag:%u", ohc->s_tag);
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
char *osmo_pfcp_ie_outer_header_creation_to_str_c(void *ctx, const struct osmo_pfcp_ie_outer_header_creation *ohc)
|
|
{
|
|
OSMO_NAME_C_IMPL(ctx, 128, "ERROR", osmo_pfcp_ie_outer_header_creation_to_str_buf, ohc)
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_outer_header_creation(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
return osmo_pfcp_ie_outer_header_creation_to_str_buf(buf, buflen, encode_from);
|
|
}
|
|
|
|
int osmo_pfcp_dec_activate_predefined_rules(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_activate_predefined_rules *activate_predefined_rules = decode_to;
|
|
osmo_strlcpy(activate_predefined_rules->str, (const char *)tlv->val, OSMO_MIN(sizeof(activate_predefined_rules->str), tlv->len+1));
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_activate_predefined_rules(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_activate_predefined_rules *activate_predefined_rules = encode_from;
|
|
unsigned int l = strlen(activate_predefined_rules->str);
|
|
if (l)
|
|
memcpy(msgb_put(tlv->dst, l), activate_predefined_rules->str, l);
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_activate_predefined_rules(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_activate_predefined_rules *activate_predefined_rules = encode_from;
|
|
return osmo_quote_str_buf3(buf, buflen, activate_predefined_rules->str,
|
|
strnlen(activate_predefined_rules->str, sizeof(activate_predefined_rules->str)));
|
|
}
|
|
|
|
int osmo_pfcp_dec_outer_header_removal(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_outer_header_removal *outer_header_removal = decode_to;
|
|
ENSURE_LENGTH_IS_AT_LEAST(1);
|
|
outer_header_removal->desc = tlv->val[0];
|
|
|
|
if (tlv->len > 1) {
|
|
outer_header_removal->gtp_u_extension_header_del_present = true;
|
|
memcpy(outer_header_removal->gtp_u_extension_header_del_bits, &tlv->val[1],
|
|
sizeof(outer_header_removal->gtp_u_extension_header_del_bits));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_outer_header_removal(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_outer_header_removal *outer_header_removal = encode_from;
|
|
msgb_put_u8(tlv->dst, outer_header_removal->desc);
|
|
if (outer_header_removal->gtp_u_extension_header_del_present) {
|
|
memcpy(msgb_put(tlv->dst, sizeof(outer_header_removal->gtp_u_extension_header_del_bits)),
|
|
outer_header_removal->gtp_u_extension_header_del_bits,
|
|
sizeof(outer_header_removal->gtp_u_extension_header_del_bits));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_outer_header_removal(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_outer_header_removal *outer_header_removal = encode_from;
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
OSMO_STRBUF_PRINTF(sb, "%s", osmo_pfcp_outer_header_removal_desc_str(outer_header_removal->desc));
|
|
if (outer_header_removal->gtp_u_extension_header_del_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",ext-hdr-del:0x%x", outer_header_removal->gtp_u_extension_header_del_bits[0]);
|
|
return sb.chars_needed;
|
|
}
|
|
|
|
int osmo_pfcp_dec_ue_ip_address(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *tlv)
|
|
{
|
|
struct osmo_pfcp_ie_ue_ip_address *ue_ip_address = decode_to;
|
|
const uint8_t *pos;
|
|
uint8_t flags;
|
|
|
|
pos = tlv->val;
|
|
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("flags", pos, 1);
|
|
flags = *pos;
|
|
pos++;
|
|
|
|
ue_ip_address->ipv6_prefix_length_present = flags & (1 << 6);
|
|
ue_ip_address->chv6 = flags & (1 << 5);
|
|
ue_ip_address->chv4 = flags & (1 << 4);
|
|
ue_ip_address->ipv6_prefix_delegation_bits_present = flags & (1 << 3);
|
|
ue_ip_address->ip_is_destination = flags & (1 << 2);
|
|
ue_ip_address->ip_addr.v4_present = flags & (1 << 1);
|
|
ue_ip_address->ip_addr.v6_present = flags & (1 << 0);
|
|
|
|
if (ue_ip_address->ip_addr.v4_present) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv4 address", pos, 4);
|
|
osmo_sockaddr_from_octets(&ue_ip_address->ip_addr.v4, pos, 4);
|
|
pos += 4;
|
|
}
|
|
if (ue_ip_address->ip_addr.v6_present) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 address", pos, 16);
|
|
osmo_sockaddr_from_octets(&ue_ip_address->ip_addr.v6, pos, 16);
|
|
pos += 16;
|
|
}
|
|
|
|
if (ue_ip_address->ipv6_prefix_delegation_bits_present) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 prefix delegation bits", pos, 1);
|
|
ue_ip_address->ipv6_prefix_delegation_bits = *pos;
|
|
pos++;
|
|
}
|
|
if (ue_ip_address->ipv6_prefix_length_present) {
|
|
ENSURE_REMAINING_LENGTH_IS_AT_LEAST("IPv6 prefix length", pos, 1);
|
|
ue_ip_address->ipv6_prefix_length = *pos;
|
|
pos++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_ue_ip_address(struct osmo_gtlv_put *tlv, const void *decoded_struct, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_ue_ip_address *ue_ip_address = encode_from;
|
|
unsigned int l;
|
|
uint8_t flags;
|
|
|
|
flags = 0
|
|
| (ue_ip_address->ipv6_prefix_length_present ? (1 << 6) : 0)
|
|
| (ue_ip_address->chv6 ? (1 << 5) : 0)
|
|
| (ue_ip_address->chv4 ? (1 << 4) : 0)
|
|
| (ue_ip_address->ipv6_prefix_delegation_bits_present ? (1 << 3) : 0)
|
|
| (ue_ip_address->ip_is_destination ? (1 << 2) : 0)
|
|
| (ue_ip_address->ip_addr.v4_present ? (1 << 1) : 0)
|
|
| (ue_ip_address->ip_addr.v6_present ? (1 << 0) : 0)
|
|
;
|
|
|
|
msgb_put_u8(tlv->dst, flags);
|
|
|
|
if (ue_ip_address->ip_addr.v4_present) {
|
|
if (ue_ip_address->ip_addr.v4.u.sin.sin_family != AF_INET)
|
|
RETURN_ERROR(-EINVAL,
|
|
"ue_ip_address IE indicates IPv4 address, but there is no ipv4_addr");
|
|
l = sizeof(ue_ip_address->ip_addr.v4.u.sin.sin_addr);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &ue_ip_address->ip_addr.v4);
|
|
}
|
|
if (ue_ip_address->ip_addr.v6_present) {
|
|
if (ue_ip_address->ip_addr.v6.u.sin6.sin6_family != AF_INET6)
|
|
RETURN_ERROR(-EINVAL,
|
|
"ue_ip_address IE indicates IPv6 address, but there is no ipv6_addr");
|
|
l = sizeof(ue_ip_address->ip_addr.v6.u.sin6.sin6_addr);
|
|
osmo_sockaddr_to_octets(msgb_put(tlv->dst, l), l, &ue_ip_address->ip_addr.v6);
|
|
}
|
|
|
|
if (ue_ip_address->ipv6_prefix_delegation_bits_present)
|
|
msgb_put_u8(tlv->dst, ue_ip_address->ipv6_prefix_delegation_bits);
|
|
|
|
if (ue_ip_address->ipv6_prefix_length_present)
|
|
msgb_put_u8(tlv->dst, ue_ip_address->ipv6_prefix_length);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int osmo_pfcp_enc_to_str_ue_ip_address(char *buf, size_t buflen, const void *encode_from)
|
|
{
|
|
const struct osmo_pfcp_ie_ue_ip_address *uia = encode_from;
|
|
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
|
if (uia->chv4)
|
|
OSMO_STRBUF_PRINTF(sb, "chv4");
|
|
if (uia->chv6)
|
|
OSMO_STRBUF_PRINTF(sb, "%schv4", sb.pos ? "," : "");
|
|
if (uia->ip_is_destination)
|
|
OSMO_STRBUF_PRINTF(sb, "%sdst", sb.pos ? "," : "");
|
|
|
|
if (sb.pos)
|
|
OSMO_STRBUF_PRINTF(sb, ",");
|
|
OSMO_STRBUF_APPEND(sb, osmo_pfcp_ip_addrs_to_str_buf, &uia->ip_addr);
|
|
|
|
if (uia->ipv6_prefix_delegation_bits_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",ipv6-prefix-deleg:%x", uia->ipv6_prefix_delegation_bits);
|
|
if (uia->ipv6_prefix_length_present)
|
|
OSMO_STRBUF_PRINTF(sb, ",ipv6-prefix-len:%u", uia->ipv6_prefix_length);
|
|
return sb.chars_needed;
|
|
}
|