diff --git a/openbsc/.gitignore b/openbsc/.gitignore index 518a9603f..8ce3b7033 100644 --- a/openbsc/.gitignore +++ b/openbsc/.gitignore @@ -82,6 +82,7 @@ tests/oap/oap_test tests/gtphub/gtphub_test tests/mm_auth/mm_auth_test tests/xid/xid_test +tests/sndcp_xid/sndcp_xid_test tests/atconfig tests/atlocal diff --git a/openbsc/configure.ac b/openbsc/configure.ac index f63d61ffa..9dc2f8d97 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -231,6 +231,7 @@ AC_OUTPUT( tests/gtphub/Makefile tests/mm_auth/Makefile tests/xid/Makefile + tests/sndcp_xid/Makefile doc/Makefile doc/examples/Makefile Makefile) diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am index cae8ba488..a91322fa6 100644 --- a/openbsc/include/openbsc/Makefile.am +++ b/openbsc/include/openbsc/Makefile.am @@ -25,6 +25,7 @@ noinst_HEADERS = \ gprs_llc_xid.h \ gprs_sgsn.h \ gprs_sndcp.h \ + gprs_sndcp_xid.h \ gprs_utils.h \ gsm_04_08.h \ gsm_04_11.h \ diff --git a/openbsc/include/openbsc/gprs_sndcp_xid.h b/openbsc/include/openbsc/gprs_sndcp_xid.h new file mode 100644 index 000000000..02904a7d2 --- /dev/null +++ b/openbsc/include/openbsc/gprs_sndcp_xid.h @@ -0,0 +1,216 @@ +/* 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 . + */ + +#pragma once + +#include +#include + +#define CURRENT_SNDCP_VERSION 0 /* See 3GPP TS 44.065, clause 8 */ +#define MAX_ENTITIES 32 /* 3GPP TS 44.065 reserves 5 bit + * for compression enitity number */ + +#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */ +#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */ +#define MAX_ROHC 16 /* Maximum number of ROHC compression profiles */ + +/* According to: 3GPP TS 44.065, 6.5.1.1 Format of the protocol control + * information compression field (Figure 7) and 3GPP TS 44.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 */ + int algo; + + /* Number of contained PCOMP / DCOMP values */ + uint8_t comp_len; + + /* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */ + uint8_t comp[MAX_COMP]; + + /* Note: Only one of the following struct pointers may, + be used. Unused pointers must be set to NULL! */ + struct gprs_sndcp_pcomp_rfc1144_params *rfc1144_params; + struct gprs_sndcp_pcomp_rfc2507_params *rfc2507_params; + struct gprs_sndcp_pcomp_rohc_params *rohc_params; + struct gprs_sndcp_dcomp_v42bis_params *v42bis_params; + struct gprs_sndcp_dcomp_v44_params *v44_params; +}; + +/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */ +enum gprs_sndcp_hdr_comp_algo { + RFC_1144, /* TCP/IP header compression, see also 6.5.2 */ + RFC_2507, /* TCP/UDP/IP header compression, see also: 6.5.3 */ + ROHC /* Robust Header Compression, see also 6.5.4 */ +}; + +/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */ +enum gprs_sndcp_data_comp_algo { + V42BIS, /* V.42bis data compression, see also 6.6.2 */ + V44 /* V44 data compression, see also: 6.6.3 */ +}; + +/* According to: 3GPP TS 44.065, 8 SNDCP XID parameters */ +enum gprs_sndcp_xid_param_types { + SNDCP_XID_VERSION_NUMBER, + SNDCP_XID_DATA_COMPRESSION, /* See also: subclause 6.6.1 */ + SNDCP_XID_PROTOCOL_COMPRESSION, /* See also: subclause 6.5.1 */ +}; + +/* According to: 3GPP TS 44.065, 6.5.2.1 Parameters (Table 5) */ +struct gprs_sndcp_pcomp_rfc1144_params { + uint8_t nsapi_len; /* Number of applicable NSAPIs + * (default 0) */ + uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ + int s01; /* (default 15) */ +}; + +/* According to: 3GPP TS 44.065, 6.5.2.2 Assignment of PCOMP values */ +enum gprs_sndcp_pcomp_rfc1144_pcomp { + RFC1144_PCOMP1, /* Uncompressed TCP */ + RFC1144_PCOMP2, /* Compressed TCP */ + RFC1144_PCOMP_NUM /* Number of pcomp values */ +}; + +/* According to: 3GPP TS 44.065, 6.5.3.1 Parameters (Table 6) */ +struct gprs_sndcp_pcomp_rfc2507_params { + uint8_t nsapi_len; /* Number of applicable NSAPIs + * (default 0) */ + uint8_t nsapi[MAX_NSAPI]; /* 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: 3GPP TS 44.065, 6.5.3.2 Assignment of PCOMP values for RFC2507 */ +enum gprs_sndcp_pcomp_rfc2507_pcomp { + RFC2507_PCOMP1, /* Full Header */ + RFC2507_PCOMP2, /* Compressed TCP */ + RFC2507_PCOMP3, /* Compressed TCP non delta */ + RFC2507_PCOMP4, /* Compressed non TCP */ + RFC2507_PCOMP5, /* Context state */ + RFC2507_PCOMP_NUM /* Number of pcomp values */ +}; + +/* According to: 3GPP TS 44.065, 6.5.4.1 Parameter (Table 10) */ +struct gprs_sndcp_pcomp_rohc_params { + uint8_t nsapi_len; /* Number of applicable NSAPIs + * (default 0) */ + uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ + int max_cid; /* (default 15) */ + int max_header; /* (default 168) */ + uint8_t profile_len; /* (default 1) */ + uint16_t profile[MAX_ROHC]; /* (default 0, ROHC uncompressed) */ +}; + +/* According to: 3GPP TS 44.065, 6.5.4.2 Assignment of PCOMP values for ROHC */ +enum gprs_sndcp_pcomp_rohc_pcomp { + ROHC_PCOMP1, /* ROHC small CIDs */ + ROHC_PCOMP2, /* ROHC large CIDs */ + ROHC_PCOMP_NUM /* 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: 3GPP TS 44.065, 6.6.2.1 Parameters (Table 7a) */ +struct gprs_sndcp_dcomp_v42bis_params { + uint8_t nsapi_len; /* Number of applicable NSAPIs + * (default 0) */ + uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ + int p0; /* (default 3) */ + int p1; /* (default 2048) */ + int p2; /* (default 20) */ + +}; + +/* According to: 3GPP TS 44.065, 6.6.2.2 Assignment of DCOMP values */ +enum gprs_sndcp_dcomp_v42bis_dcomp { + V42BIS_DCOMP1, /* V.42bis enabled */ + V42BIS_DCOMP_NUM /* Number of dcomp values */ +}; + +/* According to: 3GPP TS 44.065, 6.6.3.1 Parameters (Table 7c) */ +struct gprs_sndcp_dcomp_v44_params { + uint8_t nsapi_len; /* Number of applicable NSAPIs + * (default 0) */ + uint8_t nsapi[MAX_NSAPI]; /* 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: 3GPP TS 44.065, 6.6.3.2 Assignment of DCOMP values */ +enum gprs_sndcp_dcomp_v44_dcomp { + V44_DCOMP1, /* Packet method compressed */ + V44_DCOMP2, /* Multi packet method compressed */ + V44_DCOMP_NUM /* Number of dcomp values */ +}; + +/* 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); + +/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ +struct llist_head *gprs_sndcp_parse_xid(const void *ctx, + const uint8_t * src, + unsigned int src_len, + const struct llist_head *comp_fields_req); + +/* 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); + +/* Dump a list with SNDCP-XID fields (Debug) */ +void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields, + unsigned int logl); + diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index 59136bd32..3b5839997 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -72,6 +72,7 @@ osmo_sgsn_SOURCES = \ gprs_sgsn.c \ gprs_sndcp.c \ gprs_sndcp_vty.c \ + gprs_sndcp_xid.c \ sgsn_main.c \ sgsn_vty.c \ sgsn_libgtp.c \ @@ -98,6 +99,7 @@ osmo_sgsn_LDADD = \ $(LIBCRYPTO_LIBS) \ -lrt \ -lgtp \ + -lm \ $(NULL) if BUILD_IU osmo_sgsn_LDADD += \ diff --git a/openbsc/src/gprs/gprs_sndcp_xid.c b/openbsc/src/gprs/gprs_sndcp_xid.c new file mode 100644 index 000000000..270bdee11 --- /dev/null +++ b/openbsc/src/gprs/gprs_sndcp_xid.c @@ -0,0 +1,1803 @@ +/* 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 >= 0); + 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] < 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) +{ + 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 rc; + int byte_counter = 0; + uint8_t comp_bytes[512]; + uint8_t xid_version_number[1] = { CURRENT_SNDCP_VERSION }; + + OSMO_ASSERT(comp_fields); + OSMO_ASSERT(dst); + OSMO_ASSERT(dst_maxlen >= 2 + sizeof(xid_version_number)); + + /* Bail if there is no input */ + if (llist_empty(comp_fields)) + 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); + 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(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; + + /* 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 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; + + if (!comp_fields) + return -EINVAL; + if (!lt) + return -EINVAL; + + memset(lt, 0, lt_len * sizeof(lt)); + + 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(lt)); + return -EINVAL; + } + + 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(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]; + + 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(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(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"); + } + +} diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am index 7396c52d9..7b145f789 100644 --- a/openbsc/tests/Makefile.am +++ b/openbsc/tests/Makefile.am @@ -10,7 +10,9 @@ SUBDIRS = \ subscr \ mm_auth \ xid \ + sndcp_xid \ $(NULL) + if BUILD_NAT SUBDIRS += \ bsc-nat \ diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am index d148c4879..cf8810160 100644 --- a/openbsc/tests/sgsn/Makefile.am +++ b/openbsc/tests/sgsn/Makefile.am @@ -56,7 +56,8 @@ sgsn_test_LDADD = \ $(top_builddir)/src/gprs/gprs_gb_parse.o \ $(top_builddir)/src/gprs/oap.o \ $(top_builddir)/src/gprs/oap_messages.o \ - $(top_builddir)/src/gprs/gprs_llc_xid.o \ + $(top_builddir)/src/gprs/gprs_llc_xid.o \ + $(top_builddir)/src/gprs/gprs_sndcp_xid.o \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOCORE_LIBS) \ @@ -66,7 +67,9 @@ sgsn_test_LDADD = \ $(LIBCRYPTO_LIBS) \ $(LIBGTP_LIBS) \ -lrt \ + -lm \ $(NULL) + if BUILD_IU sgsn_test_LDADD += \ $(top_builddir)/src/libiu/libiu.a \ diff --git a/openbsc/tests/sndcp_xid/Makefile.am b/openbsc/tests/sndcp_xid/Makefile.am new file mode 100644 index 000000000..99b9d1a4f --- /dev/null +++ b/openbsc/tests/sndcp_xid/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS) + +EXTRA_DIST = sndcp_xid_test.ok + +noinst_PROGRAMS = sndcp_xid_test + +sndcp_xid_test_SOURCES = sndcp_xid_test.c + +sndcp_xid_test_LDADD = \ + $(top_builddir)/src/gprs/gprs_sndcp_xid.o \ + $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOGB_LIBS) \ + $(LIBCARES_LIBS) \ + $(LIBCRYPTO_LIBS) \ + -lgtp -lrt -lm + + diff --git a/openbsc/tests/sndcp_xid/sndcp_xid_test.c b/openbsc/tests/sndcp_xid/sndcp_xid_test.c new file mode 100644 index 000000000..3a3361970 --- /dev/null +++ b/openbsc/tests/sndcp_xid/sndcp_xid_test.c @@ -0,0 +1,282 @@ +/* Test SNDCP-XID Encoding/Decoding */ + +/* (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 + +/* Test SNDCP-XID decoding with a real world sample */ +static void test_xid_decode_realworld(const void *ctx) +{ + struct llist_head *comp_fields; + int rc; + printf("Testing SNDCP XID-Decoder/Encoder (real world data)\n"); + + /* Example of a real world SNDCP-XID message */ + uint8_t xid[] = + { 0x00, 0x01, 0x00, 0x02, 0x31, 0x82, 0x02, 0x27, 0x89, 0xff, 0xe0, + 0x00, 0x0f, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, + 0x01, 0x02, 0x00, 0x03, 0x01, 0x03, 0x00, 0x04, 0x01, 0x04, 0x00, 0x05, + 0x01, 0x05, 0x00, 0x06, 0x00, 0x07, 0x01, 0x07, 0x00, 0x08, 0x01, 0x08, + 0x80, 0x00, 0x04, 0x12, 0x00, 0x40, 0x07 }; + uint8_t xid_r[512]; + + /* Parse and show contained comp fields */ + comp_fields = gprs_sndcp_parse_xid(ctx, xid, sizeof(xid), NULL); + OSMO_ASSERT(comp_fields); + printf("Decoded:\n"); + gprs_sndcp_dump_comp_fields(comp_fields, DSNDCP); + + /* Encode comp-fields again */ + rc = gprs_sndcp_compile_xid(xid_r,sizeof(xid_r), comp_fields); + printf("Result length=%i\n",rc); + printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid))); + printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc)); + + OSMO_ASSERT(rc == 54); + OSMO_ASSERT(memcmp(xid, xid_r, sizeof(xid)) == 0); + + /* Free comp fields */ + talloc_free(comp_fields); + + printf("\n"); +} + +/* Encode and decode test with artificial test data */ +static void test_xid_encode_decode(const void *ctx) +{ + printf("Testing SNDCP XID-Encoder/Decoder\n"); + + LLIST_HEAD(comp_fields); + struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params; + struct gprs_sndcp_comp_field rfc1144_comp_field; + struct gprs_sndcp_pcomp_rfc2507_params rfc2507_params; + struct gprs_sndcp_comp_field rfc2507_comp_field; + struct gprs_sndcp_pcomp_rohc_params rohc_params; + struct gprs_sndcp_comp_field rohc_comp_field; + struct gprs_sndcp_dcomp_v42bis_params v42bis_params; + struct gprs_sndcp_comp_field v42bis_comp_field; + struct gprs_sndcp_dcomp_v44_params v44_params; + struct gprs_sndcp_comp_field v44_comp_field; + struct llist_head *comp_fields_dec; + + uint8_t xid[512]; + unsigned int xid_len = sizeof(xid); + int rc; + + memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); + memset(&rfc2507_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); + memset(&rohc_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); + memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); + memset(&v44_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); + + /* Setup which NSAPIs shall make use of rfc1144 */ + rfc1144_params.nsapi[0] = 5; + rfc1144_params.nsapi_len = 1; + + /* Setup rfc1144 operating parameters */ + rfc1144_params.s01 = 7; + + /* Setup rfc1144 compression field */ + rfc1144_comp_field.p = 1; + rfc1144_comp_field.entity = 0; + rfc1144_comp_field.algo = RFC_1144; + rfc1144_comp_field.comp[RFC1144_PCOMP1] = 1; + rfc1144_comp_field.comp[RFC1144_PCOMP2] = 2; + rfc1144_comp_field.comp_len = RFC1144_PCOMP_NUM; + rfc1144_comp_field.rfc1144_params = &rfc1144_params; + + /* Setup which NSAPIs shall make use of rfc1144 */ + rfc2507_params.nsapi[0] = 6; + rfc2507_params.nsapi_len = 1; + + /* Setup rfc2507 operating parameters */ + rfc2507_params.f_max_period = 256; + rfc2507_params.f_max_time = 5; + rfc2507_params.max_header = 168; + rfc2507_params.tcp_space = 15; + rfc2507_params.non_tcp_space = 15; + + /* Setup rfc2507 compression field */ + rfc2507_comp_field.p = 1; + rfc2507_comp_field.entity = 1; + rfc2507_comp_field.algo = RFC_2507; + rfc2507_comp_field.comp[RFC2507_PCOMP1] = 3; + rfc2507_comp_field.comp[RFC2507_PCOMP2] = 4; + rfc2507_comp_field.comp[RFC2507_PCOMP3] = 5; + rfc2507_comp_field.comp[RFC2507_PCOMP4] = 6; + rfc2507_comp_field.comp[RFC2507_PCOMP5] = 7; + rfc2507_comp_field.comp_len = RFC2507_PCOMP_NUM; + rfc2507_comp_field.rfc2507_params = &rfc2507_params; + + /* Setup which NSAPIs shall make use of ROHC */ + rohc_params.nsapi[0] = 5; + rohc_params.nsapi[1] = 6; + rohc_params.nsapi[2] = 7; + rohc_params.nsapi[3] = 8; + rohc_params.nsapi[4] = 9; + rohc_params.nsapi[5] = 10; + rohc_params.nsapi[6] = 11; + rohc_params.nsapi[7] = 12; + rohc_params.nsapi[8] = 13; + rohc_params.nsapi[9] = 14; + rohc_params.nsapi[10] = 15; + rohc_params.nsapi_len = 11; + + /* Setup ROHC operating parameters */ + rohc_params.max_cid = 15; /* default */ + rohc_params.max_header = 168; /* default */ + rohc_params.profile[0] = ROHC_UNCOMPRESSED; + rohc_params.profile[1] = ROHC_RTP; + rohc_params.profile[2] = ROHCV2_RTP; + rohc_params.profile[3] = ROHC_UDP; + rohc_params.profile[4] = ROHCv2_UDP; + rohc_params.profile[5] = ROHC_ESP; + rohc_params.profile[6] = ROHCV2_ESP; + rohc_params.profile[7] = ROHC_IP; + rohc_params.profile[8] = ROHCV2_IP; + rohc_params.profile[9] = ROHC_LLA; + rohc_params.profile[10] = ROHC_LLA_WITH_R_MODE; + rohc_params.profile[11] = ROHC_TCP; + rohc_params.profile[12] = ROHC_RTP_UDP_LITE; + rohc_params.profile[13] = ROHCV2_RTP_UDP_LITE; + rohc_params.profile[14] = ROHC_UDP_LITE; + rohc_params.profile[15] = ROHCV2_UDP_LITE; + rohc_params.profile_len = 16; + + /* Setup ROHC compression field */ + rohc_comp_field.p = 1; + rohc_comp_field.entity = 2; + rohc_comp_field.algo = ROHC; + rohc_comp_field.comp[ROHC_PCOMP1] = 8; + rohc_comp_field.comp[ROHC_PCOMP2] = 9; + rohc_comp_field.comp_len = ROHC_PCOMP_NUM; + rohc_comp_field.rohc_params = &rohc_params; + + /* Setup which NSAPIs shall make use of v42bis */ + v42bis_params.nsapi[0] = 5; + v42bis_params.nsapi_len = 1; + + /* Setup v42bis operating parameters */ + v42bis_params.p0 = 3; + v42bis_params.p1 = 2048; + v42bis_params.p2 = 20; + + /* Setup v42bis compression field */ + v42bis_comp_field.p = 1; + v42bis_comp_field.entity = 3; + v42bis_comp_field.algo = V42BIS; + v42bis_comp_field.comp[V42BIS_DCOMP1] = 10; + v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM; + v42bis_comp_field.v42bis_params = &v42bis_params; + + /* Setup which NSAPIs shall make use of v44 */ + v44_params.nsapi[0] = 5; + v44_params.nsapi_len = 1; + + /* Setup v44 operating parameters */ + v44_params.c0 = 0x80; + v44_params.p0 = 3; + v44_params.p1t = 300; + v44_params.p1r = 300; + v44_params.p3t = 600; + v44_params.p3r = 600; + + /* Setup v44 compression field */ + v44_comp_field.p = 1; + v44_comp_field.entity = 3; + v44_comp_field.algo = V44; + v44_comp_field.comp[V44_DCOMP1] = 10; + v44_comp_field.comp[V44_DCOMP2] = 11; + v44_comp_field.comp_len = V44_DCOMP_NUM; + v44_comp_field.v44_params = &v44_params; + + /* Add compression field(s) to list */ + llist_add(&v44_comp_field.list, &comp_fields); + llist_add(&v42bis_comp_field.list, &comp_fields); + llist_add(&rfc1144_comp_field.list, &comp_fields); + llist_add(&rfc2507_comp_field.list, &comp_fields); + llist_add(&rohc_comp_field.list, &comp_fields); + printf("Test input data:\n"); + gprs_sndcp_dump_comp_fields(&comp_fields, DSNDCP); + + /* Encode SNDCP-XID fields */ + rc = gprs_sndcp_compile_xid(xid, xid_len, &comp_fields); + OSMO_ASSERT(rc > 0); + + printf("Encoded: %s (%i bytes)\n", osmo_hexdump_nospc(xid, rc), rc); + + /* Parse and show contained comp fields */ + comp_fields_dec = gprs_sndcp_parse_xid(ctx, xid, rc, NULL); + OSMO_ASSERT(comp_fields_dec); + + printf("Decoded:\n"); + gprs_sndcp_dump_comp_fields(comp_fields_dec, DSNDCP); + + /* Free comp fields */ + talloc_free(comp_fields_dec); +} + +static struct log_info_cat gprs_categories[] = { + [DSNDCP] = { + .name = "DSNDCP", + .description = + "GPRS Sub-Network Dependent Control Protocol (SNDCP)", + .enabled = 1,.loglevel = LOGL_DEBUG, + } +}; + +static struct log_info info = { + .cat = gprs_categories, + .num_cat = ARRAY_SIZE(gprs_categories), +}; + +int main(int argc, char **argv) +{ + void *xid_ctx; + + osmo_init_logging(&info); + + xid_ctx = talloc_named_const(NULL, 0, "xid_ctx"); + + test_xid_decode_realworld(xid_ctx); + test_xid_encode_decode(xid_ctx); + + printf("Done\n"); + + talloc_report_full(xid_ctx, stderr); + OSMO_ASSERT(talloc_total_blocks(xid_ctx) == 1); + return 0; +} + +/* stubs */ +struct osmo_prim_hdr; +int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) +{ + abort(); +} diff --git a/openbsc/tests/sndcp_xid/sndcp_xid_test.ok b/openbsc/tests/sndcp_xid/sndcp_xid_test.ok new file mode 100644 index 000000000..f3572827c --- /dev/null +++ b/openbsc/tests/sndcp_xid/sndcp_xid_test.ok @@ -0,0 +1,11 @@ +Testing SNDCP XID-Decoder/Encoder (real world data) +Decoded: +Result length=54 +Encoded: 000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007 +Rencoded: 000100023182022789ffe0000f00a8000000010101000201020003010300040104000501050006000701070008010880000412004007 + +Testing SNDCP XID-Encoder/Decoder +Test input data: +Encoded: 000100011a83010dab00208003012c012c02580258830007a000200308001402408000041200200781010c3456700040010005a80f000f82022789ffe0000f00a80000000101010002010200030103000401040005010500060007010700080108 (97 bytes) +Decoded: +Done diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at index 6470ab9aa..85a81d6d5 100644 --- a/openbsc/tests/testsuite.at +++ b/openbsc/tests/testsuite.at @@ -129,3 +129,10 @@ AT_KEYWORDS([xid]) cat $abs_srcdir/xid/xid_test.ok > expout AT_CHECK([$abs_top_builddir/tests/xid/xid_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([sndcp_xid]) +AT_KEYWORDS([sndcp_xid]) +cat $abs_srcdir/sndcp_xid/sndcp_xid_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/sndcp_xid/sndcp_xid_test], [], [expout], [ignore]) +AT_CLEANUP +