[PATCH] openbsc[master]: SLHC (RFC1144 header compression) integration 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/.

dexter gerrit-no-reply at lists.osmocom.org
Fri Aug 26 10:34:35 UTC 2016


Hello Harald Welte, Jenkins Builder,

I'd like you to reexamine a change.  Please visit

    https://gerrit.osmocom.org/635

to look at the new patch set (#14).

SLHC (RFC1144 header compression) integration and unit-test

The previously pushed slhc implementation has been modified to compile
and function outside of the kernel. Also debug log messages were added
and datatypes ware matched. The implementation is now ready to be used

Change-Id: I7a638e88a43b3eb9d006751a03ef2570e36613f0
---
M openbsc/.gitignore
M openbsc/configure.ac
M openbsc/include/openbsc/Makefile.am
M openbsc/include/openbsc/debug.h
M openbsc/include/openbsc/gprs_llc.h
M openbsc/include/openbsc/gprs_sndcp.h
A openbsc/include/openbsc/gprs_sndcp_comp.h
A openbsc/include/openbsc/gprs_sndcp_pcomp.h
M openbsc/include/openbsc/sgsn.h
R openbsc/include/openbsc/slhc.h
M openbsc/src/gprs/Makefile.am
M openbsc/src/gprs/gprs_llc.c
M openbsc/src/gprs/gprs_sndcp.c
A openbsc/src/gprs/gprs_sndcp_comp.c
A openbsc/src/gprs/gprs_sndcp_pcomp.c
M openbsc/src/gprs/sgsn_libgtp.c
M openbsc/src/gprs/sgsn_main.c
M openbsc/src/gprs/sgsn_vty.c
M openbsc/src/gprs/slhc.c
M openbsc/tests/Makefile.am
M openbsc/tests/sgsn/Makefile.am
A openbsc/tests/slhc/Makefile.am
A openbsc/tests/slhc/slhc_test.c
A openbsc/tests/slhc/slhc_test.ok
M openbsc/tests/testsuite.at
25 files changed, 1,968 insertions(+), 119 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openbsc refs/changes/35/635/14

diff --git a/openbsc/.gitignore b/openbsc/.gitignore
index 8ce3b70..e75b9eb 100644
--- a/openbsc/.gitignore
+++ b/openbsc/.gitignore
@@ -83,6 +83,7 @@
 tests/mm_auth/mm_auth_test
 tests/xid/xid_test
 tests/sndcp_xid/sndcp_xid_test
+tests/slhc/slhc_test
 
 tests/atconfig
 tests/atlocal
diff --git a/openbsc/configure.ac b/openbsc/configure.ac
index 53072bd..18980b0 100644
--- a/openbsc/configure.ac
+++ b/openbsc/configure.ac
@@ -231,6 +231,7 @@
     tests/mm_auth/Makefile
     tests/xid/Makefile
     tests/sndcp_xid/Makefile
+    tests/slhc/Makefile
     doc/Makefile
     doc/examples/Makefile
     Makefile)
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index e200b05..88a7e2c 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -18,7 +18,8 @@
 		 gprs_gb_parse.h smpp.h meas_feed.h \
 		 gprs_gsup_client.h bsc_msg_filter.h \
 		 oap.h oap_messages.h \
-		 gtphub.h gprs_llc_xid.h gprs_sndcp.h gprs_sndcp_xid.h
+		 gtphub.h gprs_llc_xid.h gprs_sndcp.h gprs_sndcp_xid.h \
+		slhc.h gprs_sndcp_comp.h gprs_sndcp_pcomp.h
 
 openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
 openbscdir = $(includedir)/openbsc
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index 43ebb19..90ddca5 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -29,6 +29,7 @@
 	DBSSGP,
 	DLLC,
 	DSNDCP,
+	DSLHC,
 	DNAT,
 	DCTRL,
 	DSMPP,
diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h
index c3b82b1..8b01467 100644
--- a/openbsc/include/openbsc/gprs_llc.h
+++ b/openbsc/include/openbsc/gprs_llc.h
@@ -174,6 +174,15 @@
 	 * able to create the compression entity. */
 	struct llist_head *xid;
 
+	/* Compression entities */
+	struct {
+		/* In these two list_heads we will store the
+		 * data and protocol compression entities,
+		 * together with their compression states */
+		struct llist_head *proto;
+		struct llist_head *data;
+	} comp;
+
 	/* Internal management */
 	uint32_t age_timestamp;
 };
diff --git a/openbsc/include/openbsc/gprs_sndcp.h b/openbsc/include/openbsc/gprs_sndcp.h
index fef871a..d970240 100644
--- a/openbsc/include/openbsc/gprs_sndcp.h
+++ b/openbsc/include/openbsc/gprs_sndcp.h
@@ -21,6 +21,16 @@
 	struct llist_head frag_list;
 
 	struct osmo_timer_list timer;
+
+	/* Holds state to know which compression mode is used
+	 * when the packet is re-assembled */
+	uint8_t pcomp;
+	uint8_t dcomp;
+
+	/* Holds the pointers to the compression entity list
+	 * that is used when the re-assembled packet is decompressed */
+	struct llist_head *proto;
+	struct llist_head *data;
 };
 
 /* See 6.7.1.2 Reassembly */
@@ -50,4 +60,20 @@
 
 extern struct llist_head gprs_sndcp_entities;
 
+/* Set of SNDCP-XID negotiation (See also: TS 144 065,
+ * Section 6.8 XID parameter negotiation) */
+int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi);
+
+/* Process SNDCP-XID indication (See also: TS 144 065,
+ * Section 6.8 XID parameter negotiation) */
+int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
+		     struct gprs_llc_xid_field *xid_field_response,
+		     struct gprs_llc_lle *lle);
+
+/* Process SNDCP-XID indication
+ * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
+int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
+		      struct gprs_llc_xid_field *xid_field_request,
+		      struct gprs_llc_lle *lle);
+
 #endif	/* INT_SNDCP_H */
diff --git a/openbsc/include/openbsc/gprs_sndcp_comp.h b/openbsc/include/openbsc/gprs_sndcp_comp.h
new file mode 100644
index 0000000..87ab638
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_sndcp_comp.h
@@ -0,0 +1,82 @@
+/* GPRS SNDCP header compression entity management tools */
+
+/* (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>
+#include <openbsc/gprs_sndcp_xid.h>
+
+/* Header / Data compression entity */
+struct gprs_sndcp_comp {
+	struct llist_head list;
+
+	/* Serves as an ID in case we want to delete this entity later */
+	unsigned int entity;	/* see also: 6.5.1.1.3 and 6.6.1.1.3 */
+
+	/* Specifies to which NSAPIs the compression entity is assigned */
+	uint8_t nsapi_len;	/* Number of applicable NSAPIs (default 0) */
+	uint8_t nsapi[MAX_NSAPI];	/* Applicable NSAPIs (default 0) */
+
+	/* Assigned pcomp values */
+	uint8_t comp_len;	/* Number of contained PCOMP / DCOMP values */
+	uint8_t comp[MAX_COMP];	/* see also: 6.5.1.1.5 and 6.6.1.1.5 */
+
+	/* Algorithm parameters */
+	int algo;		/* Algorithm type (see gprs_sndcp_xid.h) */
+	int compclass;		/* See gprs_sndcp_xid.h/c */
+	void *state;		/* Algorithm status and parameters */
+};
+
+#define MAX_COMP 16	/* Maximum number of possible pcomp/dcomp values */
+#define MAX_NSAPI 11	/* Maximum number usable NSAPIs */
+
+/* Allocate a compression enitiy list */
+struct llist_head *gprs_sndcp_comp_alloc(const void *ctx);
+
+/* Free a compression entitiy list */
+void gprs_sndcp_comp_free(struct llist_head *comp_entities);
+
+/* Delete a compression entity */
+void gprs_sndcp_comp_delete(struct llist_head *comp_entities, unsigned int entity);
+
+/* Create and Add a new compression entity
+ * (returns a pointer to the compression entity that has just been created) */
+struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
+					    struct llist_head *comp_entities,
+					    const struct gprs_sndcp_comp_field
+					    *comp_field);
+
+/* Find which compression entity handles the specified pcomp/dcomp */
+struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
+						*comp_entities, uint8_t comp);
+
+/* Find which compression entity handles the specified nsapi */
+struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
+						 *comp_entities, uint8_t nsapi);
+
+/* Find a comp_index for a given pcomp/dcomp value */
+uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
+				uint8_t comp);
+
+/* Find a pcomp/dcomp value for a given comp_index */
+uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
+			         uint8_t comp_index);
diff --git a/openbsc/include/openbsc/gprs_sndcp_pcomp.h b/openbsc/include/openbsc/gprs_sndcp_pcomp.h
new file mode 100644
index 0000000..0733866
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_sndcp_pcomp.h
@@ -0,0 +1,44 @@
+/* GPRS SNDCP header compression handler */
+
+/* (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>
+#include <openbsc/gprs_sndcp_comp.h>
+
+/* Initalize header compression */
+int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
+			  const struct gprs_sndcp_comp_field *comp_field);
+
+/* Terminate header compression */
+void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity);
+
+/* Expand packet header */
+int gprs_sndcp_pcomp_expand(uint8_t *data_o, uint8_t *data_i, unsigned int len,
+			    uint8_t pcomp,
+			    const struct llist_head *comp_entities);
+
+/* Compress packet header */
+int gprs_sndcp_pcomp_compress(uint8_t *data_o, uint8_t *data_i,
+			      unsigned int len, uint8_t *pcomp,
+			      const struct llist_head *comp_entities,
+			      uint8_t nsapi);
diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h
index 59126fe..acd3e4b 100644
--- a/openbsc/include/openbsc/sgsn.h
+++ b/openbsc/include/openbsc/sgsn.h
@@ -89,6 +89,12 @@
 	int dynamic_lookup;
 
 	struct oap_config oap;
