[PATCH] openbsc[master]: Adding compression control and final fixups

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 19 17:22:27 UTC 2016


Hello Harald Welte, Jenkins Builder,

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

    https://gerrit.osmocom.org/642

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

Adding compression control and final fixups

- Add module to handle compression entities
- Add module to control header compression
- Introduce VTY commands for heade compression configuration
- Add changes in sndcp and llc to integrate header compression

Change-Id: Ia00260dc09978844c2865957b4d43000b78b5e43
---
M openbsc/include/openbsc/Makefile.am
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
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_vty.c
M openbsc/tests/sgsn/Makefile.am
14 files changed, 1,342 insertions(+), 34 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openbsc refs/changes/42/642/21

diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index f402fea..88a7e2c 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -19,7 +19,7 @@
 		 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 \
-		slhc.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/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h
index c3b82b1..4cd6c2e 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 this 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..cd6ee4e 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_confirmation,
+		      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..69441fb
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_sndcp_comp.h
@@ -0,0 +1,84 @@
+/* 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_entities_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 */
+int gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
+			    int comp);
+
+/* Find a pcomp/dcomp value for a given comp_index */
+int gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
+			     int 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..2528a86
--- /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,
+			      int 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/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am
index 245636b..2910c02 100644
--- a/openbsc/src/gprs/Makefile.am
+++ b/openbsc/src/gprs/Makefile.am
@@ -27,7 +27,7 @@
 			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 \
-			slhc.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 79795d6..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);
@@ -88,7 +89,9 @@
 		llist_add(&l3_xid_field->list, &xid_fields);
 	}
 
-	llme->xid = gprs_llc_copy_xid(llme->xid, &xid_fields);
+	/* Store generated XID for later reference */
+	talloc_free(llme->xid);
+	llme->xid = gprs_llc_copy_xid(llme, &xid_fields);
 
 	return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields);
 }
@@ -120,7 +123,9 @@
 	llist_add(&xid_iovui.list, &xid_fields);
 	llist_add(&xid_reset.list, &xid_fields);
 
-	llme->xid = gprs_llc_copy_xid(llme->xid, &xid_fields);
+	/* Store generated XID for later reference */
+	talloc_free(llme->xid);
+	llme->xid = gprs_llc_copy_xid(llme, &xid_fields);
 
 	return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields);
 }
@@ -135,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);
@@ -145,11 +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,
-				     xid_field->data);
+			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: */
@@ -163,9 +177,10 @@
 				 * inquiry. There is a remainig risk of
 				 * malfunction! */
 				LOGP(DLLC, LOGL_NOTICE,
-				     "Ignoring XID-Field: XID: type=%i, data_len=%i, data=%s\n",
+				     "Ignoring XID-Field: XID: type=%d, data_len=%d, data=%s\n",
 				     xid_field->type, xid_field->data_len,
-				     xid_field->data);
+				     osmo_hexdump_nospc(xid_field->data,
+				     xid_field->data_len));
 			}
 		}
 		talloc_free(xid_fields);
@@ -197,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);
@@ -220,7 +231,7 @@
 				 * when a phone submits values which defer from
 				 * the default! */
 				LOGP(DLLC, LOGL_NOTICE,
-				     "Echoing XID-Field: XID: type=%i, data_len=%i, data=%s\n",
+				     "Echoing XID-Field: XID: type=%d, data_len=%d, data=%s\n",
 				     xid_field->type, xid_field->data_len,
 				     osmo_hexdump_nospc(xid_field->data,
 							xid_field->data_len));
@@ -229,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);
 			}
 		}
 
@@ -518,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..9a6d89a 100644
--- a/openbsc/src/gprs/gprs_sndcp.c
+++ b/openbsc/src/gprs/gprs_sndcp.c
@@ -35,6 +35,59 @@
 #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 0	/* 0=Disabled, 1=Enabled */
+
+#if DEBUG_IP_PACKETS == 1
+/* 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;
+
+	if(len > 90)
+		len_short = 90;
+	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);
+	if (data[9] == 0x06) {
+		DEBUGP(DSNDCP,"%s: Protocol type: TCP\n", info);
+		tcp_flags = data[33];
+
+		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]);
+	}
+}
+#endif
 
 /* Chapter 7.2: SN-PDU Formats */
 struct sndcp_common_hdr {
@@ -143,6 +196,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 +229,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");
+	debug_ip_packet(npdu, npdu_len,1,"defrag_segments()");
+#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) {
+		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 +429,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 +467,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 +533,38 @@
 	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) {
+		talloc_free(compr);
+		return -EIO;
+	}
+	else {
+		msg->len = rc;
+		memcpy(msg->data,compr,rc);
+	}
+	talloc_free(compr);
+#if DEBUG_IP_PACKETS == 1
+	debug_ip_packet(msg->data, msg->len,0,"sndcp_initdata_req()");
+	DEBUGP(DSNDCP,"====================================================\n");
+	DEBUGP(DSNDCP,"::::::::::::::::::::::::::::::::::::::::::::::::::::\n");
+	DEBUGP(DSNDCP,"                                                    \n");
+#endif
 
 	sne = gprs_sndcp_entity_by_lle(lle, nsapi);
 	if (!sne) {
@@ -469,7 +586,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 +606,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 +629,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 +659,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");
+	debug_ip_packet(npdu, npdu_len,1,"sndcp_llunitdata_ind()");
+#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) {
+		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 +770,328 @@
 	case LL_STATUS_IND:
 }
 #endif
