/* 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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; }