blob: 59a54259de983e149c64cceb01d07f40777349fe [file] [log] [blame]
Philipp2c7f8372016-08-26 16:58:41 +02001/* Test SLHC/RFC1144 TCP/IP Header compression/decompression */
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 <openbsc/slhc.h>
23#include <openbsc/debug.h>
24
25#include <osmocom/core/talloc.h>
26#include <osmocom/core/utils.h>
27
28#include <osmocom/core/application.h>
29
30#include <stdio.h>
31#include <string.h>
32#include <arpa/inet.h>
33
34/* Number of compression slots (S0-1) */
35#define SLOTS 8
36
37/* Maximum packet bytes to display */
38#define DISP_MAX_BYTES 100
39
40/* Sample packets to test with */
41#define PACKETS_LEN 6
42char *packets[] = {
43 "4510004046dd40004006a9a7c0a8646ec0a864640017ad8b81980100f3ac984d801800e32a1600000101080a000647de06d1bf5efffd18fffd20fffd23fffd27",
44 "4510005b46de40004006a98bc0a8646ec0a864640017ad8b8198010cf3ac984d801800e3867500000101080a000647df06d1bf61fffb03fffd1ffffd21fffe22fffb05fffa2001fff0fffa2301fff0fffa2701fff0fffa1801fff0",
45 "4510003746df40004006a9aec0a8646ec0a864640017ad8b81980133f3ac989f801800e35fd700000101080a000647e106d1bf63fffd01",
46 "4510003746e040004006a9adc0a8646ec0a864640017ad8b81980136f3ac98a2801800e35fd200000101080a000647e106d1bf64fffb01",
47 "4510007446e140004006a96fc0a8646ec0a864640017ad8b81980139f3ac98a5801800e37b9b00000101080a000647e206d1bf640d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a57656c6c636f6d6520746f20706f6c6c75780d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0d0a0d0a",
48 "4510004246e240004006a9a0c0a8646ec0a864640017ad8b81980179f3ac98a5801800e3dab000000101080a000647ec06d1bf6f706f6c6c7578206c6f67696e3a20"
49};
50
51/* Compress a packet using Van Jacobson RFC1144 header compression */
52static int compress(uint8_t *data_o, uint8_t *data_i, int len,
53 struct slcompress *comp)
54{
55 uint8_t *comp_ptr; /* Not used */
56 int compr_len;
57
58 /* Create a working copy of the incoming data */
59 memcpy(data_o, data_i, len);
60
61 /* Run compressor */
62 compr_len = slhc_compress(comp, data_i, len, data_o, &comp_ptr, 0);
63 return compr_len;
64}
65
66/* Expand a packet using Van Jacobson RFC1144 header compression */
67static int expand(uint8_t *data_o, uint8_t *data_i, int len,
68 struct slcompress *comp)
69{
70 int data_decompressed_len;
71
72 /* Create a working copy of the incoming data */
73 memcpy(data_o, data_i, len);
74
75 /* Handle an uncompressed packet (learn header information */
76 if ((data_i[0] & SL_TYPE_UNCOMPRESSED_TCP) == SL_TYPE_UNCOMPRESSED_TCP) {
77 data_o[0] &= 0x4F;
78 data_decompressed_len = slhc_remember(comp, data_o, len);
79 return data_decompressed_len;
80 }
81
82 /* Uncompress compressed packets */
83 else if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
84 data_decompressed_len = slhc_uncompress(comp, data_o, len);
85 return data_decompressed_len;
86 }
87
88 /* Regular or unknown packets will not be touched */
89 return len;
90}
91
92/* Calculate IP Header checksum */
93static uint16_t calc_ip_csum(uint8_t *data, int len)
94{
95 int i;
96 uint32_t accumulator = 0;
97 uint16_t *pointer = (uint16_t *) data;
98
99 for (i = len; i > 1; i -= 2) {
100 accumulator += *pointer;
101 pointer++;
102 }
103
104 if (len % 2)
105 accumulator += *pointer;
106
107 accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff);
108 accumulator += (accumulator >> 16) & 0xffff;
109 return (~accumulator);
110}
111
112/* Calculate TCP/IP checksum */
113static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len)
114{
115 uint8_t *buf;
116 uint16_t csum;
117
118 buf = talloc_zero_size(ctx, len);
119 memset(buf, 0, len);
120 memcpy(buf, packet + 12, 8);
121 buf[9] = packet[9];
122 buf[11] = (len - 20) & 0xFF;
123 buf[10] = (len - 20) >> 8 & 0xFF;
124 memcpy(buf + 12, packet + 20, len - 20);
125 csum = calc_ip_csum(buf, len - 20 + 12);
126 talloc_free(buf);
127 return csum;
128}
129
130/* Check TCP/IP packet */
131static void check_packet(const void *ctx, uint8_t *packet, int len)
132{
133 /* Check IP header */
134 OSMO_ASSERT(len > 20);
135 OSMO_ASSERT(calc_ip_csum(packet, 20) == 0);
136
137 /* Check TCP packet */
138 if (packet[9] != 0x06)
139 return;
140 OSMO_ASSERT(len > 40);
141 OSMO_ASSERT(calc_tcpip_csum(ctx, packet, len) == 0);
142}
143
144/* Strip TCP options from TCP/IP packet */
145static int strip_tcp_options(const void *ctx, uint8_t *packet, int len)
146{
147 uint8_t doff;
148 uint16_t csum;
149
150 /* Check if the packet can be handled here */
151 if (len < 37)
152 return len;
153 if (packet[9] != 0x06)
154 return len;
155
156 /* Strip TCP/IP options from packet */
157 doff = ((packet[32] >> 4) & 0x0F) * 4;
158 memmove(packet + 40, packet + doff + 20, len - 40 - (doff - 20));
159 len = len - (doff - 20);
160
161 /* Repair data offset (TCP header length) */
162 packet[32] &= 0x0F;
163 packet[32] |= 0x50;
164
165 /* Repair checksum */
166 packet[36] = 0;
167 packet[37] = 0;
168 csum = calc_tcpip_csum(ctx, packet, len);
169 packet[36] = csum & 0xFF;
170 packet[37] = csum >> 8 & 0xFF;
171
172 /* Repair total length */
173 packet[3] = len & 0xFF;
174 packet[2] = len >> 8 & 0xFF;
175
176 /* Repair IP header checksum */
177 packet[10] = 0;
178 packet[11] = 0;
179 csum = calc_ip_csum(packet, 20);
180 packet[10] = csum & 0xFF;
181 packet[11] = csum >> 8 & 0xFF;
182 printf("csum=%04x\n", csum);
183
184 return len;
185}
186
187/* Compress / Decompress packets */
188static void test_slhc(const void *ctx)
189{
190 char packet_ascii[2048];
191 int i;
192
193 struct slcompress *comp;
194 uint8_t packet[1024];
195 int packet_len;
196 uint8_t packet_compr[1024];
197 int packet_compr_len;
198 uint8_t packet_decompr[1024];
199 int packet_decompr_len;
200
201 printf("Allocating compression state...\n");
202 comp = slhc_init(ctx, SLOTS, SLOTS);
203 OSMO_ASSERT(comp);
204
205 for(i=0;i<PACKETS_LEN;i++) {
206 /* Read input file */
207 memset(packet_ascii, 0, sizeof(packet_ascii));
208 memset(packet, 0, sizeof(packet));
209 memset(packet_compr, 0, sizeof(packet_compr));
210 memset(packet_decompr, 0, sizeof(packet_decompr));
211 strcpy(packet_ascii,packets[i]);
212
213 packet_len =
214 osmo_hexparse(packet_ascii, packet, sizeof(packet));
215 check_packet(ctx, packet, packet_len);
216 packet_len = strip_tcp_options(ctx, packet, packet_len);
217 check_packet(ctx, packet, packet_len);
218
219 /* Run compression/decompression algorithm */
220 printf("Compressing...\n");
221 packet_compr_len =
222 compress(packet_compr, packet, packet_len, comp);
223 printf("Decompressing...\n");
224 packet_decompr_len =
225 expand(packet_decompr, packet_compr, packet_compr_len,
226 comp);
227 OSMO_ASSERT(packet_decompr_len == packet_len);
228 check_packet(ctx,packet_decompr,packet_decompr_len);
229
230 /* Display results */
231 printf("Results:\n");
232 if (packet_compr_len > DISP_MAX_BYTES)
233 packet_compr_len = DISP_MAX_BYTES;
234 if (packet_len > DISP_MAX_BYTES)
235 packet_len = DISP_MAX_BYTES;
236 if (packet_decompr_len > DISP_MAX_BYTES)
237 packet_decompr_len = DISP_MAX_BYTES;
238 printf("Original Packet: (%i bytes) %s\n", packet_len,
239 osmo_hexdump_nospc(packet, packet_len));
240 printf("DecompressedPacket: (%i bytes) %s\n",
241 packet_decompr_len, osmo_hexdump_nospc(packet_decompr,
242 packet_decompr_len));
243 printf("CompressedPacket: (%i bytes) %s\n", packet_compr_len,
244 osmo_hexdump_nospc(packet_compr, packet_compr_len));
245 slhc_o_status(comp);
246 slhc_o_status(comp);
247
248 printf("\n");
249 }
250
251 printf("Freeing compression state...\n");
252 slhc_free(comp);
253 printf("\n");
254}
255
256static struct log_info_cat gprs_categories[] = {
257 [DSNDCP] = {
258 .name = "DSNDCP",
259 .description =
260 "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
261 .enabled = 1,.loglevel = LOGL_DEBUG,
262 },
263 [DSLHC] = {
264 .name = "DSLHC",
265 .description =
266 "Van Jacobson RFC1144 TCP/IP header compression (SLHC)",
267 .enabled = 1,.loglevel = LOGL_DEBUG,
268 }
269};
270
271static struct log_info info = {
272 .cat = gprs_categories,
273 .num_cat = ARRAY_SIZE(gprs_categories),
274};
275
276int main(int argc, char **argv)
277{
278 void *ctx;
279
280 osmo_init_logging(&info);
281
282 ctx = talloc_named_const(NULL, 0, "slhc_ctx");
283
284 test_slhc(ctx);
285
286 printf("Done\n");
287
288 talloc_report_full(ctx, stderr);
289 OSMO_ASSERT(talloc_total_blocks(ctx) == 1);
290 return 0;
291}
292
293/* stubs */
294struct osmo_prim_hdr;
295int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
296{
297 abort();
298}