blob: 3e026032ef91a8eb878ad3d22205ce59e35642e1 [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));
58 } else if (comp_field->rfc2507_params) {
59 comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len;
60 memcpy(comp_entity->nsapi,
61 comp_field->rfc2507_params->nsapi,
62 sizeof(comp_entity->nsapi));
63 } else if (comp_field->rohc_params) {
64 comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len;
65 memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi,
66 sizeof(comp_entity->nsapi));
67 } else if (comp_field->v42bis_params) {
68 comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
69 memcpy(comp_entity->nsapi,
70 comp_field->v42bis_params->nsapi,
71 sizeof(comp_entity->nsapi));
72 } else if (comp_field->v44_params) {
Philippae9beda2016-09-28 15:10:14 +020073 comp_entity->nsapi_len = comp_field->v44_params->nsapi_len;
Philippf1f34362016-08-26 17:00:21 +020074 memcpy(comp_entity->nsapi,
Philipp7d309352016-11-15 19:37:05 +010075 comp_field->v44_params->nsapi,
Philippf1f34362016-08-26 17:00:21 +020076 sizeof(comp_entity->nsapi));
77 } else {
78 /* The caller is expected to check carefully if the all
79 * data fields required for compression entity creation
80 * are present. Otherwise we blow an assertion here */
81 OSMO_ASSERT(false);
82 }
83 comp_entity->algo = comp_field->algo;
84
85 /* Check if an NSAPI is selected, if not, it does not make sense
86 * to create the compression entity, since the caller should
87 * have checked the presence of the NSAPI, we blow an assertion
88 * in case of missing NSAPIs */
89 OSMO_ASSERT(comp_entity->nsapi_len > 0);
90
91 /* Determine of which class our compression entity will be
92 * (Protocol or Data compresson ?) */
93 comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field);
94
Philippf1f34362016-08-26 17:00:21 +020095 /* Create an algorithm specific compression context */
96 if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
97 if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
98 talloc_free(comp_entity);
99 comp_entity = NULL;
100 }
Stefan Sperling60509682018-11-12 13:03:04 +0100101 } else if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION) {
Philipp73f83d52016-09-02 13:38:01 +0200102 if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) {
103 talloc_free(comp_entity);
104 comp_entity = NULL;
105 }
Stefan Sperling60509682018-11-12 13:03:04 +0100106 } else {
107 /* comp_field is somehow invalid */
108 OSMO_ASSERT(false);
Philippf1f34362016-08-26 17:00:21 +0200109 }
110
Philipp Maierd0ef1ed2017-01-23 15:36:37 +0100111 /* Bail on failure */
Philippf1f34362016-08-26 17:00:21 +0200112 if (comp_entity == NULL) {
113 LOGP(DSNDCP, LOGL_ERROR,
Philipp Maierd0ef1ed2017-01-23 15:36:37 +0100114 "Compression entity creation failed!\n");
Philippf1f34362016-08-26 17:00:21 +0200115 return NULL;
116 }
Philipp Maierd0ef1ed2017-01-23 15:36:37 +0100117
118 /* Display info message */
Philippf1f34362016-08-26 17:00:21 +0200119 if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
120 LOGP(DSNDCP, LOGL_INFO,
121 "New header compression entity (%d) created.\n",
122 comp_entity->entity);
123 } else {
124 LOGP(DSNDCP, LOGL_INFO,
125 "New data compression entity (%d) created.\n",
126 comp_entity->entity);
127 }
128
129 return comp_entity;
130}
131
132/* Allocate a compression enitiy list */
133struct llist_head *gprs_sndcp_comp_alloc(const void *ctx)
134{
135 struct llist_head *lh;
136
137 lh = talloc_zero(ctx, struct llist_head);
138 INIT_LLIST_HEAD(lh);
139
140 return lh;
141}
142
143/* Free a compression entitiy list */
144void gprs_sndcp_comp_free(struct llist_head *comp_entities)
145{
146 struct gprs_sndcp_comp *comp_entity;
147
148 /* We expect the caller to take care of allocating a
149 * compression entity list properly. Attempting to
150 * free a non existing list clearly points out
151 * a malfunction. */
152 OSMO_ASSERT(comp_entities);
153
154 llist_for_each_entry(comp_entity, comp_entities, list) {
155 /* Free compression entity */
156 if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
157 LOGP(DSNDCP, LOGL_INFO,
158 "Deleting header compression entity %d ...\n",
159 comp_entity->entity);
160 gprs_sndcp_pcomp_term(comp_entity);
161 } else {
162 LOGP(DSNDCP, LOGL_INFO,
163 "Deleting data compression entity %d ...\n",
164 comp_entity->entity);
Philipp73f83d52016-09-02 13:38:01 +0200165 gprs_sndcp_dcomp_term(comp_entity);
Philippf1f34362016-08-26 17:00:21 +0200166 }
167 }
168
169 talloc_free(comp_entities);
170}
171
172/* Delete a compression entity */
173void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
174 unsigned int entity)
175{
176 struct gprs_sndcp_comp *comp_entity;
177 struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
178
179 OSMO_ASSERT(comp_entities);
180
181 llist_for_each_entry(comp_entity, comp_entities, list) {
182 if (comp_entity->entity == entity) {
183 comp_entity_to_delete = comp_entity;
184 break;
185 }
186 }
187
188 if (!comp_entity_to_delete)
189 return;
190
191 if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
192 LOGP(DSNDCP, LOGL_INFO,
193 "Deleting header compression entity %d ...\n",
194 comp_entity_to_delete->entity);
195 gprs_sndcp_pcomp_term(comp_entity_to_delete);
196 } else {
197 LOGP(DSNDCP, LOGL_INFO,
198 "Deleting data compression entity %d ...\n",
199 comp_entity_to_delete->entity);
200 }
201
202 /* Delete compression entity */
203 llist_del(&comp_entity_to_delete->list);
204 talloc_free(comp_entity_to_delete);
205}
206
207/* Create and Add a new compression entity
208 * (returns a pointer to the compression entity that has just been created) */
209struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
210 struct llist_head *comp_entities,
211 const struct gprs_sndcp_comp_field
212 *comp_field)
213{
214 struct gprs_sndcp_comp *comp_entity;
215
216 OSMO_ASSERT(comp_entities);
217 OSMO_ASSERT(comp_field);
218
219 /* Just to be sure, if the entity is already in
220 * the list it will be deleted now */
221 gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
222
223 /* Create and add a new entity to the list */
224 comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
225
226 if (!comp_entity)
227 return NULL;
228
229 llist_add(&comp_entity->list, comp_entities);
230 return comp_entity;
231}
232
233/* Find which compression entity handles the specified pcomp/dcomp */
234struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
235 *comp_entities, uint8_t comp)
236{
237 struct gprs_sndcp_comp *comp_entity;
238 int i;
239
240 OSMO_ASSERT(comp_entities);
241
242 llist_for_each_entry(comp_entity, comp_entities, list) {
243 for (i = 0; i < comp_entity->comp_len; i++) {
244 if (comp_entity->comp[i] == comp)
245 return comp_entity;
246 }
247 }
248
249 LOGP(DSNDCP, LOGL_ERROR,
250 "Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
251 comp);
252 return NULL;
253}
254
255/* Find which compression entity handles the specified nsapi */
256struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
257 *comp_entities, uint8_t nsapi)
258{
259 struct gprs_sndcp_comp *comp_entity;
260 int i;
261
262 OSMO_ASSERT(comp_entities);
263
264 llist_for_each_entry(comp_entity, comp_entities, list) {
265 for (i = 0; i < comp_entity->nsapi_len; i++) {
266 if (comp_entity->nsapi[i] == nsapi)
267 return comp_entity;
268 }
269 }
270
271 return NULL;
272}
273
274/* Find a comp_index for a given pcomp/dcomp value */
275uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
276 uint8_t comp)
277{
278 /* Note: This function returns a normalized version of the comp value,
279 * which matches up with the position of the comp field. Since comp=0
280 * is reserved for "no compression", the index value starts counting
281 * at one. The return value is the PCOMPn/DCOMPn value one can find
282 * in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
283
284 int i;
285 OSMO_ASSERT(comp_entity);
286
287 /* A pcomp/dcomp value of zero is reserved for "no comproession",
288 * So we just bail and return zero in this case */
289 if (comp == 0)
290 return 0;
291
292 /* Look in the pcomp/dcomp list for the index */
293 for (i = 0; i < comp_entity->comp_len; i++) {
294 if (comp_entity->comp[i] == comp)
295 return i + 1;
296 }
297
298 LOGP(DSNDCP, LOGL_ERROR,
299 "Could not find a matching comp_index for given pcomp/dcomp value %d\n",
300 comp);
301 return 0;
302}
303
304/* Find a pcomp/dcomp value for a given comp_index */
305uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
306 uint8_t comp_index)
307{
308 OSMO_ASSERT(comp_entity);
309
310 /* A comp_index of zero translates to zero right away. */
311 if (comp_index == 0)
312 return 0;
313
314 if (comp_index > comp_entity->comp_len) {
315 LOGP(DSNDCP, LOGL_ERROR,
316 "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
317 comp_index);
318 return 0;
319 }
320
321 /* Look in the pcomp/dcomp list for the comp_index, see
322 * note in gprs_sndcp_comp_get_idx() */
323 return comp_entity->comp[comp_index - 1];
324}