[PATCH] openbsc[master]: SNDCP: add V.42bis data compression functionality

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 Sep 2 16:17:08 UTC 2016


Hello Jenkins Builder,

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

    https://gerrit.osmocom.org/803

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

SNDCP: add V.42bis data compression functionality

 - Add compression control for V.42bis Add code to handle compression
   (gprs_sndcp_dcomp.c/h)
 - Add Adjustments in SNDCP
 - Add VTY commands

Change-Id: I6d36cbdf2f5c5f83ca9ba57c70452f02b8582e7e
---
M openbsc/include/openbsc/Makefile.am
A openbsc/include/openbsc/gprs_sndcp_dcomp.h
M openbsc/include/openbsc/sgsn.h
M openbsc/src/gprs/Makefile.am
M openbsc/src/gprs/gprs_sndcp.c
M openbsc/src/gprs/gprs_sndcp_comp.c
A openbsc/src/gprs/gprs_sndcp_dcomp.c
M openbsc/src/gprs/sgsn_main.c
M openbsc/src/gprs/sgsn_vty.c
M openbsc/tests/sgsn/Makefile.am
10 files changed, 636 insertions(+), 42 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/openbsc refs/changes/03/803/3

diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 9bea689..e21b485 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -20,7 +20,7 @@
 		 oap.h oap_messages.h \
 		 gtphub.h gprs_llc_xid.h gprs_sndcp.h gprs_sndcp_xid.h \
 		 iu.h slhc.h gprs_sndcp_comp.h gprs_sndcp_pcomp.h v42bis.h \
-		v42bis_private.h
+		v42bis_private.h gprs_sndcp_dcomp.h
 
 openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
 openbscdir = $(includedir)/openbsc
diff --git a/openbsc/include/openbsc/gprs_sndcp_dcomp.h b/openbsc/include/openbsc/gprs_sndcp_dcomp.h
new file mode 100644
index 0000000..9a32e9e
--- /dev/null
+++ b/openbsc/include/openbsc/gprs_sndcp_dcomp.h
@@ -0,0 +1,55 @@
+/* GPRS SNDCP data 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>
+
+/* Note: The decompressed packet may have a maximum size of:
+ * Return value * MAX_DATADECOMPR_FAC */
+#define MAX_DATADECOMPR_FAC 10
+
+/* Note: In unacknowledged mode (SN_UNITDATA), the comression state is reset
+ * for every NPDU. The compressor needs a reasonably large payload to operate
+ * effectively (yield positive compression gain). For packets shorter than 100
+ * byte, no positive compression gain can be expected so we will skip the
+ * compression for short packets. */
+#define MIN_COMPR_PAYLOAD 100
+
+/* Initalize data compression */
+int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
+			  const struct gprs_sndcp_comp_field *comp_field);
+
+/* Terminate data compression */
+void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity);
+
+/* Expand packet */
+int gprs_sndcp_dcomp_expand(uint8_t *data_o, uint8_t *data_i, unsigned int len,
+			    uint8_t pcomp,
+			    const struct llist_head *comp_entities);
+
+/* Compress packet */
+int gprs_sndcp_dcomp_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 2d70f5a..12d918e 100644
--- a/openbsc/include/openbsc/sgsn.h
+++ b/openbsc/include/openbsc/sgsn.h
@@ -100,6 +100,15 @@
 		int passive;
 		int s01;
 	} pcomp_rfc1144;
