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