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