+
+	/* V.42vis data compression */
+	struct {
+		int active;
+		int passive;
+		int p0;
+		int p1;
+		int p2;
+	} dcomp_v42bis;
 };
 
 struct sgsn_instance {
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am
index f479d56..98abb3d 100644
--- a/openbsc/src/gprs/Makefile.am
+++ b/openbsc/src/gprs/Makefile.am
@@ -27,7 +27,8 @@
 			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 gprs_sndcp_comp.c gprs_sndcp_pcomp.c v42bis.c
+			slhc.c gprs_sndcp_comp.c gprs_sndcp_pcomp.c v42bis.c \
+			gprs_sndcp_dcomp.c
 osmo_sgsn_LDADD = 	\
 			$(top_builddir)/src/libcommon/libcommon.a \
 			-lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \
diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c
index c3020c9..5344776 100644
--- a/openbsc/src/gprs/gprs_sndcp.c
+++ b/openbsc/src/gprs/gprs_sndcp.c
@@ -38,6 +38,7 @@
 #include <openbsc/gprs_llc_xid.h>
 #include <openbsc/gprs_sndcp_xid.h>
 #include <openbsc/gprs_sndcp_pcomp.h>
+#include <openbsc/gprs_sndcp_dcomp.h>
 #include <openbsc/gprs_sndcp_comp.h>
 
 #define DEBUG_IP_PACKETS 0	/* 0=Disabled, 1=Enabled */
@@ -270,7 +271,8 @@
 	uint8_t *npdu;
 	int npdu_len;
 	int rc;
-	uint8_t *expnd;
+	uint8_t *hdr_expnd = NULL;
+	uint8_t *data_expnd = NULL;
 
 	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,
@@ -314,18 +316,34 @@
 	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
 	DEBUGP(DSNDCP, "===================================================\n");
 #endif
-	expnd = talloc_zero_size(msg, msg->len + MAX_HDRDECOMPR_INCR);
-	rc = gprs_sndcp_pcomp_expand(expnd, npdu, npdu_len,
+	/* Apply data decompression */
+	data_expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC);
+	rc = gprs_sndcp_dcomp_expand(data_expnd, npdu, npdu_len,
+				     sne->defrag.dcomp, sne->defrag.data);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR, "Data decompression failed!\n");
+		talloc_free(data_expnd);
+		return -EIO;
+	}
+
+	/* Apply header decompression */
+	hdr_expnd = talloc_zero_size(msg, rc + MAX_HDRDECOMPR_INCR);
+	rc = gprs_sndcp_pcomp_expand(hdr_expnd, data_expnd, rc,
 				     sne->defrag.pcomp, sne->defrag.proto);
 	if (rc < 0) {
 		LOGP(DSNDCP, LOGL_ERROR,
 		     "TCP/IP Header decompression failed!\n");
-		talloc_free(expnd);
+		talloc_free(hdr_expnd);
+		talloc_free(data_expnd);
 		return -EIO;
-	} else
-		npdu_len = rc;
+	}
+
+	/* Modify npu length, hdr_expnd is handed directly handed
+	 * over to gsn_rx_sndcp_ud_ind(), see below */
+	npdu_len = rc;
+
 #if DEBUG_IP_PACKETS == 1
-	debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()");
+	debug_ip_packet(hdr_expnd, npdu_len, 1, "defrag_segments()");
 	DEBUGP(DSNDCP, "===================================================\n");
 	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
 	DEBUGP(DSNDCP, "                                                   \n");
@@ -333,9 +351,10 @@
 
 	/* Hand off packet to gtp */
 	rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
-				  sne->nsapi, msg, npdu_len, expnd);
+				  sne->nsapi, msg, npdu_len, hdr_expnd);
 
-	talloc_free(expnd);
+	talloc_free(hdr_expnd);
+	talloc_free(data_expnd);
 	return rc;
 }
 
@@ -608,7 +627,8 @@
 	uint8_t pcomp = 0;
 	uint8_t dcomp = 0;
 	int rc;
-	uint8_t *compr;
+	uint8_t *hdr_compr = NULL;
+	uint8_t *data_compr = NULL;
 	uint8_t *msg_ptr;
 
 	/* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
@@ -620,19 +640,34 @@
 	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,
+	/* Apply header compression */
+	hdr_compr = talloc_zero_size(msg, msg->len);
+	rc = gprs_sndcp_pcomp_compress(hdr_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);
+		talloc_free(hdr_compr);
 		return -EIO;
-	} else {
-		msgb_get(msg, msg->len);
-		msg_ptr = msgb_put(msg, rc);
-		memcpy(msg_ptr, compr, rc);
 	}
-	talloc_free(compr);
+
+	/* Apply data compression */
+	data_compr = talloc_zero_size(msg, msg->len);
+	rc = gprs_sndcp_dcomp_compress(data_compr, hdr_compr, rc, &dcomp,
+				       lle->llme->comp.data, nsapi);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR, "Data compression failed!\n");
+		talloc_free(hdr_compr);
+		talloc_free(data_compr);
+		return -EIO;
+	}
+
+	/* Copy results back to msg buffer */
+	msgb_get(msg, msg->len);
+	msg_ptr = msgb_put(msg, rc);
+	memcpy(msg_ptr, data_compr, rc);
+
+	talloc_free(hdr_compr);
+	talloc_free(data_compr);
 #if DEBUG_IP_PACKETS == 1
 	DEBUGP(DSNDCP, "===================================================\n");
 	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
