blob: 5f6fb2ce486c587822e213f35998ade57d06bd6c [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;
144 case 1:
145 type = SL_TYPE_UNCOMPRESSED_TCP;
146 break;
147 case 2:
148 type = SL_TYPE_COMPRESSED_TCP;
149 break;
150 default:
151 LOGP(DSNDCP, LOGL_ERROR,
152 "rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
153 pcomp_index);
154 type = SL_TYPE_IP;
155 break;
156 }
157
158 /* Restore the original version nibble on
159 * marked uncompressed packets */
160 if (type == SL_TYPE_UNCOMPRESSED_TCP) {
161 /* Just in case the phone tags uncompressed tcp-data
162 * (normally this is handled by pcomp so there is
163 * no need for tagging the data) */
164 data[0] &= 0x4F;
165 data_decompressed_len = slhc_remember(comp, data, len);
166 return data_decompressed_len;
167 }
168
169 /* Uncompress compressed packets */
170 else if (type == SL_TYPE_COMPRESSED_TCP) {
171 data_decompressed_len = slhc_uncompress(comp, data, len);
172 return data_decompressed_len;
173 }
174
175 /* Regular or unknown packets will not be touched */
176 return len;
177}
178
179/* Expand packet header */
180int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
181 const struct llist_head *comp_entities)
182{
183 int rc;
184 uint8_t pcomp_index = 0;
185 struct gprs_sndcp_comp *comp_entity;
186
187 OSMO_ASSERT(data);
188 OSMO_ASSERT(comp_entities);
189
190 LOGP(DSNDCP, LOGL_DEBUG,
191 "Header compression entity list: comp_entities=%p\n",
192 comp_entities);
193
194 LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp);
195
196 /* Skip on pcomp=0 */
197 if (pcomp == 0) {
198 return len;
199 }
200
201 /* Find out which compression entity handles the data */
202 comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
203
204 /* Skip compression if no suitable compression entity can be found */
205 if (!comp_entity) {
206 return len;
207 }
208
209 /* Note: Only protocol compression entities may appear in
210 * protocol compression context */
211 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
212
213 /* Note: Currently RFC1144 is the only compression method we
214 * support, so the only allowed algorithm is RFC1144 */
215 OSMO_ASSERT(comp_entity->algo == RFC_1144);
216
217 /* Find pcomp_index */
218 pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
219
220 /* Run decompression algo */
221 rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state);
222 slhc_i_status(comp_entity->state);
223 slhc_o_status(comp_entity->state);
224
225 LOGP(DSNDCP, LOGL_DEBUG,
226 "Header expansion done, old length=%d, new length=%d, entity=%p\n",
227 len, rc, comp_entity);
228
229 return rc;
230}
231
232/* Compress packet header */
233int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
234 const struct llist_head *comp_entities,
235 uint8_t nsapi)
236{
237 int rc;
238 uint8_t pcomp_index = 0;
239 struct gprs_sndcp_comp *comp_entity;
240
241 OSMO_ASSERT(data);
242 OSMO_ASSERT(pcomp);
243 OSMO_ASSERT(comp_entities);
244
245 LOGP(DSNDCP, LOGL_DEBUG,
246 "Header compression entity list: comp_entities=%p\n",
247 comp_entities);
248
249 /* Find out which compression entity handles the data */
250 comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
251
252 /* Skip compression if no suitable compression entity can be found */
253 if (!comp_entity) {
254 *pcomp = 0;
255 return len;
256 }
257
258 /* Note: Only protocol compression entities may appear in
259 * protocol compression context */
260 OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
261
262 /* Note: Currently RFC1144 is the only compression method we
263 * support, so the only allowed algorithm is RFC1144 */
264 OSMO_ASSERT(comp_entity->algo == RFC_1144);
265
266 /* Run compression algo */
267 rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state);
268 slhc_i_status(comp_entity->state);
269 slhc_o_status(comp_entity->state);
270
271 /* Find pcomp value */
272 *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
273
274 LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
275
276 LOGP(DSNDCP, LOGL_DEBUG,
277 "Header compression done, old length=%d, new length=%d, entity=%p\n",
278 len, rc, comp_entity);
279 return rc;
280}