+
+	/* RFC1144 TCP/IP Header compression */
+	struct {
+		int enabled;
+		int s01;
+	} pcomp_rfc1144;
 };
 
 struct sgsn_instance {
diff --git a/openbsc/include/openbsc/slhc_vj.h b/openbsc/include/openbsc/slhc.h
similarity index 97%
rename from openbsc/include/openbsc/slhc_vj.h
rename to openbsc/include/openbsc/slhc.h
index 8716d59..cd5a47c 100644
--- a/openbsc/include/openbsc/slhc_vj.h
+++ b/openbsc/include/openbsc/slhc.h
@@ -171,7 +171,8 @@
 #define NULLSLCOMPR	(struct slcompress *)0
 
 /* In slhc.c: */
-struct slcompress *slhc_init(int rslots, int tslots);
+struct slcompress *slhc_init(const void *ctx, int rslots, int tslots);
+
 void slhc_free(struct slcompress *comp);
 
 int slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
@@ -180,4 +181,7 @@
 int slhc_remember(struct slcompress *comp, unsigned char *icp, int isize);
 int slhc_toss(struct slcompress *comp);
 
+void slhc_i_status(struct slcompress *comp);
+void slhc_o_status(struct slcompress *comp);
+
 #endif	/* _SLHC_H */
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am
index fa4a3dd..2910c02 100644
--- a/openbsc/src/gprs/Makefile.am
+++ b/openbsc/src/gprs/Makefile.am
@@ -26,7 +26,8 @@
 			sgsn_ctrl.c sgsn_auth.c gprs_subscriber.c \
 			gprs_utils.c gprs_gsup_client.c \
 			sgsn_cdr.c sgsn_ares.c \
-			oap.c oap_messages.c gprs_llc_xid.c gprs_sndcp_xid.c
+			oap.c oap_messages.c gprs_llc_xid.c gprs_sndcp_xid.c \
+			slhc.c gprs_sndcp_comp.c gprs_sndcp_pcomp.c
 osmo_sgsn_LDADD = 	\
 			$(top_builddir)/src/libcommon/libcommon.a \
 			-lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c
index d6c8866..34b7afe 100644
--- a/openbsc/src/gprs/gprs_llc.c
+++ b/openbsc/src/gprs/gprs_llc.c
@@ -39,6 +39,7 @@
 #include <openbsc/crc24.h>
 #include <openbsc/sgsn.h>
 #include <openbsc/gprs_llc_xid.h>
+#include <openbsc/gprs_sndcp_comp.h>
 #include <openbsc/gprs_sndcp.h>
 
 static struct gprs_llc_llme *llme_alloc(uint32_t tlli);
@@ -139,6 +140,16 @@
 
 	struct llist_head *xid_fields;
 	struct gprs_llc_xid_field *xid_field;
+	struct gprs_llc_xid_field *xid_field_request;
+	struct gprs_llc_xid_field *xid_field_request_l3 = NULL;
+
+	/* Pick layer3 XID from the XID request we have sent last */
+	if (lle->llme->xid) {
+		llist_for_each_entry(xid_field_request, lle->llme->xid, list) {
+			if (xid_field_request->type == GPRS_LLC_XID_T_L3_PAR)
+				xid_field_request_l3 = xid_field_request;
+		}
+	}
 
 	/* Parse and analyze XID-Response */
 	xid_fields = gprs_llc_parse_xid(NULL, bytes, bytes_len);
@@ -149,12 +160,10 @@
 		llist_for_each_entry(xid_field, xid_fields, list) {
 
 			/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
-			if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) {
-				LOGP(DLLC, LOGL_NOTICE,
-				     "Ignoring SNDCP-XID-Field: XID: type=%i, data_len=%i, data=%s\n",
-				     xid_field->type, xid_field->data_len,
-				     osmo_hexdump_nospc(xid_field->data,
-				     xid_field->data_len));
+			if (xid_field->type == GPRS_LLC_XID_T_L3_PAR &&
+			    xid_field_request_l3) {
+				sndcp_sn_xid_conf(xid_field,
+						  xid_field_request_l3, lle);
 			}
 
 			/* Process LLC-XID fields: */
@@ -203,10 +212,6 @@
 	struct gprs_llc_xid_field *xid_field;
 	struct gprs_llc_xid_field *xid_field_response;
 
-	/* Flush eventually pending XID fields */
-	talloc_free(lle->llme->xid);
-	lle->llme->xid = NULL;
-
 	/* Parse and analyze XID-Request */
 	xid_fields =
 	    gprs_llc_parse_xid(lle->llme, bytes_request, bytes_request_len);
@@ -235,6 +240,23 @@
 				    (lle->llme, xid_field);
 				llist_add(&xid_field_response->list,
 					  xid_fields_response);
+			}
+		}
+
+		/* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
+		llist_for_each_entry(xid_field, xid_fields, list) {
+			if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) {
+
+				xid_field_response =
+				    talloc_zero(lle->llme,
+						struct gprs_llc_xid_field);
+				rc = sndcp_sn_xid_ind(xid_field,
+						      xid_field_response, lle);
+				if (rc == 0)
+					llist_add(&xid_field_response->list,
+						  xid_fields_response);
+				else
+					talloc_free(xid_field_response);
 			}
 		}
 
@@ -524,11 +546,16 @@
 
 	llist_add(&llme->list, &gprs_llc_llmes);
 
+	llme->comp.proto = gprs_sndcp_comp_alloc(llme);
+	llme->comp.data = gprs_sndcp_comp_alloc(llme);
+
 	return llme;
 }
 
 static void llme_free(struct gprs_llc_llme *llme)
 {
+	gprs_sndcp_comp_free(llme->comp.proto);
+	gprs_sndcp_comp_free(llme->comp.data);
 	talloc_free(llme->xid);
 	llist_del(&llme->list);
 	talloc_free(llme);
diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c
index 4f71121..05957db 100644
--- a/openbsc/src/gprs/gprs_sndcp.c
+++ b/openbsc/src/gprs/gprs_sndcp.c
@@ -35,6 +35,131 @@
 #include <openbsc/gprs_llc.h>
 #include <openbsc/sgsn.h>
 #include <openbsc/gprs_sndcp.h>
+#include <openbsc/gprs_llc_xid.h>
+#include <openbsc/gprs_sndcp_xid.h>
+#include <openbsc/gprs_sndcp_pcomp.h>
+#include <openbsc/gprs_sndcp_comp.h>
+
+#define DEBUG_IP_PACKETS 1	/* 0=Disabled, 1=Enabled */
+
+#if DEBUG_IP_PACKETS == 1
+/* Calculate TCP/IP checksum */
+static uint16_t calc_ip_csum(uint8_t *data, int len)
+{
+	int i;
+	uint32_t accumulator = 0;
+	uint16_t *pointer = (uint16_t *) data;
+
+	for (i = len; i > 1; i -= 2) {
+		accumulator += *pointer;
+		pointer++;
+	}
+
+	if (len % 2)
+		accumulator += *pointer;
+
+	accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff);
+	accumulator += (accumulator >> 16) & 0xffff;
+	return (~accumulator);
+}
+
+/* Calculate TCP/IP checksum */
+static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len)
+{
+	uint8_t *buf;
+	uint16_t csum;
+
+	buf = talloc_zero_size(ctx, len);
+	memset(buf, 0, len);
+	memcpy(buf, packet + 12, 8);
+	buf[9] = packet[9];
+	buf[11] = (len - 20) & 0xFF;
+	buf[10] = (len - 20) >> 8 & 0xFF;
+	memcpy(buf + 12, packet + 20, len - 20);
+	csum = calc_ip_csum(buf, len - 20 + 12);
+	talloc_free(buf);
+	return csum;
+}
+
+/* Show some ip packet details */
+static void debug_ip_packet(uint8_t *data, int len, int dir, char *info)
+{
+	uint8_t tcp_flags;
+	char flags_debugmsg[256];
+	int len_short;
+	static unsigned int packet_count = 0;
+	static unsigned int tcp_csum_err_count = 0;
+	static unsigned int ip_csum_err_count = 0;
+
+	packet_count++;
+
+	if (len > 80)
+		len_short = 80;
+	else
+		len_short = len;
+
+	if (dir)
+		DEBUGP(DSNDCP, "%s: PHONE => NETWORK: %s\n", info,
+		       osmo_hexdump_nospc(data, len_short));
+	else
+		DEBUGP(DSNDCP, "%s: PHONE <= NETWORK: %s\n", info,
+		       osmo_hexdump_nospc(data, len_short));
+
+	DEBUGP(DSNDCP, "%s: Length.: %d\n", info, len);
+	DEBUGP(DSNDCP, "%s: NO.: %d\n", info, packet_count);
+
+	if (len < 20) {
+		DEBUGP(DSNDCP, "%s: Error: Short IP packet!\n", info);
+		return;
+	}
+
+	if (calc_ip_csum(data, 20) != 0) {
+		DEBUGP(DSNDCP, "%s: Bad IP-Header checksum!\n", info);
+		ip_csum_err_count++;
+	} else
+		DEBUGP(DSNDCP, "%s: IP-Header checksum ok.\n", info);
+
+	if (data[9] == 0x06) {
+		if (len < 40) {
+			DEBUGP(DSNDCP, "%s: Error: Short TCP packet!\n", info);
+			return;
+		}
+
+		DEBUGP(DSNDCP, "%s: Protocol type: TCP\n", info);
+		tcp_flags = data[33];
+
+		if (calc_tcpip_csum(NULL, data, len) != 0) {
+			DEBUGP(DSNDCP, "%s: Bad TCP checksum!\n", info);
+			tcp_csum_err_count++;
+		} else
+			DEBUGP(DSNDCP, "%s: TCP checksum ok.\n", info);
+
+		memset(flags_debugmsg, 0, sizeof(flags_debugmsg));
+		if (tcp_flags & 1)
+			strcat(flags_debugmsg, "FIN ");
+		if (tcp_flags & 2)
+			strcat(flags_debugmsg, "SYN ");
+		if (tcp_flags & 4)
+			strcat(flags_debugmsg, "RST ");
+		if (tcp_flags & 8)
+			strcat(flags_debugmsg, "PSH ");
+		if (tcp_flags & 16)
+			strcat(flags_debugmsg, "ACK ");
+		if (tcp_flags & 32)
+			strcat(flags_debugmsg, "URG ");
+		DEBUGP(DSNDCP, "%s: FLAGS: %s\n", info, flags_debugmsg);
+	} else if (data[9] == 0x11) {
+		DEBUGP(DSNDCP, "%s: Protocol type: UDP\n", info);
+	} else {
+		DEBUGP(DSNDCP, "%s: Protocol type: (%02x)\n", info, data[9]);
+	}
+
+	DEBUGP(DSNDCP, "%s: IP-Header checksum errors: %d\n", info,
+	       ip_csum_err_count);
+	DEBUGP(DSNDCP, "%s: TCP-Checksum errors: %d\n", info,
+	       tcp_csum_err_count);
+}
+#endif
 
 /* Chapter 7.2: SN-PDU Formats */
 struct sndcp_common_hdr {
@@ -143,6 +268,9 @@
 	struct msgb *msg;
 	unsigned int seg_nr;
 	uint8_t *npdu;
+	int npdu_len;
+	int rc;
+	uint8_t *expnd;
 
 	LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u "
 		"num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi,
@@ -173,16 +301,46 @@
 		talloc_free(dqe);
 	}
 
+	npdu_len = sne->defrag.tot_len;
+
 	/* FIXME: cancel timer */
 
 	/* actually send the N-PDU to the SGSN core code, which then
 	 * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */
-	return sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
-				    sne->nsapi, msg, sne->defrag.tot_len, npdu);
+
+	/* Decompress packet */
+#if DEBUG_IP_PACKETS == 1
+	DEBUGP(DSNDCP, "                                                   \n");
+	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+	DEBUGP(DSNDCP, "===================================================\n");
+#endif
+	expnd = talloc_zero_size(msg, msg->len + 64);
+	rc = gprs_sndcp_pcomp_expand(expnd, npdu, npdu_len,
+				     sne->defrag.pcomp, sne->defrag.proto);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "TCP/IP Header decompression failed!\n");
+		talloc_free(expnd);
+		return -EIO;
+	} else
+		npdu_len = rc;
+#if DEBUG_IP_PACKETS == 1
+	debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()");
+	DEBUGP(DSNDCP, "===================================================\n");
+	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+	DEBUGP(DSNDCP, "                                                   \n");
+#endif
+
+	/* Hand off packet to gtp */
+	rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
+				  sne->nsapi, msg, npdu_len, expnd);
+
+	talloc_free(expnd);
+	return rc;
 }
 
