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