blob: c71cc8982e1b1d96b5dd88060b9195520e9877bb [file] [log] [blame]
Philippf1f34362016-08-26 17:00:21 +02001/* GPRS SNDCP header compression entity management tools */
2
3/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
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#include <stdio.h>
23#include <string.h>
24#include <stdint.h>
25#include <math.h>
26#include <errno.h>
27#include <stdbool.h>
28
29#include <osmocom/core/linuxlist.h>
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/utils.h>
32
Neels Hofmeyr396f2e62017-09-04 15:13:25 +020033#include <osmocom/sgsn/debug.h>
34#include <osmocom/sgsn/gprs_sndcp_xid.h>
35#include <osmocom/sgsn/gprs_sndcp_comp.h>
36#include <osmocom/sgsn/gprs_sndcp_pcomp.h>
37#include <osmocom/sgsn/gprs_sndcp_dcomp.h>
Philippf1f34362016-08-26 17:00:21 +020038
39/* Create a new compression entity from a XID-Field */
40static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx,
41 const struct
42 gprs_sndcp_comp_field
43 *comp_field)
44{
45 struct gprs_sndcp_comp *comp_entity;
46 comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp);
47
48 /* Copy relevant information from the SNDCP-XID field */
49 comp_entity->entity = comp_field->entity;
50 comp_entity->comp_len = comp_field->comp_len;
51 memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp));
52
53 if (comp_field->rfc1144_params) {
54 comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len;
55 memcpy(comp_entity->nsapi,
56 comp_field->rfc1144_params->nsapi,
57 sizeof(comp_entity->nsapi));
Stefan Sperlingc5721542018-11-07 16:33:39 +010058 comp_entity->algo.pcomp = comp_field->algo.pcomp;
Philippf1f34362016-08-26 17:00:21 +020059 } else if (comp_field->rfc2507_params) {
60 comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len;
61 memcpy(comp_entity->nsapi,
62 comp_field->rfc2507_params->nsapi,
63 sizeof(comp_entity->nsapi));
Stefan Sperlingc5721542018-11-07 16:33:39 +010064 comp_entity->algo.pcomp = comp_field->algo.pcomp;
Philippf1f34362016-08-26 17:00:21 +020065 } else if (comp_field->rohc_params) {
66 comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len;
67 memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi,
68 sizeof(comp_entity->nsapi));
Stefan Sperlingc5721542018-11-07 16:33:39 +010069 comp_entity->algo.pcomp = comp_field->algo.pcomp;
Philippf1f34362016-08-26 17:00:21 +020070 } else if (comp_field->v42bis_params) {
71 comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
72 memcpy(comp_entity->nsapi,
73 comp_field->v42bis_params->nsapi,
74 sizeof(comp_entity->nsapi));
Stefan Sperlingc5721542018-11-07 16:33:39 +010075 comp_entity->algo.dcomp = comp_field->algo.dcomp;
Philippf1f34362016-08-26 17:00:21 +020076 } else if (comp_field->v44_params) {
Philippae9beda2016-09-28 15:10:14 +020077 comp_entity->nsapi_len = comp_field->v44_params->nsapi_len;
Philippf1f34362016-08-26 17:00:21 +020078 memcpy(comp_entity->nsapi,
Philipp7d309352016-11-15 19:37:05 +010079 comp_field->v44_params->nsapi,
Philippf1f34362016-08-26 17:00:21 +020080 sizeof(comp_entity->nsapi));
Stefan Sperlingc5721542018-11-07 16:33:39 +010081 comp_entity->algo.dcomp = comp_field->algo.dcomp;
Philippf1f34362016-08-26 17:00:21 +020082 } else {
83 /* The caller is expected to check carefully if the all
84 * data fields required for compression entity creation
85 * are present. Otherwise we blow an assertion here */
86 OSMO_ASSERT(false);
87 }
Philippf1f34362016-08-26 17:00:21 +020088
89 /* Check if an NSAPI is selected, if not, it does not make sense
90 * to create the compression entity, since the caller should
91 * have checked the presence of the NSAPI, we blow an assertion
92 * in case of missing NSAPIs */
93 OSMO_ASSERT(comp_entity->nsapi_len > 0);
94
95 /* Determine of which class our compression entity will be
96 * (Protocol or Data compresson ?) */
97 comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field);
98
Philippf1f34362016-08-26 17:00:21 +020099 /* Create an algorithm specific compression context */
Stefan Sperlingc5721542018-11-07 16:33:39 +0100100 switch (comp_entity->compclass) {
101 case SNDCP_XID_PROTOCOL_COMPRESSION:
Philippf1f34362016-08-26 17:00:21 +0200102 if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
103 talloc_free(comp_entity);
104 comp_entity = NULL;
105 }
Stefan Sperlingc5721542018-11-07 16:33:39 +0100106 break;
107 case SNDCP_XID_DATA_COMPRESSION:
Philipp73f83d52016-09-02 13:38:01 +0200108 if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) {
109 talloc_free(comp_entity);
110 comp_entity = NULL;
111 }
Stefan Sperlingc5721542018-11-07 16:33:39 +0100112 break;
113 default:
Stefan Sperling60509682018-11-12 13:03:04 +0100114 /* comp_field is somehow invalid */
115 OSMO_ASSERT(false);
Philippf1f34362016-08-26 17:00:21 +0200116 }
117
Philipp Maierd0ef1ed2017-01-23 15:36:37 +0100118 /* Bail on failure */
Philippf1f34362016-08-26 17:00:21 +0200119 if (comp_entity == NULL) {
120 LOGP(DSNDCP, LOGL_ERROR,
Philipp Maierd0ef1ed2017-01-23 15:36:37 +0100121 "Compression entity creation failed!\n");
Philippf1f34362016-08-26 17:00:21 +0200122 return NULL;
123 }
Philipp Maierd0ef1ed2017-01-23 15:36:37 +0100124
125 /* Display info message */
Philippf1f34362016-08-26 17:00:21 +0200126 if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
127 LOGP(DSNDCP, LOGL_INFO,
128 "New header compression entity (%d) created.\n",
129 comp_entity->entity);
130 } else {
131 LOGP(DSNDCP, LOGL_INFO,
132 "New data compression entity (%d) created.\n",
133 comp_entity->entity);
134 }
135
136 return comp_entity;
137}
138
139/* Allocate a compression enitiy list */
140struct llist_head *gprs_sndcp_comp_alloc(const void *ctx)
141{
142 struct llist_head *lh;
143
144 lh = talloc_zero(ctx, struct llist_head);
145 INIT_LLIST_HEAD(lh);
146
147 return lh;
148}
149
150/* Free a compression entitiy list */
151void gprs_sndcp_comp_free(struct llist_head *comp_entities)
152{
153 struct gprs_sndcp_comp *comp_entity;
154
155 /* We expect the caller to take care of allocating a
156 * compression entity list properly. Attempting to
157 * free a non existing list clearly points out
158 * a malfunction. */
159 OSMO_ASSERT(comp_entities);
160
161 llist_for_each_entry(comp_entity, comp_entities, list) {
162 /* Free compression entity */
Pau Espin Pedrolb7214142019-04-27 13:34:23 +0200163 switch (comp_entity->compclass) {
164 case SNDCP_XID_PROTOCOL_COMPRESSION:
Philippf1f34362016-08-26 17:00:21 +0200165 LOGP(DSNDCP, LOGL_INFO,
166 "Deleting header compression entity %d ...\n",
167 comp_entity->entity);
168 gprs_sndcp_pcomp_term(comp_entity);
Pau Espin Pedrolb7214142019-04-27 13:34:23 +0200169 break;
170 case SNDCP_XID_DATA_COMPRESSION:
Philippf1f34362016-08-26 17:00:21 +0200171 LOGP(DSNDCP, LOGL_INFO,
172 "Deleting data compression entity %d ...\n",
173 comp_entity->entity);
Philipp73f83d52016-09-02 13:38:01 +0200174 gprs_sndcp_dcomp_term(comp_entity);
Pau Espin Pedrolb7214142019-04-27 13:34:23 +0200175 break;
176 default:
177 LOGP(DSNDCP, LOGL_INFO,
178 "Invalid compression class %d!\n", comp_entity->compclass);
179 OSMO_ASSERT(false);
Philippf1f34362016-08-26 17:00:21 +0200180 }
181 }
182
183 talloc_free(comp_entities);
184}
185
186/* Delete a compression entity */
187void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
188 unsigned int entity)
189{
190 struct gprs_sndcp_comp *comp_entity;
191 struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
192
193 OSMO_ASSERT(comp_entities);
194
195 llist_for_each_entry(comp_entity, comp_entities, list) {
196 if (comp_entity->entity == entity) {
197 comp_entity_to_delete = comp_entity;
198 break;
199 }
200 }
201
202 if (!comp_entity_to_delete)
203 return;
204
205 if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
206 LOGP(DSNDCP, LOGL_INFO,
207 "Deleting header compression entity %d ...\n",
208 comp_entity_to_delete->entity);
209 gprs_sndcp_pcomp_term(comp_entity_to_delete);
210 } else {
211 LOGP(DSNDCP, LOGL_INFO,
212 "Deleting data compression entity %d ...\n",
213 comp_entity_to_delete->entity);
214 }
215
216 /* Delete compression entity */
217 llist_del(&comp_entity_to_delete->list);
218 talloc_free(comp_entity_to_delete);
219}
220
221/* Create and Add a new compression entity
222 * (returns a pointer to the compression entity that has just been created) */
223struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
224 struct llist_head *comp_entities,
225 const struct gprs_sndcp_comp_field
226 *comp_field)
227{
228 struct gprs_sndcp_comp *comp_entity;
229
230 OSMO_ASSERT(comp_entities);
231 OSMO_ASSERT(comp_field);
232
233 /* Just to be sure, if the entity is already in
234 * the list it will be deleted now */
235 gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
236
237 /* Create and add a new entity to the list */
238 comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
239
240 if (!comp_entity)
241 return NULL;
242
243 llist_add(&comp_entity->list, comp_entities);
244 return comp_entity;
245}
246
247/* Find which compression entity handles the specified pcomp/dcomp */
248struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
249 *comp_entities, uint8_t comp)
250{
251 struct gprs_sndcp_comp *comp_entity;
252 int i;
253
254 OSMO_ASSERT(comp_entities);
255
256 llist_for_each_entry(comp_entity, comp_entities, list) {
257 for (i = 0; i < comp_entity->comp_len; i++) {
258 if (comp_entity->comp[i] == comp)
259 return comp_entity;
260 }
261 }
262
263 LOGP(DSNDCP, LOGL_ERROR,
264 "Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
265 comp);
266 return NULL;
267}
268
269/* Find which compression entity handles the specified nsapi */
270struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
271 *comp_entities, uint8_t nsapi)
272{
273 struct gprs_sndcp_comp *comp_entity;
274 int i;
275
276 OSMO_ASSERT(comp_entities);
277
278 llist_for_each_entry(comp_entity, comp_entities, list) {
279 for (i = 0; i < comp_entity->nsapi_len; i++) {
280 if (comp_entity->nsapi[i] == nsapi)
281 return comp_entity;
282 }
283 }
284
285 return NULL;
286}
287
288/* Find a comp_index for a given pcomp/dcomp value */
289uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
290 uint8_t comp)
291{
292 /* Note: This function returns a normalized version of the comp value,
293 * which matches up with the position of the comp field. Since comp=0
294 * is reserved for "no compression", the index value starts counting
295 * at one. The return value is the PCOMPn/DCOMPn value one can find
296 * in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
297
298 int i;
299 OSMO_ASSERT(comp_entity);
300
301 /* A pcomp/dcomp value of zero is reserved for "no comproession",
302 * So we just bail and return zero in this case */
303 if (comp == 0)
304 return 0;
305
306 /* Look in the pcomp/dcomp list for the index */
307 for (i = 0; i < comp_entity->comp_len; i++) {
308 if (comp_entity->comp[i] == comp)
309 return i + 1;
310 }
311
312 LOGP(DSNDCP, LOGL_ERROR,
313 "Could not find a matching comp_index for given pcomp/dcomp value %d\n",
314 comp);
315 return 0;
316}
317
318/* Find a pcomp/dcomp value for a given comp_index */
319uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
320 uint8_t comp_index)
321{
322 OSMO_ASSERT(comp_entity);
323
324 /* A comp_index of zero translates to zero right away. */
325 if (comp_index == 0)
326 return 0;
327
328 if (comp_index > comp_entity->comp_len) {
329 LOGP(DSNDCP, LOGL_ERROR,
330 "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
331 comp_index);
332 return 0;
333 }
334
335 /* Look in the pcomp/dcomp list for the comp_index, see
336 * note in gprs_sndcp_comp_get_idx() */
337 return comp_entity->comp[comp_index - 1];
338}