-static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, uint8_t *hdr,
-			unsigned int len)
+static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg,
+			uint8_t *hdr, unsigned int len)
 {
 	struct sndcp_common_hdr *sch;
 	struct sndcp_udata_hdr *suh;
@@ -343,7 +501,8 @@
 };
 
 /* returns '1' if there are more fragments to send, '0' if none */
-static int sndcp_send_ud_frag(struct sndcp_frag_state *fs)
+static int sndcp_send_ud_frag(struct sndcp_frag_state *fs,
+			      uint8_t pcomp, uint8_t dcomp)
 {
 	struct gprs_sndcp_entity *sne = fs->sne;
 	struct gprs_llc_lle *lle = sne->lle;
@@ -380,8 +539,8 @@
 	if (sch->first) {
 		scomph = (struct sndcp_comp_hdr *)
 				msgb_put(fmsg, sizeof(*scomph));
-		scomph->pcomp = 0;
-		scomph->dcomp = 0;
+		scomph->pcomp = pcomp;
+		scomph->dcomp = dcomp;
 	}
 
 	/* append the user-data header */
@@ -446,8 +605,37 @@
 	struct sndcp_comp_hdr *scomph;
 	struct sndcp_udata_hdr *suh;
 	struct sndcp_frag_state fs;
+	uint8_t pcomp = 0;
+	uint8_t dcomp = 0;
+	int rc;
+	uint8_t *compr;
 
 	/* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
+
+	/* Compress packet */
+#if DEBUG_IP_PACKETS == 1
+	DEBUGP(DSNDCP, "                                                   \n");
+	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+	DEBUGP(DSNDCP, "===================================================\n");
+	debug_ip_packet(msg->data, msg->len, 0, "sndcp_initdata_req()");
+#endif
+	compr = talloc_zero_size(msg, msg->len);
+	rc = gprs_sndcp_pcomp_compress(compr, msg->data, msg->len, &pcomp,
+				       lle->llme->comp.proto, nsapi);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR, "TCP/IP Header compression failed!\n");
+		talloc_free(compr);
+		return -EIO;
+	} else {
+		msg->len = rc;
+		memcpy(msg->data, compr, rc);
+	}
+	talloc_free(compr);
+#if DEBUG_IP_PACKETS == 1
+	DEBUGP(DSNDCP, "===================================================\n");
+	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+	DEBUGP(DSNDCP, "                                                   \n");
+#endif
 
 	sne = gprs_sndcp_entity_by_lle(lle, nsapi);
 	if (!sne) {
@@ -469,7 +657,7 @@
 		/* call function to generate and send fragments until all
 		 * of the N-PDU has been sent */
 		while (1) {
-			int rc = sndcp_send_ud_frag(&fs);
+			int rc = sndcp_send_ud_frag(&fs,pcomp,dcomp);
 			if (rc == 0)
 				return 0;
 			if (rc < 0)
@@ -489,8 +677,8 @@
 	sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff;
 
 	scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph));
-	scomph->pcomp = 0;
-	scomph->dcomp = 0;
+	scomph->pcomp = pcomp;
+	scomph->dcomp = dcomp;
 
 	/* prepend common SNDCP header */
 	sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch));
@@ -512,6 +700,8 @@
 	uint8_t *npdu;
 	uint16_t npdu_num __attribute__((unused));
 	int npdu_len;
+	int rc;
+	uint8_t *expnd;
 
 	sch = (struct sndcp_common_hdr *) hdr;
 	if (sch->first) {
@@ -540,26 +730,58 @@
 	/* FIXME: move this RA_ID up to the LLME or even higher */
 	bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg));
 
+	if(scomph) {
+		sne->defrag.pcomp = scomph->pcomp;
+		sne->defrag.dcomp = scomph->dcomp;
+		sne->defrag.proto = lle->llme->comp.proto;
+		sne->defrag.data = lle->llme->comp.data;
+	}
+
 	/* any non-first segment is by definition something to defragment
 	 * as is any segment that tells us there are more segments */
 	if (!sch->first || sch->more)
 		return defrag_input(sne, msg, hdr, len);
 
-	if (scomph && (scomph->pcomp || scomph->dcomp)) {
-		LOGP(DSNDCP, LOGL_ERROR, "We don't support compression yet\n");
-		return -EIO;
-	}
-
 	npdu_num = (suh->npdu_high << 8) | suh->npdu_low;
 	npdu = (uint8_t *)suh + sizeof(*suh);
-	npdu_len = (msg->data + msg->len) - npdu;
+	npdu_len = (msg->data + msg->len) - npdu - 3; /* -3 'removes' the FCS */
+
 	if (npdu_len <= 0) {
 		LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len);
 		return -EIO;
 	}
 	/* actually send the N-PDU to the SGSN core code, which then
 	 * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */
-	return sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu);
+
+	/* Decompress packet */
+#if DEBUG_IP_PACKETS == 1
+	DEBUGP(DSNDCP, "                                                   \n");
+	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+	DEBUGP(DSNDCP, "===================================================\n");
+#endif
+	expnd = talloc_zero_size(msg, npdu_len + 64);
+	rc = gprs_sndcp_pcomp_expand(expnd, npdu, npdu_len,
+				     sne->defrag.pcomp, sne->defrag.proto);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "TCP/IP Header decompression failed!\n");
+		talloc_free(expnd);
+		return -EIO;
+	} else
+		npdu_len = rc;
+#if DEBUG_IP_PACKETS == 1
+	debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()");
+	DEBUGP(DSNDCP, "===================================================\n");
+	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+	DEBUGP(DSNDCP, "                                                   \n");
+#endif
+
+	/* Hand off packet to gtp */
+	rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli,
+				  sne->nsapi, msg, npdu_len, expnd);
+
+	talloc_free(expnd);
+	return rc;
 }
 
 #if 0
@@ -619,3 +841,322 @@
 	case LL_STATUS_IND:
 }
 #endif
