]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/ipcomp_core.c
xnu-1699.24.23.tar.gz
[apple/xnu.git] / bsd / netinet6 / ipcomp_core.c
index 9ef5e3d14e39180845bc2948e167978d8183e7e9..55dcbf5069fe651a4d6e68fb40ed899635ecdcf1 100644 (file)
@@ -1,4 +1,5 @@
-/*     $KAME: ipcomp_core.c,v 1.10 2000/02/22 14:04:23 itojun Exp $    */
+/*     $FreeBSD: src/sys/netinet6/ipcomp_core.c,v 1.1.2.2 2001/07/03 11:01:54 ume Exp $        */
+/*     $KAME: ipcomp_core.c,v 1.24 2000/10/23 04:24:22 itojun Exp $    */
 
 /*
  * Copyright (C) 1999 WIDE Project.
  * RFC2393 IP payload compression protocol (IPComp).
  */
 
-#define _IP_VHL
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
-#include "opt_inet.h"
-#endif
 
 #include <sys/param.h>
 #include <sys/systm.h>
 
 #include <net/if.h>
 #include <net/route.h>
-#include <net/netisr.h>
-#include <net/zlib.h>
+#if ZLIB
+#include <libkern/zlib.h>
+#endif
 #include <kern/cpu_number.h>
 
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
 #include <netinet6/ipcomp.h>
+#if INET6
+#include <netinet6/ipcomp6.h>
+#endif
 #include <netinet6/ipsec.h>
+#if INET6
+#include <netinet6/ipsec6.h>
+#endif
 
 #include <net/net_osdep.h>
 
-static void *deflate_alloc __P((void *, u_int, u_int));
-static void deflate_free __P((void *, void *));
-static int deflate_common __P((struct mbuf *, struct mbuf *, size_t *, int));
-static int deflate_compress __P((struct mbuf *, struct mbuf *, size_t *));
-static int deflate_decompress __P((struct mbuf *, struct mbuf *, size_t *));
+#if ZLIB
+static void *deflate_alloc(void *, u_int, u_int);
+static void deflate_free(void *, void *);
+static int deflate_common(struct mbuf *, struct mbuf *, size_t *, int);
+static int deflate_compress(struct mbuf *, struct mbuf *, size_t *);
+static int deflate_decompress(struct mbuf *, struct mbuf *, size_t *);
 
 /*
  * We need to use default window size (2^15 = 32Kbytes as of writing) for
@@ -82,28 +84,82 @@ static int deflate_window_out = -12;
 static const int deflate_window_in = -1 * MAX_WBITS;   /* don't change it */
 static int deflate_memlevel = MAX_MEM_LEVEL;
 
