blob: 489106b47d14036b17257c2428b538dad120694d [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) {
87 comp_entity->state =
88 v42bis_init(ctx, NULL, comp_field->v42bis_params->p0,
89 comp_field->v42bis_params->p1,
90 comp_field->v42bis_params->p2,
91 &tx_v42bis_frame_handler, NULL,
92 V42BIS_MAX_OUTPUT_LENGTH,
93 &rx_v42bis_data_handler, NULL,
94 V42BIS_MAX_OUTPUT_LENGTH);
95 LOGP(DSNDCP, LOGL_INFO,
96 "V.42bis data compression initalized.\n");
97 return 0;
98 }
99
100 /* Just in case someone tries to initalize an unknown or unsupported
101 * data compresson. Since everything is checked during the SNDCP
102 * negotiation process, this should never happen! */
103 OSMO_ASSERT(false);
104}
105
106/* Terminate data compression */
107void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity)
108{
109 /* Note: This function is automatically called from
110 * gprs_sndcp_comp.c when a data compression
111 * entity is deleted by gprs_sndcp.c */
112
113 OSMO_ASSERT(comp_entity);
114
115 if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
116 && comp_entity->algo == V42BIS) {
117 if (comp_entity->state) {
118 v42bis_free((v42bis_state_t *) comp_entity->state);
119 comp_entity->state = NULL;
120 }
121 LOGP(DSNDCP, LOGL_INFO,
122 "V.42bis data compression terminated.\n");
123 return;
124 }
125
126 /* Just in case someone tries to terminate an unknown or unsupported
127 * data compresson. Since everything is checked during the SNDCP
128 * negotiation process, this should never happen! */
129 OSMO_ASSERT(false);
130}
131
132/* Perform a full reset of the V.42bis compression state */
133static void v42bis_reset(v42bis_state_t *comp)
134{
135 /* This function performs a complete reset of the V.42bis compression
136 * state by reinitalizing the state withe the previously negotiated
137 * parameters. */
138
139 int p0, p1, p2;
140 p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0;
141 p1 = comp->decompress.v42bis_parm_n2;
142 p2 = comp->decompress.v42bis_parm_n7;
143
144 DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n",
145 comp, p0, p1, p2);
146
147 v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL,
148 V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL,
149 V42BIS_MAX_OUTPUT_LENGTH);
150}
151
152/* Compress a packet using V.42bis data compression */
153static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data,
154 unsigned int len, v42bis_state_t *comp)
155{
156 /* Note: This implementation may only be used to compress SN_UNITDATA
157 * packets, since it resets the compression state for each NPDU. */
158
159 uint8_t *data_o;
160 int rc;
161 int skip = 0;
162 struct v42bis_output_buffer compressed_data;
163
164 /* Don't bother with short packets */
165 if (len < MIN_COMPR_PAYLOAD)
166 skip = 1;
167
168 /* Skip if compression is not enabled for TX direction */
169 if (!comp->compress.v42bis_parm_p0)
170 skip = 1;
171
172 /* Skip compression */
173 if (skip) {
174 *pcomp_index = 0;
175 return len;
176 }
177
178 /* Reset V.42bis compression state */
179 v42bis_reset(comp);
180
181 /* Run compressor */
182 data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC);
183 compressed_data.buf = data_o;
184 compressed_data.buf_pointer = data_o;
185 compressed_data.len = 0;
186 comp->compress.user_data = (&compressed_data);
187 rc = v42bis_compress(comp, data, len);
188 if (rc < 0) {
189 LOGP(DSNDCP, LOGL_ERROR,
190 "Data compression failed, skipping...\n");
191 skip = 1;
192 }
193 rc = v42bis_compress_flush(comp);
194 if (rc < 0) {
195 LOGP(DSNDCP, LOGL_ERROR,
196 "Data compression failed, skipping...\n");
197 skip = 1;
198 }
199
200 /* The compressor might yield negative compression gain, in
201 * this case, we just decide to send the packat as normal,
202 * uncompressed payload => skip compresssion */
203 if (compressed_data.len >= len) {
204 LOGP(DSNDCP, LOGL_ERROR,
205 "Data compression ineffective, skipping...\n");
206 skip = 1;
207 }
208
209 /* Skip compression */
210 if (skip) {
211 *pcomp_index = 0;
212 talloc_free(data_o);
213 return len;
214 }
215
216 *pcomp_index = 1;
217 memcpy(data, data_o, compressed_data.len);
218 talloc_free(data_o);
219
220 return compressed_data.len;
221}
222
223/* Expand a packet using V.42bis data compression */
224static int v42bis_expand_unitdata(uint8_t *data, unsigned int len,
225 uint8_t pcomp_index, v42bis_state_t *comp)
226{
227 /* Note: This implementation may only be used to compress SN_UNITDATA
228 * packets, since it resets the compression state for each NPDU. */
229
230 int rc;
231 struct v42bis_output_buffer uncompressed_data;
232 uint8_t *data_i;
233
234 /* Skip when the packet is marked as uncompressed */
235 if (pcomp_index == 0) {
236 return len;
237 }
238
239 /* Reset V.42bis compression state */
240 v42bis_reset(comp);
241
242 /* Decompress packet */
243 data_i = talloc_zero_size(comp, len);
244 memcpy(data_i, data, len);
245 uncompressed_data.buf = data;
246 uncompressed_data.buf_pointer = data;
247 uncompressed_data.len = 0;
248 comp->decompress.user_data = (&uncompressed_data);
249 rc = v42bis_decompress(comp, data_i, len);
250 talloc_free(data_i);
251 if (rc < 0)
252 return -EINVAL;
253 rc = v42bis_decompress_flush(comp);
254 if (rc < 0)
255 return -EINVAL;
256
257 return uncompressed_data.len;
258}
259
260/* Expand packet */
261int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
262 const struct llist_head *comp_entities)
263{
264 int rc;
265 uint8_t pcomp_index = 0;
266 struct gprs_sndcp_comp *comp_entity;
267
268 OSMO_ASSERT(data);
269 OSMO_ASSERT(comp_entities);
270
271 LOGP(DSNDCP, LOGL_DEBUG,
272 "Data compression entity list: comp_entities=%p\n", comp_entities);
273
274 LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp);
275
276 /* Skip on pcomp=0 */
277 if (pcomp == 0) {
278 return len;
279 }
280
281 /* Find out which compression entity handles the data */
282 comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
283
284 /* Skip compression if no suitable compression entity can be found */
285 if (!comp_entity) {
286 return len;
287 }
288
289 /* Note: Only data compression entities may appear in
290 * data compression context */
291 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
292
293 /* Note: Currently V42BIS is the only compression method we
294 * support, so the only allowed algorithm is V42BIS */
295 OSMO_ASSERT(comp_entity->algo == V42BIS);
296
297 /* Find pcomp_index */
298 pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
299
300 /* Run decompression algo */
301 rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state);
302
303 LOGP(DSNDCP, LOGL_DEBUG,
304 "Data expansion done, old length=%d, new length=%d, entity=%p\n",
305 len, rc, comp_entity);
306
307 return rc;
308}
309
310/* Compress packet */
311int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
312 const struct llist_head *comp_entities,
313 uint8_t nsapi)
314{
315 int rc;
316 uint8_t pcomp_index = 0;
317 struct gprs_sndcp_comp *comp_entity;
318
319 OSMO_ASSERT(data);
320 OSMO_ASSERT(pcomp);
321 OSMO_ASSERT(comp_entities);
322
323 LOGP(DSNDCP, LOGL_DEBUG,
324 "Data compression entity list: comp_entities=%p\n", comp_entities);
325
326 /* Find out which compression entity handles the data */
327 comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
328
329 /* Skip compression if no suitable compression entity can be found */
330 if (!comp_entity) {
331 *pcomp = 0;
332 return len;
333 }
334
335 /* Note: Only data compression entities may appear in
336 * data compression context */
337 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
338
339 /* Note: Currently V42BIS is the only compression method we
340 * support, so the only allowed algorithm is V42BIS */
341 OSMO_ASSERT(comp_entity->algo == V42BIS);
342
343 /* Run compression algo */
344 rc = v42bis_compress_unitdata(&pcomp_index, data, len,
345 comp_entity->state);
346
347 /* Find pcomp value */
348 *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
349
350 LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp);
351
352 LOGP(DSNDCP, LOGL_DEBUG,
353 "Data compression done, old length=%d, new length=%d, entity=%p\n",
354 len, rc, comp_entity);
355
356 return rc;
357}