+
+/* Generate SNDCP-XID message */
+static int gprs_llc_gen_sndcp_xid(uint8_t *bytes, int bytes_len, uint8_t nsapi)
+{
+	LLIST_HEAD(comp_fields);
+	struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params;
+	struct gprs_sndcp_comp_field rfc1144_comp_field;
+
+	memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
+
+	/* Setup which NSAPIs shall make use of rfc1144 */
+	rfc1144_params.nsapi[0] = nsapi;
+	rfc1144_params.nsapi_len = 1;
+
+	/* Setup rfc1144 operating parameters */
+	rfc1144_params.s01 = sgsn->cfg.pcomp_rfc1144.s01;
+
+	/* 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;
+
+	/* Add compression field(s) to list */
+	if (sgsn->cfg.pcomp_rfc1144.enabled)
+		llist_add(&rfc1144_comp_field.list, &comp_fields);
+
+	/* Compile bytestream */
+	return gprs_sndcp_compile_xid(bytes, bytes_len, &comp_fields);
+}
+
+/* Set of SNDCP-XID bnegotiation (See also: TS 144 065,
+ * Section 6.8 XID parameter negotiation) */
+int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi)
+{
+	/* Note: The specification requires the SNDCP-User to set of an
+	 * SNDCP xid request. See also 3GPP TS 44.065, 6.8 XID parameter
+	 * negotiation, Figure 11: SNDCP XID negotiation procedure. In
+	 * our case the SNDCP-User is sgsn_libgtp.c, which calls
+	 * sndcp_sn_xid_req directly. */
+
+	uint8_t l3params[1024];
+	int xid_len;
+	struct gprs_llc_xid_field xid_field_request;
+
+	/* Generate compression parameter bytestream */
+	xid_len = gprs_llc_gen_sndcp_xid(l3params, sizeof(l3params), nsapi);
+
+	/* Send XID with the SNDCP-XID bytetsream included */
+	if (xid_len > 0) {
+		xid_field_request.type = GPRS_LLC_XID_T_L3_PAR;
+		xid_field_request.data = l3params;
+		xid_field_request.data_len = xid_len;
+		return gprs_ll_xid_req(lle, &xid_field_request);
+	}
+
+	/* When bytestream can not be generated, proceed without SNDCP-XID */
+	else {
+		return gprs_ll_xid_req(lle, NULL);
+	}
+}
+
+/* Handle header compression entites */
+static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field,
+				 struct gprs_llc_lle *lle)
+{
+	/* Note: This functions also transforms the comp_field into its
+	 * echo form (strips comp values, resets propose bit etc...)
+	 * the processed comp_fields can then be sent back as XID-
+	 * Response without further modification. */
+
+	/* Delete propose bit */
+	comp_field->p = 0;
+
+	/* Process proposed parameters */
+	switch (comp_field->algo) {
+	case RFC_1144:
+		if (sgsn->cfg.pcomp_rfc1144.enabled &&
+		    comp_field->rfc1144_params->nsapi_len > 0) {
+			LOGP(DSNDCP, LOGL_DEBUG,
+			     "Accepting RFC1144 header compression...\n");
+			gprs_sndcp_comp_add(lle->llme, lle->llme->comp.proto,
+					    comp_field);
+		} else {
+			LOGP(DSNDCP, LOGL_DEBUG,
+			     "Rejecting RFC1144 header compression...\n");
+			gprs_sndcp_comp_delete(lle->llme->comp.proto,
+					       comp_field->entity);
+			comp_field->rfc1144_params->nsapi_len = 0;
+		}
+		break;
+	case RFC_2507:
+		/* RFC 2507 is not yet supported,
+		 * so we set applicable nsapis to zero */
+		LOGP(DSNDCP, LOGL_DEBUG,
+		     "Rejecting RFC2507 header compression...\n");
+		comp_field->rfc2507_params->nsapi_len = 0;
+		gprs_sndcp_comp_delete(lle->llme->comp.proto,
+				       comp_field->entity);
+		break;
+	case ROHC:
+		/* ROHC is not yet supported,
+		 * so we set applicable nsapis to zero */
+		LOGP(DSNDCP, LOGL_DEBUG,
+		     "Rejecting ROHC header compression...\n");
+		comp_field->rohc_params->nsapi_len = 0;
+		gprs_sndcp_comp_delete(lle->llme->comp.proto,
+				       comp_field->entity);
+		break;
+	}
+
+	return 0;
+}
+
+/* Hanle data compression entites */
+static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field,
+				 struct gprs_llc_lle *lle)
+{
+	/* See note in handle_pcomp_entities() */
+
+	/* Delete propose bit */
+	comp_field->p = 0;
+
+	/* Process proposed parameters */
+	switch (comp_field->algo) {
+	case V42BIS:
+		/* V42BIS is not yet supported,
+		 * so we set applicable nsapis to zero */
+		LOGP(DSNDCP, LOGL_DEBUG,
+		     "Rejecting V.42bis data compression...\n");
+		comp_field->v42bis_params->nsapi_len = 0;
+		gprs_sndcp_comp_delete(lle->llme->comp.data,
+				       comp_field->entity);
+		break;
+	case V44:
+		/* V44 is not yet supported,
+		 * so we set applicable nsapis to zero */
+		LOGP(DSNDCP, LOGL_DEBUG,
+		     "Rejecting V.44 data compression...\n");
+		comp_field->v44_params->nsapi_len = 0;
+		gprs_sndcp_comp_delete(lle->llme->comp.data,
+				       comp_field->entity);
+		break;
+	}
+
+	return 0;
+
+}
+
+/* Process SNDCP-XID indication
+ * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
+int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
+		     struct gprs_llc_xid_field *xid_field_response,
+		     struct gprs_llc_lle *lle)
+{
+	/* Note: This function computes the SNDCP-XID response that is sent
+	 * back to the phone when a phone originated XID is received. The
+	 * Input XID fields are directly processed and the result is directly
+	 * handed back. */
+
+	int rc;
+	int compclass;
+
+	struct llist_head *comp_fields;
+	struct gprs_sndcp_comp_field *comp_field;
+
+	OSMO_ASSERT(xid_field_indication);
+	OSMO_ASSERT(xid_field_response);
+	OSMO_ASSERT(lle);
+
+	/* Parse SNDCP-CID XID-Field */
+	comp_fields = gprs_sndcp_parse_xid(lle->llme,
+					   xid_field_indication->data,
+					   xid_field_indication->data_len,
+					   NULL);
+	if (!comp_fields)
+		return -EINVAL;
+
+	/* Don't bother with empty indications */
+	if (llist_empty(comp_fields)) {
+		xid_field_response->data = NULL;
+		xid_field_response->data_len = 0;
+		DEBUGP(DSNDCP,
+		       "SNDCP-XID indication did not contain any parameters!\n");
+		return 0;
+	}
+
+	/* Handle compression entites */
+	DEBUGP(DSNDCP, "Unmodified SNDCP-XID received from the phone:\n");
+	gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
+
+	llist_for_each_entry(comp_field, comp_fields, list) {
+		compclass = gprs_sndcp_get_compression_class(comp_field);
+		if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
+			rc = handle_pcomp_entities(comp_field, lle);
+		else if (compclass == SNDCP_XID_DATA_COMPRESSION)
+			rc = handle_dcomp_entities(comp_field, lle);
+		else {
+			gprs_sndcp_comp_delete(lle->llme->comp.proto,
+					       comp_field->entity);
+			gprs_sndcp_comp_delete(lle->llme->comp.data,
+					       comp_field->entity);
+			rc = 0;
+		}
+
+		if (rc < 0) {
+			talloc_free(comp_fields);
+			return -EINVAL;
+		}
+	}
+
+	DEBUGP(DSNDCP,
+	       "Modified version of received SNDCP-XID to be sent back from the ggsn:\n");
+	gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
+
+	/* Reserve some memory to store the modified SNDCP-XID bytes */
+	xid_field_response->data =
+	    talloc_zero_size(lle->llme, xid_field_indication->data_len);
+
+	/* Set Type flag for response */
+	xid_field_response->type = GPRS_LLC_XID_T_L3_PAR;
+
+	/* Compile modified SNDCP-XID bytes */
+	rc = gprs_sndcp_compile_xid(xid_field_response->data,
+				    xid_field_indication->data_len,
+				    comp_fields);
+
+	if (rc > 0)
+		xid_field_response->data_len = rc;
+	else {
+		talloc_free(xid_field_response->data);
+		xid_field_response->data = NULL;
+		xid_field_response->data_len = 0;
+		return -EINVAL;
+	}
+
+	talloc_free(comp_fields);
+
+	return 0;
+}
+
+/* Process SNDCP-XID indication
+ * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
+int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
+		      struct gprs_llc_xid_field *xid_field_request,
+		      struct gprs_llc_lle *lle)
+{
+	/* Note: This function handles an incomming SNDCP-XID confirmiation.
+	 * Since the confirmation fields may lack important parameters we
+	 * will reconstruct these missing fields using the original request
+	 * we have sent. After that we will create (or delete) the
+	 * compression entites */
+
+	struct llist_head *comp_fields_req;
+	struct llist_head *comp_fields_conf;
+	struct gprs_sndcp_comp_field *comp_field;
+	int rc;
+	int compclass;
+
+	/* We need both, the confirmation that is sent back by the phone,
+	 * and the original request we have sent. If one of this is missing
+	 * we can not process the confirmation, the caller must check if
+	 * request and confirmation fields are available. */
+	OSMO_ASSERT(xid_field_conf);
+	OSMO_ASSERT(xid_field_request);
+
+	/* Parse SNDCP-CID XID-Field */
+	comp_fields_req = gprs_sndcp_parse_xid(lle->llme,
+					       xid_field_request->data,
+					       xid_field_request->data_len,
+					       NULL);
+	if (!comp_fields_req)
+		return -EINVAL;
+
+	DEBUGP(DSNDCP, "Unmodified SNDCP-XID sent from the ggsn:\n");
+	gprs_sndcp_dump_comp_fields(comp_fields_req, LOGL_DEBUG);
+
+	/* Parse SNDCP-CID XID-Field */
+	comp_fields_conf = gprs_sndcp_parse_xid(lle->llme,
+						xid_field_conf->data,
+						xid_field_conf->data_len,
+						comp_fields_req);
+	if (!comp_fields_conf)
+		return -EINVAL;
+
+	DEBUGP(DSNDCP,
+	       "Modified SNDCP-XID received from the phone:\n");
+	gprs_sndcp_dump_comp_fields(comp_fields_conf, LOGL_DEBUG);
+
+	/* Handle compression entites */
+	llist_for_each_entry(comp_field, comp_fields_conf, list) {
+		compclass = gprs_sndcp_get_compression_class(comp_field);
+		if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
+			rc = handle_pcomp_entities(comp_field, lle);
+		else if (compclass == SNDCP_XID_DATA_COMPRESSION)
+			rc = handle_dcomp_entities(comp_field, lle);
+		else {
+			gprs_sndcp_comp_delete(lle->llme->comp.proto,
+					       comp_field->entity);
+			gprs_sndcp_comp_delete(lle->llme->comp.data,
+					       comp_field->entity);
+			rc = 0;
+		}
+
+		if (rc < 0) {
+			talloc_free(comp_fields_req);
+			talloc_free(comp_fields_conf);
+			return -EINVAL;
+		}
+	}
+
+	talloc_free(comp_fields_req);
+	talloc_free(comp_fields_conf);
+
+	return 0;
+}
diff --git a/openbsc/src/gprs/gprs_sndcp_comp.c b/openbsc/src/gprs/gprs_sndcp_comp.c
new file mode 100644
index 0000000..ede584e
--- /dev/null
+++ b/openbsc/src/gprs/gprs_sndcp_comp.c
@@ -0,0 +1,320 @@
+/* GPRS SNDCP header compression entity management tools */
+
+/* (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 <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gprs_sndcp_xid.h>
+#include <openbsc/gprs_sndcp_comp.h>
+#include <openbsc/gprs_sndcp_pcomp.h>
+
+/* Create a new compression entity from a XID-Field */
+static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx,
+						      const struct
+						      gprs_sndcp_comp_field
+						      *comp_field)
+{
+	struct gprs_sndcp_comp *comp_entity;
+	comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp);
+
+	/* Copy relevant information from the SNDCP-XID field */
+	comp_entity->entity = comp_field->entity;
+	comp_entity->comp_len = comp_field->comp_len;
+	memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp));
+
+	if (comp_field->rfc1144_params) {
+		comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len;
+		memcpy(comp_entity->nsapi,
+		       comp_field->rfc1144_params->nsapi,
+		       sizeof(comp_entity->nsapi));
+	} else if (comp_field->rfc2507_params) {
+		comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len;
+		memcpy(comp_entity->nsapi,
+		       comp_field->rfc2507_params->nsapi,
+		       sizeof(comp_entity->nsapi));
+	} else if (comp_field->rohc_params) {
+		comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len;
+		memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi,
+		       sizeof(comp_entity->nsapi));
+	} else if (comp_field->v42bis_params) {
+		comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
+		memcpy(comp_entity->nsapi,
+		       comp_field->v42bis_params->nsapi,
+		       sizeof(comp_entity->nsapi));
+	} else if (comp_field->v44_params) {
+		comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
+		memcpy(comp_entity->nsapi,
+		       comp_field->v42bis_params->nsapi,
+		       sizeof(comp_entity->nsapi));
+	} else {
+		/* The caller is expected to check carefully if the all
+		 * data fields required for compression entity creation
+		 * are present. Otherwise we blow an assertion here */
+		OSMO_ASSERT(false);
+	}
+	comp_entity->algo = comp_field->algo;
+
+	/* Check if an NSAPI is selected, if not, it does not make sense
+	 * to create the compression entity, since the caller should
+	 * have checked the presence of the NSAPI, we blow an assertion
+	 * in case of missing NSAPIs */
+	OSMO_ASSERT(comp_entity->nsapi_len > 0);
+
+	/* Determine of which class our compression entity will be
+	 * (Protocol or Data compresson ?) */
+	comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field);
+
+	OSMO_ASSERT(comp_entity->compclass != -1);
+
+	/* Create an algorithm specific compression context */
+	if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
+		if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
+			talloc_free(comp_entity);
+			comp_entity = NULL;
+		}
+	} else {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "We don't support data compression yet!\n");
+		talloc_free(comp_entity);
+		return NULL;
+	}
+
+	/* Display info message */
+	if (comp_entity == NULL) {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "Header compression entity (%d) creation failed!\n",
+		     comp_entity->entity);
+		return NULL;
+	}
+	if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
+		LOGP(DSNDCP, LOGL_INFO,
+		     "New header compression entity (%d) created.\n",
+		     comp_entity->entity);
+	} else {
+		LOGP(DSNDCP, LOGL_INFO,
+		     "New data compression entity (%d) created.\n",
+		     comp_entity->entity);
+	}
+
+	return comp_entity;
+}
+
+/* Allocate a compression enitiy list */
+struct llist_head *gprs_sndcp_comp_alloc(const void *ctx)
+{
+	struct llist_head *lh;
+
+	lh = talloc_zero(ctx, struct llist_head);
+	INIT_LLIST_HEAD(lh);
+
+	return lh;
+}
+
+/* Free a compression entitiy list */
+void gprs_sndcp_comp_free(struct llist_head *comp_entities)
+{
+	struct gprs_sndcp_comp *comp_entity;
+
+	/* We expect the caller to take care of allocating a
+	 * compression entity list properly. Attempting to
+	 * free a non existing list clearly points out
+	 * a malfunction. */
+	OSMO_ASSERT(comp_entities);
+
+	llist_for_each_entry(comp_entity, comp_entities, list) {
+		/* Free compression entity */
+		if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
+			LOGP(DSNDCP, LOGL_INFO,
+			     "Deleting header compression entity %d ...\n",
+			     comp_entity->entity);
+			gprs_sndcp_pcomp_term(comp_entity);
+		} else {
+			LOGP(DSNDCP, LOGL_INFO,
+			     "Deleting data compression entity %d ...\n",
+			     comp_entity->entity);
+		}
+	}
+
+	talloc_free(comp_entities);
+}
+
+/* Delete a compression entity */
+void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
+			    unsigned int entity)
+{
+	struct gprs_sndcp_comp *comp_entity;
+	struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
+
+	OSMO_ASSERT(comp_entities);
+
+	llist_for_each_entry(comp_entity, comp_entities, list) {
+		if (comp_entity->entity == entity) {
+			comp_entity_to_delete = comp_entity;
+			break;
+		}
+	}
+
+	if (!comp_entity_to_delete)
+		return;
+
+	if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
+		LOGP(DSNDCP, LOGL_INFO,
+		     "Deleting header compression entity %d ...\n",
+		     comp_entity_to_delete->entity);
+		gprs_sndcp_pcomp_term(comp_entity_to_delete);
+	} else {
+		LOGP(DSNDCP, LOGL_INFO,
+		     "Deleting data compression entity %d ...\n",
+		     comp_entity_to_delete->entity);
+	}
+
+	/* Delete compression entity */
+	llist_del(&comp_entity_to_delete->list);
+	talloc_free(comp_entity_to_delete);
+}
+
+/* Create and Add a new compression entity
+ * (returns a pointer to the compression entity that has just been created) */
+struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
+					    struct llist_head *comp_entities,
+					    const struct gprs_sndcp_comp_field
+					    *comp_field)
+{
+	struct gprs_sndcp_comp *comp_entity;
+
+	OSMO_ASSERT(comp_entities);
+	OSMO_ASSERT(comp_field);
+
+	/* Just to be sure, if the entity is already in
+	 * the list it will be deleted now */
+	gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
+
+	/* Create and add a new entity to the list */
+	comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
+
+	if (!comp_entity)
+		return NULL;
+
+	llist_add(&comp_entity->list, comp_entities);
+	return comp_entity;
+}
+
+/* Find which compression entity handles the specified pcomp/dcomp */
+struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
+						*comp_entities, uint8_t comp)
+{
+	struct gprs_sndcp_comp *comp_entity;
+	int i;
+
+	OSMO_ASSERT(comp_entities);
+
+	llist_for_each_entry(comp_entity, comp_entities, list) {
+		for (i = 0; i < comp_entity->comp_len; i++) {
+			if (comp_entity->comp[i] == comp)
+				return comp_entity;
+		}
+	}
+
+	LOGP(DSNDCP, LOGL_ERROR,
+	     "Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
+	     comp);
+	return NULL;
+}
+
+/* Find which compression entity handles the specified nsapi */
+struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
+						 *comp_entities, uint8_t nsapi)
+{
+	struct gprs_sndcp_comp *comp_entity;
+	int i;
+
+	OSMO_ASSERT(comp_entities);
+
+	llist_for_each_entry(comp_entity, comp_entities, list) {
+		for (i = 0; i < comp_entity->nsapi_len; i++) {
+			if (comp_entity->nsapi[i] == nsapi)
+				return comp_entity;
+		}
+	}
+
+	return NULL;
+}
+
+/* Find a comp_index for a given pcomp/dcomp value */
+uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
+				uint8_t comp)
+{
+	/* Note: This function returns a normalized version of the comp value,
+	 * which matches up with the position of the comp field. Since comp=0
+	 * is reserved for "no compression", the index value starts counting
+	 * at one. The return value is the PCOMPn/DCOMPn value one can find 
+	 * in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
+
+	int i;
+	OSMO_ASSERT(comp_entity);
+
+	/* A pcomp/dcomp value of zero is reserved for "no comproession",
+	 * So we just bail and return zero in this case */
+	if (comp == 0)
+		return 0;
+
+	/* Look in the pcomp/dcomp list for the index */
+	for (i = 0; i < comp_entity->comp_len; i++) {
+		if (comp_entity->comp[i] == comp)
+			return i + 1;
+	}
+
+	LOGP(DSNDCP, LOGL_ERROR,
+	     "Could not find a matching comp_index for given pcomp/dcomp value %d\n",
+	     comp);
+	return 0;
+}
+
+/* Find a pcomp/dcomp value for a given comp_index */
+uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
+			         uint8_t comp_index)
+{
+	OSMO_ASSERT(comp_entity);
+
+	/* A comp_index of zero translates to zero right away. */
+	if (comp_index == 0)
+		return 0;
+
+	if (comp_index > comp_entity->comp_len) {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
+		     comp_index);
+		return 0;
+	}
+
+	/* Look in the pcomp/dcomp list for the comp_index, see
+	 * note in gprs_sndcp_comp_get_idx() */
+	return comp_entity->comp[comp_index - 1];
+}
diff --git a/openbsc/src/gprs/gprs_sndcp_pcomp.c b/openbsc/src/gprs/gprs_sndcp_pcomp.c
new file mode 100644
index 0000000..eca48b6
--- /dev/null
+++ b/openbsc/src/gprs/gprs_sndcp_pcomp.c
@@ -0,0 +1,288 @@
+/* GPRS SNDCP header compression handler */
+
+/* (C) 2016 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <errno.h>
+#include <stdbool.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/gprs_llc.h>
+#include <openbsc/sgsn.h>
+#include <openbsc/gprs_sndcp_xid.h>
+#include <openbsc/slhc.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_sndcp_comp.h>
+#include <openbsc/gprs_sndcp_pcomp.h>
+
+/* Initalize header compression */
+int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
+			  const struct gprs_sndcp_comp_field *comp_field)
+{
+	/* Note: This function is automatically called from
+	 * gprs_sndcp_comp.c when a new header compression
+	 * entity is created by gprs_sndcp.c */
+
+	OSMO_ASSERT(comp_entity);
+	OSMO_ASSERT(comp_field);
+
+	if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
+	    && comp_entity->algo == RFC_1144) {
+		comp_entity->state =
+		    slhc_init(ctx, comp_field->rfc1144_params->s01 + 1,
+			      comp_field->rfc1144_params->s01 + 1);
+		LOGP(DSNDCP, LOGL_INFO,
+		     "RFC1144 header compression initalized.\n");
+		return 0;
+	}
+
+	/* Just in case someone tries to initalize an unknown or unsupported
+	 * header compresson. Since everything is checked during the SNDCP
+	 * negotiation process, this should never happen! */
+	OSMO_ASSERT(false);
+}
+
+/* Terminate header compression */
+void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity)
+{
+	/* Note: This function is automatically called from
+	 * gprs_sndcp_comp.c when a header compression
+	 * entity is deleted by gprs_sndcp.c */
+
+	OSMO_ASSERT(comp_entity);
+
+	if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
+	    && comp_entity->algo == RFC_1144) {
+		if (comp_entity->state) {
+			slhc_free((struct slcompress *)comp_entity->state);
+			comp_entity->state = NULL;
+		}
+		LOGP(DSNDCP, LOGL_INFO,
+		     "RFC1144 header compression terminated.\n");
+		return;
+	}
+
+	/* Just in case someone tries to initalize an unknown or unsupported
+	 * header compresson. Since everything is checked during the SNDCP
+	 * negotiation process, this should never happen! */
+	OSMO_ASSERT(false);
+}
+
+/* Compress a packet using Van Jacobson RFC1144 header compression */
+static int gprs_sndcp_pcomp_rfc1144_compress(uint8_t *pcomp_index,
+					     uint8_t *data_o, uint8_t *data_i,
+					     unsigned int len,
+					     struct slcompress *comp)
+{
+	uint8_t *comp_ptr;
+	int compr_len;
+
+	/* Create a working copy of the incoming data */
+	memcpy(data_o, data_i, len);
+
+	/* Run compressor */
+	compr_len = slhc_compress(comp, data_i, len, data_o, &comp_ptr, 0);
+
+	/* Generate pcomp_index */
+	if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
+		*pcomp_index = 2;
+		data_o[0] &= ~SL_TYPE_COMPRESSED_TCP;
+	} else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) ==
+		   SL_TYPE_UNCOMPRESSED_TCP) {
+		*pcomp_index = 1;
+		data_o[0] &= 0x4F;
+	} else
+		*pcomp_index = 0;
+
+	return compr_len;
+}
+
+/* Expand a packet using Van Jacobson RFC1144 header compression */
+static int gprs_sndcp_pcomp_rfc1144_expand(uint8_t *data_o, uint8_t *data_i,
+					   unsigned int len,
+					   uint8_t pcomp_index,
+					   struct slcompress *comp)
+{
+	int data_decompressed_len;
+	int type;
+
+	/* Note: this function should never be called with pcomp_index=0,
+	 * since this condition is already filtered
+	 * out by gprs_sndcp_pcomp_expand() */
+
+	/* Determine the data type by the PCOMP index */
+	switch (pcomp_index) {
+	case 0:
+		type = SL_TYPE_IP;
+	case 1:
+		type = SL_TYPE_UNCOMPRESSED_TCP;
+		break;
+	case 2:
+		type = SL_TYPE_COMPRESSED_TCP;
+		break;
+	default:
+		LOGP(DSNDCP, LOGL_ERROR, "gprs_sndcp_pcomp_rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
+		     pcomp_index);
+		type = SL_TYPE_IP;
+		break;
+	}
+
+	/* Create a working copy of the incoming data */
+	memcpy(data_o, data_i, len);
+
+	/* Restore the original version nibble on
+	 * marked uncompressed packets */
+	if (type == SL_TYPE_UNCOMPRESSED_TCP) {
+		/* Just in case the phone tags uncompressed tcp-data
+		 * (normally this is handled by pcomp so there is
+		 * no need for tagging the data) */
+		data_o[0] &= 0x4F;
+		data_decompressed_len = slhc_remember(comp, data_o, len);
+		return data_decompressed_len;
+	}
+
+	/* Uncompress compressed packets */
+	else if (type == SL_TYPE_COMPRESSED_TCP) {
+		data_decompressed_len = slhc_uncompress(comp, data_o, len);
+		return data_decompressed_len;
+	}
+
+	/* Regular or unknown packets will not be touched */
+	return len;
+}
+
+/* Expand packet header */
+int gprs_sndcp_pcomp_expand(uint8_t *data_o, uint8_t *data_i,
+			    unsigned int len, uint8_t pcomp,
+			    const struct llist_head *comp_entities)
+{
+	int rc;
+	uint8_t pcomp_index = 0;
+	struct gprs_sndcp_comp *comp_entity;
+
+	OSMO_ASSERT(data_o);
+	OSMO_ASSERT(data_i);
+	OSMO_ASSERT(comp_entities);
+
+	LOGP(DSNDCP, LOGL_DEBUG, "Compression entity list: comp_entities=%p\n",
+	     comp_entities);
+
+	LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp);
+
+	/* Skip on pcomp=0 */
+	if (pcomp == 0) {
+		memcpy(data_o, data_i, len);
+		return len;
+	}
+
+	/* Find out which compression entity handles the data */
+	comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
+
+	/* Skip compression if no suitable compression entity can be found */
+	if (comp_entity == NULL) {
+		memcpy(data_o, data_i, len);
+		return len;
+	}
+
+	/* Note: Only protocol compression entities may appear in
+	 * protocol compression context */
+	OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
+
+	/* Note: Currently RFC1144 is the only compression method we
+	 * support, so the only allowed algorithm is RFC1144 */
+	OSMO_ASSERT(comp_entity->algo == RFC_1144);
+
+	/* Find pcomp_index */
+	pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
+
+	/* Run decompression algo */
+	rc = gprs_sndcp_pcomp_rfc1144_expand(data_o, data_i, len, pcomp_index,
+					     comp_entity->state);
+	slhc_i_status(comp_entity->state);
+	slhc_o_status(comp_entity->state);
+
+	LOGP(DSNDCP, LOGL_DEBUG,
+	     "Header expansion done, old length=%d, new length=%d, entity=%p\n",
+	     len, rc, comp_entity);
+
+	return rc;
+}
+
+/* Compress packet header */
+int gprs_sndcp_pcomp_compress(uint8_t *data_o, uint8_t *data_i,
+			      unsigned int len, uint8_t *pcomp,
+			      const struct llist_head *comp_entities,
+			      uint8_t nsapi)
+{
+	int rc;
+	uint8_t pcomp_index = 0;
+	struct gprs_sndcp_comp *comp_entity;
+
+	OSMO_ASSERT(data_o);
+	OSMO_ASSERT(data_i);
+	OSMO_ASSERT(pcomp);
+	OSMO_ASSERT(comp_entities);
+
+	LOGP(DSNDCP, LOGL_DEBUG, "Compression entity list: comp_entities=%p\n",
+	     comp_entities);
+
+	/* Find out which compression entity handles the data */
+	comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
+
+	/* Skip compression if no suitable compression entity can be found */
+	if (!comp_entity) {
+		*pcomp = 0;
+		memcpy(data_o, data_i, len);
+		return len;
+	}
+
+	/* Note: Only protocol compression entities may appear in
+	 * protocol compression context */
+	OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
+
+	/* Note: Currently RFC1144 is the only compression method we
+	 * support, so the only allowed algorithm is RFC1144 */
+	OSMO_ASSERT(comp_entity->algo == RFC_1144);
+
+	/* Run compression algo */
+	rc = gprs_sndcp_pcomp_rfc1144_compress(&pcomp_index, data_o, data_i,
+					       len, comp_entity->state);
+	slhc_i_status(comp_entity->state);
+	slhc_o_status(comp_entity->state);
+
+	/* Find pcomp value */
+	*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
+
+	LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
+
+	LOGP(DSNDCP, LOGL_DEBUG,
+	     "Header expansion done, old length=%d, new length=%d, entity=%p\n",
+	     len, rc, comp_entity);
+	return rc;
+}
diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c
index be7637a..6e6bbfd 100644
--- a/openbsc/src/gprs/sgsn_libgtp.c
+++ b/openbsc/src/gprs/sgsn_libgtp.c
@@ -47,6 +47,7 @@
 #include <openbsc/gprs_sgsn.h>
 #include <openbsc/gprs_gmm.h>
 #include <openbsc/gsm_subscriber.h>