+
+/* Generate SNDCP-XID message */
+static int gprs_llc_generate_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);
+
+	/* Comile 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_bytes[1024];
+	int sndcp_xid_bytes_len;
+	struct gprs_llc_xid_field xid_field_request;
+
+	/* Generate compression parameter bytestream */
+	sndcp_xid_bytes_len = gprs_llc_generate_sndcp_xid(l3params_bytes,
+							  sizeof
+							  (l3params_bytes),
+							  nsapi);
+
+	/* Send XID with the SNDCP-XID bytetsream included */
+	if (sndcp_xid_bytes_len > 0) {
+		xid_field_request.type = GPRS_LLC_XID_T_L3_PAR;
+		xid_field_request.data = l3params_bytes;
+		xid_field_request.data_len = sndcp_xid_bytes_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);
+	}
+}
+
+/* Hanle 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 conpression...\n");
+			gprs_sndcp_comp_entities_add(lle->llme,
+						     lle->llme->comp.proto,
+						     comp_field);
+		} else {
+			LOGP(DSNDCP, LOGL_DEBUG,
+			     "Rejecting RFC1144 header conpression...\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 conpression...\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 conpression...\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)
+{
+	/* 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 V42BIS:
+		/* V42BIS is not yet supported,
+		 * so we set applicable nsapis to zero */
+		LOGP(DSNDCP, LOGL_DEBUG,
+		     "Rejecting V42BIS data conpression...\n");
+		comp_field->rfc2507_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 V44 data conpression...\n");
+		comp_field->rohc_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_confirmation,
+		      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_confirmation);
+	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_confirmation->data,
+						xid_field_confirmation->
+						data_len, comp_fields_req);
+	if (!comp_fields_conf)
+		return -EINVAL;
+
+	DEBUGP(DSNDCP,
+	       "Modified version of received 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..19cd7b1
--- /dev/null
+++ b/openbsc/src/gprs/gprs_sndcp_comp.c
@@ -0,0 +1,319 @@
+/* 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)
+{
+	OSMO_ASSERT(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;
+	}
+
+	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_entities_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;
+		}
+	}
+
+	LOGP(DSNDCP, LOGL_ERROR,
+	     "Could not find a matching compression entity for given nsapi value %d\n",
+	     nsapi);
+	return NULL;
+}
+
+/* Find a comp_index for a given pcomp/dcomp value */
+int gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity, int comp)
+{
+	int i;
+
+	OSMO_ASSERT(comp_entity);
+
+	/* A pcomp/dcomp field set to zero always disables
+	 * all sort of compression and is assigned fix. So we
+	 * just 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 */
+int gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
+			     int 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 */
+	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..0fb72b6
--- /dev/null
+++ b/openbsc/src/gprs/gprs_sndcp_pcomp.c
@@ -0,0 +1,264 @@
+/* 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 */
+
+	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 */
+
+	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;	/* Required by slhc_compress() */
+	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 = -1;
+
+	/* 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 1:
+		type = SL_TYPE_UNCOMPRESSED_TCP;
+		break;
+	case 2:
+		type = SL_TYPE_COMPRESSED_TCP;
+		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-datas
+		 * (normally this is handled by pcomp so there is
+		 * no need for tagging the datas) */
+		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);
+
+	/* 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\n", len, rc);
+
+	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, int 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);
+
+	/* 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 done, old length=%d, new length=%d\n",
+	     len, rc);
+	return rc;
+}
diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c
index be7637a..f548dd6 100644
--- a/openbsc/src/gprs/sgsn_libgtp.c
+++ b/openbsc/src/gprs/sgsn_libgtp.c
@@ -307,6 +307,9 @@
 static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
 {
 	struct sgsn_signal_data sig_data;
+	int rc_pdp;
+	int rc_xid;
+	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_pdp = gsm48_tx_gsm_act_pdp_acc(pctx);
+	if(rc_pdp < 0)
+		return rc_pdp;
+
+	/* Send SNDCP XID to MS */
+	lle = &pctx->mm->gb.llme->lle[pctx->sapi];
+	rc_xid = sndcp_sn_xid_req(lle,pctx->nsapi);
+	if(rc_xid < 0)
+		return rc_xid;
+
+	return rc_pdp;
 }
 
 /* The GGSN has confirmed the creation of a PDP Context */
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
index e6dc68d..c7cc268 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -269,6 +269,13 @@
 	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 +1081,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 +1157,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/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index 3e66978..1804280 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -33,6 +33,8 @@
         $(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) \

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

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: Ia00260dc09978844c2865957b4d43000b78b5e43
Gerrit-PatchSet: 21
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