-struct ipcomp_algorithm ipcomp_algorithms[] = {
-       { NULL, NULL, -1 },
-       { NULL, NULL, -1 },
+static z_stream        deflate_stream;
+static z_stream        inflate_stream;
+#endif /* ZLIB */
+
+static const struct ipcomp_algorithm ipcomp_algorithms[] = {
+#if ZLIB
        { deflate_compress, deflate_decompress, 90 },
-       { NULL, NULL, 90 },
+#endif /* ZLIB */
 };
 
+const struct ipcomp_algorithm *
+ipcomp_algorithm_lookup(
+#if ZLIB
+               int idx
+#else
+               __unused int idx
+#endif
+               )
+{
+#if ZLIB
+       if (idx == SADB_X_CALG_DEFLATE) {
+               /*
+                * Avert your gaze, ugly hack follows!
+                * We init here so our malloc can allocate using M_WAIT.
+                * We don't want to allocate if ipcomp isn't used, and we
+                * don't want to allocate on the input or output path.
+                * Allocation fails if we use M_NOWAIT because init allocates
+                * something like 256k (ouch).
+                */
+               if (deflate_stream.zalloc == NULL) {
+                       deflate_stream.zalloc = deflate_alloc;
+                       deflate_stream.zfree = deflate_free;
+                       if (deflateInit2(&deflate_stream, deflate_policy, Z_DEFLATED,
+                                       deflate_window_out, deflate_memlevel, Z_DEFAULT_STRATEGY)) {
+                               /* Allocation failed */
+                               bzero(&deflate_stream, sizeof(deflate_stream));
+#if IPSEC_DEBUG
+                               printf("ipcomp_algorithm_lookup: deflateInit2 failed.\n");
+#endif
+                       }
+               }
+               
+               if (inflate_stream.zalloc == NULL) {
+                       inflate_stream.zalloc = deflate_alloc;
+                       inflate_stream.zfree = deflate_free;
+                       if (inflateInit2(&inflate_stream, deflate_window_in)) {
+                               /* Allocation failed */
+                               bzero(&inflate_stream, sizeof(inflate_stream));
+#if IPSEC_DEBUG
+                               printf("ipcomp_algorithm_lookup: inflateInit2 failed.\n");
+#endif
+                       }
+               }
+
+               return &ipcomp_algorithms[0];
+       }
+#endif /* ZLIB */
+       return NULL;
+}
+
+#if ZLIB
 static void *
-deflate_alloc(aux, items, siz)
-       void *aux;
-       u_int items;
-       u_int siz;
+deflate_alloc(
+       __unused void *aux,
+       u_int items,
+       u_int siz)
 {
        void *ptr;
-       MALLOC(ptr, void *, items * siz, M_TEMP, M_NOWAIT);
+       ptr = _MALLOC(items * siz, M_TEMP, M_NOWAIT);
        return ptr;
 }
 
 static void
-deflate_free(aux, ptr)
-       void *aux;
-       void *ptr;
+deflate_free(
+       __unused void *aux,
+       void *ptr)
 {
        FREE(ptr, M_TEMP);
 }
@@ -117,165 +173,210 @@ deflate_common(m, md, lenp, mode)
 {
        struct mbuf *mprev;
        struct mbuf *p;
-       struct mbuf *n, *n0 = NULL, **np;
-       z_stream zs;
+       struct mbuf *n = NULL, *n0 = NULL, **np;
+       z_stream *zs;
        int error = 0;
        int zerror;
        size_t offset;
-       int firsttime, final, flush;
+
+#define MOREBLOCK() \
+do { \
+       /* keep the reply buffer into our chain */              \
+       if (n) {                                                \
+               n->m_len = zs->total_out - offset;              \
+               offset = zs->total_out;                         \
+               *np = n;                                        \
+               np = &n->m_next;                                \
+               n = NULL;                                       \
+       }                                                       \
+                                                               \
+       /* get a fresh reply buffer */                          \
+       MGET(n, M_DONTWAIT, MT_DATA);                           \
+       if (n) {                                                \
+               MCLGET(n, M_DONTWAIT);                          \
+       }                                                       \
+       if (!n) {                                               \
+               error = ENOBUFS;                                \
+               goto fail;                                      \
+       }                                                       \
+       n->m_len = 0;                                           \
+       n->m_len = M_TRAILINGSPACE(n);                          \
+       n->m_next = NULL;                                       \
+       /*                                                      \
+        * if this is the first reply buffer, reserve           \
+        * region for ipcomp header.                            \
+        */                                                     \
+       if (*np == NULL) {                                      \
+               n->m_len -= sizeof(struct ipcomp);              \
+               n->m_data += sizeof(struct ipcomp);             \
+       }                                                       \
+                                                               \
+       zs->next_out = mtod(n, u_int8_t *);                     \
+       zs->avail_out = n->m_len;                               \
+} while (0)
 
        for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
                ;
        if (!mprev)
                panic("md is not in m in deflate_common");
 
-       bzero(&zs, sizeof(zs));
-       zs.zalloc = deflate_alloc;
-       zs.zfree = deflate_free;
 
-       zerror = mode ? inflateInit2(&zs, deflate_window_in)
-                     : deflateInit2(&zs, deflate_policy, Z_DEFLATED,
-                               deflate_window_out, deflate_memlevel,
-                               Z_DEFAULT_STRATEGY);
-       if (zerror != Z_OK) {
+       zs = mode ? &inflate_stream : &deflate_stream;
+       if (zs->zalloc == NULL) {
+               /*
+                * init is called in ipcomp_algorithm_lookup.
+                * if zs->zalloc is NULL, either init hasn't been called (unlikely)
+                * or init failed because of no memory.
+                */
                error = ENOBUFS;
                goto fail;
        }
+       
+       zs->next_in = 0;
+       zs->avail_in = 0;
+       zs->next_out = 0;
+       zs->avail_out = 0;
 
        n0 = n = NULL;
        np = &n0;
        offset = 0;
-       firsttime = 1;
-       final = 0;
-       flush = Z_NO_FLUSH;
        zerror = 0;
        p = md;
-       while (1) {
-               /*
-                * first time, we need to setup the buffer before calling
-                * compression function.
-                */
-               if (firsttime)
-                       firsttime = 0;
-               else {
-                       zerror = mode ? inflate(&zs, flush)
-                                     : deflate(&zs, flush);
-               }
+       while (p && p->m_len == 0) {
+               p = p->m_next;
+       }
 
+       /* input stream and output stream are available */
+       while (p && zs->avail_in == 0) {
                /* get input buffer */
-               if (p && zs.avail_in == 0) {
-                       zs.next_in = mtod(p, u_int8_t *);
-                       zs.avail_in = p->m_len;
+               if (p && zs->avail_in == 0) {
+                       zs->next_in = mtod(p, u_int8_t *);
+                       zs->avail_in = p->m_len;
                        p = p->m_next;
-                       if (!p) {
-                               final = 1;
-                               flush = Z_PARTIAL_FLUSH;
+                       while (p && p->m_len == 0) {
+                               p = p->m_next;
                        }
                }
 
                /* get output buffer */
-               if (zs.next_out == NULL || zs.avail_out == 0) {
-                       /* keep the reply buffer into our chain */
-                       if (n) {
-                               n->m_len = zs.total_out - offset;
-                               offset = zs.total_out;
-                               *np = n;
-                               np = &n->m_next;
-                       }
+               if (zs->next_out == NULL || zs->avail_out == 0) {
+                       MOREBLOCK();
+               }
 
-                       /* get a fresh reply buffer */
-                       MGET(n, M_DONTWAIT, MT_DATA);
-                       if (n) {
-                               MCLGET(n, M_DONTWAIT);
-                       }
-                       if (!n) {
-                               error = ENOBUFS;
-                               goto fail;
-                       }
-                       n->m_len = 0;
-                       n->m_len = M_TRAILINGSPACE(n);
-                       n->m_next = NULL;
-                       /*
-                        * if this is the first reply buffer, reserve
-                        * region for ipcomp header.
-                        */
-                       if (*np == NULL) {
-                               n->m_len -= sizeof(struct ipcomp);
-                               n->m_data += sizeof(struct ipcomp);
+               zerror = mode ? inflate(zs, Z_NO_FLUSH)
+                             : deflate(zs, Z_NO_FLUSH);
+
+               if (zerror == Z_STREAM_END)
+                       ; /*once more.*/
+               else if (zerror == Z_OK) {
+                       /* inflate: Z_OK can indicate the end of decode */
+                       if (mode && !p && zs->avail_out != 0)
+                               goto terminate;
+                       
+                       /* else once more.*/
+               } else {
+                       if (zs->msg) {
+                               ipseclog((LOG_ERR, "ipcomp_%scompress: "
+                                   "%sflate(Z_NO_FLUSH): %s\n",
+                                   mode ? "de" : "", mode ? "in" : "de",
+                                   zs->msg));
+                       } else {
+                               ipseclog((LOG_ERR, "ipcomp_%scompress: "
+                                   "%sflate(Z_NO_FLUSH): unknown error (%d)\n",
+                                   mode ? "de" : "", mode ? "in" : "de",
+                                   zerror));
                        }
+                       mode ? inflateReset(zs) : deflateReset(zs);
+/*                     mode ? inflateEnd(zs) : deflateEnd(zs);*/
+                       error = EINVAL;
+                       goto fail;
+               }
+       }
 
-                       zs.next_out = mtod(n, u_int8_t *);
-                       zs.avail_out = n->m_len;
+       if (zerror == Z_STREAM_END)
+               goto terminate;
+
+       /* termination */
+       while (1) {
+               /* get output buffer */
+               if (zs->next_out == NULL || zs->avail_out == 0) {
+                       MOREBLOCK();
                }
 
-               if (zerror == Z_OK) {
-                       /*
-                        * to terminate deflate/inflate process, we need to
-                        * call {in,de}flate() with different flushing methods.
-                        *
-                        * deflate() needs at least one Z_PARTIAL_FLUSH,
-                        * then use Z_FINISH until we get to the end.
-                        * (if we use Z_FLUSH without Z_PARTIAL_FLUSH, deflate()
-                        * will assume contiguous single output buffer, and that
-                        * is not what we want)
-                        * inflate() does not care about flushing method, but
-                        * needs output buffer until it gets to the end.
-                        *
-                        * the most outer loop will be terminated with
-                        * Z_STREAM_END.
-                        */
-                       if (final == 1) {
-                               /* reached end of mbuf chain */
-                               if (mode == 0)
-                                       final = 2;
-                               else
-                                       final = 3;
-                       } else if (final == 2) {
-                               /* terminate deflate case */
-                               flush = Z_FINISH;
-                       } else if (final == 3) {
-                               /* terminate inflate case */
-                               ;
-                       }
-               } else if (zerror == Z_STREAM_END)
+               zerror = mode ? inflate(zs, Z_FINISH)
+                             : deflate(zs, Z_FINISH);
+
+               if (zerror == Z_STREAM_END)
                        break;
+               else if (zerror == Z_OK)
+                       ; /*once more.*/
                else {
-                       ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n",
-                               mode ? "de" : "", mode ? "in" : "de",
-                               zs.msg ? zs.msg : "unknown error"));
+                       if (zs->msg) {
+                               ipseclog((LOG_ERR, "ipcomp_%scompress: "
+                                   "%sflate(Z_FINISH): %s\n",
+                                   mode ? "de" : "", mode ? "in" : "de",
+                                   zs->msg));
+                       } else {
+                               ipseclog((LOG_ERR, "ipcomp_%scompress: "
+                                   "%sflate(Z_FINISH): unknown error (%d)\n",
+                                   mode ? "de" : "", mode ? "in" : "de",
+                                   zerror));
+                       }
+                       mode ? inflateReset(zs) : deflateReset(zs);
+/*                     mode ? inflateEnd(zs) : deflateEnd(zs); */
                        error = EINVAL;
                        goto fail;
                }
        }
-       zerror = mode ? inflateEnd(&zs) : deflateEnd(&zs);
-       if (zerror != Z_OK) {
-               ipseclog((LOG_ERR, "ipcomp_%scompress: %sflate: %s\n",
-                       mode ? "de" : "", mode ? "in" : "de",
-                       zs.msg ? zs.msg : "unknown error"));
-               error = EINVAL;
-               goto fail;
-       }
+
+terminate:
        /* keep the final reply buffer into our chain */
        if (n) {
-               n->m_len = zs.total_out - offset;
-               offset = zs.total_out;
+               n->m_len = zs->total_out - offset;
+               offset = zs->total_out;
                *np = n;
                np = &n->m_next;
+               n = NULL;
        }
 
        /* switch the mbuf to the new one */
        mprev->m_next = n0;
        m_freem(md);
-       *lenp = zs.total_out;
+       *lenp = zs->total_out;
+
+       /* reset the inflate/deflate state */
+       zerror = mode ? inflateReset(zs) : deflateReset(zs);
+       if (zerror != Z_OK) {
+               /*
+                * A failure here is uncommon. If this does
+                * fail, the packet can still be used but
+                * the z_stream will be messed up so subsequent
+                * inflates/deflates will probably fail.
+                */
+               if (zs->msg) {
+                       ipseclog((LOG_ERR, "ipcomp_%scompress: "
+                           "%sflateEnd: %s\n",
+                           mode ? "de" : "", mode ? "in" : "de",
+                           zs->msg));
+               } else {
+                       ipseclog((LOG_ERR, "ipcomp_%scompress: "
+                           "%sflateEnd: unknown error (%d)\n",
+                           mode ? "de" : "", mode ? "in" : "de",
+                           zerror));
+               }
+       }
 
        return 0;
 
 fail:
        if (m)
                m_freem(m);
+       if (n)
+               m_freem(n);
        if (n0)
                m_freem(n0);
        return error;
+#undef MOREBLOCK
 }
 
 static int
@@ -309,3 +410,4 @@ deflate_decompress(m, md, lenp)
 
        return deflate_common(m, md, lenp, 1);
 }
+#endif /* ZLIB */