+#include <openbsc/gprs_sndcp.h>
 
 #include <gtp.h>
 #include <pdp.h>
@@ -307,6 +308,8 @@
 static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
 {
 	struct sgsn_signal_data sig_data;
+	int rc;
+	struct gprs_llc_lle *lle;
 
 	/* Inform others about it */
 	memset(&sig_data, 0, sizeof(sig_data));
@@ -314,7 +317,17 @@
 	osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data);
 
 	/* Send PDP CTX ACT to MS */
-	return gsm48_tx_gsm_act_pdp_acc(pctx);
+	rc = gsm48_tx_gsm_act_pdp_acc(pctx);
+	if(rc < 0)
+		return rc;
+
+	/* Send SNDCP XID to MS */
+	lle = &pctx->mm->gb.llme->lle[pctx->sapi];
+	rc = sndcp_sn_xid_req(lle,pctx->nsapi);
+	if(rc < 0)
+		return rc;
+
+	return 0;
 }
 
 /* The GGSN has confirmed the creation of a PDP Context */
diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
index 52fc985..894ce84 100644
--- a/openbsc/src/gprs/sgsn_main.c
+++ b/openbsc/src/gprs/sgsn_main.c
@@ -292,6 +292,11 @@
 		.description = "SCCP User Adaptation (SUA)",
 		.enabled = 1, .loglevel = LOGL_DEBUG,
 	},
