aboutsummaryrefslogtreecommitdiffstats
path: root/src/gprs/gprs_sndcp_pcomp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gprs/gprs_sndcp_pcomp.c')
-rw-r--r--src/gprs/gprs_sndcp_pcomp.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/gprs/gprs_sndcp_pcomp.c b/src/gprs/gprs_sndcp_pcomp.c
new file mode 100644
index 000000000..a2236c3b1
--- /dev/null
+++ b/src/gprs/gprs_sndcp_pcomp.c
@@ -0,0 +1,282 @@
+/* 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) {
+ OSMO_ASSERT(comp_field->rfc1144_params);
+ 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 terminate an unknown or unsupported
+ * data 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 rfc1144_compress(uint8_t *pcomp_index, uint8_t *data,
+ unsigned int len, struct slcompress *comp)
+{
+ uint8_t *comp_ptr;
+ int compr_len;
+ uint8_t *data_o;
+
+ /* Create a working copy of the incoming data */
+ data_o = talloc_zero_size(comp, len);
+ memcpy(data_o, data, len);
+
+ /* Run compressor */
+ compr_len = slhc_compress(comp, data, 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;
+ memcpy(data, data_o, compr_len);
+ } else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) ==
+ SL_TYPE_UNCOMPRESSED_TCP) {
+ *pcomp_index = 1;
+ data_o[0] &= 0x4F;
+ memcpy(data, data_o, compr_len);
+ } else
+ *pcomp_index = 0;
+
+ talloc_free(data_o);
+ return compr_len;
+}
+
+/* Expand a packet using Van Jacobson RFC1144 header compression */
+static int rfc1144_expand(uint8_t *data, 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;
+ break;
+ case 1:
+ type = SL_TYPE_UNCOMPRESSED_TCP;
+ break;
+ case 2:
+ type = SL_TYPE_COMPRESSED_TCP;
+ break;
+ default:
+ LOGP(DSNDCP, LOGL_ERROR,
+ "rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
+ pcomp_index);
+ type = SL_TYPE_IP;
+ break;
+ }
+
+ /* 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[0] &= 0x4F;
+ data_decompressed_len = slhc_remember(comp, data, len);
+ return data_decompressed_len;
+ }
+
+ /* Uncompress compressed packets */
+ else if (type == SL_TYPE_COMPRESSED_TCP) {
+ data_decompressed_len = slhc_uncompress(comp, data, 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, 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);
+ OSMO_ASSERT(comp_entities);
+
+ LOGP(DSNDCP, LOGL_DEBUG,
+ "Header 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) {
+ 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) {
+ 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 = rfc1144_expand(data, 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, 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);
+ OSMO_ASSERT(pcomp);
+ OSMO_ASSERT(comp_entities);
+
+ LOGP(DSNDCP, LOGL_DEBUG,
+ "Header 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;
+ 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 = rfc1144_compress(&pcomp_index, data, 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 compression done, old length=%d, new length=%d, entity=%p\n",
+ len, rc, comp_entity);
+ return rc;
+}