blob: b0f95b4865551dfa85339ef850c36c994553c8ad [file] [log] [blame]
Philipp73f83d52016-09-02 13:38:01 +02001/* GPRS SNDCP data compression handler */
2
3/* (C) 2016 by Sysmocom s.f.m.c. GmbH
4 * All Rights Reserved
5 *
6 * Author: Philipp Maier
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <stdio.h>
24#include <string.h>
25#include <stdint.h>
26#include <math.h>
27#include <errno.h>
28#include <stdbool.h>
29
30#include <osmocom/core/utils.h>
31#include <osmocom/core/msgb.h>
32#include <osmocom/core/linuxlist.h>
33#include <osmocom/core/talloc.h>
34#include <osmocom/gsm/tlv.h>
35
36#include <openbsc/gprs_llc.h>
37#include <openbsc/sgsn.h>
38#include <openbsc/gprs_sndcp_xid.h>
39#include <openbsc/v42bis.h>
40#include <openbsc/v42bis_private.h>
41#include <openbsc/debug.h>
42#include <openbsc/gprs_sndcp_comp.h>
43#include <openbsc/gprs_sndcp_dcomp.h>
44
45/* A struct to capture the output data of compressor and decompressor */
46struct v42bis_output_buffer {
47 uint8_t *buf;
48 uint8_t *buf_pointer;
49 int len;
50};
51
52/* Handler to capture the output data from the compressor */
53void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len)
54{
55 struct v42bis_output_buffer *output_buffer =
56 (struct v42bis_output_buffer *)user_data;
57 memcpy(output_buffer->buf_pointer, pkt, len);
58 output_buffer->buf_pointer += len;
59 output_buffer->len += len;
60 return;
61}
62
63/* Handler to capture the output data from the decompressor */
64void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len)
65{
66 struct v42bis_output_buffer *output_buffer =
67 (struct v42bis_output_buffer *)user_data;
68 memcpy(output_buffer->buf_pointer, buf, len);
69 output_buffer->buf_pointer += len;
70 output_buffer->len += len;
71 return;
72}
73
74/* Initalize data compression */
75int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
76 const struct gprs_sndcp_comp_field *comp_field)
77{
78 /* Note: This function is automatically called from
79 * gprs_sndcp_comp.c when a new data compression
80 * entity is created by gprs_sndcp.c */
81
82 OSMO_ASSERT(comp_entity);
83 OSMO_ASSERT(comp_field);
84
85 if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
86 && comp_entity->algo == V42BIS) {
Philipp143a2742016-11-25 15:23:50 +010087 OSMO_ASSERT(comp_field->v42bis_params);
Philipp73f83d52016-09-02 13:38:01 +020088 comp_entity->state =
89 v42bis_init(ctx, NULL, comp_field->v42bis_params->p0,
90 comp_field->v42bis_params->p1,
91 comp_field->v42bis_params->p2,
92 &tx_v42bis_frame_handler, NULL,
93 V42BIS_MAX_OUTPUT_LENGTH,
94 &rx_v42bis_data_handler, NULL,
95 V42BIS_MAX_OUTPUT_LENGTH);
96 LOGP(DSNDCP, LOGL_INFO,
97 "V.42bis data compression initalized.\n");
98 return 0;
99 }
100
101 /* Just in case someone tries to initalize an unknown or unsupported
102 * data compresson. Since everything is checked during the SNDCP
103 * negotiation process, this should never happen! */
104 OSMO_ASSERT(false);
105}
106
107/* Terminate data compression */
108void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity)
109{
110 /* Note: This function is automatically called from
111 * gprs_sndcp_comp.c when a data compression
112 * entity is deleted by gprs_sndcp.c */
113
114 OSMO_ASSERT(comp_entity);
115
116 if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
117 && comp_entity->algo == V42BIS) {
118 if (comp_entity->state) {
119 v42bis_free((v42bis_state_t *) comp_entity->state);
120 comp_entity->state = NULL;
121 }
122 LOGP(DSNDCP, LOGL_INFO,
123 "V.42bis data compression terminated.\n");
124 return;
125 }
126
127 /* Just in case someone tries to terminate an unknown or unsupported
128 * data compresson. Since everything is checked during the SNDCP
129 * negotiation process, this should never happen! */
130 OSMO_ASSERT(false);
131}
132
133/* Perform a full reset of the V.42bis compression state */
134static void v42bis_reset(v42bis_state_t *comp)
135{
136 /* This function performs a complete reset of the V.42bis compression
137 * state by reinitalizing the state withe the previously negotiated
138 * parameters. */
139
140 int p0, p1, p2;
141 p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0;
142 p1 = comp->decompress.v42bis_parm_n2;
143 p2 = comp->decompress.v42bis_parm_n7;
144
145 DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n",
146 comp, p0, p1, p2);
147
148 v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL,
149 V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL,
150 V42BIS_MAX_OUTPUT_LENGTH);
151}
152
153/* Compress a packet using V.42bis data compression */
154static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data,
155 unsigned int len, v42bis_state_t *comp)
156{
157 /* Note: This implementation may only be used to compress SN_UNITDATA
158 * packets, since it resets the compression state for each NPDU. */
159
160 uint8_t *data_o;
161 int rc;
162 int skip = 0;
163 struct v42bis_output_buffer compressed_data;
164
165 /* Don't bother with short packets */
166 if (len < MIN_COMPR_PAYLOAD)
167 skip = 1;
168
169 /* Skip if compression is not enabled for TX direction */
170 if (!comp->compress.v42bis_parm_p0)
171 skip = 1;
172
173 /* Skip compression */
174 if (skip) {
175 *pcomp_index = 0;
176 return len;
177 }
178
179 /* Reset V.42bis compression state */
180 v42bis_reset(comp);
181
182 /* Run compressor */
183 data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC);
184 compressed_data.buf = data_o;
185 compressed_data.buf_pointer = data_o;
186 compressed_data.len = 0;
187 comp->compress.user_data = (&compressed_data);
188 rc = v42bis_compress(comp, data, len);
189 if (rc < 0) {
190 LOGP(DSNDCP, LOGL_ERROR,
191 "Data compression failed, skipping...\n");
192 skip = 1;
193 }
194 rc = v42bis_compress_flush(comp);
195 if (rc < 0) {
196 LOGP(DSNDCP, LOGL_ERROR,
197 "Data compression failed, skipping...\n");
198 skip = 1;
199 }
200
201 /* The compressor might yield negative compression gain, in
202 * this case, we just decide to send the packat as normal,
203 * uncompressed payload => skip compresssion */
204 if (compressed_data.len >= len) {
205 LOGP(DSNDCP, LOGL_ERROR,
206 "Data compression ineffective, skipping...\n");
207 skip = 1;
208 }
209
210 /* Skip compression */
211 if (skip) {
212 *pcomp_index = 0;
213 talloc_free(data_o);
214 return len;
215 }
216
217 *pcomp_index = 1;
218 memcpy(data, data_o, compressed_data.len);
219 talloc_free(data_o);
220
221 return compressed_data.len;
222}
223
224/* Expand a packet using V.42bis data compression */
225static int v42bis_expand_unitdata(uint8_t *data, unsigned int len,
226 uint8_t pcomp_index, v42bis_state_t *comp)
227{
228 /* Note: This implementation may only be used to compress SN_UNITDATA
229 * packets, since it resets the compression state for each NPDU. */
230
231 int rc;
232 struct v42bis_output_buffer uncompressed_data;
233 uint8_t *data_i;
234
235 /* Skip when the packet is marked as uncompressed */
236 if (pcomp_index == 0) {
237 return len;
238 }
239
240 /* Reset V.42bis compression state */
241 v42bis_reset(comp);
242
243 /* Decompress packet */
244 data_i = talloc_zero_size(comp, len);
245 memcpy(data_i, data, len);
246 uncompressed_data.buf = data;
247 uncompressed_data.buf_pointer = data;
248 uncompressed_data.len = 0;
249 comp->decompress.user_data = (&uncompressed_data);
250 rc = v42bis_decompress(comp, data_i, len);
251 talloc_free(data_i);
252 if (rc < 0)
253 return -EINVAL;
254 rc = v42bis_decompress_flush(comp);
255 if (rc < 0)
256 return -EINVAL;
257
258 return uncompressed_data.len;
259}
260
261/* Expand packet */
262int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
263 const struct llist_head *comp_entities)
264{
265 int rc;
266 uint8_t pcomp_index = 0;
267 struct gprs_sndcp_comp *comp_entity;
268
269 OSMO_ASSERT(data);
270 OSMO_ASSERT(comp_entities);
271
272 LOGP(DSNDCP, LOGL_DEBUG,
273 "Data compression entity list: comp_entities=%p\n", comp_entities);
274
275 LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp);
276
277 /* Skip on pcomp=0 */
278 if (pcomp == 0) {
279 return len;
280 }
281
282 /* Find out which compression entity handles the data */
283 comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
284
285 /* Skip compression if no suitable compression entity can be found */
286 if (!comp_entity) {
287 return len;
288 }
289
290 /* Note: Only data compression entities may appear in
291 * data compression context */
292 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
293
294 /* Note: Currently V42BIS is the only compression method we
295 * support, so the only allowed algorithm is V42BIS */
296 OSMO_ASSERT(comp_entity->algo == V42BIS);
297
298 /* Find pcomp_index */
299 pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
300
301 /* Run decompression algo */
302 rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state);
303
304 LOGP(DSNDCP, LOGL_DEBUG,
305 "Data expansion done, old length=%d, new length=%d, entity=%p\n",
306 len, rc, comp_entity);
307
308 return rc;
309}
310
311/* Compress packet */
312int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
313 const struct llist_head *comp_entities,
314 uint8_t nsapi)
315{
316 int rc;
317 uint8_t pcomp_index = 0;
318 struct gprs_sndcp_comp *comp_entity;
319
320 OSMO_ASSERT(data);
321 OSMO_ASSERT(pcomp);
322 OSMO_ASSERT(comp_entities);
323
324 LOGP(DSNDCP, LOGL_DEBUG,
325 "Data compression entity list: comp_entities=%p\n", comp_entities);
326
327 /* Find out which compression entity handles the data */
328 comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
329
330 /* Skip compression if no suitable compression entity can be found */
331 if (!comp_entity) {
332 *pcomp = 0;
333 return len;
334 }
335
336 /* Note: Only data compression entities may appear in
337 * data compression context */
338 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
339
340 /* Note: Currently V42BIS is the only compression method we
341 * support, so the only allowed algorithm is V42BIS */
342 OSMO_ASSERT(comp_entity->algo == V42BIS);
343
344 /* Run compression algo */
345 rc = v42bis_compress_unitdata(&pcomp_index, data, len,
346 comp_entity->state);
347
348 /* Find pcomp value */
349 *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
350
351 LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp);
352
353 LOGP(DSNDCP, LOGL_DEBUG,
354 "Data compression done, old length=%d, new length=%d, entity=%p\n",
355 len, rc, comp_entity);
356
357 return rc;
358}