]>
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> | |
1c79356b A |
53 | #include <net/zlib.h> |
54 | #include <kern/cpu_number.h> | |
55 | ||
1c79356b | 56 | #include <netinet6/ipcomp.h> |
9bccf70c A |
57 | #if INET6 |
58 | #include <netinet6/ipcomp6.h> | |
59 | #endif | |
1c79356b | 60 | #include <netinet6/ipsec.h> |
9bccf70c A |
61 | #if INET6 |
62 | #include <netinet6/ipsec6.h> | |
63 | #endif | |
1c79356b A |
64 | |
65 | #include <net/net_osdep.h> | |
66 | ||
91447636 A |
67 | static void *deflate_alloc(void *, u_int, u_int); |
68 | static void deflate_free(void *, void *); | |
69 | static int deflate_common(struct mbuf *, struct mbuf *, size_t *, int); | |
70 | static int deflate_compress(struct mbuf *, struct mbuf *, size_t *); | |
71 | static int deflate_decompress(struct mbuf *, struct mbuf *, size_t *); | |
1c79356b A |
72 | |
73 | /* | |
74 | * We need to use default window size (2^15 = 32Kbytes as of writing) for | |
75 | * inbound case. Otherwise we get interop problem. | |
76 | * Use negative value to avoid Adler32 checksum. This is an undocumented | |
77 | * feature in zlib (see ipsec wg mailing list archive in January 2000). | |
78 | */ | |
79 | static int deflate_policy = Z_DEFAULT_COMPRESSION; | |
80 | static int deflate_window_out = -12; | |
81 | static const int deflate_window_in = -1 * MAX_WBITS; /* don't change it */ | |
82 | static int deflate_memlevel = MAX_MEM_LEVEL; | |
83 | ||
9bccf70c A |
84 | static z_stream deflate_stream; |
85 | static z_stream inflate_stream; | |
86 | ||
87 | static const struct ipcomp_algorithm ipcomp_algorithms[] = { | |
1c79356b | 88 | { deflate_compress, deflate_decompress, 90 }, |
1c79356b A |
89 | }; |
90 | ||
9bccf70c A |
91 | const struct ipcomp_algorithm * |
92 | ipcomp_algorithm_lookup(idx) | |
93 | int idx; | |
94 | { | |
95 | ||
96 | if (idx == SADB_X_CALG_DEFLATE) { | |
97 | /* | |
98 | * Avert your gaze, ugly hack follows! | |
99 | * We init here so our malloc can allocate using M_WAIT. | |
100 | * We don't want to allocate if ipcomp isn't used, and we | |
101 | * don't want to allocate on the input or output path. | |
102 | * Allocation fails if we use M_NOWAIT because init allocates | |
103 | * something like 256k (ouch). | |
104 | */ | |
105 | if (deflate_stream.zalloc == NULL) { | |
106 | deflate_stream.zalloc = deflate_alloc; | |
107 | deflate_stream.zfree = deflate_free; | |
108 | if (deflateInit2(&deflate_stream, deflate_policy, Z_DEFLATED, | |
109 | deflate_window_out, deflate_memlevel, Z_DEFAULT_STRATEGY)) { | |
110 | /* Allocation failed */ | |
111 | bzero(&deflate_stream, sizeof(deflate_stream)); | |
112 | #if IPSEC_DEBUG | |
113 | printf("ipcomp_algorithm_lookup: deflateInit2 failed.\n"); | |
114 | #endif | |
115 | } | |
116 | } | |
117 | ||
118 | if (inflate_stream.zalloc == NULL) { | |
119 | inflate_stream.zalloc = deflate_alloc; | |
120 | inflate_stream.zfree = deflate_free; | |
121 | if (inflateInit2(&inflate_stream, deflate_window_in)) { | |
122 | /* Allocation failed */ | |
123 | bzero(&inflate_stream, sizeof(inflate_stream)); | |
124 | #if IPSEC_DEBUG | |
125 | printf("ipcomp_algorithm_lookup: inflateInit2 failed.\n"); | |
126 | #endif | |
127 | } | |
128 | } | |
129 | ||
130 | return &ipcomp_algorithms[0]; | |
131 | } | |
132 | return NULL; | |
133 | } | |
134 | ||
1c79356b A |
135 | static void * |
136 | deflate_alloc(aux, items, siz) | |
137 | void *aux; | |
138 | u_int items; | |
139 | u_int siz; | |
140 | { | |
141 | void *ptr; | |
9bccf70c | 142 | ptr = _MALLOC(items * siz, M_TEMP, M_WAIT); |
1c79356b A |
143 | return ptr; |
144 | } | |
145 | ||
146 | static void | |
147 | deflate_free(aux, ptr) | |
148 | void *aux; | |
149 | void *ptr; | |
150 | { | |
151 | FREE(ptr, M_TEMP); | |
152 | } | |
153 | ||
154 | static int | |
155 | deflate_common(m, md, lenp, mode) | |
156 | struct mbuf *m; | |
157 | struct mbuf *md; | |
158 | size_t *lenp; | |
159 | int mode; /* 0: compress 1: decompress */ | |
160 | { | |
161 | struct mbuf *mprev; | |
162 | struct mbuf *p; | |
9bccf70c A |
163 | struct mbuf *n = NULL, *n0 = NULL, **np; |
164 | z_stream *zs; | |
1c79356b A |
165 | int error = 0; |
166 | int zerror; | |
167 | size_t offset; | |
9bccf70c A |
168 | |
169 | #define MOREBLOCK() \ | |
170 | do { \ | |
171 | /* keep the reply buffer into our chain */ \ | |
172 | if (n) { \ | |
173 | n->m_len = zs->total_out - offset; \ | |
174 | offset = zs->total_out; \ | |
175 | *np = n; \ | |
176 | np = &n->m_next; \ | |
177 | n = NULL; \ | |
178 | } \ | |
179 | \ | |
180 | /* get a fresh reply buffer */ \ | |
181 | MGET(n, M_DONTWAIT, MT_DATA); \ | |
182 | if (n) { \ | |
183 | MCLGET(n, M_DONTWAIT); \ | |
184 | } \ | |
185 | if (!n) { \ | |
186 | error = ENOBUFS; \ | |
187 | goto fail; \ | |
188 | } \ | |
189 | n->m_len = 0; \ | |
190 | n->m_len = M_TRAILINGSPACE(n); \ | |
191 | n->m_next = NULL; \ | |
192 | /* \ | |
193 | * if this is the first reply buffer, reserve \ | |
194 | * region for ipcomp header. \ | |
195 | */ \ | |
196 | if (*np == NULL) { \ | |
197 | n->m_len -= sizeof(struct ipcomp); \ | |
198 | n->m_data += sizeof(struct ipcomp); \ | |
199 | } \ | |
200 | \ | |
201 | zs->next_out = mtod(n, u_int8_t *); \ | |
202 | zs->avail_out = n->m_len; \ | |
203 | } while (0) | |
1c79356b A |
204 | |
205 | for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next) | |
206 | ; | |
207 | if (!mprev) | |
208 | panic("md is not in m in deflate_common"); | |
209 | ||
1c79356b | 210 | |
9bccf70c A |
211 | zs = mode ? &inflate_stream : &deflate_stream; |
212 | if (zs->zalloc == NULL) { | |
213 | /* | |
214 | * init is called in ipcomp_algorithm_lookup. | |
215 | * if zs->zalloc is NULL, either init hasn't been called (unlikely) | |
216 | * or init failed because of no memory. | |
217 | */ | |
1c79356b A |
218 | error = ENOBUFS; |
219 | goto fail; | |
220 | } | |
9bccf70c A |
221 | |
222 | zs->next_in = 0; | |
223 | zs->avail_in = 0; | |
224 | zs->next_out = 0; | |
225 | zs->avail_out = 0; | |
1c79356b A |
226 | |
227 | n0 = n = NULL; | |
228 | np = &n0; | |
229 | offset = 0; | |
1c79356b A |
230 | zerror = 0; |
231 | p = md; | |
9bccf70c A |
232 | while (p && p->m_len == 0) { |
233 | p = p->m_next; | |
234 | } | |
1c79356b | 235 | |
9bccf70c A |
236 | /* input stream and output stream are available */ |
237 | while (p && zs->avail_in == 0) { | |
1c79356b | 238 | /* get input buffer */ |
9bccf70c A |
239 | if (p && zs->avail_in == 0) { |
240 | zs->next_in = mtod(p, u_int8_t *); | |
241 | zs->avail_in = p->m_len; | |
1c79356b | 242 | p = p->m_next; |
9bccf70c A |
243 | while (p && p->m_len == 0) { |
244 | p = p->m_next; | |
1c79356b A |
245 | } |
246 | } | |
247 | ||
248 | /* get output buffer */ | |
9bccf70c A |
249 | if (zs->next_out == NULL || zs->avail_out == 0) { |
250 | MOREBLOCK(); | |
251 | } | |
1c79356b | 252 | |
9bccf70c A |
253 | zerror = mode ? inflate(zs, Z_NO_FLUSH) |
254 | : deflate(zs, Z_NO_FLUSH); | |
255 | ||
256 | if (zerror == Z_STREAM_END) | |
257 | ; /*once more.*/ | |
258 | else if (zerror == Z_OK) { | |
259 | /* inflate: Z_OK can indicate the end of decode */ | |
260 | if (mode && !p && zs->avail_out != 0) | |
261 | goto terminate; | |
262 | else | |
263 | ; /*once more.*/ | |
264 | } else { | |
265 | if (zs->msg) { | |
266 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
267 | "%sflate(Z_NO_FLUSH): %s\n", | |
268 | mode ? "de" : "", mode ? "in" : "de", | |
269 | zs->msg)); | |
270 | } else { | |
271 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
272 | "%sflate(Z_NO_FLUSH): unknown error (%d)\n", | |
273 | mode ? "de" : "", mode ? "in" : "de", | |
274 | zerror)); | |
1c79356b | 275 | } |
9bccf70c A |
276 | mode ? inflateReset(zs) : deflateReset(zs); |
277 | /* mode ? inflateEnd(zs) : deflateEnd(zs);*/ | |
278 | error = EINVAL; | |
279 | goto fail; | |
280 | } | |
281 | } | |
1c79356b | 282 | |
9bccf70c A |
283 | if (zerror == Z_STREAM_END) |
284 | goto terminate; | |
285 | ||
286 | /* termination */ | |
287 | while (1) { | |
288 | /* get output buffer */ | |
289 | if (zs->next_out == NULL || zs->avail_out == 0) { | |
290 | MOREBLOCK(); | |
1c79356b A |
291 | } |
292 | ||
9bccf70c A |
293 | zerror = mode ? inflate(zs, Z_FINISH) |
294 | : deflate(zs, Z_FINISH); | |
295 | ||
296 | if (zerror == Z_STREAM_END) | |
1c79356b | 297 | break; |
9bccf70c A |
298 | else if (zerror == Z_OK) |
299 | ; /*once more.*/ | |
1c79356b | 300 | else { |
9bccf70c A |
301 | if (zs->msg) { |
302 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
303 | "%sflate(Z_FINISH): %s\n", | |
304 | mode ? "de" : "", mode ? "in" : "de", | |
305 | zs->msg)); | |
306 | } else { | |
307 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
308 | "%sflate(Z_FINISH): unknown error (%d)\n", | |
309 | mode ? "de" : "", mode ? "in" : "de", | |
310 | zerror)); | |
311 | } | |
312 | mode ? inflateReset(zs) : deflateReset(zs); | |
313 | /* mode ? inflateEnd(zs) : deflateEnd(zs); */ | |
1c79356b A |
314 | error = EINVAL; |
315 | goto fail; | |
316 | } | |
317 | } | |
9bccf70c A |
318 | |
319 | terminate: | |
1c79356b A |
320 | /* keep the final reply buffer into our chain */ |
321 | if (n) { | |
9bccf70c A |
322 | n->m_len = zs->total_out - offset; |
323 | offset = zs->total_out; | |
1c79356b A |
324 | *np = n; |
325 | np = &n->m_next; | |
9bccf70c | 326 | n = NULL; |
1c79356b A |
327 | } |
328 | ||
329 | /* switch the mbuf to the new one */ | |
330 | mprev->m_next = n0; | |
331 | m_freem(md); | |
9bccf70c A |
332 | *lenp = zs->total_out; |
333 | ||
334 | /* reset the inflate/deflate state */ | |
335 | zerror = mode ? inflateReset(zs) : deflateReset(zs); | |
336 | if (zerror != Z_OK) { | |
337 | /* | |
338 | * A failure here is uncommon. If this does | |
339 | * fail, the packet can still be used but | |
340 | * the z_stream will be messed up so subsequent | |
341 | * inflates/deflates will probably fail. | |
342 | */ | |
343 | if (zs->msg) { | |
344 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
345 | "%sflateEnd: %s\n", | |
346 | mode ? "de" : "", mode ? "in" : "de", | |
347 | zs->msg)); | |
348 | } else { | |
349 | ipseclog((LOG_ERR, "ipcomp_%scompress: " | |
350 | "%sflateEnd: unknown error (%d)\n", | |
351 | mode ? "de" : "", mode ? "in" : "de", | |
352 | zerror)); | |
353 | } | |
354 | } | |
1c79356b A |
355 | |
356 | return 0; | |
357 | ||
358 | fail: | |
359 | if (m) | |
360 | m_freem(m); | |
9bccf70c A |
361 | if (n) |
362 | m_freem(n); | |
1c79356b A |
363 | if (n0) |
364 | m_freem(n0); | |
365 | return error; | |
9bccf70c | 366 | #undef MOREBLOCK |
1c79356b A |
367 | } |
368 | ||
369 | static int | |
370 | deflate_compress(m, md, lenp) | |
371 | struct mbuf *m; | |
372 | struct mbuf *md; | |
373 | size_t *lenp; | |
374 | { | |
375 | if (!m) | |
376 | panic("m == NULL in deflate_compress"); | |
377 | if (!md) | |
378 | panic("md == NULL in deflate_compress"); | |
379 | if (!lenp) | |
380 | panic("lenp == NULL in deflate_compress"); | |
381 | ||
382 | return deflate_common(m, md, lenp, 0); | |
383 | } | |
384 | ||
385 | static int | |
386 | deflate_decompress(m, md, lenp) | |
387 | struct mbuf *m; | |
388 | struct mbuf *md; | |
389 | size_t *lenp; | |
390 | { | |
391 | if (!m) | |
392 | panic("m == NULL in deflate_decompress"); | |
393 | if (!md) | |
394 | panic("md == NULL in deflate_decompress"); | |
395 | if (!lenp) | |
396 | panic("lenp == NULL in deflate_decompress"); | |
397 | ||
398 | return deflate_common(m, md, lenp, 1); | |
399 | } |