blob: a2236c3b1b10551b831e085a361a45978ae1f48f [file] [log] [blame]
Philippf1f34362016-08-26 17:00:21 +02001/* GPRS SNDCP header 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/slhc.h>
40#include <openbsc/debug.h>
41#include <openbsc/gprs_sndcp_comp.h>
42#include <openbsc/gprs_sndcp_pcomp.h>
43
44/* Initalize header compression */
45int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
46 const struct gprs_sndcp_comp_field *comp_field)
47{
48 /* Note: This function is automatically called from
49 * gprs_sndcp_comp.c when a new header compression
50 * entity is created by gprs_sndcp.c */
51
52 OSMO_ASSERT(comp_entity);
53 OSMO_ASSERT(comp_field);
54
55 if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
56 && comp_entity->algo == RFC_1144) {
Philipp143a2742016-11-25 15:23:50 +010057 OSMO_ASSERT(comp_field->rfc1144_params);
Philippf1f34362016-08-26 17:00:21 +020058 comp_entity->state =
59 slhc_init(ctx, comp_field->rfc1144_params->s01 + 1,
60 comp_field->rfc1144_params->s01 + 1);
61 LOGP(DSNDCP, LOGL_INFO,
62 "RFC1144 header compression initalized.\n");
63 return 0;
64 }
65
66 /* Just in case someone tries to initalize an unknown or unsupported
67 * header compresson. Since everything is checked during the SNDCP
68 * negotiation process, this should never happen! */
69 OSMO_ASSERT(false);
70}
71
72/* Terminate header compression */
73void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity)
74{
75 /* Note: This function is automatically called from
76 * gprs_sndcp_comp.c when a header compression
77 * entity is deleted by gprs_sndcp.c */
78
79 OSMO_ASSERT(comp_entity);
80
81 if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
82 && comp_entity->algo == RFC_1144) {
83 if (comp_entity->state) {
84 slhc_free((struct slcompress *)comp_entity->state);
85 comp_entity->state = NULL;
86 }
87 LOGP(DSNDCP, LOGL_INFO,
88 "RFC1144 header compression terminated.\n");
89 return;
90 }
91
92 /* Just in case someone tries to terminate an unknown or unsupported
93 * data compresson. Since everything is checked during the SNDCP
94 * negotiation process, this should never happen! */
95 OSMO_ASSERT(false);
96}
97
98/* Compress a packet using Van Jacobson RFC1144 header compression */
99static int rfc1144_compress(uint8_t *pcomp_index, uint8_t *data,
100 unsigned int len, struct slcompress *comp)
101{
102 uint8_t *comp_ptr;
103 int compr_len;
104 uint8_t *data_o;
105
106 /* Create a working copy of the incoming data */
107 data_o = talloc_zero_size(comp, len);
108 memcpy(data_o, data, len);
109
110 /* Run compressor */
111 compr_len = slhc_compress(comp, data, len, data_o, &comp_ptr, 0);
112
113 /* Generate pcomp_index */
114 if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
115 *pcomp_index = 2;
116 data_o[0] &= ~SL_TYPE_COMPRESSED_TCP;
117 memcpy(data, data_o, compr_len);
118 } else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) ==
119 SL_TYPE_UNCOMPRESSED_TCP) {
120 *pcomp_index = 1;
121 data_o[0] &= 0x4F;
122 memcpy(data, data_o, compr_len);
123 } else
124 *pcomp_index = 0;
125
126 talloc_free(data_o);
127 return compr_len;
128}
129
130/* Expand a packet using Van Jacobson RFC1144 header compression */
131static int rfc1144_expand(uint8_t *data, unsigned int len, uint8_t pcomp_index,
132 struct slcompress *comp)
133{
134 int data_decompressed_len;
135 int type;
136
137 /* Note: this function should never be called with pcomp_index=0,
138 * since this condition is already filtered
139 * out by gprs_sndcp_pcomp_expand() */
140
141 /* Determine the data type by the PCOMP index */
142 switch (pcomp_index) {
143 case 0:
144 type = SL_TYPE_IP;
Philippae9beda2016-09-28 15:10:14 +0200145 break;
Philippf1f34362016-08-26 17:00:21 +0200146 case 1:
147 type = SL_TYPE_UNCOMPRESSED_TCP;
148 break;
149 case 2:
150 type = SL_TYPE_COMPRESSED_TCP;
151 break;
152 default:
153 LOGP(DSNDCP, LOGL_ERROR,
154 "rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
155 pcomp_index);
156 type = SL_TYPE_IP;
157 break;
158 }
159
160 /* Restore the original version nibble on
161 * marked uncompressed packets */
162 if (type == SL_TYPE_UNCOMPRESSED_TCP) {
163 /* Just in case the phone tags uncompressed tcp-data
164 * (normally this is handled by pcomp so there is
165 * no need for tagging the data) */
166 data[0] &= 0x4F;
167 data_decompressed_len = slhc_remember(comp, data, len);
168 return data_decompressed_len;
169 }
170
171 /* Uncompress compressed packets */
172 else if (type == SL_TYPE_COMPRESSED_TCP) {
173 data_decompressed_len = slhc_uncompress(comp, data, len);
174 return data_decompressed_len;
175 }
176
177 /* Regular or unknown packets will not be touched */
178 return len;
179}
180
181/* Expand packet header */
182int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
183 const struct llist_head *comp_entities)
184{
185 int rc;
186 uint8_t pcomp_index = 0;
187 struct gprs_sndcp_comp *comp_entity;
188
189 OSMO_ASSERT(data);
190 OSMO_ASSERT(comp_entities);
191
192 LOGP(DSNDCP, LOGL_DEBUG,
193 "Header compression entity list: comp_entities=%p\n",
194 comp_entities);
195
196 LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp);
197
198 /* Skip on pcomp=0 */
199 if (pcomp == 0) {
200 return len;
201 }
202
203 /* Find out which compression entity handles the data */
204 comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
205
206 /* Skip compression if no suitable compression entity can be found */
207 if (!comp_entity) {
208 return len;
209 }
210
211 /* Note: Only protocol compression entities may appear in
212 * protocol compression context */
213 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
214
215 /* Note: Currently RFC1144 is the only compression method we
216 * support, so the only allowed algorithm is RFC1144 */
217 OSMO_ASSERT(comp_entity->algo == RFC_1144);
218
219 /* Find pcomp_index */
220 pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
221
222 /* Run decompression algo */
223 rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state);
224 slhc_i_status(comp_entity->state);
225 slhc_o_status(comp_entity->state);
226
227 LOGP(DSNDCP, LOGL_DEBUG,
228 "Header expansion done, old length=%d, new length=%d, entity=%p\n",
229 len, rc, comp_entity);
230
231 return rc;
232}
233
234/* Compress packet header */
235int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
236 const struct llist_head *comp_entities,
237 uint8_t nsapi)
238{
239 int rc;
240 uint8_t pcomp_index = 0;
241 struct gprs_sndcp_comp *comp_entity;
242
243 OSMO_ASSERT(data);
244 OSMO_ASSERT(pcomp);
245 OSMO_ASSERT(comp_entities);
246
247 LOGP(DSNDCP, LOGL_DEBUG,
248 "Header compression entity list: comp_entities=%p\n",
249 comp_entities);
250
251 /* Find out which compression entity handles the data */
252 comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
253
254 /* Skip compression if no suitable compression entity can be found */
255 if (!comp_entity) {
256 *pcomp = 0;
257 return len;
258 }
259
260 /* Note: Only protocol compression entities may appear in
261 * protocol compression context */
262 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
263
264 /* Note: Currently RFC1144 is the only compression method we
265 * support, so the only allowed algorithm is RFC1144 */
266 OSMO_ASSERT(comp_entity->algo == RFC_1144);
267
268 /* Run compression algo */
269 rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state);
270 slhc_i_status(comp_entity->state);
271 slhc_o_status(comp_entity->state);
272
273 /* Find pcomp value */
274 *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
275
276 LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
277
278 LOGP(DSNDCP, LOGL_DEBUG,
279 "Header compression done, old length=%d, new length=%d, entity=%p\n",
280 len, rc, comp_entity);
281 return rc;
282}