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