@@ -703,7 +738,8 @@
 	uint16_t npdu_num __attribute__((unused));
 	int npdu_len;
 	int rc;
-	uint8_t *expnd;
+	uint8_t *hdr_expnd = NULL;
+	uint8_t *data_expnd = NULL;
 
 	sch = (struct sndcp_common_hdr *) hdr;
 	if (sch->first) {
@@ -761,18 +797,35 @@
 	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
 	DEBUGP(DSNDCP, "===================================================\n");
 #endif
-	expnd = talloc_zero_size(msg, npdu_len + MAX_HDRDECOMPR_INCR);
-	rc = gprs_sndcp_pcomp_expand(expnd, npdu, npdu_len,
+
+	/* Apply data decompression */
+	data_expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC);
+	rc = gprs_sndcp_dcomp_expand(data_expnd, npdu, npdu_len,
+				     sne->defrag.dcomp, sne->defrag.data);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR, "Data decompression failed!\n");
+		talloc_free(data_expnd);
+		return -EIO;
+	}
+
+	/* Apply header decompression */
+	hdr_expnd = talloc_zero_size(msg, rc + MAX_HDRDECOMPR_INCR);
+	rc = gprs_sndcp_pcomp_expand(hdr_expnd, data_expnd, rc,
 				     sne->defrag.pcomp, sne->defrag.proto);
 	if (rc < 0) {
 		LOGP(DSNDCP, LOGL_ERROR,
 		     "TCP/IP Header decompression failed!\n");
-		talloc_free(expnd);
+		talloc_free(hdr_expnd);
+		talloc_free(data_expnd);
 		return -EIO;
-	} else
-		npdu_len = rc;
+	}
+
+	/* Modify npu length, hdr_expnd is handed directly handed
+	 * over to gsn_rx_sndcp_ud_ind(), see below */
+	npdu_len = rc;
+
 #if DEBUG_IP_PACKETS == 1
-	debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()");
+	debug_ip_packet(hdr_expnd, npdu_len, 1, "sndcp_llunitdata_ind()");
 	DEBUGP(DSNDCP, "===================================================\n");
 	DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
 	DEBUGP(DSNDCP, "                                                   \n");
@@ -780,9 +833,10 @@
 
 	/* Hand off packet to gtp */
 	rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli,
-				  sne->nsapi, msg, npdu_len, expnd);
+				  sne->nsapi, msg, npdu_len, hdr_expnd);
 
-	talloc_free(expnd);
+	talloc_free(data_expnd);
+	talloc_free(hdr_expnd);
 	return rc;
 }
 
@@ -851,8 +905,11 @@
 	LLIST_HEAD(comp_fields);
 	struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params;
 	struct gprs_sndcp_comp_field rfc1144_comp_field;
+	struct gprs_sndcp_dcomp_v42bis_params v42bis_params;
+	struct gprs_sndcp_comp_field v42bis_comp_field;
 
 	memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
+	memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
 
 	/* Setup rfc1144 */
 	if (sgsn->cfg.pcomp_rfc1144.active) {
@@ -868,6 +925,23 @@
 		rfc1144_comp_field.rfc1144_params = &rfc1144_params;
 		entity++;
 		llist_add(&rfc1144_comp_field.list, &comp_fields);
+	}
+
+	/* Setup V.42bis */
+	if (sgsn->cfg.dcomp_v42bis.active) {
+		v42bis_params.nsapi[0] = nsapi;
+		v42bis_params.nsapi_len = 1;
+		v42bis_params.p0 = sgsn->cfg.dcomp_v42bis.p0;
+		v42bis_params.p1 = sgsn->cfg.dcomp_v42bis.p1;
+		v42bis_params.p2 = sgsn->cfg.dcomp_v42bis.p2;
+		v42bis_comp_field.p = 1;
+		v42bis_comp_field.entity = entity;
+		v42bis_comp_field.algo = V42BIS;
+		v42bis_comp_field.comp[V42BIS_DCOMP1] = 1;
+		v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM;
+		v42bis_comp_field.v42bis_params = &v42bis_params;
+		entity++;
+		llist_add(&v42bis_comp_field.list, &comp_fields);
 	}
 
 	/* Compile bytestream */
