/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */ /* (C) 2016 by sysmocom s.f.m.c. GmbH * 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 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* When the propose bit in an SNDCP-XID compression field is set to zero, * the algorithm identifier is stripped. The algoritm parameters are specific * for each algorithms. The following struct is used to pass the information * about the referenced algorithm to the parser. */ struct entity_algo_table { unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */ unsigned int algo; /* see also: 6.5.1.1.4 and 6.6.1.1.4 */ unsigned int compclass; /* Can be either SNDCP_XID_DATA_COMPRESSION or SNDCP_XID_PROTOCOL_COMPRESSION */ }; /* FUNCTIONS RELATED TO SNDCP-XID ENCODING */ /* Encode applicable sapis (works the same in all three compression schemes) */ static int encode_pcomp_applicable_sapis(uint8_t *dst, const uint8_t *nsapis, uint8_t nsapis_len) { /* NOTE: Buffer *dst needs offer at 2 bytes * of space to store the generation results */ uint16_t blob; uint8_t nsapi; int i; /* Bail if number of possible nsapis exceeds valid range * (Only 11 nsapis possible for PDP-Contexts) */ OSMO_ASSERT(nsapis_len <= 11); /* Encode applicable SAPIs */ blob = 0; for (i = 0; i < nsapis_len; i++) { nsapi = nsapis[i]; /* Only NSAPI 5 to 15 are applicable for user traffic (PDP- * contexts). Only for these NSAPIs SNDCP-XID parameters * can apply. See also 3GPP TS 44.065, 5.1 Service primitives */ OSMO_ASSERT(nsapi >= 5 && nsapi <= 15); blob |= (1 << nsapi); } /* Store result */ *dst = (blob >> 8) & 0xFF; dst++; *dst = blob & 0xFF; return 2; } /* Encode rfc1144 parameter field * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ static int encode_pcomp_rfc1144_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_pcomp_rfc1144_params *params) { /* NOTE: Buffer *dst should offer at least 3 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 3); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode s01 (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ OSMO_ASSERT(params->s01 >= 0); OSMO_ASSERT(params->s01 <= 255); *dst = params->s01; dst++; dst_counter++; /* Return generated length */ return dst_counter; } /* * Encode rfc2507 parameter field * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ static int encode_pcomp_rfc2507_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_pcomp_rfc2507_params *params) { /* NOTE: Buffer *dst should offer at least 3 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 9); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->f_max_period >= 1); OSMO_ASSERT(params->f_max_period <= 65535); *dst = (params->f_max_period >> 8) & 0xFF; dst++; dst_counter++; *dst = (params->f_max_period) & 0xFF; dst++; dst_counter++; /* Encode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->f_max_time >= 1); OSMO_ASSERT(params->f_max_time <= 255); *dst = params->f_max_time; dst++; dst_counter++; /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->max_header >= 60); OSMO_ASSERT(params->max_header <= 255); *dst = params->max_header; dst++; dst_counter++; /* Encode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->tcp_space >= 3); OSMO_ASSERT(params->tcp_space <= 255); *dst = params->tcp_space; dst++; dst_counter++; /* Encode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ OSMO_ASSERT(params->non_tcp_space >= 3); OSMO_ASSERT(params->non_tcp_space <= 65535); *dst = (params->non_tcp_space >> 8) & 0xFF; dst++; dst_counter++; *dst = (params->non_tcp_space) & 0xFF; dst++; dst_counter++; /* Return generated length */ return dst_counter; } /* Encode ROHC parameter field * (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ static int encode_pcomp_rohc_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_pcomp_rohc_params *params) { /* NOTE: Buffer *dst should offer at least 36 * (2 * 16 Profiles + 2 * 3 Parameter) bytes * of memory space to store generation results */ int i; int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 38); /* Bail if number of ROHC profiles exceeds limit * (ROHC supports only a maximum of 16 different profiles) */ OSMO_ASSERT(params->profile_len <= 16); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ OSMO_ASSERT(params->max_cid >= 0); OSMO_ASSERT(params->max_cid <= 16383); *dst = (params->max_cid >> 8) & 0xFF; dst++; *dst = params->max_cid & 0xFF; dst++; dst_counter += 2; /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ OSMO_ASSERT(params->max_header >= 60); OSMO_ASSERT(params->max_header <= 255); *dst = (params->max_header >> 8) & 0xFF; dst++; *dst = params->max_header & 0xFF; dst++; dst_counter += 2; /* Encode ROHC Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ for (i = 0; i < params->profile_len; i++) { *dst = (params->profile[i] >> 8) & 0xFF; dst++; *dst = params->profile[i] & 0xFF; dst++; dst_counter += 2; } /* Return generated length */ return dst_counter; } /* Encode V.42bis parameter field * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ static int encode_dcomp_v42bis_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_dcomp_v42bis_params *params) { /* NOTE: Buffer *dst should offer at least 6 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 6); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ OSMO_ASSERT(params->p0 >= 0); OSMO_ASSERT(params->p0 <= 3); *dst = params->p0 & 0x03; dst++; dst_counter++; /* Encode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ OSMO_ASSERT(params->p1 >= 512); OSMO_ASSERT(params->p1 <= 65535); *dst = (params->p1 >> 8) & 0xFF; dst++; *dst = params->p1 & 0xFF; dst++; dst_counter += 2; /* Encode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ OSMO_ASSERT(params->p2 >= 6); OSMO_ASSERT(params->p2 <= 250); *dst = params->p2; dst++; dst_counter++; /* Return generated length */ return dst_counter; } /* Encode V44 parameter field * (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ static int encode_dcomp_v44_params(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_dcomp_v44_params *params) { /* NOTE: Buffer *dst should offer at least 12 bytes * of space to store the generation results */ int dst_counter = 0; int rc; OSMO_ASSERT(dst_maxlen >= 12); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode applicable SAPIs */ rc = encode_pcomp_applicable_sapis(dst, params->nsapi, params->nsapi_len); dst += rc; dst_counter += rc; /* Encode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->c0 == 0x80 || params->c0 == 0xC0); *dst = params->c0 & 0xC0; dst++; dst_counter++; /* Encode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p0 >= 0); OSMO_ASSERT(params->p0 <= 3); *dst = params->p0 & 0x03; dst++; dst_counter++; /* Encode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p1t >= 256); OSMO_ASSERT(params->p1t <= 65535); *dst = (params->p1t >> 8) & 0xFF; dst++; *dst = params->p1t & 0xFF; dst++; dst_counter += 2; /* Encode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p1r >= 256); OSMO_ASSERT(params->p1r <= 65535); *dst = (params->p1r >> 8) & 0xFF; dst++; *dst = params->p1r & 0xFF; dst++; dst_counter += 2; /* Encode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p3t >= 0); OSMO_ASSERT(params->p3t <= 65535); OSMO_ASSERT(params->p3t >= 2 * params->p1t); *dst = (params->p3t >> 8) & 0xFF; dst++; *dst = params->p3t & 0xFF; dst++; dst_counter += 2; /* Encode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ OSMO_ASSERT(params->p3r >= 0); OSMO_ASSERT(params->p3r <= 65535); OSMO_ASSERT(params->p3r >= 2 * params->p1r); *dst = (params->p3r >> 8) & 0xFF; dst++; *dst = params->p3r & 0xFF; dst++; dst_counter += 2; /* Return generated length */ return dst_counter; } /* Encode data or protocol control information compression field * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and * 3GPP TS 44.065, 6.5.1.1, Figure 7) */ static int encode_comp_field(uint8_t *dst, unsigned int dst_maxlen, const struct gprs_sndcp_comp_field *comp_field) { int dst_counter = 0; int len; int expected_length; int i; uint8_t payload_bytes[256]; int payload_bytes_len = -1; /* If possible, try do encode payload bytes first */ if (comp_field->rfc1144_params) { payload_bytes_len = encode_pcomp_rfc1144_params(payload_bytes, sizeof(payload_bytes), comp_field->rfc1144_params); } else if (comp_field->rfc2507_params) { payload_bytes_len = encode_pcomp_rfc2507_params(payload_bytes, sizeof(payload_bytes), comp_field->rfc2507_params); } else if (comp_field->rohc_params) { payload_bytes_len = encode_pcomp_rohc_params(payload_bytes, sizeof(payload_bytes), comp_field->rohc_params); } else if (comp_field->v42bis_params) { payload_bytes_len = encode_dcomp_v42bis_params(payload_bytes, sizeof(payload_bytes), comp_field->v42bis_params); } else if (comp_field->v44_params) { payload_bytes_len = encode_dcomp_v44_params(payload_bytes, sizeof(payload_bytes), comp_field->v44_params); } else OSMO_ASSERT(false); /* Bail immediately if payload byte generation failed */ OSMO_ASSERT(payload_bytes_len >= 0); /* Bail if comp_len is out of bounds */ OSMO_ASSERT(comp_field->comp_len <= sizeof(comp_field->comp)); /* Calculate length field of the data block */ if (comp_field->p) { len = payload_bytes_len + ceil((double)(comp_field->comp_len) / 2.0); expected_length = len + 3; } else { len = payload_bytes_len; expected_length = len + 2; } /* Bail immediately if no sufficient memory space is supplied */ OSMO_ASSERT(dst_maxlen >= expected_length); /* Check if the entity number is within bounds */ OSMO_ASSERT(comp_field->entity <= 0x1f); /* Check if the algorithm number is within bounds */ OSMO_ASSERT(comp_field->algo >= 0 || comp_field->algo <= 0x1f); /* Zero out buffer */ memset(dst, 0, dst_maxlen); /* Encode Propose bit */ if (comp_field->p) *dst |= (1 << 7); /* Encode entity number */ *dst |= comp_field->entity & 0x1F; dst++; dst_counter++; /* Encode algorithm number */ if (comp_field->p) { *dst |= comp_field->algo & 0x1F; dst++; dst_counter++; } /* Encode length field */ *dst |= len & 0xFF; dst++; dst_counter++; /* Encode PCOMP/DCOMP values */ if (comp_field->p) { for (i = 0; i < comp_field->comp_len; i++) { /* Check if submitted PCOMP/DCOMP values are within bounds */ if (comp_field->comp[i] > 0x0F) return -EINVAL; if (i & 1) { *dst |= comp_field->comp[i] & 0x0F; dst++; dst_counter++; } else *dst |= (comp_field->comp[i] << 4) & 0xF0; } if (i & 1) { dst++; dst_counter++; } } /* Append payload bytes */ memcpy(dst, payload_bytes, payload_bytes_len); dst_counter += payload_bytes_len; /* Return generated length */ return dst_counter; } /* Find out to which compression class the specified comp-field belongs * (header compression or data compression?) */ int gprs_sndcp_get_compression_class(const struct gprs_sndcp_comp_field *comp_field) { OSMO_ASSERT(comp_field); if (comp_field->rfc1144_params) return SNDCP_XID_PROTOCOL_COMPRESSION; else if (comp_field->rfc2507_params) return SNDCP_XID_PROTOCOL_COMPRESSION; else if (comp_field->rohc_params) return SNDCP_XID_PROTOCOL_COMPRESSION; else if (comp_field->v42bis_params) return SNDCP_XID_DATA_COMPRESSION; else if (comp_field->v44_params) return SNDCP_XID_DATA_COMPRESSION; else return -EINVAL; } /* Convert all compression fields to bytstreams */ static int gprs_sndcp_pack_fields(const struct llist_head *comp_fields, uint8_t *dst, unsigned int dst_maxlen, int class) { struct gprs_sndcp_comp_field *comp_field; int byte_counter = 0; int rc; llist_for_each_entry_reverse(comp_field, comp_fields, list) { if (class == gprs_sndcp_get_compression_class(comp_field)) { rc = encode_comp_field(dst + byte_counter, dst_maxlen - byte_counter, comp_field); /* When input data is correct, there is * no reason for the encoder to fail! */ OSMO_ASSERT(rc >= 0); byte_counter += rc; } } /* Return generated length */ return byte_counter; } /* Transform a list with compression fields into an SNDCP-XID message (dst) */ int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen, const struct llist_head *comp_fields, int version) { int rc; int byte_counter = 0; uint8_t comp_bytes[512]; uint8_t xid_version_number[1]; OSMO_ASSERT(comp_fields); OSMO_ASSERT(dst); OSMO_ASSERT(dst_maxlen >= 2 + sizeof(xid_version_number)); /* Prepend header with version number */ if (version >= 0) { xid_version_number[0] = (uint8_t) (version & 0xff); dst = tlv_put(dst, SNDCP_XID_VERSION_NUMBER, sizeof(xid_version_number), xid_version_number); byte_counter += (sizeof(xid_version_number) + 2); } /* Stop if there is no compression fields supplied */ if (llist_empty(comp_fields)) return byte_counter; /* Add data compression fields */ rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes, sizeof(comp_bytes), SNDCP_XID_DATA_COMPRESSION); OSMO_ASSERT(rc >= 0); if (rc > 0) { dst = tlv_put(dst, SNDCP_XID_DATA_COMPRESSION, rc, comp_bytes); byte_counter += rc + 2; } /* Add header compression fields */ rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes, sizeof(comp_bytes), SNDCP_XID_PROTOCOL_COMPRESSION); OSMO_ASSERT(rc >= 0); if (rc > 0) { dst = tlv_put(dst, SNDCP_XID_PROTOCOL_COMPRESSION, rc, comp_bytes); byte_counter += rc + 2; } /* Return generated length */ return byte_counter; } /* FUNCTIONS RELATED TO SNDCP-XID DECODING */ /* Decode applicable sapis (works the same in all three compression schemes) */ static int decode_pcomp_applicable_sapis(uint8_t *nsapis, uint8_t *nsapis_len, const uint8_t *src, unsigned int src_len) { uint16_t blob; int i; int nsapi_len = 0; /* Exit immediately if no result can be stored */ if (!nsapis) return -EINVAL; /* Exit immediately if not enough input data is available */ if (src_len < 2) return -EINVAL; /* Read bitmask */ blob = *src; blob = (blob << 8) & 0xFF00; src++; blob |= (*src) & 0xFF; blob = (blob >> 5); /* Decode applicable SAPIs */ for (i = 0; i < 15; i++) { if ((blob >> i) & 1) { nsapis[nsapi_len] = i + 5; nsapi_len++; } } /* Return consumed length */ *nsapis_len = nsapi_len; return 2; } /* Decode 16 bit field */ static int decode_pcomp_16_bit_field(int *value_int, uint16_t * value_uint16, const uint8_t *src, unsigned int src_len, int value_min, int value_max) { uint16_t blob; /* Reset values to zero (just to be sure) */ if (value_int) *value_int = -1; if (value_uint16) *value_uint16 = 0; /* Exit if not enough src are available */ if (src_len < 2) return -EINVAL; /* Decode bit value */ blob = *src; blob = (blob << 8) & 0xFF00; src++; blob |= *src; /* Check if parsed value is within bounds */ if (blob < value_min) return -EINVAL; if (blob > value_max) return -EINVAL; /* Hand back results to the caller */ if (value_int) *value_int = blob; if (value_uint16) *value_uint16 = blob; /* Return consumed length */ return 2; } /* Decode 8 bit field */ static int decode_pcomp_8_bit_field(int *value_int, uint8_t *value_uint8, const uint8_t *src, unsigned int src_len, int value_min, int value_max) { uint8_t blob; /* Reset values to invalid (just to be sure) */ if (value_int) *value_int = -1; if (value_uint8) *value_uint8 = 0; /* Exit if not enough src are available */ if (src_len < 1) return -EINVAL; /* Decode bit value */ blob = *src; /* Check if parsed value is within bounds */ if (blob < value_min) return -EINVAL; if (blob > value_max) return -EINVAL; /* Hand back results to the caller */ if (value_int) *value_int = blob; if (value_uint8) *value_uint8 = blob; /* Return consumed length */ return 1; } /* Decode rfc1144 parameter field see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ static int decode_pcomp_rfc1144_params(struct gprs_sndcp_pcomp_rfc1144_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->s01 = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode parameter S0 -1 * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ rc = decode_pcomp_8_bit_field(¶ms->s01, NULL, src, src_len - byte_counter, 0, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Decode rfc2507 parameter field * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ static int decode_pcomp_rfc2507_params(struct gprs_sndcp_pcomp_rfc2507_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->f_max_period = -1; params->f_max_time = -1; params->max_header = -1; params->tcp_space = -1; params->non_tcp_space = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_16_bit_field(¶ms->f_max_period, NULL, src, src_len - byte_counter, 1, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_8_bit_field(¶ms->f_max_time, NULL, src, src_len - byte_counter, 1, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_8_bit_field(¶ms->max_header, NULL, src, src_len - byte_counter, 60, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_8_bit_field(¶ms->tcp_space, NULL, src, src_len - byte_counter, 3, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ rc = decode_pcomp_16_bit_field(¶ms->non_tcp_space, NULL, src, src_len - byte_counter, 3, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Decode ROHC parameter field (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ static int decode_pcomp_rohc_params(struct gprs_sndcp_pcomp_rohc_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; int i; /* Mark all optional parameters invalid by default */ params->max_cid = -1; params->max_header = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ rc = decode_pcomp_16_bit_field(¶ms->max_cid, NULL, src, src_len - byte_counter, 0, 16383); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ rc = decode_pcomp_16_bit_field(¶ms->max_header, NULL, src, src_len - byte_counter, 60, 255); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ for (i = 0; i < 16; i++) { params->profile_len = 0; rc = decode_pcomp_16_bit_field(NULL, ¶ms->profile[i], src, src_len - byte_counter, 0, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; params->profile_len = i + 1; } /* Return consumed length */ return byte_counter; } /* Decode V.42bis parameter field * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ static int decode_dcomp_v42bis_params(struct gprs_sndcp_dcomp_v42bis_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->p0 = -1; params->p1 = -1; params->p2 = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ rc = decode_pcomp_8_bit_field(¶ms->p0, NULL, src, src_len - byte_counter, 0, 3); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ rc = decode_pcomp_16_bit_field(¶ms->p1, NULL, src, src_len - byte_counter, 512, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ rc = decode_pcomp_8_bit_field(¶ms->p2, NULL, src, src_len - byte_counter, 6, 250); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Decode V44 parameter field (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ static int decode_dcomp_v44_params(struct gprs_sndcp_dcomp_v44_params *params, const uint8_t *src, unsigned int src_len) { int rc; int byte_counter = 0; /* Mark all optional parameters invalid by default */ params->c0 = -1; params->p0 = -1; params->p1t = -1; params->p1r = -1; params->p3t = -1; params->p3r = -1; /* Decode applicable SAPIs */ rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, src, src_len); if (rc > 0) { byte_counter += rc; src += rc; } else return byte_counter; /* Decode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_8_bit_field(¶ms->c0, NULL, src, src_len - byte_counter, 0, 255); if (rc <= 0) return byte_counter; if ((params->c0 != 0x80) && (params->c0 != 0xC0)) return -EINVAL; byte_counter += rc; src += rc; /* Decode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_8_bit_field(¶ms->p0, NULL, src, src_len - byte_counter, 0, 3); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p1t, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p1r, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; byte_counter += rc; src += rc; /* Decode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p3t, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; if (params->p3t < 2 * params->p1t) return -EINVAL; byte_counter += rc; src += rc; /* Decode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ rc = decode_pcomp_16_bit_field(¶ms->p3r, NULL, src, src_len - byte_counter, 265, 65535); if (rc <= 0) return byte_counter; if (params->p3r < 2 * params->p1r) return -EINVAL; byte_counter += rc; src += rc; /* Return consumed length */ return byte_counter; } /* Lookup algorithm identfier by entity ID */ static int lookup_algorithm_identifier(int entity, const struct entity_algo_table *lt, unsigned int lt_len, int compclass) { int i; if (!lt) return -1; for (i = 0; i < lt_len; i++) { if ((lt[i].entity == entity) && (lt[i].compclass == compclass)) return lt[i].algo; } return -1; } /* Helper function for decode_comp_field(), decodes * numeric pcomp/dcomp values */ static int decode_comp_values(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, int compclass) { int src_counter = 0; int i; if (comp_field->p) { /* Determine the number of expected PCOMP/DCOMP values */ if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { /* For protocol compression */ switch (comp_field->algo) { case RFC_1144: comp_field->comp_len = RFC1144_PCOMP_NUM; break; case RFC_2507: comp_field->comp_len = RFC2507_PCOMP_NUM; break; case ROHC: comp_field->comp_len = ROHC_PCOMP_NUM; break; /* Exit if the algorithem type encodes something unknown / unspecified */ default: return -EINVAL; } } else { /* For data compression */ switch (comp_field->algo) { case V42BIS: comp_field->comp_len = V42BIS_DCOMP_NUM; break; case V44: comp_field->comp_len = V44_DCOMP_NUM; break; /* Exit if the algorithem type encodes something unknown / unspecified */ default: return -EINVAL; } } for (i = 0; i < comp_field->comp_len; i++) { if (i & 1) { comp_field->comp[i] = (*src) & 0x0F; src++; src_counter++; } else comp_field->comp[i] = ((*src) >> 4) & 0x0F; } if (i & 1) { src++; src_counter++; } } return src_counter; } /* Helper function for decode_comp_field(), decodes the parameters * which are protocol compression specific */ static int decode_pcomp_params(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, int src_len) { int rc; switch (comp_field->algo) { case RFC_1144: comp_field->rfc1144_params = talloc_zero(comp_field, struct gprs_sndcp_pcomp_rfc1144_params); rc = decode_pcomp_rfc1144_params(comp_field->rfc1144_params, src, src_len); if (rc < 0) talloc_free(comp_field->rfc1144_params); break; case RFC_2507: comp_field->rfc2507_params = talloc_zero(comp_field, struct gprs_sndcp_pcomp_rfc2507_params); rc = decode_pcomp_rfc2507_params(comp_field->rfc2507_params, src, src_len); if (rc < 0) talloc_free(comp_field->rfc1144_params); break; case ROHC: comp_field->rohc_params = talloc_zero(comp_field, struct gprs_sndcp_pcomp_rohc_params); rc = decode_pcomp_rohc_params(comp_field->rohc_params, src, src_len); if (rc < 0) talloc_free(comp_field->rohc_params); break; /* If no suitable decoder is detected, leave the remaining bytes undecoded */ default: rc = src_len; } if (rc < 0) { comp_field->rfc1144_params = NULL; comp_field->rfc2507_params = NULL; comp_field->rohc_params = NULL; } return rc; } /* Helper function for decode_comp_field(), decodes the parameters * which are data compression specific */ static int decode_dcomp_params(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, int src_len) { int rc; switch (comp_field->algo) { case V42BIS: comp_field->v42bis_params = talloc_zero(comp_field, struct gprs_sndcp_dcomp_v42bis_params); rc = decode_dcomp_v42bis_params(comp_field->v42bis_params, src, src_len); if (rc < 0) talloc_free(comp_field->v42bis_params); break; case V44: comp_field->v44_params = talloc_zero(comp_field, struct gprs_sndcp_dcomp_v44_params); rc = decode_dcomp_v44_params(comp_field->v44_params, src, src_len); if (rc < 0) talloc_free(comp_field->v44_params); break; /* If no suitable decoder is detected, * leave the remaining bytes undecoded */ default: rc = src_len; } if (rc < 0) { comp_field->v42bis_params = NULL; comp_field->v44_params = NULL; } return rc; } /* Decode data or protocol control information compression field * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and * 3GPP TS 44.065, 6.5.1.1, Figure 7) */ static int decode_comp_field(struct gprs_sndcp_comp_field *comp_field, const uint8_t *src, unsigned int src_len, const struct entity_algo_table *lt, unsigned int lt_len, int compclass) { int src_counter = 0; unsigned int len; int rc; OSMO_ASSERT(comp_field); /* Exit immediately if it is clear that no parseable data is present */ if (src_len < 1 || !src) return -EINVAL; /* Zero out target struct */ memset(comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); /* Decode Propose bit and Entity number */ if ((*src) & 0x80) comp_field->p = 1; comp_field->entity = (*src) & 0x1F; src_counter++; src++; /* Decode algorithm number (if present) */ if (comp_field->p) { comp_field->algo = (*src) & 0x1F; src_counter++; src++; } /* Alternatively take the information from the lookup table */ else comp_field->algo = lookup_algorithm_identifier(comp_field->entity, lt, lt_len, compclass); /* Decode length field */ len = *src; src_counter++; src++; /* Decode PCOMP/DCOMP values */ rc = decode_comp_values(comp_field, src, compclass); if (rc < 0) return -EINVAL; src_counter += rc; src += rc; len -= rc; /* Decode algorithm specific payload data */ if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) rc = decode_pcomp_params(comp_field, src, len); else if (compclass == SNDCP_XID_DATA_COMPRESSION) rc = decode_dcomp_params(comp_field, src, len); else return -EINVAL; if (rc >= 0) src_counter += rc; else return -EINVAL; /* Return consumed length */ return src_counter; } /* Helper function for gprs_sndcp_decode_xid() to decode XID blocks */ static int decode_xid_block(struct llist_head *comp_fields, uint8_t tag, uint16_t tag_len, const uint8_t *val, const struct entity_algo_table *lt, unsigned int lt_len) { struct gprs_sndcp_comp_field *comp_field; int byte_counter = 0; int comp_field_count = 0; int rc; byte_counter = 0; do { /* Bail if more than the maximum number of comp_fields is generated */ if (comp_field_count > MAX_ENTITIES * 2) { return -EINVAL; } /* Parse and add comp_field */ comp_field = talloc_zero(comp_fields, struct gprs_sndcp_comp_field); rc = decode_comp_field(comp_field, val + byte_counter, tag_len - byte_counter, lt, lt_len, tag); if (rc < 0) { talloc_free(comp_field); return -EINVAL; } byte_counter += rc; llist_add(&comp_field->list, comp_fields); comp_field_count++; } while (tag_len - byte_counter > 0); return byte_counter; } /* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ static int gprs_sndcp_decode_xid(int *version, struct llist_head *comp_fields, const uint8_t *src, unsigned int src_len, const struct entity_algo_table *lt, unsigned int lt_len) { int src_pos = 0; uint8_t tag; uint16_t tag_len; const uint8_t *val; int byte_counter = 0; int rc; int tlv_count = 0; /* Preset version value as invalid */ if (version) *version = -1; /* Valid TLV-Tag and types */ static const struct tlv_definition sndcp_xid_def = { .def = { [SNDCP_XID_VERSION_NUMBER] = {TLV_TYPE_TLV,}, [SNDCP_XID_DATA_COMPRESSION] = {TLV_TYPE_TLV,}, [SNDCP_XID_PROTOCOL_COMPRESSION] = {TLV_TYPE_TLV,}, }, }; /* Parse TLV-Encoded SNDCP-XID message and defer payload to the apporpiate sub-parser functions */ while (1) { /* Bail if an the maximum number of TLV fields * have been parsed */ if (tlv_count >= 3) { talloc_free(comp_fields); return -EINVAL; } /* Parse TLV field */ rc = tlv_parse_one(&tag, &tag_len, &val, &sndcp_xid_def, src + src_pos, src_len - src_pos); if (rc > 0) src_pos += rc; else { talloc_free(comp_fields); return -EINVAL; } /* Decode sndcp xid version number */ if (version && tag == SNDCP_XID_VERSION_NUMBER) *version = val[0]; /* Decode compression parameters */ if ((tag == SNDCP_XID_PROTOCOL_COMPRESSION) || (tag == SNDCP_XID_DATA_COMPRESSION)) { rc = decode_xid_block(comp_fields, tag, tag_len, val, lt, lt_len); if (rc < 0) { talloc_free(comp_fields); return -EINVAL; } else byte_counter += rc; } /* Stop when no further TLV elements can be expected */ if (src_len - src_pos <= 2) break; tlv_count++; } return 0; } /* Fill up lookutable from a list with comression entitiy fields */ static int gprs_sndcp_fill_table(struct entity_algo_table *lt, unsigned int lt_len, const struct llist_head *comp_fields) { struct gprs_sndcp_comp_field *comp_field; int i = 0; int rc; if (!comp_fields) return -EINVAL; if (!lt) return -EINVAL; memset(lt, 0, sizeof(*lt)); llist_for_each_entry(comp_field, comp_fields, list) { if (comp_field->algo >= 0) { lt[i].entity = comp_field->entity; lt[i].algo = comp_field->algo; rc = gprs_sndcp_get_compression_class(comp_field); if (rc < 0) { memset(lt, 0, sizeof(*lt)); return -EINVAL; } lt[i].compclass = rc; i++; } } return i; } /* Complete comp field params * (if a param (dst) is not valid, it will be copied from source (src) */ static int complete_comp_field_params(struct gprs_sndcp_comp_field *comp_field_dst, const struct gprs_sndcp_comp_field *comp_field_src) { if (comp_field_dst->algo < 0) return -EINVAL; if (comp_field_dst->rfc1144_params && comp_field_src->rfc1144_params) { if (comp_field_dst->rfc1144_params->s01 < 0) { comp_field_dst->rfc1144_params->s01 = comp_field_src->rfc1144_params->s01; } return 0; } if (comp_field_dst->rfc2507_params && comp_field_src->rfc2507_params) { if (comp_field_dst->rfc2507_params->f_max_period < 0) { comp_field_dst->rfc2507_params->f_max_period = comp_field_src->rfc2507_params->f_max_period; } if (comp_field_dst->rfc2507_params->f_max_time < 0) { comp_field_dst->rfc2507_params->f_max_time = comp_field_src->rfc2507_params->f_max_time; } if (comp_field_dst->rfc2507_params->max_header < 0) { comp_field_dst->rfc2507_params->max_header = comp_field_src->rfc2507_params->max_header; } if (comp_field_dst->rfc2507_params->tcp_space < 0) { comp_field_dst->rfc2507_params->tcp_space = comp_field_src->rfc2507_params->tcp_space; } if (comp_field_dst->rfc2507_params->non_tcp_space < 0) { comp_field_dst->rfc2507_params->non_tcp_space = comp_field_src->rfc2507_params->non_tcp_space; } return 0; } if (comp_field_dst->rohc_params && comp_field_src->rohc_params) { if (comp_field_dst->rohc_params->max_cid < 0) { comp_field_dst->rohc_params->max_cid = comp_field_src->rohc_params->max_cid; } if (comp_field_dst->rohc_params->max_header < 0) { comp_field_dst->rohc_params->max_header = comp_field_src->rohc_params->max_header; } if (comp_field_dst->rohc_params->profile_len > 0) { memcpy(comp_field_dst->rohc_params->profile, comp_field_src->rohc_params->profile, sizeof(comp_field_dst->rohc_params->profile)); comp_field_dst->rohc_params->profile_len = comp_field_src->rohc_params->profile_len; } return 0; } if (comp_field_dst->v42bis_params && comp_field_src->v42bis_params) { if (comp_field_dst->v42bis_params->p0 < 0) { comp_field_dst->v42bis_params->p0 = comp_field_src->v42bis_params->p0; } if (comp_field_dst->v42bis_params->p1 < 0) { comp_field_dst->v42bis_params->p1 = comp_field_src->v42bis_params->p1; } if (comp_field_dst->v42bis_params->p2 < 0) { comp_field_dst->v42bis_params->p2 = comp_field_src->v42bis_params->p2; } return 0; } if (comp_field_dst->v44_params && comp_field_src->v44_params) { if (comp_field_dst->v44_params->c0 < 0) { comp_field_dst->v44_params->c0 = comp_field_src->v44_params->c0; } if (comp_field_dst->v44_params->p0 < 0) { comp_field_dst->v44_params->p0 = comp_field_src->v44_params->p0; } if (comp_field_dst->v44_params->p1t < 0) { comp_field_dst->v44_params->p1t = comp_field_src->v44_params->p1t; } if (comp_field_dst->v44_params->p1r < 0) { comp_field_dst->v44_params->p1r = comp_field_src->v44_params->p1r; } if (comp_field_dst->v44_params->p3t < 0) { comp_field_dst->v44_params->p3t = comp_field_src->v44_params->p3t; } if (comp_field_dst->v44_params->p3r < 0) { comp_field_dst->v44_params->p3r = comp_field_src->v44_params->p3r; } return 0; } /* There should be at least exist one param set * in the destination struct, otherwise something * must be wrong! */ return -EINVAL; } /* Complete missing parameters in a comp_field */ static int gprs_sndcp_complete_comp_field(struct gprs_sndcp_comp_field *comp_field, const struct llist_head *comp_fields) { struct gprs_sndcp_comp_field *comp_field_src; int rc = 0; llist_for_each_entry(comp_field_src, comp_fields, list) { if (comp_field_src->entity == comp_field->entity) { /* Complete header fields */ if (comp_field_src->comp_len > 0) { memcpy(comp_field->comp, comp_field_src->comp, sizeof(comp_field_src->comp)); comp_field->comp_len = comp_field_src->comp_len; } /* Complete parameter fields */ rc = complete_comp_field_params(comp_field, comp_field_src); } } return rc; } /* Complete missing parameters of all comp_field in a list */ static int gprs_sndcp_complete_comp_fields(struct llist_head *comp_fields_incomplete, const struct llist_head *comp_fields) { struct gprs_sndcp_comp_field *comp_field_incomplete; int rc; llist_for_each_entry(comp_field_incomplete, comp_fields_incomplete, list) { rc = gprs_sndcp_complete_comp_field(comp_field_incomplete, comp_fields); if (rc < 0) return -EINVAL; } return 0; } /* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ struct llist_head *gprs_sndcp_parse_xid(int *version, const void *ctx, const uint8_t *src, unsigned int src_len, const struct llist_head *comp_fields_req) { int rc; int lt_len; struct llist_head *comp_fields; struct entity_algo_table lt[MAX_ENTITIES * 2]; /* In case of a zero length field, just exit */ if (src_len == 0) return NULL; /* We should go any further if we have a field length greater * zero and a null pointer as buffer! */ OSMO_ASSERT(src); comp_fields = talloc_zero(ctx, struct llist_head); INIT_LLIST_HEAD(comp_fields); if (comp_fields_req) { /* Generate lookup table */ lt_len = gprs_sndcp_fill_table(lt, MAX_ENTITIES * 2, comp_fields_req); if (lt_len < 0) { talloc_free(comp_fields); return NULL; } /* Parse SNDCP-CID XID-Field */ rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len, lt, lt_len); if (rc < 0) { talloc_free(comp_fields); return NULL; } rc = gprs_sndcp_complete_comp_fields(comp_fields, comp_fields_req); if (rc < 0) { talloc_free(comp_fields); return NULL; } } else { /* Parse SNDCP-CID XID-Field */ rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len, NULL, 0); if (rc < 0) { talloc_free(comp_fields); return NULL; } } return comp_fields; } /* Helper for gprs_sndcp_dump_comp_fields(), * dumps protocol compression parameters */ static void dump_pcomp_params(const struct gprs_sndcp_comp_field *comp_field, unsigned int logl) { int i; switch (comp_field->algo) { case RFC_1144: if (comp_field->rfc1144_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc1144_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc1144_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->rfc1144_params->nsapi_len); if (comp_field->rfc1144_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->rfc1144_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->rfc1144_params->nsapi[i]); } LOGP(DSNDCP, logl, " s01=%d;\n", comp_field->rfc1144_params->s01); LOGP(DSNDCP, logl, " }\n"); break; case RFC_2507: if (comp_field->rfc2507_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc2507_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc2507_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->rfc2507_params->nsapi_len); if (comp_field->rfc2507_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->rfc2507_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->rfc2507_params->nsapi[i]); } LOGP(DSNDCP, logl, " f_max_period=%d;\n", comp_field->rfc2507_params->f_max_period); LOGP(DSNDCP, logl, " f_max_time=%d;\n", comp_field->rfc2507_params->f_max_time); LOGP(DSNDCP, logl, " max_header=%d;\n", comp_field->rfc2507_params->max_header); LOGP(DSNDCP, logl, " tcp_space=%d;\n", comp_field->rfc2507_params->tcp_space); LOGP(DSNDCP, logl, " non_tcp_space=%d;\n", comp_field->rfc2507_params->non_tcp_space); LOGP(DSNDCP, logl, " }\n"); break; case ROHC: if (comp_field->rohc_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rohc_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rohc_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->rohc_params->nsapi_len); if (comp_field->rohc_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->rohc_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->rohc_params->nsapi[i]); } LOGP(DSNDCP, logl, " max_cid=%d;\n", comp_field->rohc_params->max_cid); LOGP(DSNDCP, logl, " max_header=%d;\n", comp_field->rohc_params->max_header); LOGP(DSNDCP, logl, " profile_len=%d;\n", comp_field->rohc_params->profile_len); if (comp_field->rohc_params->profile_len == 0) LOGP(DSNDCP, logl, " profile[] = NULL;\n"); for (i = 0; i < comp_field->rohc_params->profile_len; i++) LOGP(DSNDCP, logl, " profile[%d]=%04x;\n", i, comp_field->rohc_params->profile[i]); LOGP(DSNDCP, logl, " }\n"); break; } } /* Helper for gprs_sndcp_dump_comp_fields(), * data protocol compression parameters */ static void dump_dcomp_params(const struct gprs_sndcp_comp_field *comp_field, unsigned int logl) { int i; switch (comp_field->algo) { case V42BIS: if (comp_field->v42bis_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v42bis_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v42bis_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->v42bis_params->nsapi_len); if (comp_field->v42bis_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->v42bis_params->nsapi_len; i++) LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->v42bis_params->nsapi[i]); LOGP(DSNDCP, logl, " p0=%d;\n", comp_field->v42bis_params->p0); LOGP(DSNDCP, logl, " p1=%d;\n", comp_field->v42bis_params->p1); LOGP(DSNDCP, logl, " p2=%d;\n", comp_field->v42bis_params->p2); LOGP(DSNDCP, logl, " }\n"); break; case V44: if (comp_field->v44_params == NULL) { LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v44_params=NULL\n"); break; } LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v44_params {\n"); LOGP(DSNDCP, logl, " nsapi_len=%d;\n", comp_field->v44_params->nsapi_len); if (comp_field->v44_params->nsapi_len == 0) LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); for (i = 0; i < comp_field->v44_params->nsapi_len; i++) { LOGP(DSNDCP, logl, " nsapi[%d]=%d;\n", i, comp_field->v44_params->nsapi[i]); } LOGP(DSNDCP, logl, " c0=%d;\n", comp_field->v44_params->c0); LOGP(DSNDCP, logl, " p0=%d;\n", comp_field->v44_params->p0); LOGP(DSNDCP, logl, " p1t=%d;\n", comp_field->v44_params->p1t); LOGP(DSNDCP, logl, " p1r=%d;\n", comp_field->v44_params->p1r); LOGP(DSNDCP, logl, " p3t=%d;\n", comp_field->v44_params->p3t); LOGP(DSNDCP, logl, " p3r=%d;\n", comp_field->v44_params->p3r); LOGP(DSNDCP, logl, " }\n"); break; } } /* Dump a list with SNDCP-XID fields (Debug) */ void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields, unsigned int logl) { struct gprs_sndcp_comp_field *comp_field; int i; int compclass; OSMO_ASSERT(comp_fields); llist_for_each_entry(comp_field, comp_fields, list) { LOGP(DSNDCP, logl, "SNDCP-XID:\n"); LOGP(DSNDCP, logl, "struct gprs_sndcp_comp_field {\n"); LOGP(DSNDCP, logl, " entity=%d;\n", comp_field->entity); LOGP(DSNDCP, logl, " algo=%d;\n", comp_field->algo); LOGP(DSNDCP, logl, " comp_len=%d;\n", comp_field->comp_len); if (comp_field->comp_len == 0) LOGP(DSNDCP, logl, " comp[] = NULL;\n"); for (i = 0; i < comp_field->comp_len; i++) { LOGP(DSNDCP, logl, " comp[%d]=%d;\n", i, comp_field->comp[i]); } compclass = gprs_sndcp_get_compression_class(comp_field); if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { dump_pcomp_params(comp_field, logl); } else if (compclass == SNDCP_XID_DATA_COMPRESSION) { dump_dcomp_params(comp_field, logl); } LOGP(DSNDCP, logl, "}\n"); } }