1 /* $NetBSD: ipcomp_core.c,v 1.23 2006/11/16 01:33:45 christos Exp $ */
2 /* $KAME: ipcomp_core.c,v 1.25 2001/07/26 06:53:17 jinmei Exp $ */
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
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: ipcomp_core.c,v 1.23 2006/11/16 01:33:45 christos Exp $");
39
40 #include "opt_inet.h"
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/malloc.h>
45 #include <sys/mbuf.h>
46 #include <sys/domain.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/errno.h>
50 #include <sys/time.h>
51 #include <sys/kernel.h>
52 #include <sys/syslog.h>
53 #include <sys/queue.h>
54
55 #include <net/if.h>
56 #include <net/route.h>
57 #include <net/netisr.h>
58 #include <net/zlib.h>
59 #include <machine/cpu.h>
60
61 #include <netinet6/ipcomp.h>
62 #include <netinet6/ipsec.h>
63
64 #include <machine/stdarg.h>
65
66 #include <net/net_osdep.h>
67
68 static void *deflate_alloc __P((void *, u_int, u_int));
69 static void deflate_free __P((void *, void *));
70 static int deflate_common __P((struct mbuf *, struct mbuf *, size_t *, int));
71 static int deflate_compress __P((struct mbuf *, struct mbuf *, size_t *));
72 static int deflate_decompress __P((struct mbuf *, struct mbuf *, size_t *));
73
74 /*
75 * We need to use default window size (2^15 = 32Kbytes as of writing) for
76 * inbound case. Otherwise we get interop problem.
77 * Use negative value to avoid Adler32 checksum. This is an undocumented
78 * feature in zlib (see ipsec wg mailing list archive in January 2000).
79 */
80 static int deflate_policy = Z_DEFAULT_COMPRESSION;
81 static int deflate_window_out = -12;
82 static const int deflate_window_in = -1 * MAX_WBITS; /* don't change it */
83 static int deflate_memlevel = MAX_MEM_LEVEL;
84
85 static const struct ipcomp_algorithm ipcomp_algorithms[] = {
86 { deflate_compress, deflate_decompress, 90 },
87 };
88
89 const struct ipcomp_algorithm *
90 ipcomp_algorithm_lookup(idx)
91 int idx;
92 {
93
94 if (idx == SADB_X_CALG_DEFLATE)
95 return &ipcomp_algorithms[0];
96 return NULL;
97 }
98
99 static void *
100 deflate_alloc(void *aux, u_int items, u_int siz)
101 {
102 void *ptr;
103 ptr = malloc(items * siz, M_TEMP, M_NOWAIT);
104 return ptr;
105 }
106
107 static void
108 deflate_free(void *aux, void *ptr)
109 {
110 free(ptr, M_TEMP);
111 }
112
113 static int
114 deflate_common(m, md, lenp, mode)
115 struct mbuf *m;
116 struct mbuf *md;
117 size_t *lenp;
118 int mode; /* 0: compress 1: decompress */
119 {
120 struct mbuf *mprev;
121 struct mbuf *p;
122 struct mbuf *n = NULL, *n0 = NULL, **np;
123 z_stream zs;
124 int error = 0;
125 int zerror;
126 size_t offset;
127
128 #define MOREBLOCK() \
129 do { \
130 /* keep the reply buffer into our chain */ \
131 if (n) { \
132 n->m_len = zs.total_out - offset; \
133 offset = zs.total_out; \
134 *np = n; \
135 np = &n->m_next; \
136 n = NULL; \
137 } \
138 \
139 /* get a fresh reply buffer */ \
140 MGET(n, M_DONTWAIT, MT_DATA); \
141 if (n) { \
142 MCLGET(n, M_DONTWAIT); \
143 } \
144 if (!n) { \
145 error = ENOBUFS; \
146 goto fail; \
147 } \
148 n->m_len = 0; \
149 n->m_len = M_TRAILINGSPACE(n); \
150 n->m_next = NULL; \
151 /* \
152 * if this is the first reply buffer, reserve \
153 * region for ipcomp header. \
154 */ \
155 if (*np == NULL) { \
156 n->m_len -= sizeof(struct ipcomp); \
157 n->m_data += sizeof(struct ipcomp); \
158 } \
159 \
160 zs.next_out = mtod(n, u_int8_t *); \
161 zs.avail_out = n->m_len; \
162 } while (/*CONSTCOND*/ 0)
163
164 for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
165 ;
166 if (!mprev)
167 panic("md is not in m in deflate_common");
168
169 bzero(&zs, sizeof(zs));
170 zs.zalloc = deflate_alloc;
171 zs.zfree = deflate_free;
172
173 zerror = mode ? inflateInit2(&zs, deflate_window_in)
174 : deflateInit2(&zs, deflate_policy, Z_DEFLATED,
175 deflate_window_out, deflate_memlevel,
176 Z_DEFAULT_STRATEGY);
177 if (zerror != Z_OK) {
178 error = ENOBUFS;
179 goto fail;
180 }
181
182 n0 = n = NULL;
183 np = &n0;
184 offset = 0;
185 zerror = 0;
186 p = md;
187 while (p && p->m_len == 0) {
188 p = p->m_next;
189 }
190
191 /* input stream and output stream are available */
192 while (p && zs.avail_in == 0) {
193 /* get input buffer */
194 if (p && zs.avail_in == 0) {
195 zs.next_in = mtod(p, u_int8_t *);
196 zs.avail_in = p->m_len;
197 p = p->m_next;
198 while (p && p->m_len == 0) {
199 p = p->m_next;
200 }
201 }
202
203 /* get output buffer */
204 if (zs.next_out == NULL || zs.avail_out == 0) {
205 MOREBLOCK();
206 }
207
208 zerror = mode ? inflate(&zs, Z_NO_FLUSH)
209 : deflate(&zs, Z_NO_FLUSH);
210
211 if (zerror == Z_STREAM_END)
212 ; /* once more. */
213 else if (zerror == Z_OK) {
214 /* inflate: Z_OK can indicate the end of decode */
215 if (mode && !p && zs.avail_out != 0)
216 goto terminate;
217 } else {
218 if (zs.msg) {
219 ipseclog((LOG_ERR, "ipcomp_%scompress: "
220 "%sflate(Z_NO_FLUSH): %s\n",
221 mode ? "de" : "", mode ? "in" : "de",
222 zs.msg));
223 } else {
224 ipseclog((LOG_ERR, "ipcomp_%scompress: "
225 "%sflate(Z_NO_FLUSH): unknown error (%d)\n",
226 mode ? "de" : "", mode ? "in" : "de",
227 zerror));
228 }
229 mode ? inflateEnd(&zs) : deflateEnd(&zs);
230 error = EINVAL;
231 goto fail;
232 }
233 }
234
235 if (zerror == Z_STREAM_END)
236 goto terminate;
237
238 /* termination */
239 while (1) {
240 /* get output buffer */
241 if (zs.next_out == NULL || zs.avail_out == 0) {
242 MOREBLOCK();
243 }
244
245 zerror = mode ? inflate(&zs, Z_SYNC_FLUSH)
246 : deflate(&zs, Z_FINISH);
247
248 if (zerror == Z_STREAM_END)
249 break;
250 else if (zerror == Z_OK) {
251 if (mode && zs.avail_out != 0)
252 goto terminate;
253 } else {
254 if (zs.msg) {
255 ipseclog((LOG_ERR, "ipcomp_%scompress: "
256 "%sflate(Z_FINISH): %s\n",
257 mode ? "de" : "", mode ? "in" : "de",
258 zs.msg));
259 } else {
260 ipseclog((LOG_ERR, "ipcomp_%scompress: "
261 "%sflate(Z_FINISH): unknown error (%d)\n",
262 mode ? "de" : "", mode ? "in" : "de",
263 zerror));
264 }
265 mode ? inflateEnd(&zs) : deflateEnd(&zs);
266 error = EINVAL;
267 goto fail;
268 }
269 }
270
271 terminate:
272 zerror = mode ? inflateEnd(&zs) : deflateEnd(&zs);
273 if (zerror != Z_OK) {
274 if (zs.msg) {
275 ipseclog((LOG_ERR, "ipcomp_%scompress: "
276 "%sflateEnd: %s\n",
277 mode ? "de" : "", mode ? "in" : "de",
278 zs.msg));
279 } else {
280 ipseclog((LOG_ERR, "ipcomp_%scompress: "
281 "%sflateEnd: unknown error (%d)\n",
282 mode ? "de" : "", mode ? "in" : "de",
283 zerror));
284 }
285 error = EINVAL;
286 goto fail;
287 }
288 /* keep the final reply buffer into our chain */
289 if (n) {
290 n->m_len = zs.total_out - offset;
291 offset = zs.total_out;
292 *np = n;
293 np = &n->m_next;
294 n = NULL;
295 }
296
297 /* switch the mbuf to the new one */
298 mprev->m_next = n0;
299 m_freem(md);
300 *lenp = zs.total_out;
301
302 return 0;
303
304 fail:
305 if (m)
306 m_freem(m);
307 if (n)
308 m_freem(n);
309 if (n0)
310 m_freem(n0);
311 return error;
312 #undef MOREBLOCK
313 }
314
315 static int
316 deflate_compress(m, md, lenp)
317 struct mbuf *m;
318 struct mbuf *md;
319 size_t *lenp;
320 {
321 if (!m)
322 panic("m == NULL in deflate_compress");
323 if (!md)
324 panic("md == NULL in deflate_compress");
325 if (!lenp)
326 panic("lenp == NULL in deflate_compress");
327
328 return deflate_common(m, md, lenp, 0);
329 }
330
331 static int
332 deflate_decompress(m, md, lenp)
333 struct mbuf *m;
334 struct mbuf *md;
335 size_t *lenp;
336 {
337 if (!m)
338 panic("m == NULL in deflate_decompress");
339 if (!md)
340 panic("md == NULL in deflate_decompress");
341 if (!lenp)
342 panic("lenp == NULL in deflate_decompress");
343
344 return deflate_common(m, md, lenp, 1);
345 }
Cache object: e5afad6c30f0b36be4f87f51db08cf2e
|