+	[DSLHC] = {
+		.name = "DSLHC",
+		.description = "RFC1144 TCP/IP Header compression (SLHC)",
+		.enabled = 1, .loglevel = LOGL_DEBUG,
+	},
 };
 
 static const struct log_info gprs_log_info = {
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index e6dc68d..bc53743 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -269,6 +269,12 @@
 	vty_out(vty, " timer t3395 %d%s", g_cfg->timers.T3395, VTY_NEWLINE);
 	vty_out(vty, " timer t3397 %d%s", g_cfg->timers.T3397, VTY_NEWLINE);
 
+	if (g_cfg->pcomp_rfc1144.enabled) {
+		vty_out(vty, " compression rfc1144 slots %d%s",
+			g_cfg->pcomp_rfc1144.s01+1, VTY_NEWLINE);
+	} else
+		vty_out(vty, " no compression rfc1144%s", VTY_NEWLINE);
+
 	return CMD_SUCCESS;
 }
 
@@ -1074,6 +1080,28 @@
 	return CMD_SUCCESS;
 }
 
+#define COMPRESSION_STR "Configure compression\n"
+DEFUN(cfg_no_comp_rfc1144, cfg_no_comp_rfc1144_cmd,
+      "no compression rfc1144",
+      NO_STR COMPRESSION_STR
+      "disable rfc1144 TCP/IP header compression\n")
+{
+	g_cfg->pcomp_rfc1144.enabled = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_comp_rfc1144, cfg_comp_rfc1144_cmd,
+      "compression rfc1144 slots <1-256>",
+      COMPRESSION_STR
+      "RFC1144 Header compresion scheme\n"
+      "Number of compression state slots\n"
+      "Number of compression state slots\n")
+{
+	g_cfg->pcomp_rfc1144.enabled = 1;
+	g_cfg->pcomp_rfc1144.s01 = atoi(argv[0]) - 1;
+	return CMD_SUCCESS;
+}
+
 int sgsn_vty_init(void)
 {
 	install_element_ve(&show_sgsn_cmd);
@@ -1128,6 +1156,9 @@
 	install_element(SGSN_NODE, &cfg_sgsn_T3395_cmd);
 	install_element(SGSN_NODE, &cfg_sgsn_T3397_cmd);
 
+	install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd);
+	install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd);
+
 	return 0;
 }
 
diff --git a/openbsc/src/gprs/slhc.c b/openbsc/src/gprs/slhc.c
index 27ed252..cbdf8db 100644
--- a/openbsc/src/gprs/slhc.c
+++ b/openbsc/src/gprs/slhc.c
@@ -50,61 +50,77 @@
  *	driver code belonging close to PPP and SLIP
  */
 
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <net/slhc_vj.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/talloc.h>
+#include <openbsc/slhc.h>
+#include <openbsc/debug.h>
 
-#ifdef CONFIG_INET
-/* Entire module is for IP only */
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/termios.h>
-#include <linux/in.h>
-#include <linux/fcntl.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <net/ip.h>
-#include <net/protocol.h>
-#include <net/icmp.h>
-#include <net/tcp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <linux/timer.h>
-#include <asm/uaccess.h>
-#include <net/checksum.h>
-#include <asm/unaligned.h>
+#define ERR_PTR(x) x
+
 
 static unsigned char *encode(unsigned char *cp, unsigned short n);
 static long decode(unsigned char **cpp);
 static unsigned char * put16(unsigned char *cp, unsigned short x);
 static unsigned short pull16(unsigned char **cpp);
 
+/* Replacement for kernel space function ip_fast_csum() */
+static uint16_t ip_fast_csum(uint8_t *iph, int ihl)
+{
+	int i;
+	uint16_t temp;
+	uint32_t accumulator = 0xFFFF;
+
+	for(i=0;i<ihl*2;i++)
+	{
+		temp = ((*iph) << 8)&0xFF00;
+		iph++;
+		temp |= (*iph)&0xFF;
+		iph++;
+
+		accumulator+=temp;
+		if(accumulator>0xFFFF)
+		{
+			accumulator++;
+			accumulator&=0xFFFF;
+		}
+	}
+
+    return (uint16_t)(htons(~accumulator)&0xFFFF);
+}
+
+/* Replacement for kernel space function put_unaligned() */
+static void put_unaligned(uint16_t val, void *ptr)
+{
+	memcpy(ptr,&val,sizeof(val));
+}
+
+
 /* Allocate compression data structure
  *	slots must be in range 0 to 255 (zero meaning no compression)
  * Returns pointer to structure or ERR_PTR() on error.
  */
 struct slcompress *
-slhc_init(int rslots, int tslots)
+slhc_init(const void *ctx, int rslots, int tslots)
 {
 	register short i;
 	register struct cstate *ts;
 	struct slcompress *comp;
 
 	if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255)
-		return ERR_PTR(-EINVAL);
+		return NULL;
 
-	comp = kzalloc(sizeof(struct slcompress), GFP_KERNEL);
+	comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress));
 	if (! comp)
 		goto out_fail;
 
 	if (rslots > 0) {
 		size_t rsize = rslots * sizeof(struct cstate);
-		comp->rstate = kzalloc(rsize, GFP_KERNEL);
+		comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize);
 		if (! comp->rstate)
 			goto out_free;
 		comp->rslot_limit = rslots - 1;
@@ -112,7 +128,7 @@
 
 	if (tslots > 0) {
 		size_t tsize = tslots * sizeof(struct cstate);
-		comp->tstate = kzalloc(tsize, GFP_KERNEL);
+		comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize);
 		if (! comp->tstate)
 			goto out_free2;
 		comp->tslot_limit = tslots - 1;
@@ -141,11 +157,11 @@
 	return comp;
 
 out_free2:
-	kfree(comp->rstate);
+	talloc_free(comp->rstate);
 out_free:
-	kfree(comp);
+	talloc_free(comp);
 out_fail:
-	return ERR_PTR(-ENOMEM);
+	return NULL;
 }
 
 
@@ -153,16 +169,18 @@
 void
 slhc_free(struct slcompress *comp)
 {
+	DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n");
+
 	if ( comp == NULLSLCOMPR )
 		return;
 
 	if ( comp->tstate != NULLSLSTATE )
-		kfree( comp->tstate );
+		talloc_free(comp->tstate );
 
 	if ( comp->rstate != NULLSLSTATE )
-		kfree( comp->rstate );
+		talloc_free( comp->rstate );
 
-	kfree( comp );
+	talloc_free( comp );
 }
 
 
@@ -187,6 +205,8 @@
 	} else {
 		*cp++ = n;
 	}
+
+	DEBUGP(DSLHC, "encode(): n=%04x\n",n);
 	return cp;
 }
 
@@ -256,6 +276,7 @@
 			comp->sls_o_nontcp++;
 		else
 			comp->sls_o_tcp++;
+		DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n");
 		return isize;
 	}
 	/* Extract TCP header */
@@ -271,6 +292,7 @@
 	    ! (th->ack)){
 		/* TCP connection stuff; send as regular IP */
 		comp->sls_o_tcp++;
+		DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n");
 		return isize;
 	}
 	/*
@@ -287,6 +309,9 @@
 	 * states via linear search.  If we don't find a state
 	 * for the datagram, the oldest state is (re-)used.
 	 */
