]>
Commit | Line | Data |
---|---|---|
9bccf70c A |
1 | /* $FreeBSD: src/sys/netinet6/ipcomp_core.c,v 1.1.2.2 2001/07/03 11:01:54 ume Exp $ */ |
2 | /* $KAME: ipcomp_core.c,v 1.24 2000/10/23 04:24:22 itojun Exp $ */ | |
1c79356b A |
3 | |
4 | /* | |
5 | * Copyright (C) 1999 WIDE Project. | |
6 | * All rights reserved. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. Neither the name of the project nor the names of its contributors | |
17 | * may be used to endorse or promote products derived from this software | |
18 | * without specific prior written permission. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
30 | * SUCH DAMAGE. | |
31 | */ | |
32 | ||
33 | /* | |
34 | * RFC2393 IP payload compression protocol (IPComp). | |
35 | */ | |
36 | ||
1c79356b A |
37 | |
38 | #include <sys/param.h> | |
39 | #include <sys/systm.h> | |
40 | #include <sys/malloc.h> | |
41 | #include <sys/mbuf.h> | |
42 | #include <sys/domain.h> | |
43 | #include <sys/protosw.h> | |
44 | #include <sys/socket.h> | |
45 | #include <sys/errno.h> | |
46 | #include <sys/time.h> | |
47 | #include <sys/kernel.h> | |
48 | #include <sys/syslog.h> | |
49 | #include <sys/queue.h> | |
50 | ||
51 | #include <net/if.h> | |
52 | #include <net/route.h> | |
2d21ac55 A |
53 | #if ZLIB |
54 | #include <libkern/zlib.h> | |
55 | #endif | |
1c79356b A |
56 | #include <kern/cpu_number.h> |
57 | ||
1c79356b | 58 | #include <netinet6/ipcomp.h> |
9bccf70c A |
59 | #if INET6 |
60 | #include <netinet6/ipcomp6.h> | |
61 | #endif | |
1c79356b | 62 | #include <netinet6/ipsec.h> |
9bccf70c A |
63 | #if INET6 |
64 | #include <netinet6/ipsec6.h> | |
65 | #endif | |
1c79356b A |
66 | |
67 | #include <net/net_osdep.h> | |
68 | ||
2d21ac55 | 69 | #if ZLIB |
91447636 A |
70 | static void *deflate_alloc(void *, u_int, u_int); |
71 | static void deflate_free(void *, void *); | |
72 | static int deflate_common(struct mbuf *, struct mbuf *, size_t *, int); | |
73 | static int deflate_compress(struct mbuf *, struct mbuf *, size_t *); | |
74 | static int deflate_decompress(struct mbuf *, struct mbuf *, size_t *); | |
1c79356b A |
75 | |
76 | /* | |
77 | * We need to use default window size (2^15 = 32Kbytes as of writing) for | |
78 | * inbound case. Otherwise we get interop problem. | |
79 | * Use negative value to avoid Adler32 checksum. This is an undocumented | |
80 | * feature in zlib (see ipsec wg mailing list archive in January 2000). | |
81 | */ | |
82 | static int deflate_policy = Z_DEFAULT_COMPRESSION; | |
83 | static int deflate_window_out = -12; | |
84 | static const int deflate_window_in = -1 * MAX_WBITS; /* don't change it */ | |
85 | static int deflate_memlevel = MAX_MEM_LEVEL; | |
86 | ||
9bccf70c A |
87 | static z_stream deflate_stream; |
88 | static z_stream inflate_stream; | |
2d21ac55 | 89 | #endif /* ZLIB */ |
9bccf70c A |
90 | |
91 | static const struct ipcomp_algorithm ipcomp_algorithms[] = { | |
2d21ac55 | 92 | #if ZLIB |
1c79356b | 93 | { deflate_compress, deflate_decompress, 90 }, |
2d21ac55 | 94 | #endif /* ZLIB */ |
1c79356b A |
95 | }; |
96 | ||
9bccf70c | 97 | const struct ipcomp_algorithm * |
2d21ac55 A |
98 | ipcomp_algorithm_lookup( |
99 | #if ZLIB | |
100 | int idx | |
101 | #else | |
102 | __unused int idx | |
103 | #endif | |
104 | ) | |
9bccf70c | 105 | { |
2d21ac55 | 106 | #if ZLIB |
9bccf70c A |
107 | if (idx == SADB_X_CALG_DEFLATE) { |
108 | /* | |
109 | * Avert your gaze, ugly hack follows! | |
110 | * We init here so our malloc can allocate using M_WAIT. | |
111 | * We don't want to allocate if ipcomp isn't used, and we | |
112 | * don't want to allocate on the input or output path. | |
113 | * Allocation fails if we use M_NOWAIT because init allocates | |
114 | * something like 256k (ouch). | |
115 | */ | |
116 | if (deflate_stream.zalloc == NULL) { | |
117 | deflate_stream.zalloc = deflate_alloc; | |
118 | deflate_stream.zfree = deflate_free; | |
119 | if (deflateInit2(&deflate_stream, deflate_policy, Z_DEFLATED, | |
120 | deflate_window_out, deflate_memlevel, Z_DEFAULT_STRATEGY)) { | |
121 | /* Allocation failed */ | |
122 | bzero(&deflate_stream, sizeof(deflate_stream)); | |
123 | #if IPSEC_DEBUG | |
124 | printf("ipcomp_algorithm_lookup: deflateInit2 failed.\n"); | |
125 | #endif | |
126 | } | |
127 | } | |
128 | ||
129 | if (inflate_stream.zalloc == NULL) { | |
130 | inflate_stream.zalloc = deflate_alloc; | |
131 | inflate_stream.zfree = deflate_free; | |
132 | if (inflateInit2(&inflate_stream, deflate_window_in)) { | |
133 | /* Allocation failed */ | |
134 | bzero(&inflate_stream, sizeof(inflate_stream)); | |
135 | #if IPSEC_DEBUG | |
136 | printf("ipcomp_algorithm_lookup: inflateInit2 failed.\n"); | |
137 | #endif | |
138 | } | |
139 | } | |
140 | ||
141 | return &ipcomp_algorithms[0]; | |
142 | } | |
2d21ac55 | 143 | #endif /* ZLIB */ |
9bccf70c A |
144 | return NULL; |
145 | } | |
146 | ||
2d21ac55 | 147 | #if ZLIB |
1c79356b | 148 | static void * |
2d21ac55 A |
149 | deflate_alloc( |
150 | __unused void *aux, | |
151 | u_int items, | |
152 | u_int siz) | |
1c79356b A |
153 | { |
154 | void *ptr; | |
2d21ac55 | 155 | ptr = _MALLOC(items * siz, M_TEMP, M_NOWAIT); |
1c79356b A |
156 | return ptr; |
157 | } | |
158 | ||
159 | static void | |
2d21ac55 A |
160 | deflate_free( |
161 | __unused void *aux, | |
162 | void *ptr) | |
1c79356b A |
163 | { |
164 | FREE(ptr, M_TEMP); | |
165 | } | |
166 | ||
167 | static int | |
168 | deflate_common(m, md, lenp, mode) | |
169 | struct mbuf *m; | |
170 | struct mbuf *md; | |
171 | size_t *lenp; | |
172 | int mode; /* 0: compress 1: decompress */ | |
173 | { | |
174 | struct mbuf *mprev; | |
175 | struct mbuf *p; | |
9bccf70c A |
176 | struct mbuf *n = NULL, *n0 = NULL, **np; |
177 | z_stream *zs; | |
1c79356b A |
178 | int error = 0; |
179 | int zerror; | |
180 | size_t offset; | |
9bccf70c A |
181 | |
182 | #define MOREBLOCK() \ | |
183 | do { \ | |
184 | /* keep the reply buffer into our chain */ \ | |
185 | if (n) { \ | |
186 | n->m_len = zs->total_out - offset; \ | |
187 | offset = zs->total_out; \ | |
188 | *np = n; \ | |
189 | np = &n->m_next; \ | |
190 | n = NULL; \ | |
191 | } \ | |
192 | \ | |
193 | /* get a fresh reply buffer */ \ | |
194 | MGET(n, M_DONTWAIT, MT_DATA); \ | |
195 | if (n) { \ | |
196 | MCLGET(n, M_DONTWAIT); \ | |
197 | } \ | |
198 | if (!n) { \ | |
199 | error = ENOBUFS; \ | |
200 | goto fail; \ | |
201 | } \ | |
202 | n->m_len = 0; \ | |
203 | n->m_len = M_TRAILINGSPACE(n); \ | |
204 | n->m_next = NULL; \ | |
205 | /* \ | |
206 | * if this is the first reply buffer, reserve \ | |
207 | * region for ipcomp header. \ | |
208 | */ \ | |
209 | if (*np == NULL) { \ | |
210 | n->m_len -= sizeof(struct ipcomp); \ | |
211 | n->m_data += sizeof(struct ipcomp); \ | |
212 | } \ | |
213 | \ | |
214 | zs->next_out = mtod(n, u_int8_t *); \ | |
215 | zs->avail_out = n->m_len; \ | |
216 | } while (0) | |
1c79356b A |
217 | |
218 | for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) | |
219 | ; | |
220 | if (!mprev) | |
221 | panic("md is not in m in deflate_common"); | |
222 | ||
1c79356b | 223 | |
9bccf70c A |
224 | zs = mode ? &inflate_stream : &deflate_stream; |
225 | if (zs->zalloc == NULL) { | |
226 | /* | |
227 | * init is called in ipcomp_algorithm_lookup. | |
228 | * if zs->zalloc is NULL, either init hasn't been called (unlikely) | |
229 | * or init failed because of no memory. | |
230 | */ | |
1c79356b A |
231 | error = ENOBUFS; |
232 | goto fail; | |
233 | } | |
9bccf70c A |
234 | |
235 | zs->next_in = 0; | |
236 | zs->avail_in = 0; | |
237 | zs->next_out = 0; | |
238 | zs->avail_out = 0; | |
1c79356b A |
239 | |
240 | n0 = n = NULL; | |
241 | np = &n0; | |
242 | offset = 0; | |
1c79356b A |
243 | zerror = 0; |
244 | p = md; | |
9bccf70c A |
245 | while (p && p->m_len == 0) { |
246 | p = p->m_next; | |
247 | } | |
1c79356b | 248 | |
9bccf70c A |
249 | /* input stream and output stream are available */ |
250 | while (p && zs->avail_in == 0) { | |
1c79356b | 251 | /* get input buffer */ |
9bccf70c A |
252 | if (p && zs->avail_in == 0) { |
253 | zs->next_in = mtod(p, u_int8_t *); | |
254 | zs->avail_in = p->m_len; | |
1c79356b | 255 | p = p->m_next; |
9bccf70c A |
256 | while (p && p->m_len == 0) { |
257 | p = p->m_next; | |
1c79356b A |
258 | } |
259 | } | |
260 | ||
261 | /* get output buffer */ | |
9bccf70c A |
262 | if (zs->next_out == NULL || zs->avail_out == 0) { |
263 | MOREBLOCK(); | |
264 | } | |
1c79356b | 265 | |
9bccf70c A |
266 | zerror = mode ? inflate(zs, Z_NO_FLUSH) |
267 | : deflate(zs, Z_NO_FLUSH); | |
268 | ||
269 | if (zerror == Z_STREAM_END) | |
270 | ; /*once more.*/ | |
271 | else if (zerror == Z_OK) { | |
272 | /* inflate: Z_OK can indicate the end of decode */ | |
273 | if (mode && !p && zs->avail_out != 0) | |
274 | goto terminate; | |
2d21ac55 A |
275 | |
276 | /* else once more.*/ | |
9bccf70c A |
277 | } else { |
278 | if (zs->msg) { | |
279 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
280 | "%sflate(Z_NO_FLUSH): %s\n", | |
281 | mode ? "de" : "", mode ? "in" : "de", | |
282 | zs->msg)); | |
283 | } else { | |
284 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
285 | "%sflate(Z_NO_FLUSH): unknown error (%d)\n", | |
286 | mode ? "de" : "", mode ? "in" : "de", | |
287 | zerror)); | |
1c79356b | 288 | } |
9bccf70c A |
289 | mode ? inflateReset(zs) : deflateReset(zs); |
290 | /* mode ? inflateEnd(zs) : deflateEnd(zs);*/ | |
291 | error = EINVAL; | |
292 | goto fail; | |
293 | } | |
294 | } | |
1c79356b | 295 | |
9bccf70c A |
296 | if (zerror == Z_STREAM_END) |
297 | goto terminate; | |
298 | ||
299 | /* termination */ | |
300 | while (1) { | |
301 | /* get output buffer */ | |
302 | if (zs->next_out == NULL || zs->avail_out == 0) { | |
303 | MOREBLOCK(); | |
1c79356b A |
304 | } |
305 | ||
9bccf70c A |
306 | zerror = mode ? inflate(zs, Z_FINISH) |
307 | : deflate(zs, Z_FINISH); | |
308 | ||
309 | if (zerror == Z_STREAM_END) | |
1c79356b | 310 | break; |
9bccf70c A |
311 | else if (zerror == Z_OK) |
312 | ; /*once more.*/ | |
1c79356b | 313 | else { |
9bccf70c A |
314 | if (zs->msg) { |
315 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
316 | "%sflate(Z_FINISH): %s\n", | |
317 | mode ? "de" : "", mode ? "in" : "de", | |
318 | zs->msg)); | |
319 | } else { | |
320 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
321 | "%sflate(Z_FINISH): unknown error (%d)\n", | |
322 | mode ? "de" : "", mode ? "in" : "de", | |
323 | zerror)); | |
324 | } | |
325 | mode ? inflateReset(zs) : deflateReset(zs); | |
326 | /* mode ? inflateEnd(zs) : deflateEnd(zs); */ | |
1c79356b A |
327 | error = EINVAL; |
328 | goto fail; | |
329 | } | |
330 | } | |
9bccf70c A |
331 | |
332 | terminate: | |
1c79356b A |
333 | /* keep the final reply buffer into our chain */ |
334 | if (n) { | |
9bccf70c A |
335 | n->m_len = zs->total_out - offset; |
336 | offset = zs->total_out; | |
1c79356b A |
337 | *np = n; |
338 | np = &n->m_next; | |
9bccf70c | 339 | n = NULL; |
1c79356b A |
340 | } |
341 | ||
342 | /* switch the mbuf to the new one */ | |
343 | mprev->m_next = n0; | |
344 | m_freem(md); | |
9bccf70c A |
345 | *lenp = zs->total_out; |
346 | ||
347 | /* reset the inflate/deflate state */ | |
348 | zerror = mode ? inflateReset(zs) : deflateReset(zs); | |
349 | if (zerror != Z_OK) { | |
350 | /* | |
351 | * A failure here is uncommon. If this does | |
352 | * fail, the packet can still be used but | |
353 | * the z_stream will be messed up so subsequent | |
354 | * inflates/deflates will probably fail. | |
355 | */ | |
356 | if (zs->msg) { | |
357 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
358 | "%sflateEnd: %s\n", | |
359 | mode ? "de" : "", mode ? "in" : "de", | |
360 | zs->msg)); | |
361 | } else { | |
362 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
363 | "%sflateEnd: unknown error (%d)\n", | |
364 | mode ? "de" : "", mode ? "in" : "de", | |
365 | zerror)); | |
366 | } | |
367 | } | |
1c79356b A |
368 | |
369 | return 0; | |
370 | ||
371 | fail: | |
372 | if (m) | |
373 | m_freem(m); | |
9bccf70c A |
374 | if (n) |
375 | m_freem(n); | |
1c79356b A |
376 | if (n0) |
377 | m_freem(n0); | |
378 | return error; | |
9bccf70c | 379 | #undef MOREBLOCK |
1c79356b A |
380 | } |
381 | ||
382 | static int | |
383 | deflate_compress(m, md, lenp) | |
384 | struct mbuf *m; | |
385 | struct mbuf *md; | |
386 | size_t *lenp; | |
387 | { | |
388 | if (!m) | |
389 | panic("m == NULL in deflate_compress"); | |
390 | if (!md) | |
391 | panic("md == NULL in deflate_compress"); | |
392 | if (!lenp) | |
393 | panic("lenp == NULL in deflate_compress"); | |
394 | ||
395 | return deflate_common(m, md, lenp, 0); | |
396 | } | |
397 | ||
398 | static int | |
399 | deflate_decompress(m, md, lenp) | |
400 | struct mbuf *m; | |
401 | struct mbuf *md; | |
402 | size_t *lenp; | |
403 | { | |
404 | if (!m) | |
405 | panic("m == NULL in deflate_decompress"); | |
406 | if (!md) | |
407 | panic("md == NULL in deflate_decompress"); | |
408 | if (!lenp) | |
409 | panic("lenp == NULL in deflate_decompress"); | |
410 | ||
411 | return deflate_common(m, md, lenp, 1); | |
412 | } | |
2d21ac55 | 413 | #endif /* ZLIB */ |