@@ -975,13 +1049,19 @@
 	/* 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);
+		if (sgsn->cfg.dcomp_v42bis.passive &&
+		    comp_field->v42bis_params->nsapi_len > 0) {
+			DEBUGP(DSNDCP,
+			       "Accepting V.42bis data compression...\n");
+			gprs_sndcp_comp_add(lle->llme, lle->llme->comp.data,
+					    comp_field);
+		} else {
+			LOGP(DSNDCP, LOGL_DEBUG,
+			     "Rejecting V.42bis data compression...\n");
+			gprs_sndcp_comp_delete(lle->llme->comp.data,
+					       comp_field->entity);
+			comp_field->v42bis_params->nsapi_len = 0;
+		}
 		break;
 	case V44:
 		/* V44 is not yet supported,
diff --git a/openbsc/src/gprs/gprs_sndcp_comp.c b/openbsc/src/gprs/gprs_sndcp_comp.c
index 1a9d030..b13cb8b 100644
--- a/openbsc/src/gprs/gprs_sndcp_comp.c
+++ b/openbsc/src/gprs/gprs_sndcp_comp.c
@@ -34,6 +34,7 @@
 #include <openbsc/gprs_sndcp_xid.h>
 #include <openbsc/gprs_sndcp_comp.h>
 #include <openbsc/gprs_sndcp_pcomp.h>
+#include <openbsc/gprs_sndcp_dcomp.h>
 
 /* Create a new compression entity from a XID-Field */
 static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx,
@@ -100,16 +101,16 @@
 			comp_entity = NULL;
 		}
 	} else {
-		LOGP(DSNDCP, LOGL_ERROR,
-		     "We don't support data compression yet!\n");
-		talloc_free(comp_entity);
-		return NULL;
+		if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) {
+			talloc_free(comp_entity);
+			comp_entity = NULL;
+		}
 	}
 
 	/* Display info message */
 	if (comp_entity == NULL) {
 		LOGP(DSNDCP, LOGL_ERROR,
-		     "Header compression entity (%d) creation failed!\n",
+		     "Compression entity (%d) creation failed!\n",
 		     comp_entity->entity);
 		return NULL;
 	}
@@ -159,6 +160,7 @@
 			LOGP(DSNDCP, LOGL_INFO,
 			     "Deleting data compression entity %d ...\n",
 			     comp_entity->entity);
+			gprs_sndcp_dcomp_term(comp_entity);
 		}
 	}
 