+
+	DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n");
+
 	for ( ; ; ) {
 		if( ip->saddr == cs->cs_ip.saddr
 		 && ip->daddr == cs->cs_ip.daddr
@@ -310,11 +335,14 @@
 	 * state points to the newest and we only need to set
 	 * xmit_oldest to update the lru linkage.
 	 */
+
+	DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n");
 	comp->sls_o_misses++;
 	comp->xmit_oldest = lcs->cs_this;
 	goto uncompressed;
 
 found:
+		DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n");
 	/*
 	 * Found it -- move to the front on the connection list.
 	 */
@@ -344,6 +372,39 @@
 	 */
 	oth = &cs->cs_tcp;
 
+	/* Display a little more debug information about which of the
+	 * header fields changed unexpectedly */
+	if(ip->version != cs->cs_ip.version)
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n");
+	if(ip->ihl != cs->cs_ip.ihl)
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n");
+	if(ip->tos != cs->cs_ip.tos)
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n");
+	if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)))
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n");
+	if(ip->ttl != cs->cs_ip.ttl)
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n");
+	if(th->doff != cs->cs_tcp.doff)
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
+	if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
+		DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
+		DEBUGP(DSLHC, "slhc_compress(): ip+1 =          %s\n",
+		       osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt =  %s\n",
+		       osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4));
+	}
+	if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
+		DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
+		DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
+		DEBUGP(DSLHC, "slhc_compress(): th+1 =          %s\n",
+		       osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
+		DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
+		       osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt,
+					  ((th->doff)-5)*4));
+	}
+
+
 	if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
 	 || ip->tos != cs->cs_ip.tos
 	 || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))
@@ -351,6 +412,7 @@
 	 || th->doff != cs->cs_tcp.doff
 	 || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
 	 || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){
+		DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n");
 		goto uncompressed;
 	}
 
@@ -362,6 +424,7 @@
 	 */
 	if(th->urg){
 		deltaS = ntohs(th->urg_ptr);
+		DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n");
 		cp = encode(cp,deltaS);
 		changes |= NEW_U;
 	} else if(th->urg_ptr != oth->urg_ptr){
@@ -369,21 +432,29 @@
 		 * implementation should never do this but RFC793
 		 * doesn't prohibit the change so we have to deal
 		 * with it. */
+		DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n");
 		goto uncompressed;
 	}
 	if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
+		DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n");
 		cp = encode(cp,deltaS);
 		changes |= NEW_W;
 	}
 	if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
-		if(deltaA > 0x0000ffff)
+		if(deltaA > 0x0000ffff)	{
+			DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n");
 			goto uncompressed;
+		}
+		DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n");
 		cp = encode(cp,deltaA);
 		changes |= NEW_A;
 	}
 	if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
-		if(deltaS > 0x0000ffff)
+		if(deltaS > 0x0000ffff)	{
+			DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n");
 			goto uncompressed;
+		}
+		DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n");
 		cp = encode(cp,deltaS);
 		changes |= NEW_S;
 	}
@@ -399,17 +470,21 @@
 		if(ip->tot_len != cs->cs_ip.tot_len &&
 		   ntohs(cs->cs_ip.tot_len) == hlen)
 			break;
+		DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n");
 		goto uncompressed;
 	case SPECIAL_I:
 	case SPECIAL_D:
 		/* actual changes match one of our special case encodings --
 		 * send packet uncompressed.
 		 */
+		DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n");
 		goto uncompressed;
 	case NEW_S|NEW_A:
 		if(deltaS == deltaA &&
 		    deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
 			/* special case for echoed terminal traffic */
+			DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n");
+			DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
 			changes = SPECIAL_I;
 			cp = new_seq;
 		}
@@ -417,6 +492,8 @@
 	case NEW_S:
 		if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
 			/* special case for data xfer */
+			DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n");
+			DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
 			changes = SPECIAL_D;
 			cp = new_seq;
 		}
@@ -424,11 +501,14 @@
 	}
 	deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
 	if(deltaS != 1){
+		DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n");
 		cp = encode(cp,deltaS);
 		changes |= NEW_I;
 	}
-	if(th->psh)
+	if(th->psh) {
+		DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n");
 		changes |= TCP_PUSH_BIT;
+	}
 	/* Grab the cksum before we overwrite it below.  Then update our
 	 * state with this packet's header.
 	 */
@@ -445,6 +525,7 @@
 	if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
 		cp = ocp;
 		*cpp = ocp;
+		DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n");
 		*cp++ = changes | NEW_C;
 		*cp++ = cs->cs_this;
 		comp->xmit_current = cs->cs_this;
@@ -456,6 +537,10 @@
 	*(__sum16 *)cp = csum;
 	cp += 2;
 /* deltaS is now the size of the change section of the compressed header */
+
+	DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
+	DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
+
 	memcpy(cp,new_seq,deltaS);	/* Write list of deltas */
 	memcpy(cp+deltaS,icp+hlen,isize-hlen);
 	comp->sls_o_compressed++;
@@ -467,6 +552,7 @@
 	 * to use on future compressed packets in the protocol field).
 	 */
 uncompressed:
+	DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n");
 	memcpy(&cs->cs_ip,ip,20);
 	memcpy(&cs->cs_tcp,th,20);
 	if (ip->ihl > 5)
@@ -538,6 +624,8 @@
 
 	switch(changes & SPECIALS_MASK){
 	case SPECIAL_I:		/* Echoed terminal traffic */
+		DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n");
+
 		{
 		register short i;
 		i = ntohs(ip->tot_len) - hdrlen;
@@ -547,11 +635,13 @@
 		break;
 
 	case SPECIAL_D:			/* Unidirectional data */
+		DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n");
 		thp->seq = htonl( ntohl(thp->seq) +
 				  ntohs(ip->tot_len) - hdrlen);
 		break;
 
 	default:
+		DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n");
 		if(changes & NEW_U){
 			thp->urg = 1;
 			if((x = decode(&cp)) == -1) {
@@ -601,6 +691,7 @@
 	ip->tot_len = htons(len);
 	ip->check = 0;
 
+	DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n");
 	memmove(icp + hdrlen, cp, len - hdrlen);
 
 	cp = icp;
@@ -625,6 +716,7 @@
 
 	return len;
 bad:
+	DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n");
 	comp->sls_i_error++;
 	return slhc_toss( comp );
 }
@@ -641,6 +733,7 @@
 	if(isize < 20) {
 		/* The packet is shorter than a legal IP header */
 		comp->sls_i_runt++;
+		DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n");
 		return slhc_toss( comp );
 	}
 	/* Peek at the IP header's IHL field to find its length */
@@ -648,6 +741,7 @@
 	if(ihl < 20 / 4){
 		/* The IP header length field is too small */
 		comp->sls_i_runt++;
+		DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n");
 		return slhc_toss( comp );
 	}
 	index = icp[9];
@@ -656,10 +750,12 @@
 	if (ip_fast_csum(icp, ihl)) {
 		/* Bad IP header checksum; discard */
 		comp->sls_i_badcheck++;
+		DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n");
 		return slhc_toss( comp );
 	}
 	if(index > comp->rslot_limit) {
 		comp->sls_i_error++;
+		DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n");
 		return slhc_toss(comp);
 	}
 
@@ -683,6 +779,7 @@
 int
 slhc_toss(struct slcompress *comp)
 {
+	DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n");
 	if ( comp == NULLSLCOMPR )
 		return 0;
 
@@ -690,55 +787,27 @@
 	return 0;
 }
 
-#else /* CONFIG_INET */
-
-int
-slhc_toss(struct slcompress *comp)
+void slhc_i_status(struct slcompress *comp)
 {
-  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_toss");
-  return -EINVAL;
-}
-int
-slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
-{
-  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_uncompress");
-  return -EINVAL;
-}
-int
-slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
-	unsigned char *ocp, unsigned char **cpp, int compress_cid)
-{
-  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_compress");
-  return -EINVAL;
+	if (comp != NULLSLCOMPR) {
+		DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
+			comp->sls_i_compressed,
+			comp->sls_i_uncompressed,
+			comp->sls_i_error,
+			comp->sls_i_tossed);
+	}
 }
 
-int
-slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
+void slhc_o_status(struct slcompress *comp)
 {
-  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_remember");
-  return -EINVAL;
+	if (comp != NULLSLCOMPR) {
+		DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n",
+			comp->sls_o_compressed,
+			comp->sls_o_uncompressed,
+			comp->sls_o_tcp,
+			comp->sls_o_nontcp,
+			comp->sls_o_searches,
+			comp->sls_o_misses);
+	}
 }
 
-void
-slhc_free(struct slcompress *comp)
-{
-  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_free");
-}
-struct slcompress *
-slhc_init(int rslots, int tslots)
-{
-  printk(KERN_DEBUG "Called IP function on non IP-system: slhc_init");
-  return NULL;
-}
-
-#endif /* CONFIG_INET */
-
-/* VJ header compression */
-EXPORT_SYMBOL(slhc_init);
-EXPORT_SYMBOL(slhc_free);
-EXPORT_SYMBOL(slhc_remember);
-EXPORT_SYMBOL(slhc_compress);
-EXPORT_SYMBOL(slhc_uncompress);
-EXPORT_SYMBOL(slhc_toss);
-
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 1debb2d..d5aa356 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = gsm0408 db channel mgcp gprs abis gbproxy trau subscr mm_auth xid sndcp_xid
+SUBDIRS = gsm0408 db channel mgcp gprs abis gbproxy trau subscr mm_auth xid sndcp_xid slhc
 
 if BUILD_NAT
 SUBDIRS += bsc-nat bsc-nat-trie
diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index 45d1780..1804280 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -32,6 +32,9 @@
 	$(top_builddir)/src/gprs/oap_messages.o \
         $(top_builddir)/src/gprs/gprs_llc_xid.o \
 	$(top_builddir)/src/gprs/gprs_sndcp_xid.o \
+        $(top_builddir)/src/gprs/slhc.o \
+        $(top_builddir)/src/gprs/gprs_sndcp_comp.o \
+        $(top_builddir)/src/gprs/gprs_sndcp_pcomp.o \
 	$(top_builddir)/src/libcommon/libcommon.a \
 	$(LIBOSMOABIS_LIBS) \
 	$(LIBOSMOCORE_LIBS) \
