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