[MERGED] openbsc[master]: SNDCP: add SNDCP-XID encoder/decoder and unit test

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/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Sat Sep 24 03:18:00 UTC 2016


Harald Welte has submitted this change and it was merged.

Change subject: SNDCP: add SNDCP-XID encoder/decoder and unit test
......................................................................


SNDCP: add SNDCP-XID encoder/decoder and unit test

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/.gitignore
M openbsc/configure.ac
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
M openbsc/tests/Makefile.am
M openbsc/tests/sgsn/Makefile.am
A openbsc/tests/sndcp_xid/Makefile.am
A openbsc/tests/sndcp_xid/sndcp_xid_test.c
A openbsc/tests/sndcp_xid/sndcp_xid_test.ok
M openbsc/tests/testsuite.at
12 files changed, 2,350 insertions(+), 1 deletion(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/openbsc/.gitignore b/openbsc/.gitignore
index 518a960..8ce3b70 100644
--- a/openbsc/.gitignore
+++ b/openbsc/.gitignore
@@ -82,6 +82,7 @@
 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 f63d61f..9dc2f8d 100644
--- a/openbsc/configure.ac
+++ b/openbsc/configure.ac
@@ -231,6 +231,7 @@
     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 cae8ba4..a91322f 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -25,6 +25,7 @@
 	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 0000000..02904a7
--- /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 <info at sysmocom.de>
+ * 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/linuxlist.h>
+
+#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 59136bd..3b58399 100644
--- a/openbsc/src/gprs/Makefile.am
+++ b/openbsc/src/gprs/Makefile.am
@@ -72,6 +72,7 @@
 	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 @@
 	$(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 0000000..270bdee
--- /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 <info at sysmocom.de>
+ * 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>
+
+/* 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, &params->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(&params->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, &params->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(&params->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(&params->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(&params->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(&params->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(&params->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, &params->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(&params->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(&params->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, &params->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, &params->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(&params->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(&params->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(&params->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, &params->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(&params->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(&params->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(&params->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(&params->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(&params->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(&params->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 7396c52..7b145f7 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -10,7 +10,9 @@
 	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 d148c48..cf88101 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -56,7 +56,8 @@
 	$(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 @@
 	$(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 0000000..99b9d1a
--- /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 0000000..3a33619
--- /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 <info at sysmocom.de>
+ * 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 <openbsc/gprs_sndcp_xid.h>
+#include <openbsc/debug.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/core/application.h>
+
+#include <stdio.h>
+#include <string.h>
+
+/* 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 0000000..f357282
--- /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 6470ab9..85a81d6 100644
--- a/openbsc/tests/testsuite.at
+++ b/openbsc/tests/testsuite.at
@@ -129,3 +129,10 @@
 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
+

-- 
To view, visit https://gerrit.osmocom.org/641
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: If2d63fe2550864cafef3156b1dc0629037c49c1e
Gerrit-PatchSet: 17
Gerrit-Project: openbsc
Gerrit-Branch: master
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>



More information about the gerrit-log mailing list