blob: 0b4c67cd4267662db2752040203060b4a5cb3e54 [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 */
163 if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
164 LOGP(DSNDCP, LOGL_INFO,
165 "Deleting header compression entity %d ...\n",
166 comp_entity->entity);
167 gprs_sndcp_pcomp_term(comp_entity);
168 } else {
169 LOGP(DSNDCP, LOGL_INFO,
170 "Deleting data compression entity %d ...\n",
171 comp_entity->entity);
Philipp73f83d52016-09-02 13:38:01 +0200172 gprs_sndcp_dcomp_term(comp_entity);
Philippf1f34362016-08-26 17:00:21 +0200173 }
174 }
175
176 talloc_free(comp_entities);
177}
178
179/* Delete a compression entity */
180void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
181 unsigned int entity)
182{
183 struct gprs_sndcp_comp *comp_entity;
184 struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
185
186 OSMO_ASSERT(comp_entities);
187
188 llist_for_each_entry(comp_entity, comp_entities, list) {
189 if (comp_entity->entity == entity) {
190 comp_entity_to_delete = comp_entity;
191 break;
192 }
193 }
194
195 if (!comp_entity_to_delete)
196 return;
197
198 if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
199 LOGP(DSNDCP, LOGL_INFO,
200 "Deleting header compression entity %d ...\n",
201 comp_entity_to_delete->entity);
202 gprs_sndcp_pcomp_term(comp_entity_to_delete);
203 } else {
204 LOGP(DSNDCP, LOGL_INFO,
205 "Deleting data compression entity %d ...\n",
206 comp_entity_to_delete->entity);
207 }
208
209 /* Delete compression entity */
210 llist_del(&comp_entity_to_delete->list);
211 talloc_free(comp_entity_to_delete);
212}
213
214/* Create and Add a new compression entity
215 * (returns a pointer to the compression entity that has just been created) */
216struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
217 struct llist_head *comp_entities,
218 const struct gprs_sndcp_comp_field
219 *comp_field)
220{
221 struct gprs_sndcp_comp *comp_entity;
222
223 OSMO_ASSERT(comp_entities);
224 OSMO_ASSERT(comp_field);
225
226 /* Just to be sure, if the entity is already in
227 * the list it will be deleted now */
228 gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
229
230 /* Create and add a new entity to the list */
231 comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
232
233 if (!comp_entity)
234 return NULL;
235
236 llist_add(&comp_entity->list, comp_entities);
237 return comp_entity;
238}
239
240/* Find which compression entity handles the specified pcomp/dcomp */
241struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
242 *comp_entities, uint8_t comp)
243{
244 struct gprs_sndcp_comp *comp_entity;
245 int i;
246
247 OSMO_ASSERT(comp_entities);
248
249 llist_for_each_entry(comp_entity, comp_entities, list) {
250 for (i = 0; i < comp_entity->comp_len; i++) {
251 if (comp_entity->comp[i] == comp)
252 return comp_entity;
253 }
254 }
255
256 LOGP(DSNDCP, LOGL_ERROR,
257 "Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
258 comp);
259 return NULL;
260}
261
262/* Find which compression entity handles the specified nsapi */
263struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
264 *comp_entities, uint8_t nsapi)
265{
266 struct gprs_sndcp_comp *comp_entity;
267 int i;
268
269 OSMO_ASSERT(comp_entities);
270
271 llist_for_each_entry(comp_entity, comp_entities, list) {
272 for (i = 0; i < comp_entity->nsapi_len; i++) {
273 if (comp_entity->nsapi[i] == nsapi)
274 return comp_entity;
275 }
276 }
277
278 return NULL;
279}
280
281/* Find a comp_index for a given pcomp/dcomp value */
282uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
283 uint8_t comp)
284{
285 /* Note: This function returns a normalized version of the comp value,
286 * which matches up with the position of the comp field. Since comp=0
287 * is reserved for "no compression", the index value starts counting
288 * at one. The return value is the PCOMPn/DCOMPn value one can find
289 * in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
290
291 int i;
292 OSMO_ASSERT(comp_entity);
293
294 /* A pcomp/dcomp value of zero is reserved for "no comproession",
295 * So we just bail and return zero in this case */
296 if (comp == 0)
297 return 0;
298
299 /* Look in the pcomp/dcomp list for the index */
300 for (i = 0; i < comp_entity->comp_len; i++) {
301 if (comp_entity->comp[i] == comp)
302 return i + 1;
303 }
304
305 LOGP(DSNDCP, LOGL_ERROR,
306 "Could not find a matching comp_index for given pcomp/dcomp value %d\n",
307 comp);
308 return 0;
309}
310
311/* Find a pcomp/dcomp value for a given comp_index */
312uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
313 uint8_t comp_index)
314{
315 OSMO_ASSERT(comp_entity);
316
317 /* A comp_index of zero translates to zero right away. */
318 if (comp_index == 0)
319 return 0;
320
321 if (comp_index > comp_entity->comp_len) {
322 LOGP(DSNDCP, LOGL_ERROR,
323 "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
324 comp_index);
325 return 0;
326 }
327
328 /* Look in the pcomp/dcomp list for the comp_index, see
329 * note in gprs_sndcp_comp_get_idx() */
330 return comp_entity->comp[comp_index - 1];
331}