diff --git a/openbsc/src/gprs/gprs_sndcp_dcomp.c b/openbsc/src/gprs/gprs_sndcp_dcomp.c
new file mode 100644
index 0000000..9b04a5f
--- /dev/null
+++ b/openbsc/src/gprs/gprs_sndcp_dcomp.c
@@ -0,0 +1,366 @@
+/* GPRS SNDCP data 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/v42bis.h>
+#include <openbsc/v42bis_private.h>
+#include <openbsc/debug.h>
+#include <openbsc/gprs_sndcp_comp.h>
+#include <openbsc/gprs_sndcp_dcomp.h>
+
+/* A struct to capture the output data of compressor and decompressor */
+struct v42bis_output_buffer {
+	uint8_t *buf;
+	uint8_t *buf_pointer;
+	int len;
+};
+
+/* Handler to capture the output data from the compressor */
+void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len)
+{
+	struct v42bis_output_buffer *output_buffer =
+	    (struct v42bis_output_buffer *)user_data;
+	memcpy(output_buffer->buf_pointer, pkt, len);
+	output_buffer->buf_pointer += len;
+	output_buffer->len += len;
+	return;
+}
+
+/* Handler to capture the output data from the decompressor */
+void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len)
+{
+	struct v42bis_output_buffer *output_buffer =
+	    (struct v42bis_output_buffer *)user_data;
+	memcpy(output_buffer->buf_pointer, buf, len);
+	output_buffer->buf_pointer += len;
+	output_buffer->len += len;
+	return;
+}
+
+/* Initalize data compression */
+int gprs_sndcp_dcomp_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 data compression
+	 * entity is created by gprs_sndcp.c */
+
+	OSMO_ASSERT(comp_entity);
+	OSMO_ASSERT(comp_field);
+
+	if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
+	    && comp_entity->algo == V42BIS) {
+		comp_entity->state =
+		    v42bis_init(ctx, NULL, comp_field->v42bis_params->p0,
+				comp_field->v42bis_params->p1,
+				comp_field->v42bis_params->p2,
+				&tx_v42bis_frame_handler, NULL,
+				V42BIS_MAX_OUTPUT_LENGTH,
+				&rx_v42bis_data_handler, NULL,
+				V42BIS_MAX_OUTPUT_LENGTH);
+		LOGP(DSNDCP, LOGL_INFO,
+		     "V.42bis data compression initalized.\n");
+		return 0;
+	}
+
+	/* Just in case someone tries to initalize an unknown or unsupported
+	 * data compresson. Since everything is checked during the SNDCP
+	 * negotiation process, this should never happen! */
+	OSMO_ASSERT(false);
+}
+
+/* Terminate data compression */
+void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity)
+{
+	/* Note: This function is automatically called from
+	 * gprs_sndcp_comp.c when a data compression
+	 * entity is deleted by gprs_sndcp.c */
+
+	OSMO_ASSERT(comp_entity);
+
+	if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
+	    && comp_entity->algo == V42BIS) {
+		if (comp_entity->state) {
+			v42bis_free((v42bis_state_t *) comp_entity->state);
+			comp_entity->state = NULL;
+		}
+		LOGP(DSNDCP, LOGL_INFO,
+		     "V.42bis data compression terminated.\n");
+		return;
+	}
+
+	/* Just in case someone tries to terminate an unknown or unsupported
+	 * data compresson. Since everything is checked during the SNDCP
+	 * negotiation process, this should never happen! */
+	OSMO_ASSERT(false);
+}
+
+/* Perform a full reset of the V.42bis compression state */
+static void v42bis_reset(v42bis_state_t *comp)
+{
+	/* This function performs a complete reset of the V.42bis compression
+	 * state by reinitalizing the state withe the previously negotiated
+	 * parameters. */
+
+	int p0, p1, p2;
+	p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0;
+	p1 = comp->decompress.v42bis_parm_n2;
+	p2 = comp->decompress.v42bis_parm_n7;
+
+	DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n",
+	       comp, p0, p1, p2);
+
+	v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL,
+		    V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL,
+		    V42BIS_MAX_OUTPUT_LENGTH);
+}
+
+/* Compress a packet using V.42bis data compression */
+static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data_o,
+				    uint8_t *data_i, unsigned int len,
+				    v42bis_state_t *comp)
+{
+	/* Note: This implementation may only be used to compress SN_UNITDATA
+	 * packets, since it resets the compression state for each NPDU. */
+
+	uint8_t *data_c;
+	int rc;
+	int skip = 0;
+	struct v42bis_output_buffer compressed_data;
+
+	/* Don't bother with short packets */
+	if (len < MIN_COMPR_PAYLOAD)
+		skip = 1;
+
+	/* Skip if compression is not enabled for TX direction */
+	if (!comp->compress.v42bis_parm_p0)
+		skip = 1;
+
+	/* Skip compression */
+	if (skip) {
+		*pcomp_index = 0;
+		memcpy(data_o, data_i, len);
+		return len;
+	}
+
+	/* Reset V.42bis compression state */
+	v42bis_reset(comp);
+
+	/* Run compressor */
+	data_c = talloc_zero_size(NULL, len * MAX_DATADECOMPR_FAC);
+	compressed_data.buf = data_c;
+	compressed_data.buf_pointer = data_c;
+	compressed_data.len = 0;
+	comp->compress.user_data = (&compressed_data);
+	rc = v42bis_compress(comp, data_i, len);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "Data compression failed, skipping...\n");
+		skip = 1;
+	}
+	rc = v42bis_compress_flush(comp);
+	if (rc < 0) {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "Data compression failed, skipping...\n");
+		skip = 1;
+	}
+
+	/* The compressor might yield negative compression gain, in
+	 * this case, we just decide to send the packat as normal,
+	 * uncompressed payload => skip compresssion */
+	if (compressed_data.len >= len) {
+		LOGP(DSNDCP, LOGL_ERROR,
+		     "Data compression ineffective, skipping...\n");
+		skip = 1;
+	}
+
+	/* Skip compression */
+	if (skip) {
+		*pcomp_index = 0;
+		memcpy(data_o, data_i, len);
+		talloc_free(data_c);
+		return len;
+	}
+
+	*pcomp_index = 1;
+	memcpy(data_o, data_c, compressed_data.len);
+	talloc_free(data_c);
+
+	return compressed_data.len;
+}
+
+/* Expand a packet using V.42bis data compression */
+static int v42bis_expand_unitdata(uint8_t *data_o, uint8_t *data_i,
+				  unsigned int len, uint8_t pcomp_index,
+				  v42bis_state_t *comp)
+{
+	/* Note: This implementation may only be used to compress SN_UNITDATA
+	 * packets, since it resets the compression state for each NPDU. */
+
+	int rc;
+	struct v42bis_output_buffer uncompressed_data;
+
+	/* Skip when the packet is marked as uncompressed */
+	if (pcomp_index == 0) {
+		memcpy(data_o, data_i, len);
+		return len;
+	}
+
+	/* Reset V.42bis compression state */
+	v42bis_reset(comp);
+
+	/* Decompress packet */
+	uncompressed_data.buf = data_o;
+	uncompressed_data.buf_pointer = data_o;
+	uncompressed_data.len = 0;
+	comp->decompress.user_data = (&uncompressed_data);
+	rc = v42bis_decompress(comp, data_i, len);
+	if (rc < 0)
+		return -EINVAL;
+	rc = v42bis_decompress_flush(comp);
+	if (rc < 0)
+		return -EINVAL;
+
+	return uncompressed_data.len;
+}
+
+/* Expand packet */
+int gprs_sndcp_dcomp_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,
+	     "Data compression entity list: comp_entities=%p\n", comp_entities);
+
+	LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%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) {
+		memcpy(data_o, data_i, len);
+		return len;
+	}
+
+	/* Note: Only data compression entities may appear in
+	 * data compression context */
+	OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
+
+	/* Note: Currently V42BIS is the only compression method we
+	 * support, so the only allowed algorithm is V42BIS */
+	OSMO_ASSERT(comp_entity->algo == V42BIS);
+
+	/* Find pcomp_index */
+	pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
+
+	/* Run decompression algo */
+	rc = v42bis_expand_unitdata(data_o, data_i, len, pcomp_index,
+				    comp_entity->state);
+
+	LOGP(DSNDCP, LOGL_DEBUG,
+	     "Data expansion done, old length=%d, new length=%d, entity=%p\n",
+	     len, rc, comp_entity);
+
+	return rc;
+}
+
+/* Compress packet */
+int gprs_sndcp_dcomp_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,
+	     "Data 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 data compression entities may appear in
+	 * data compression context */
+	OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
+
+	/* Note: Currently V42BIS is the only compression method we
+	 * support, so the only allowed algorithm is V42BIS */
+	OSMO_ASSERT(comp_entity->algo == V42BIS);
+
+	/* Run compression algo */
+	rc = v42bis_compress_unitdata(&pcomp_index, data_o, data_i,
+				      len, comp_entity->state);
+
+	/* Find pcomp value */
+	*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
+
+	LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp);
+
+	LOGP(DSNDCP, LOGL_DEBUG,
+	     "Data compression done, old length=%d, new length=%d, entity=%p\n",
+	     len, rc, comp_entity);
+
+	return rc;
+}
diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
index 894ce84..f01798b 100644
--- a/openbsc/src/gprs/sgsn_main.c
+++ b/openbsc/src/gprs/sgsn_main.c
@@ -297,6 +297,11 @@
 		.description = "RFC1144 TCP/IP Header compression (SLHC)",
 		.enabled = 1, .loglevel = LOGL_DEBUG,
 	},
