This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
dexter gerrit-no-reply at lists.osmocom.orgHello Harald Welte, I'd like you to reexamine a change. Please visit https://gerrit.osmocom.org/641 to look at the new patch set (#4). Added SNDCP-XID encoder / decoder The SNDCP-XID (or layer-3 xid) is used to exchange layer-3 parameters such as compression. The encoder encodes a bytestream that is then sent as regular XID field from LLC. We will need the SNDCP-XID to negotiate the parameters for our upcomming GPRS data and header compression features Change-Id: If2d63fe2550864cafef3156b1dc0629037c49c1e --- M openbsc/include/openbsc/Makefile.am A openbsc/include/openbsc/gprs_sndcp_xid.h M openbsc/src/gprs/Makefile.am A openbsc/src/gprs/gprs_sndcp_xid.c 4 files changed, 1,971 insertions(+), 3 deletions(-) git pull ssh://gerrit.osmocom.org:29418/openbsc refs/changes/41/641/4 diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index 9e8c554..b37103f 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -18,7 +18,7 @@ gprs_gb_parse.h smpp.h meas_feed.h \ gprs_gsup_client.h bsc_msg_filter.h \ oap.h oap_messages.h \ - gtphub.h gprs_sndcp.h slhc.h gprs_llc_xid.h + gtphub.h gprs_sndcp.h slhc.h gprs_llc_xid.h gprs_sndcp_xid.h openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/gprs_sndcp_xid.h b/openbsc/include/openbsc/gprs_sndcp_xid.h new file mode 100644 index 0000000..ec14a55 --- /dev/null +++ b/openbsc/include/openbsc/gprs_sndcp_xid.h @@ -0,0 +1,220 @@ +#ifndef _GPRS_SNDCP_XID_H +#define _GPRS_SNDCP_XID_H + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> + +#define CURRENT_SNDCP_VERSION 0 /* See TS 144 065, clause 8 */ +#define MAX_ENTITIES 32 /* TS 144 065 reserves 5 bit for compr. entity num. */ + +/* According to: TS 144 065 6.5.1.1 Format of the protocol control information + compression field (Figure 7) + + TS 144 065 6.6.1.1 Format of the data compression + field (Figure 9) */ + +struct gprs_sndcp_comp_field { + struct llist_head list; + + /* Propose bit (P), see also: 6.5.1.1.2 and 6.6.1.1.2 */ + unsigned int p; + + /* Entity number, see also: 6.5.1.1.3 and 6.6.1.1.3 */ + unsigned int entity; + + /* Algorithm identifier, see also: 6.5.1.1.4 and 6.6.1.1.4 */ + unsigned int algo; + + /* Number of contained PCOMP / DCOMP values */ + unsigned int comp_len; + + /* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */ + unsigned int comp[16]; + + /* Note: Only one of the following struct pointers may, + be used unused pointers must be set to NULL! */ + struct gprs_sndcp_hdrcomp_rfc1144_params *rfc1144_params; + struct gprs_sndcp_hdrcomp_rfc2507_params *rfc2507_params; + struct gprs_sndcp_hdrcomp_rohc_params *rohc_params; + struct gprs_sndcp_datacomp_v42bis_params *v42bis_params; + struct gprs_sndcp_datacomp_v44_params *v44_params; +}; + +/* According to: TS 144 065 6.5.1.1.4 Algorithm identifier */ +enum gprs_sndcp_hdr_comp_algo { + RFC_1144 = 0, /* TCP/IP header compression, see also 6.5.2 */ + RFC_2507 = 1, /* TCP/UDP/IP header compression, see also: 6.5.3 */ + ROHC = 2, /* Robust Header Compression, see also 6.5.4 */ +}; + +/* According to: TS 144 065 6.5.1.1.4 Algorithm identifier */ +enum gprs_sndcp_data_comp_algo { + V42BIS = 0, /* V42bis data compression, see also 6.6.2 */ + V44 = 1, /* V44 data compression, see also: 6.6.3 */ +}; + +/* According to: TS 144 065 8 SNDCP XID parameters */ +enum gprs_sndcp_xid_param_types { + SNDCP_XID_VERSION_NUMBER = 0, + SNDCP_XID_DATA_COMPRESSION = 1, /* See also: subclause 6.6.1 */ + SNDCP_XID_PROTOCOL_COMPRESSION = 2, /* See also: subclause 6.5.1 */ +}; + +/* 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 gprs_sndcp_hdrcomp_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 */ +}; + + + +/* According to: TS 144 065 6.5.2.1 Parameters (Table 5) */ +struct gprs_sndcp_hdrcomp_rfc1144_params { + unsigned int nsapi_len; /* Number of applicable NSAPIs (default 0) */ + unsigned int nsapi[11]; /* Applicable NSAPIs (default 0) */ + int s01; /* (default 15) */ +}; + +/* According to: TS 144 065 6.5.2.2 Assignment of PCOMP values */ +enum gprs_sndcp_hdrcomp_rfc1144_pcomp { + RFC1144_PCOMP1 = 0, /* Uncompressed TCP */ + RFC1144_PCOMP2 = 1, /* Compressed TCP */ + RFC1144_PCOMP_NUM = 2 /* Number of pcomp values */ +}; + + + +/* According to: TS 144 065 6.5.3.1 Parameters (Table 6) */ +struct gprs_sndcp_hdrcomp_rfc2507_params { + unsigned int nsapi_len; /* Number of applicable NSAPIs (default 0) */ + unsigned int nsapi[11]; /* Applicable NSAPIs (default 0) */ + int f_max_period; /* (default 256) */ + int f_max_time; /* (default 5) */ + int max_header; /* (default 168) */ + int tcp_space; /* (default 15) */ + int non_tcp_space; /* (default 15) */ +}; + +/* According to: TS 144 065 6.5.3.2 Assignment of PCOMP values for RFC2507 */ +enum gprs_sndcp_hdrcomp_rfc2507_pcomp { + RFC2507_PCOMP1 = 0, /* Full Header */ + RFC2507_PCOMP2 = 1, /* Compressed TCP */ + RFC2507_PCOMP3 = 2, /* Compressed TCP non delta */ + RFC2507_PCOMP4 = 3, /* Compressed non TCP */ + RFC2507_PCOMP5 = 4, /* Context state */ + RFC2507_PCOMP_NUM = 5 /* Number of pcomp values */ +}; + + + +/* According to: TS 144 065 6.5.4.1 Parameter (Table 10) */ +struct gprs_sndcp_hdrcomp_rohc_params { + unsigned int nsapi_len; /* Number of applicable NSAPIs (default 0) */ + unsigned int nsapi[11]; /* Applicable NSAPIs (default 0) */ + int max_cid; /* (default 15) */ + int max_header; /* (default 168) */ + unsigned int profile_len; /* (default 1) */ + uint16_t profile[16]; /* (default 0, ROHC uncompressed) */ +}; + +/* According to: TS 144 065 6.5.4.2 Assignment of PCOMP values for ROHC */ +enum gprs_sndcp_hdrcomp_rohc_pcomp { + ROHC_PCOMP1 = 0, /* ROHC small CIDs */ + ROHC_PCOMP2 = 1, /* ROHC large CIDs */ + ROHC_PCOMP_NUM = 2 /* Number of pcomp values */ +}; + +/* ROHC compression profiles, see also: + http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.xhtml */ +enum gprs_sndcp_xid_rohc_profiles { + ROHC_UNCOMPRESSED = 0x0000, /* ROHC uncompressed [RFC5795] */ + ROHC_RTP = 0x0001, /* ROHC RTP [RFC3095] */ + ROHCV2_RTP = 0x0101, /* ROHCv2 RTP [RFC5225] */ + ROHC_UDP = 0x0002, /* ROHC UDP [RFC3095] */ + ROHCv2_UDP = 0x0102, /* ROHCv2 UDP [RFC5225] */ + ROHC_ESP = 0x0003, /* ROHC ESP [RFC3095] */ + ROHCV2_ESP = 0x0103, /* ROHCv2 ESP [RFC5225] */ + ROHC_IP = 0x0004, /* ROHC IP [RFC3843] */ + ROHCV2_IP = 0x0104, /* ROHCv2 IP [RFC5225] */ + ROHC_LLA = 0x0005, /* ROHC LLA [RFC4362] */ + ROHC_LLA_WITH_R_MODE = 0x0105, /* ROHC LLA with R-mode [RFC3408] */ + ROHC_TCP = 0x0006, /* ROHC TCP [RFC6846] */ + ROHC_RTP_UDP_LITE = 0x0007, /* ROHC RTP/UDP-Lite [RFC4019] */ + ROHCV2_RTP_UDP_LITE = 0x0107, /* ROHCv2 RTP/UDP-Lite [RFC5225] */ + ROHC_UDP_LITE = 0x0008, /* ROHC UDP-Lite [RFC4019] */ + ROHCV2_UDP_LITE = 0x0108, /* ROHCv2 UDP-Lite [RFC5225] */ +}; + + + +/* According to: TS 144 065 6.6.2.1 Parameters (Table 7a) */ +struct gprs_sndcp_datacomp_v42bis_params { + unsigned int nsapi_len; /* Number of applicable NSAPIs (default 0) */ + unsigned int nsapi[11]; /* Applicable NSAPIs (default 0) */ + int p0; /* (default 3) */ + int p1; /* (default 2048) */ + int p2; /* (default 20) */ + +}; + +/* According to: ETSI TS 144 065 6.6.2.2 Assignment of DCOMP values */ +enum gprs_sndcp_datacomp_v42bis_dcomp { + V42BIS_DCOMP1 = 0, /* V42bis enabled */ + V42BIS_DCOMP_NUM = 1 /* Number of dcomp values */ +}; + + + +/* According to: TS 144 065 6.6.3.1 Parameters (Table 7c) */ +struct gprs_sndcp_datacomp_v44_params { + unsigned int nsapi_len; /* Number of applicable NSAPIs (default 0) */ + unsigned int nsapi[11]; /* Applicable NSAPIs (default 0) */ + int c0; /* (default 10000000) */ + int p0; /* (default 3) */ + int p1t; /* Refer to subclause 6.6.3.1.4 */ + int p1r; /* Refer to subclause 6.6.3.1.5 */ + int p3t; /* (default 3 x p1t) */ + int p3r; /* (default 3 x p1r) */ +}; + +/* According to: ETSI TS 144 065 6.6.3.2 Assignment of DCOMP values */ +enum gprs_sndcp_datacomp_v44_dcomp { + V44_DCOMP1 = 0, /* Packet method compressed */ + V44_DCOMP2 = 1, /* Multi packet method compressed */ + V44_DCOMP_NUM = 2 /* Number of dcomp values */ +}; + +/* Transform a list with compression fields into an SNDCP-XID message (dst) */ +int gprs_sndcp_compile_xid(const struct llist_head *comp_fields, uint8_t *dst, + unsigned int dst_maxlen); + +/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ +int gprs_sndcp_parse_xid(struct llist_head *comp_fields, const uint8_t *src, + unsigned int dst_len, + struct gprs_sndcp_hdrcomp_entity_algo_table *lt, + unsigned int lt_len); + +/* Free a list with SNDCP-XID fields */ +void gprs_sndcp_free_comp_fields(struct llist_head *comp_fields); + +/* 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); + +/* Fill up lookutable from a list with comression entitiy fields */ +int gprs_sndcp_fill_table(struct gprs_sndcp_hdrcomp_entity_algo_table *lt, + unsigned int lt_len, + const struct llist_head *comp_fields); + +/* Dump a list with SNDCP-XID fields (Debug) */ +void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields, + unsigned int logl); + + +#endif diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index b3a5137..67e9943 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -21,7 +21,7 @@ $(OSMO_LIBS) $(LIBCRYPTO_LIBS) -lrt osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ - slhc.c \ + slhc.c gprs_sndcp_xid.c \ sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ gprs_llc.c gprs_llc_parse.c gprs_llc_vty.c \ gprs_llc_xid.c crc24.c \ @@ -32,7 +32,7 @@ osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \ - $(LIBCRYPTO_LIBS) -lrt + $(LIBCRYPTO_LIBS) -lrt -lm osmo_gtphub_SOURCES = gtphub_main.c gtphub.c gtphub_sock.c gtphub_ares.c \ gtphub_vty.c sgsn_ares.c gprs_utils.c diff --git a/openbsc/src/gprs/gprs_sndcp_xid.c b/openbsc/src/gprs/gprs_sndcp_xid.c new file mode 100644 index 0000000..070058c --- /dev/null +++ b/openbsc/src/gprs/gprs_sndcp_xid.c @@ -0,0 +1,1748 @@ +/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 144 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/tlv.h> + +#include <openbsc/debug.h> +#include <openbsc/gprs_llc.h> +#include <openbsc/sgsn.h> +#include <openbsc/gprs_sndcp_xid.h> + + +/* + * FUNCTIONS RELATED TO SNDCP-XID ENCODING + */ + +/* Encode applicable sapis (works the same in all three compression schemes) */ +static int encode_hdrcomp_applicable_sapis(uint8_t * dst, + const unsigned int *nsapis, + unsigned int nsapis_len) +{ + /* + * NOTE: Buffer *dst needs offer at 2 bytes + * of space to store the generation results + */ + + uint16_t blob; + unsigned int nsapi; + int i; + + /* 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 ETSI TS 144 065 5.1 Service primitives + */ + if ((nsapi < 5) || (nsapi > 15)) + return -EINVAL; + blob |= (1 << nsapi); + } + + /* Store result */ + *dst = (blob >> 8) & 0xFF; + dst++; + *dst = blob & 0xFF; + + return 2; +} + + +/* + * Encode rfc1144 parameter field + * (see also: ETSI TS 144 065 6.5.2.1, Table 5) + */ +static int encode_hdrcomp_rfc1144_params(uint8_t * dst, + unsigned int dst_maxlen, const struct + gprs_sndcp_hdrcomp_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; + + /* Exit immediately if no source struct is available */ + if (!params) + return -EINVAL; + + /* Exit immediately if no sufficient memory space is supplied */ + if ((dst_maxlen < 3) || !dst) + return -EINVAL; + + /* + * Exit if number of possible nsapis exceeds valid range + * (Only 11 nsapis possible for PDP-Contexts) + */ + if ((params->nsapi_len < 0) || (params->nsapi_len > 11)) + return -EINVAL; + + /* Zero out buffer */ + memset(dst, 0, dst_maxlen); + + /* Encode applicable SAPIs */ + rc = encode_hdrcomp_applicable_sapis(dst, params->nsapi, + params->nsapi_len); + dst += rc; + dst_counter += rc; + + /* Encode s01 (see also: ETSI TS 144 065 6.5.2.1, Table 5) */ + *dst = params->s01; + dst++; + dst_counter++; + + /* Return generated length */ + return dst_counter; +} + + +/* + * Encode rfc2507 parameter field + * (see also: ETSI TS 144 065 6.5.3.1, Table 6) + */ +static int encode_hdrcomp_rfc2507_params(uint8_t * dst, + unsigned int dst_maxlen, const struct + gprs_sndcp_hdrcomp_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; + + /* Exit immediately if no source struct is available */ + if (!params) + return -EINVAL; + + /* Exit immediately if no sufficient memory space is supplied */ + if ((dst_maxlen < 9) || !dst) + return -EINVAL; + + /* + * Exit if number of possible nsapis exceeds valid range + * (Only 11 nsapis possible for PDP-Contexts) + */ + if ((params->nsapi_len < 0) || (params->nsapi_len > 11)) + return -EINVAL; + + /* Zero out buffer */ + memset(dst, 0, dst_maxlen); + + /* Encode applicable SAPIs */ + rc = encode_hdrcomp_applicable_sapis(dst, params->nsapi, + params->nsapi_len); + dst += rc; + dst_counter += rc; + + /* Encode F_MAX_PERIOD (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + if ((params->f_max_period < 1) || (params->f_max_period > 65535)) + return -EINVAL; + *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: ETSI TS 144 065 6.5.3.1, Table 6) */ + if ((params->f_max_time < 1) || (params->f_max_time > 255)) + return -EINVAL; + *dst = params->f_max_time; + dst++; + dst_counter++; + + /* Encode MAX_HEADER (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + if ((params->max_header < 60) || (params->max_header > 255)) + return -EINVAL; + *dst = params->max_header; + dst++; + dst_counter++; + + /* Encode TCP_SPACE (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + if ((params->tcp_space < 3) || (params->tcp_space > 255)) + return -EINVAL; + *dst = params->tcp_space; + dst++; + dst_counter++; + + /* Encode NON_TCP_SPACE (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + if ((params->non_tcp_space < 3) || (params->tcp_space > 65535)) + return -EINVAL; + *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: ETSI TS 144 065 6.5.4.1, Table 10) + */ +static int encode_hdrcomp_rohc_params(uint8_t * dst, + unsigned int dst_maxlen, + const struct gprs_sndcp_hdrcomp_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; + + /* Exit immediately if no source struct is available */ + if (!params) + return -EINVAL; + + /* Exit immediately if no sufficient memory space is supplied */ + if ((dst_maxlen < 38) || !dst) + return -EINVAL; + + /* + * Exit if number of possible nsapis exceeds valid range + * (Only 11 nsapis possible for PDP-Contexts) + */ + if ((params->nsapi_len < 0) || (params->nsapi_len > 11)) + return -EINVAL; + + /* + * Exit if number of ROHC profiles exceeds limit + * (ROHC supports only a maximum of 16 different profiles) + */ + if ((params->profile_len < 0) || (params->profile_len > 16)) + return -EINVAL; + + /* Zero out buffer */ + memset(dst, 0, dst_maxlen); + + /* Encode applicable SAPIs */ + rc = encode_hdrcomp_applicable_sapis(dst, params->nsapi, + params->nsapi_len); + dst += rc; + dst_counter += rc; + + /* Encode MAX_CID (see also: ETSI TS 144 065 6.5.4.1, Table 10) */ + if ((params->max_cid < 0) || (params->max_cid > 16383)) + return -EINVAL; + *dst = (params->max_cid >> 8) & 0xFF; + dst++; + *dst = params->max_cid & 0xFF; + dst++; + dst_counter += 2; + + /* Encode MAX_HEADER (see also: ETSI TS 144 065 6.5.4.1, Table 10) */ + if ((params->max_header < 60) || (params->max_header > 255)) + return -EINVAL; + *dst = (params->max_header >> 8) & 0xFF; + dst++; + *dst = params->max_header & 0xFF; + dst++; + dst_counter += 2; + + /* Encode ROHC Profiles (see also: ETSI TS 144 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 V42bis parameter field + * (see also: ETSI TS 144 065 6.6.2.1, Table 7a) + */ +static int encode_datacomp_v42bis_params(uint8_t * dst, + unsigned int dst_maxlen, const struct + gprs_sndcp_datacomp_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; + + /* Exit immediately if no source struct is available */ + if (!params) + return -EINVAL; + + /* Exit immediately if no sufficient memory space is supplied */ + if ((dst_maxlen < 6) || !dst) + return -EINVAL; + + /* + * Exit if number of possible nsapis exceeds valid range + * (Only 11 nsapis possible for PDP-Contexts) + */ + if ((params->nsapi_len < 0) || (params->nsapi_len > 11)) + return -EINVAL; + + /* Zero out buffer */ + memset(dst, 0, dst_maxlen); + + /* Encode applicable SAPIs */ + rc = encode_hdrcomp_applicable_sapis(dst, params->nsapi, + params->nsapi_len); + dst += rc; + dst_counter += rc; + + /* Encode P0 (see also: ETSI TS 144 065 6.6.2.1, Table 7a) */ + if ((params->p0 < 0) || (params->p0 > 3)) + return -EINVAL; + *dst = params->p0 & 0x03; + dst++; + dst_counter++; + + /* Encode P1 (see also: ETSI TS 144 065 6.6.2.1, Table 7a) */ + if ((params->p1 < 512) || (params->p1 > 65535)) + return -EINVAL; + *dst = (params->p1 >> 8) & 0xFF; + dst++; + *dst = params->p1 & 0xFF; + dst++; + dst_counter += 2; + + /* Encode P2 (see also: ETSI TS 144 065 6.6.2.1, Table 7a) */ + if ((params->p2 < 6) || (params->p2 > 250)) + return -EINVAL; + *dst = params->p2; + dst++; + dst_counter++; + + /* Return generated length */ + return dst_counter; +} + + +/* + * Encode V44 parameter field + * (see also: ETSI TS 144 065 6.6.3.1, Table 7c) + */ +static int encode_datacomp_v44_params(uint8_t * dst, + unsigned int dst_maxlen, + const struct gprs_sndcp_datacomp_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; + + /* Exit immediately if no source struct is available */ + if (!params) + return -EINVAL; + + /* Exit immediately if no sufficient memory space is supplied */ + if ((dst_maxlen < 12) || !dst) + return -EINVAL; + + /* + * Exit if number of possible nsapis exceeds valid range + * (Only 11 nsapis possible for PDP-Contexts) + */ + if ((params->nsapi_len < 0) || (params->nsapi_len > 11)) + return -EINVAL; + + /* Zero out buffer */ + memset(dst, 0, dst_maxlen); + + /* Encode applicable SAPIs */ + rc = encode_hdrcomp_applicable_sapis(dst, params->nsapi, + params->nsapi_len); + dst += rc; + dst_counter += rc; + + /* Encode C0 (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + if ((params->c0 == 0x80) || (params->c0 == 0xC0)) { + *dst = params->c0 & 0xC0; + dst++; + dst_counter++; + } else + return -EINVAL; + + /* Encode P0 (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + if ((params->p0 < 0) || (params->p0 > 3)) + return -EINVAL; + *dst = params->p0 & 0x03; + dst++; + dst_counter++; + + /* Encode P1T (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + if ((params->p1t < 256) || (params->p1t > 65535)) + return -EINVAL; + *dst = (params->p1t >> 8) & 0xFF; + dst++; + *dst = params->p1t & 0xFF; + dst++; + dst_counter += 2; + + /* Encode P1R (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + if ((params->p1r < 256) || (params->p1r > 65535)) + return -EINVAL; + *dst = (params->p1r >> 8) & 0xFF; + dst++; + *dst = params->p1r & 0xFF; + dst++; + dst_counter += 2; + + /* Encode P3T (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + if ((params->p3t < 0) || (params->p3t > 65535)) + return -EINVAL; + if (params->p3t < 2 * params->p1t) + return -EINVAL; + *dst = (params->p3t >> 8) & 0xFF; + dst++; + *dst = params->p3t & 0xFF; + dst++; + dst_counter += 2; + + /* Encode P3R (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + if ((params->p3r < 0) || (params->p3r > 65535)) + return -EINVAL; + if (params->p3r < 2 * params->p1r) + return -EINVAL; + *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: ETSI TS 144 065 6.6.1.1, Figure 9 and + * ETSI TS 144 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_hdrcomp_rfc1144_params(payload_bytes, + sizeof(payload_bytes), + comp_field-> + rfc1144_params); + else if (comp_field->rfc2507_params) + payload_bytes_len = + encode_hdrcomp_rfc2507_params(payload_bytes, + sizeof(payload_bytes), + comp_field-> + rfc2507_params); + else if (comp_field->rohc_params) + payload_bytes_len = + encode_hdrcomp_rohc_params(payload_bytes, + sizeof(payload_bytes), + comp_field->rohc_params); + else if (comp_field->v42bis_params) + payload_bytes_len = + encode_datacomp_v42bis_params(payload_bytes, + sizeof(payload_bytes), + comp_field-> + v42bis_params); + else if (comp_field->v44_params) + payload_bytes_len = + encode_datacomp_v44_params(payload_bytes, + sizeof(payload_bytes), + comp_field->v44_params); + else + return -EINVAL; + + /* Exit immediately if payload byte generation failed */ + if (payload_bytes_len < 0) + return -EINVAL; + + /* Exit immediately if no source struct is available */ + if (!comp_field) + return -EINVAL; + + /* Check if comp_len is within bounds */ + if ((comp_field->comp_len < 0) || (comp_field->comp_len > 16)) + return -EINVAL; + + /* 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; + } + + /* Exit immediately if no sufficient memory space is supplied */ + if ((dst_maxlen < expected_length) || !dst) + return -EINVAL; + + /* Check if the entity number is within bounds */ + if ((comp_field->entity < 0) || (comp_field->entity > 0x1f)) + return -EINVAL; + + /* Check if the algorithm number is within bounds */ + if ((comp_field->algo < 0) || (comp_field->algo > 0x1f)) + return -EINVAL; + + /* 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] < 0) + || (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) +{ + 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(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); + + /* Immediately stop on error */ + if (rc < 0) + return rc; + + 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(const struct llist_head *comp_fields, uint8_t * dst, + unsigned int dst_maxlen) +{ + int rc; + int byte_counter = 0; + uint8_t comp_bytes[512]; + uint8_t xid_version_number[1] = { CURRENT_SNDCP_VERSION }; + + /* Exit immediately if no sufficient memory space is supplied */ + if ((dst_maxlen < 2 + sizeof(xid_version_number)) || !dst) + return -EINVAL; + + /* Prepend header */ + dst = + tlv_put(dst, SNDCP_XID_VERSION_NUMBER, + sizeof(xid_version_number), xid_version_number); + byte_counter += (sizeof(xid_version_number) + 2); + + /* Add data compression fields */ + rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes, + sizeof(comp_bytes), + SNDCP_XID_DATA_COMPRESSION); + if (rc < 0) + return rc; + else 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); + if (rc < 0) + return rc; + else 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_hdrcomp_applicable_sapis(const uint8_t * src, + unsigned int src_len, + unsigned int *nsapis, + unsigned int *nsapis_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_hdrcomp_16_bit_field(const uint8_t * src, + unsigned int src_len, + int value_min, int value_max, + int *value_int, + uint16_t * value_uint16) +{ + 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_hdrcomp_8_bit_field(const uint8_t * src, + unsigned int src_len, + int value_min, int value_max, + int *value_int, + uint8_t * value_uint8) +{ + 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: ETSI TS 144 065 6.5.2.1, Table 5) */ +static int decode_hdrcomp_rfc1144_params(const uint8_t * src, + unsigned int src_len, struct + gprs_sndcp_hdrcomp_rfc1144_params + *params) +{ + int rc; + int byte_counter = 0; + + /* Exit immediately if no result can be stored */ + if (!params) + return -EINVAL; + + /* Mark all optional parameters invalid by default */ + params->s01=-1; + + /* Decode applicable SAPIs */ + rc = decode_hdrcomp_applicable_sapis(src, src_len, + params->nsapi, + ¶ms->nsapi_len); + if (rc > 0) { + byte_counter += rc; + src += rc; + } else + return byte_counter; + + /* + * Decode parameter S0 -1 + * (see also: ETSI TS 144 065 6.5.2.1, Table 5) + */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, 0, + 255, ¶ms->s01, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Return consumed length */ + return byte_counter; +} + +/* + * Decode rfc2507 parameter field + * (see also: ETSI TS 144 065 6.5.3.1, Table 6) + */ +static int decode_hdrcomp_rfc2507_params(const uint8_t * src, + unsigned int src_len, struct + gprs_sndcp_hdrcomp_rfc2507_params + *params) +{ + int rc; + int byte_counter = 0; + + /* Exit immediately if no result can be stored */ + if (!params) + return -EINVAL; + + /* 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_hdrcomp_applicable_sapis(src, src_len, + params->nsapi, + ¶ms->nsapi_len); + if (rc > 0) { + byte_counter += rc; + src += rc; + } else + return byte_counter; + + /* Decode F_MAX_PERIOD (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 1, 65535, ¶ms->f_max_period, + NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode F_MAX_TIME (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, 1, + 255, ¶ms->f_max_time, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode MAX_HEADER (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, + 60, 255, ¶ms->max_header, + NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode TCP_SPACE (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, 3, + 255, ¶ms->tcp_space, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode NON_TCP_SPACE (see also: ETSI TS 144 065 6.5.3.1, Table 6) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 3, 65535, ¶ms->non_tcp_space, + NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Return consumed length */ + return byte_counter; +} + +/* Decode ROHC parameter field (see also: ETSI TS 144 065 6.5.4.1, Table 10) */ +static int decode_hdrcomp_rohc_params(const uint8_t * src, + unsigned int src_len, + struct gprs_sndcp_hdrcomp_rohc_params + *params) +{ + int rc; + int byte_counter = 0; + int i; + + /* Exit immediately if no result can be stored */ + if (!params) + return -EINVAL; + + /* Mark all optional parameters invalid by default */ + params->max_cid=-1; + params->max_header=-1; + + /* Decode applicable SAPIs */ + rc = decode_hdrcomp_applicable_sapis(src, src_len, + params->nsapi, + ¶ms->nsapi_len); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode MAX_CID (see also: ETSI TS 144 065 6.5.4.1, Table 10) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 0, 16383, ¶ms->max_cid, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode MAX_HEADER (see also: ETSI TS 144 065 6.5.4.1, Table 10) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 60, 255, ¶ms->max_header, + NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode Profiles (see also: ETSI TS 144 065 6.5.4.1, Table 10) */ + for (i = 0; i < 16; i++) { + params->profile_len = 0; + rc = decode_hdrcomp_16_bit_field(src, + src_len - byte_counter, + 0, 65535, NULL, + ¶ms->profile[i]); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + params->profile_len = i + 1; + } + + /* Return consumed length */ + return byte_counter; +} + + +/* + * Decode V42bis parameter field + * (see also: ETSI TS 144 065 6.6.2.1, Table 7a) + */ +static int decode_datacomp_v42bis_params(const uint8_t * src, + unsigned int src_len, struct + gprs_sndcp_datacomp_v42bis_params + *params) +{ + int rc; + int byte_counter = 0; + + /* Exit immediately if no result can be stored */ + if (!params) + return -EINVAL; + + /* Mark all optional parameters invalid by default */ + params->p0=-1; + params->p1=-1; + params->p2=-1; + + /* Decode applicable SAPIs */ + rc = decode_hdrcomp_applicable_sapis(src, src_len, + params->nsapi, + ¶ms->nsapi_len); + if (rc > 0) { + byte_counter += rc; + src += rc; + } else + return byte_counter; + + /* Decode P0 (see also: ETSI TS 144 065 6.6.2.1, Table 7a) */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, 0, + 3, ¶ms->p0, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode P1 (see also: ETSI TS 144 065 6.6.2.1, Table 7a) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 512, 65535, ¶ms->p1, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode P2 (see also: ETSI TS 144 065 6.6.2.1, Table 7a) */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, 6, + 250, ¶ms->p2, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Return consumed length */ + return byte_counter; +} + + +/* Decode V44 parameter field (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ +static int decode_datacomp_v44_params(const uint8_t * src, + unsigned int src_len, + struct gprs_sndcp_datacomp_v44_params + *params) +{ + int rc; + int byte_counter = 0; + + /* Exit immediately if no result can be stored */ + if (!params) + return -EINVAL; + + /* 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_hdrcomp_applicable_sapis(src, src_len, + params->nsapi, + ¶ms->nsapi_len); + if (rc > 0) { + byte_counter += rc; + src += rc; + } else + return byte_counter; + + /* Decode C0 (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, 0, + 255, ¶ms->c0, NULL); + if (rc <= 0) + return byte_counter; + if ((params->c0 != 0x80) && (params->c0 != 0xC0)) + return -EINVAL; + byte_counter += rc; + src += rc; + + /* Decode P0 (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + rc = decode_hdrcomp_8_bit_field(src, src_len - byte_counter, 0, + 3, ¶ms->p0, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode P1T (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 265, 65535, ¶ms->p1t, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode P1R (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 265, 65535, ¶ms->p1r, NULL); + if (rc <= 0) + return byte_counter; + byte_counter += rc; + src += rc; + + /* Decode P3T (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 265, 65535, ¶ms->p3t, NULL); + if (rc <= 0) + return byte_counter; + if (params->p3t < 2 * params->p1t) + return -EINVAL; + byte_counter += rc; + src += rc; + + /* Decode P3R (see also: ETSI TS 144 065 6.6.3.1, Table 7c) */ + rc = decode_hdrcomp_16_bit_field(src, src_len - byte_counter, + 265, 65535, ¶ms->p3r, NULL); + 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, struct + gprs_sndcp_hdrcomp_entity_algo_table + *lt, unsigned int lt_len, + int compclass) +{ + int i; + if ((lt) && (lt_len > 0)) { + 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 algorithm specific + */ +static int decode_comp_params(struct gprs_sndcp_comp_field *comp_field, + const uint8_t * src, int src_len, + int compclass) +{ + int rc; + + if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { + /* For protocol compression */ + switch (comp_field->algo) { + case RFC_1144: + comp_field->rfc1144_params = talloc_zero(NULL, struct + gprs_sndcp_hdrcomp_rfc1144_params); + rc = decode_hdrcomp_rfc1144_params(src, src_len, + comp_field-> + rfc1144_params); + if (rc < 0) + talloc_free(comp_field->rfc1144_params); + break; + case RFC_2507: + comp_field->rfc2507_params = talloc_zero(NULL, struct + gprs_sndcp_hdrcomp_rfc2507_params); + rc = decode_hdrcomp_rfc2507_params(src, src_len, + comp_field-> + rfc2507_params); + if (rc < 0) + talloc_free(comp_field->rfc1144_params); + break; + case ROHC: + comp_field->rohc_params = talloc_zero(NULL, struct + gprs_sndcp_hdrcomp_rohc_params); + rc = decode_hdrcomp_rohc_params(src, src_len, + comp_field-> + rohc_params); + 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; + } + } else { + /* For data compression */ + switch (comp_field->algo) { + case V42BIS: + comp_field->v42bis_params = talloc_zero(NULL, struct + gprs_sndcp_datacomp_v42bis_params); + rc = decode_datacomp_v42bis_params(src, src_len, + comp_field-> + v42bis_params); + if (rc < 0) + talloc_free(comp_field->v42bis_params); + break; + case V44: + comp_field->v44_params = talloc_zero(NULL, struct + gprs_sndcp_datacomp_v44_params); + rc = decode_datacomp_v44_params(src, src_len, + comp_field-> + v44_params); + 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->rfc1144_params = NULL; + comp_field->rfc2507_params = NULL; + comp_field->rohc_params = NULL; + comp_field->v42bis_params = NULL; + comp_field->v44_params = NULL; + } + + return rc; +} + + +/* + * Decode data or protocol control information compression field + * (see also: ETSI TS 144 065 6.6.1.1, Figure 9 and + * ETSI TS 144 065 6.5.1.1, Figure 7) + */ +static int decode_comp_field(const uint8_t * src, unsigned int src_len, + struct gprs_sndcp_comp_field *comp_field, + struct + gprs_sndcp_hdrcomp_entity_algo_table + *lt, unsigned int lt_len, int compclass) +{ + int src_counter = 0; + unsigned int len; + int rc; + + /* Exit immediately if it is clear that no + parseable data is present */ + if ((src_len < 1) || !src) + return -EINVAL; + + /* Exit immediately if no result can be stored */ + if (!comp_field) + 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 */ + rc = decode_comp_params(comp_field, src, len, compclass); + if (rc >= 0) + src_counter += rc; + else + return -EINVAL; + + + /* Return consumed length */ + return src_counter; +} + + +/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ +int gprs_sndcp_parse_xid(struct llist_head *comp_fields, const uint8_t * src, + unsigned int src_len, + struct gprs_sndcp_hdrcomp_entity_algo_table + *lt, unsigned int lt_len) +{ + int src_pos = 0; + uint8_t tag; + uint16_t tag_len; + const uint8_t *val; + struct gprs_sndcp_comp_field *comp_field; + int rc; + int byte_counter = 0; + int comp_field_count = 0; + + /* 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) { + src_pos += + tlv_parse_one(&tag, &tag_len, &val, &sndcp_xid_def, + src + src_pos, src_len - src_pos); + + /* Decode compression parameters */ + if ((tag == SNDCP_XID_PROTOCOL_COMPRESSION) + || (tag == SNDCP_XID_DATA_COMPRESSION)) { + byte_counter = 0; + do { + /* Bail if more than the maximum number of + comp_fields is generated */ + if (comp_field_count > MAX_ENTITIES * 2) { + gprs_sndcp_free_comp_fields + (comp_fields); + return -EINVAL; + } + + /* Parse and add comp_field */ + comp_field = + talloc_zero(NULL, + struct + gprs_sndcp_comp_field); + + rc = decode_comp_field(val + byte_counter, + tag_len - + byte_counter, + comp_field, lt, + lt_len, tag); + + if (rc < 0) { + talloc_free(comp_field); + gprs_sndcp_free_comp_fields + (comp_fields); + return -EINVAL; + } else { + byte_counter += rc; + llist_add(&comp_field->list, + comp_fields); + } + + comp_field_count++; + } + while (tag_len - byte_counter > 0); + } + + /* Stop when no further TLV elements can be expected */ + if (src_len - src_pos <= 2) + break; + } + + return 0; +} + + +/* Free a list with SNDCP-XID fields */ +void gprs_sndcp_free_comp_fields(struct llist_head *comp_fields) +{ + struct gprs_sndcp_comp_field *comp_field; + struct llist_head *lh, *lh2; + + /* Exit immediately if no list is present */ + if (!comp_fields) + return; + + llist_for_each_entry(comp_field, comp_fields, list) { + if (comp_field->rfc1144_params) + talloc_free(comp_field->rfc1144_params); + if (comp_field->rfc2507_params) + talloc_free(comp_field->rfc2507_params); + if (comp_field->rohc_params) + talloc_free(comp_field->rohc_params); + if (comp_field->v42bis_params) + talloc_free(comp_field->v42bis_params); + if (comp_field->v44_params) + talloc_free(comp_field->v44_params); + } + + llist_for_each_safe(lh, lh2, comp_fields) { + llist_del(lh); + talloc_free(lh); + } +} + + +/* Fill up lookutable from a list with comression entitiy fields */ +int gprs_sndcp_fill_table(struct gprs_sndcp_hdrcomp_entity_algo_table *lt, + unsigned int lt_len, + const struct llist_head *comp_fields) +{ + struct gprs_sndcp_comp_field *comp_field; + int i = 0; + + if (!(comp_fields)) + return -EINVAL; + if (!(lt)) + return -EINVAL; + + memset(lt, 0, + lt_len * + sizeof(struct gprs_sndcp_hdrcomp_entity_algo_table)); + + llist_for_each_entry(comp_field, comp_fields, list) { + + lt[i].entity = comp_field->entity; + lt[i].algo = comp_field->algo; + lt[i].compclass = + gprs_sndcp_get_compression_class(comp_field); + + if (lt[i].compclass < 0) { + memset(lt, 0, lt_len * + sizeof(struct + gprs_sndcp_hdrcomp_entity_algo_table)); + return -EINVAL; + } + + i++; + } + + return i; +} + + +/* + * 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: + LOGP(DSNDCP, LOGL_DEBUG, + " gprs_sndcp_hdrcomp_rfc1144_params {\n"); + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi_len=%i;\n", + comp_field->rfc1144_params->nsapi_len); + if (comp_field->rfc1144_params->nsapi_len == 0) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[] = NULL;\n"); + for (i = 0; i < comp_field->rfc1144_params->nsapi_len; i++) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[%i]=%i;\n", i, + comp_field->rfc1144_params->nsapi[i]); + LOGP(DSNDCP, logl, " s01=%i;\n", + comp_field->rfc1144_params->s01); + LOGP(DSNDCP, logl, " }\n"); + break; + case RFC_2507: + LOGP(DSNDCP, LOGL_DEBUG, + " gprs_sndcp_hdrcomp_rfc2507_params {\n"); + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi_len=%i;\n", + comp_field->rfc2507_params->nsapi_len); + if (comp_field->rfc2507_params->nsapi_len == 0) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[] = NULL;\n"); + for (i = 0; i < comp_field->rfc2507_params->nsapi_len; i++) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[%i]=%i;\n", i, + comp_field->rfc2507_params->nsapi[i]); + LOGP(DSNDCP, LOGL_DEBUG, + " f_max_period=%i;\n", + comp_field->rfc2507_params->f_max_period); + LOGP(DSNDCP, LOGL_DEBUG, + " f_max_time=%i;\n", + comp_field->rfc2507_params->f_max_time); + LOGP(DSNDCP, LOGL_DEBUG, + " max_header=%i;\n", + comp_field->rfc2507_params->max_header); + LOGP(DSNDCP, LOGL_DEBUG, + " tcp_space=%i;\n", + comp_field->rfc2507_params->tcp_space); + LOGP(DSNDCP, LOGL_DEBUG, + " non_tcp_space=%i;\n", + comp_field->rfc2507_params->non_tcp_space); + LOGP(DSNDCP, logl, " }\n"); + break; + case ROHC: + LOGP(DSNDCP, LOGL_DEBUG, + " gprs_sndcp_hdrcomp_rohc_params {\n"); + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi_len=%i;\n", + comp_field->rohc_params->nsapi_len); + if (comp_field->rohc_params->nsapi_len == 0) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[] = NULL;\n"); + for (i = 0; i < comp_field->rohc_params->nsapi_len; i++) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[%i]=%i;\n", i, + comp_field->rohc_params->nsapi[i]); + LOGP(DSNDCP, LOGL_DEBUG, + " max_cid=%i;\n", + comp_field->rohc_params->max_cid); + LOGP(DSNDCP, LOGL_DEBUG, + " max_header=%i;\n", + comp_field->rohc_params->max_header); + if (comp_field->rohc_params->profile_len == 0) + LOGP(DSNDCP, LOGL_DEBUG, + " profile[] = NULL;\n"); + for (i = 0; i < comp_field->rohc_params->profile_len; i++) + LOGP(DSNDCP, LOGL_DEBUG, + " profile[%i]=%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: + LOGP(DSNDCP, LOGL_DEBUG, + " gprs_sndcp_datacomp_v42bis_params {\n"); + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi_len=%i;\n", + comp_field->v42bis_params->nsapi_len); + if (comp_field->v42bis_params->nsapi_len == 0) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[] = NULL;\n"); + for (i = 0; i < comp_field->v42bis_params->nsapi_len; i++) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[%i]=%i;\n", i, + comp_field->v42bis_params->nsapi[i]); + LOGP(DSNDCP, logl, " p0=%i;\n", + comp_field->v42bis_params->p0); + LOGP(DSNDCP, logl, " p1=%i;\n", + comp_field->v42bis_params->p1); + LOGP(DSNDCP, logl, " p2=%i;\n", + comp_field->v42bis_params->p2); + LOGP(DSNDCP, logl, " }\n"); + break; + case V44: + LOGP(DSNDCP, LOGL_DEBUG, + " gprs_sndcp_datacomp_v44_params {\n"); + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi_len=%i;\n", + comp_field->v44_params->nsapi_len); + if (comp_field->v44_params->nsapi_len == 0) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[] = NULL;\n"); + for (i = 0; i < comp_field->v44_params->nsapi_len; i++) + LOGP(DSNDCP, LOGL_DEBUG, + " nsapi[%i]=%i;\n", i, + comp_field->v44_params->nsapi[i]); + LOGP(DSNDCP, logl, " c0=%i;\n", + comp_field->v44_params->c0); + LOGP(DSNDCP, logl, " p0=%i;\n", + comp_field->v44_params->p0); + LOGP(DSNDCP, logl, " p1t=%i;\n", + comp_field->v44_params->p1t); + LOGP(DSNDCP, logl, " p1r=%i;\n", + comp_field->v44_params->p1r); + LOGP(DSNDCP, logl, " p3t=%i;\n", + comp_field->v44_params->p3t); + LOGP(DSNDCP, logl, " p3r=%i;\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; + + llist_for_each_entry(comp_field, comp_fields, list) { + LOGP(DSNDCP, logl, "SNDCP-XID:\n"); + LOGP(DSNDCP, LOGL_DEBUG, + "struct gprs_sndcp_comp_field {\n"); + LOGP(DSNDCP, logl, " entity=%i;\n", comp_field->entity); + LOGP(DSNDCP, logl, " algo=%i;\n", comp_field->algo); + LOGP(DSNDCP, logl, " comp_len=%i;\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[%i]=%i;\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"); + LOGP(DSNDCP, logl, "\n"); + } + +} -- To view, visit https://gerrit.osmocom.org/641 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: newpatchset Gerrit-Change-Id: If2d63fe2550864cafef3156b1dc0629037c49c1e Gerrit-PatchSet: 4 Gerrit-Project: openbsc Gerrit-Branch: master Gerrit-Owner: dexter <pmaier at sysmocom.de> Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>