283 lines
8.1 KiB
C
283 lines
8.1 KiB
C
/* 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;
|
|
}
|