+	[DV42BIS] = {
+		.name = "DV42BIS",
+		.description = "V.42bis data compression (SNDCP)",
+		.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 0eea350..1b477e5 100644
--- a/openbsc/src/gprs/sgsn_vty.c
+++ b/openbsc/src/gprs/sgsn_vty.c
@@ -277,6 +277,26 @@
 	} else
 		vty_out(vty, " no compression rfc1144%s", VTY_NEWLINE);
 
+	if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 1) {
+		vty_out(vty,
+			" compression v42bis active direction sgsn codewords %d strlen %d%s",
+			g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
+			VTY_NEWLINE);
+	} else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 2) {
+		vty_out(vty,
+			" compression v42bis active direction ms codewords %d strlen %d%s",
+			g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
+			VTY_NEWLINE);
+	} else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 3) {
+		vty_out(vty,
+			" compression v42bis active direction both codewords %d strlen %d%s",
+			g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
+			VTY_NEWLINE);
+	} else if (g_cfg->dcomp_v42bis.passive) {
+		vty_out(vty, " compression v42bis passive%s", VTY_NEWLINE);
+	} else
+		vty_out(vty, " no compression v42bis%s", VTY_NEWLINE);
+
 	return CMD_SUCCESS;
 }
 
@@ -1117,6 +1137,59 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_no_comp_v42bis, cfg_no_comp_v42bis_cmd,
+      "no compression v42bis",
+      NO_STR COMPRESSION_STR "disable V.42bis data compression\n")
+{
+	g_cfg->dcomp_v42bis.active = 0;
+	g_cfg->dcomp_v42bis.passive = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_comp_v42bis, cfg_comp_v42bis_cmd,
+      "compression v42bis active direction (ms|sgsn|both) codewords <512-65535> strlen <6-250>",
+      COMPRESSION_STR
+      "V.42bis data compresion scheme\n"
+      "Compression is actively proposed\n"
+      "Direction in which the compression shall be active (p0)\n"
+      "Compress ms->sgsn direction only\n"
+      "Compress sgsn->ms direction only\n"
+      "Both directions\n"
+      "Number of codewords (p1)\n"
+      "Number of codewords\n"
+      "Maximum string length (p2)\n" "Maximum string length\n")
+{
+	g_cfg->dcomp_v42bis.active = 1;
+	g_cfg->dcomp_v42bis.passive = 1;
+
+	switch (argv[0][0]) {
+	case 'm':
+		g_cfg->dcomp_v42bis.p0 = 1;
+		break;
+	case 's':
+		g_cfg->dcomp_v42bis.p0 = 2;
+		break;
+	case 'b':
+		g_cfg->dcomp_v42bis.p0 = 3;
+		break;
+	}
+
+	g_cfg->dcomp_v42bis.p1 = atoi(argv[1]);
+	g_cfg->dcomp_v42bis.p2 = atoi(argv[2]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_comp_v42bisp, cfg_comp_v42bisp_cmd,
+      "compression v42bis passive",
+      COMPRESSION_STR
+      "V.42bis data compresion scheme\n"
+      "Compression is available on request\n")
+{
+	g_cfg->dcomp_v42bis.active = 0;
+	g_cfg->dcomp_v42bis.passive = 1;
+	return CMD_SUCCESS;
+}
+
 int sgsn_vty_init(void)
 {
 	install_element_ve(&show_sgsn_cmd);
@@ -1174,7 +1247,9 @@
 	install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd);
 	install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd);
 	install_element(SGSN_NODE, &cfg_comp_rfc1144p_cmd);