diff --git a/openbsc/tests/slhc/Makefile.am b/openbsc/tests/slhc/Makefile.am
new file mode 100644
index 0000000..d21990f
--- /dev/null
+++ b/openbsc/tests/slhc/Makefile.am
@@ -0,0 +1,21 @@
+AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS)
+
+EXTRA_DIST = slhc_test.ok
+
+noinst_PROGRAMS = slhc_test
+
+slhc_test_SOURCES = slhc_test.c
+
+slhc_test_LDADD = \
+	$(top_builddir)/src/gprs/slhc.o \
+	$(top_builddir)/src/libcommon/libcommon.a \
+	$(LIBOSMOABIS_LIBS) \
+	$(LIBOSMOCORE_LIBS) \
+	$(LIBOSMOGSM_LIBS) \
+	$(LIBOSMOGB_LIBS) \
+	$(LIBCARES_LIBS) \
+	$(LIBCRYPTO_LIBS) \
+	-lgtp -lrt -lm
+
+
diff --git a/openbsc/tests/slhc/slhc_test.c b/openbsc/tests/slhc/slhc_test.c
new file mode 100644
index 0000000..59a5425
--- /dev/null
+++ b/openbsc/tests/slhc/slhc_test.c
@@ -0,0 +1,298 @@
+/* Test SLHC/RFC1144 TCP/IP Header compression/decompression */
+
+/* (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/slhc.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>
+#include <arpa/inet.h>
+
+/* Number of compression slots (S0-1) */
+#define SLOTS 8
+
+/* Maximum packet bytes to display */
+#define DISP_MAX_BYTES 100
+
+/* Sample packets to test with */
+#define PACKETS_LEN 6
+char *packets[] = {
+	"4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27",
+	"4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0",
+	"4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01",
+	"4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01",
+	"4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a",
+	"4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20"
+};
+
+/* Compress a packet using Van Jacobson RFC1144 header compression */
+static int compress(uint8_t *data_o, uint8_t *data_i, int len,
+		    struct slcompress *comp)
+{
+	uint8_t *comp_ptr;	/* Not used */
+	int compr_len;
+
+	/* Create a working copy of the incoming data */
+	memcpy(data_o, data_i, len);
+
+	/* Run compressor */
+	compr_len = slhc_compress(comp, data_i, len, data_o, &comp_ptr, 0);
+	return compr_len;
+}
+
+/* Expand a packet using Van Jacobson RFC1144 header compression */
+static int expand(uint8_t *data_o, uint8_t *data_i, int len,
+		  struct slcompress *comp)
+{
+	int data_decompressed_len;
+
+	/* Create a working copy of the incoming data */
+	memcpy(data_o, data_i, len);
+
+	/* Handle an uncompressed packet (learn header information */
+	if ((data_i[0] & SL_TYPE_UNCOMPRESSED_TCP) == SL_TYPE_UNCOMPRESSED_TCP) {
+		data_o[0] &= 0x4F;
+		data_decompressed_len = slhc_remember(comp, data_o, len);
+		return data_decompressed_len;
+	}
+
+	/* Uncompress compressed packets */
+	else if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
+		data_decompressed_len = slhc_uncompress(comp, data_o, len);
+		return data_decompressed_len;
+	}
+
+	/* Regular or unknown packets will not be touched */
+	return len;
+}
+
+/* Calculate IP Header checksum */
+static uint16_t calc_ip_csum(uint8_t *data, int len)
+{
+	int i;
+	uint32_t accumulator = 0;
+	uint16_t *pointer = (uint16_t *) data;
+
+	for (i = len; i > 1; i -= 2) {
+		accumulator += *pointer;
+		pointer++;
+	}
+
+	if (len % 2)
+		accumulator += *pointer;
+
+	accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff);
+	accumulator += (accumulator >> 16) & 0xffff;
+	return (~accumulator);
+}
+
+/* Calculate TCP/IP checksum */
+static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len)
+{
+	uint8_t *buf;
+	uint16_t csum;
+
+	buf = talloc_zero_size(ctx, len);
+	memset(buf, 0, len);
+	memcpy(buf, packet + 12, 8);
+	buf[9] = packet[9];
+	buf[11] = (len - 20) & 0xFF;
+	buf[10] = (len - 20) >> 8 & 0xFF;
+	memcpy(buf + 12, packet + 20, len - 20);
+	csum = calc_ip_csum(buf, len - 20 + 12);
+	talloc_free(buf);
+	return csum;
+}
+
+/* Check TCP/IP packet */
+static void check_packet(const void *ctx, uint8_t *packet, int len)
+{
+	/* Check IP header */
+	OSMO_ASSERT(len > 20);
+	OSMO_ASSERT(calc_ip_csum(packet, 20) == 0);
+
+	/* Check TCP packet */
+	if (packet[9] != 0x06)
+		return;
+	OSMO_ASSERT(len > 40);
+	OSMO_ASSERT(calc_tcpip_csum(ctx, packet, len) == 0);
+}
+
+/* Strip TCP options from TCP/IP packet */
+static int strip_tcp_options(const void *ctx, uint8_t *packet, int len)
+{
+	uint8_t doff;
+	uint16_t csum;
+
+	/* Check if the packet can be handled here */
+	if (len < 37)
+		return len;
+	if (packet[9] != 0x06)
+		return len;
+
+	/* Strip TCP/IP options from packet */
+	doff = ((packet[32] >> 4) & 0x0F) * 4;
+	memmove(packet + 40, packet + doff + 20, len - 40 - (doff - 20));
+	len = len - (doff - 20);
+
+	/* Repair data offset (TCP header length) */
+	packet[32] &= 0x0F;
+	packet[32] |= 0x50;
+
+	/* Repair checksum */
+	packet[36] = 0;
+	packet[37] = 0;
+	csum = calc_tcpip_csum(ctx, packet, len);
+	packet[36] = csum & 0xFF;
+	packet[37] = csum >> 8 & 0xFF;
+
+	/* Repair total length */
+	packet[3] = len & 0xFF;
+	packet[2] = len >> 8 & 0xFF;
+
+	/* Repair IP header checksum */
+	packet[10] = 0;
+	packet[11] = 0;
+	csum = calc_ip_csum(packet, 20);
+	packet[10] = csum & 0xFF;
+	packet[11] = csum >> 8 & 0xFF;
+	printf("csum=%04x\n", csum);
+
+	return len;
+}
+
+/* Compress / Decompress packets */
+static void test_slhc(const void *ctx)
+{
+	char packet_ascii[2048];
+	int i;
+
+	struct slcompress *comp;
+	uint8_t packet[1024];
+	int packet_len;
+	uint8_t packet_compr[1024];
+	int packet_compr_len;
+	uint8_t packet_decompr[1024];
+	int packet_decompr_len;
+
+	printf("Allocating compression state...\n");
+	comp = slhc_init(ctx, SLOTS, SLOTS);
+	OSMO_ASSERT(comp);
+
+	for(i=0;i<PACKETS_LEN;i++) {
+		/* Read input file */
+		memset(packet_ascii, 0, sizeof(packet_ascii));
+		memset(packet, 0, sizeof(packet));
+		memset(packet_compr, 0, sizeof(packet_compr));
+		memset(packet_decompr, 0, sizeof(packet_decompr));
+		strcpy(packet_ascii,packets[i]);
+
+		packet_len =
+		    osmo_hexparse(packet_ascii, packet, sizeof(packet));
+		check_packet(ctx, packet, packet_len);
+		packet_len = strip_tcp_options(ctx, packet, packet_len);
+		check_packet(ctx, packet, packet_len);
+
+		/* Run compression/decompression algorithm */
+		printf("Compressing...\n");
+		packet_compr_len =
+		    compress(packet_compr, packet, packet_len, comp);
+		printf("Decompressing...\n");
+		packet_decompr_len =
+		    expand(packet_decompr, packet_compr, packet_compr_len,
+			   comp);
+		OSMO_ASSERT(packet_decompr_len == packet_len);
+		check_packet(ctx,packet_decompr,packet_decompr_len);
+
+		/* Display results */
+		printf("Results:\n");
+		if (packet_compr_len > DISP_MAX_BYTES)
+			packet_compr_len = DISP_MAX_BYTES;
+		if (packet_len > DISP_MAX_BYTES)
+			packet_len = DISP_MAX_BYTES;
+		if (packet_decompr_len > DISP_MAX_BYTES)
+			packet_decompr_len = DISP_MAX_BYTES;
+		printf("Original Packet:    (%i bytes) %s\n", packet_len,
+		       osmo_hexdump_nospc(packet, packet_len));
+		printf("DecompressedPacket: (%i bytes) %s\n",
+		       packet_decompr_len, osmo_hexdump_nospc(packet_decompr,
+							      packet_decompr_len));
+		printf("CompressedPacket:   (%i bytes) %s\n", packet_compr_len,
+		       osmo_hexdump_nospc(packet_compr, packet_compr_len));
+		slhc_o_status(comp);
+		slhc_o_status(comp);
+
+		printf("\n");
+	}
+
+	printf("Freeing compression state...\n");
+	slhc_free(comp);
+	printf("\n");
+}
+
+static struct log_info_cat gprs_categories[] = {
+	[DSNDCP] = {
+		    .name = "DSNDCP",
+		    .description =
+		    "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
+		    .enabled = 1,.loglevel = LOGL_DEBUG,
+		    },
+	[DSLHC] = {
+		   .name = "DSLHC",
+		   .description =
+		   "Van Jacobson RFC1144 TCP/IP header compression (SLHC)",
+		   .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 *ctx;
+
+	osmo_init_logging(&info);
+
+	ctx = talloc_named_const(NULL, 0, "slhc_ctx");
+
+	test_slhc(ctx);
+
+	printf("Done\n");
+
+	talloc_report_full(ctx, stderr);
+	OSMO_ASSERT(talloc_total_blocks(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/slhc/slhc_test.ok b/openbsc/tests/slhc/slhc_test.ok
new file mode 100644
index 0000000..636241d
--- /dev/null
+++ b/openbsc/tests/slhc/slhc_test.ok
@@ -0,0 +1,52 @@
+Allocating compression state...
+csum=b3a9
+Compressing...
+Decompressing...
+Results:
+Original Packet:    (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
+DecompressedPacket: (52 bytes) 4510003446dd40004006a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
+CompressedPacket:   (52 bytes) 7510003446dd40004000a9b3c0a8646ec0a864640017ad8b81980100f3ac984d501800e371410000fffd18fffd20fffd23fffd27
+
+csum=97a9
+Compressing...
+Decompressing...
+Results:
+Original Packet:    (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
+DecompressedPacket: (79 bytes) 4510004f46de40004006a997c0a8646ec0a864640017ad8b8198010cf3ac984d501800e3cda40000fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
+CompressedPacket:   (43 bytes) df00cda4fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0
+
+csum=baa9
+Compressing...
+Decompressing...
+Results:
+Original Packet:    (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01
+DecompressedPacket: (43 bytes) 4510002b46df40004006a9bac0a8646ec0a864640017ad8b81980133f3ac989f501800e3a70a0000fffd01
+CompressedPacket:   (9 bytes) dc00a70a5227fffd01
+
+csum=b9a9
+Compressing...
+Decompressing...
+Results:
+Original Packet:    (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01
+DecompressedPacket: (43 bytes) 4510002b46e040004006a9b9c0a8646ec0a864640017ad8b81980136f3ac98a2501800e3a7060000fffb01
+CompressedPacket:   (7 bytes) db00a706fffb01
+
+csum=7ba9
+Compressing...
+Decompressing...
+Results:
+Original Packet:    (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
+DecompressedPacket: (100 bytes) 4510006846e140004006a97bc0a8646ec0a864640017ad8b81980139f3ac98a5501800e3c2d000000d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d
+CompressedPacket:   (68 bytes) db00c2d00d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a
+
+csum=aca9
+Compressing...
+Decompressing...
+Results:
+Original Packet:    (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20
+DecompressedPacket: (54 bytes) 4510003646e240004006a9acc0a8646ec0a864640017ad8b81980179f3ac98a5501800e321fb0000706f6c6c7578206c6f67696e3a20
+CompressedPacket:   (18 bytes) df0021fb706f6c6c7578206c6f67696e3a20
+
+Freeing compression state...
+
+Done
diff --git a/openbsc/tests/testsuite.at b/openbsc/tests/testsuite.at
index 85a81d6..5f37b8e 100644
--- a/openbsc/tests/testsuite.at
+++ b/openbsc/tests/testsuite.at
@@ -136,3 +136,8 @@
 AT_CHECK([$abs_top_builddir/tests/sndcp_xid/sndcp_xid_test], [], [expout], [ignore])
 AT_CLEANUP
 
+AT_SETUP([slhc])
+AT_KEYWORDS([slhc])
+cat $abs_srcdir/slhc/slhc_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/slhc/slhc_test], [], [expout], [ignore])
+AT_CLEANUP

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

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: I7a638e88a43b3eb9d006751a03ef2570e36613f0
Gerrit-PatchSet: 14
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