-
+	install_element(SGSN_NODE, &cfg_no_comp_v42bis_cmd);
+	install_element(SGSN_NODE, &cfg_comp_v42bis_cmd);
+	install_element(SGSN_NODE, &cfg_comp_v42bisp_cmd);
 	return 0;
 }
 
diff --git a/openbsc/tests/sgsn/Makefile.am b/openbsc/tests/sgsn/Makefile.am
index 4d4431e..48b99b2 100644
--- a/openbsc/tests/sgsn/Makefile.am
+++ b/openbsc/tests/sgsn/Makefile.am
@@ -36,6 +36,7 @@
         $(top_builddir)/src/gprs/gprs_sndcp_comp.o \
         $(top_builddir)/src/gprs/gprs_sndcp_pcomp.o \
         $(top_builddir)/src/gprs/v42bis.o \
+        $(top_builddir)/src/gprs/gprs_sndcp_dcomp.o \
 	$(top_builddir)/src/libcommon/libcommon.a \
 	$(LIBOSMOABIS_LIBS) \
 	$(LIBOSMOCORE_LIBS) \

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

Gerrit-MessageType: newpatchset
Gerrit-Change-Id: I6d36cbdf2f5c5f83ca9ba57c70452f02b8582e7e
Gerrit-PatchSet: 3
Gerrit-Project: openbsc
Gerrit-Branch: master
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder



More information about the gerrit-log mailing list