Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netgraph/ng_deflate.c
Show All 35 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/endian.h> | #include <sys/endian.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||||
#include <sys/zlib.h> | #include <contrib/zlib/zlib.h> | ||||
#include <netgraph/ng_message.h> | #include <netgraph/ng_message.h> | ||||
#include <netgraph/netgraph.h> | #include <netgraph/netgraph.h> | ||||
#include <netgraph/ng_parse.h> | #include <netgraph/ng_parse.h> | ||||
#include <netgraph/ng_deflate.h> | #include <netgraph/ng_deflate.h> | ||||
#include "opt_netgraph.h" | #include "opt_netgraph.h" | ||||
Show All 24 Lines | |||||
static ng_constructor_t ng_deflate_constructor; | static ng_constructor_t ng_deflate_constructor; | ||||
static ng_rcvmsg_t ng_deflate_rcvmsg; | static ng_rcvmsg_t ng_deflate_rcvmsg; | ||||
static ng_shutdown_t ng_deflate_shutdown; | static ng_shutdown_t ng_deflate_shutdown; | ||||
static ng_newhook_t ng_deflate_newhook; | static ng_newhook_t ng_deflate_newhook; | ||||
static ng_rcvdata_t ng_deflate_rcvdata; | static ng_rcvdata_t ng_deflate_rcvdata; | ||||
static ng_disconnect_t ng_deflate_disconnect; | static ng_disconnect_t ng_deflate_disconnect; | ||||
/* Helper functions */ | /* Helper functions */ | ||||
static void *z_alloc(void *, u_int items, u_int size); | static int ng_deflate_compress(node_p, struct mbuf *, struct mbuf **); | ||||
static void z_free(void *, void *ptr); | static int ng_deflate_decompress(node_p, struct mbuf *, struct mbuf **); | ||||
static int ng_deflate_compress(node_p node, | static void ng_deflate_reset_req(node_p); | ||||
struct mbuf *m, struct mbuf **resultp); | |||||
static int ng_deflate_decompress(node_p node, | |||||
struct mbuf *m, struct mbuf **resultp); | |||||
static void ng_deflate_reset_req(node_p node); | |||||
/* Parse type for struct ng_deflate_config. */ | /* Parse type for struct ng_deflate_config. */ | ||||
static const struct ng_parse_struct_field ng_deflate_config_type_fields[] | static const struct ng_parse_struct_field ng_deflate_config_type_fields[] | ||||
= NG_DEFLATE_CONFIG_INFO; | = NG_DEFLATE_CONFIG_INFO; | ||||
static const struct ng_parse_type ng_deflate_config_type = { | static const struct ng_parse_type ng_deflate_config_type = { | ||||
&ng_parse_struct_type, | &ng_parse_struct_type, | ||||
ng_deflate_config_type_fields | ng_deflate_config_type_fields | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | if (priv->cfg.enable) { | ||||
priv->cfg.enable = 0; | priv->cfg.enable = 0; | ||||
} | } | ||||
/* Configuration is OK, reset to it. */ | /* Configuration is OK, reset to it. */ | ||||
priv->cfg = *cfg; | priv->cfg = *cfg; | ||||
if (priv->cfg.enable) { | if (priv->cfg.enable) { | ||||
priv->cx.next_in = NULL; | priv->cx.next_in = NULL; | ||||
priv->cx.zalloc = z_alloc; | |||||
priv->cx.zfree = z_free; | |||||
int res; | int res; | ||||
if (priv->compress) { | if (priv->compress) { | ||||
if ((res = deflateInit2(&priv->cx, | if ((res = deflateInit2(&priv->cx, | ||||
Z_DEFAULT_COMPRESSION, Z_DEFLATED, | Z_DEFAULT_COMPRESSION, Z_DEFLATED, | ||||
-cfg->windowBits, 8, | -cfg->windowBits, 8, | ||||
Z_DEFAULT_STRATEGY)) != Z_OK) { | Z_DEFAULT_STRATEGY)) != Z_OK) { | ||||
log(LOG_NOTICE, | log(LOG_NOTICE, | ||||
"deflateInit2: error %d, %s\n", | "deflateInit2: error %d, %s\n", | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | ng_deflate_rcvdata(hook_p hook, item_p item) | ||||
NGI_GET_M(item, m); | NGI_GET_M(item, m); | ||||
/* Compress */ | /* Compress */ | ||||
if (priv->compress) { | if (priv->compress) { | ||||
if ((error = ng_deflate_compress(node, m, &out)) != 0) { | if ((error = ng_deflate_compress(node, m, &out)) != 0) { | ||||
NG_FREE_ITEM(item); | NG_FREE_ITEM(item); | ||||
log(LOG_NOTICE, "%s: error: %d\n", __func__, error); | log(LOG_NOTICE, "%s: error: %d\n", __func__, error); | ||||
return (error); | return (error); | ||||
} | } | ||||
} else { /* Decompress */ | } else { /* Decompress */ | ||||
if ((error = ng_deflate_decompress(node, m, &out)) != 0) { | if ((error = ng_deflate_decompress(node, m, &out)) != 0) { | ||||
NG_FREE_ITEM(item); | NG_FREE_ITEM(item); | ||||
log(LOG_NOTICE, "%s: error: %d\n", __func__, error); | log(LOG_NOTICE, "%s: error: %d\n", __func__, error); | ||||
if (priv->ctrlnode != 0) { | if (priv->ctrlnode != 0) { | ||||
struct ng_mesg *msg; | struct ng_mesg *msg; | ||||
/* Need to send a reset-request. */ | /* Need to send a reset-request. */ | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | ng_deflate_disconnect(hook_p hook) | ||||
return (0); | return (0); | ||||
} | } | ||||
/************************************************************************ | /************************************************************************ | ||||
HELPER STUFF | HELPER STUFF | ||||
************************************************************************/ | ************************************************************************/ | ||||
/* | /* | ||||
* Space allocation and freeing routines for use by zlib routines. | |||||
*/ | |||||
static void * | |||||
z_alloc(void *notused, u_int items, u_int size) | |||||
{ | |||||
return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT)); | |||||
} | |||||
static void | |||||
z_free(void *notused, void *ptr) | |||||
{ | |||||
free(ptr, M_NETGRAPH_DEFLATE); | |||||
} | |||||
/* | |||||
* Compress/encrypt a packet and put the result in a new mbuf at *resultp. | * Compress/encrypt a packet and put the result in a new mbuf at *resultp. | ||||
* The original mbuf is not free'd. | * The original mbuf is not free'd. | ||||
*/ | */ | ||||
static int | static int | ||||
ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp) | ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp) | ||||
{ | { | ||||
const priv_p priv = NG_NODE_PRIVATE(node); | const priv_p priv = NG_NODE_PRIVATE(node); | ||||
int outlen, inlen; | int outlen, inlen; | ||||
Show All 32 Lines | ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp) | ||||
} else { | } else { | ||||
priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ | priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ | ||||
priv->cx.avail_in = inlen - 1; | priv->cx.avail_in = inlen - 1; | ||||
} | } | ||||
priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN; | priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN; | ||||
priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN; | priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN; | ||||
/* Compress. */ | /* Compress. */ | ||||
rtn = deflate(&priv->cx, Z_PACKET_FLUSH); | rtn = deflate(&priv->cx, Z_SYNC_FLUSH); | ||||
/* Check return value. */ | /* Check return value. */ | ||||
if (rtn != Z_OK) { | if (rtn != Z_OK) { | ||||
priv->stats.Errors++; | priv->stats.Errors++; | ||||
log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n", | log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n", | ||||
rtn, priv->cx.msg); | rtn, priv->cx.msg); | ||||
NG_FREE_M(m); | NG_FREE_M(m); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* Calculate resulting size. */ | /* Calculate resulting size. */ | ||||
outlen -= priv->cx.avail_out; | outlen -= priv->cx.avail_out; | ||||
/* | |||||
* Z_SYNC_FLUSH completes the current deflate block and follows | |||||
* it with an empty stored block that is three bits plus filler | |||||
* bits to the next byte, followed by four bytes (00 00 ff ff). | |||||
* RFC 1979 Section 2.1, "Data" requires the four bytes be | |||||
* removed before transmission. | |||||
*/ | |||||
outlen -= 4; | |||||
MPASS(outlen > 0); | |||||
MPASS(priv->outbuf[outlen + 0] == 0x00); | |||||
MPASS(priv->outbuf[outlen + 1] == 0x00); | |||||
MPASS(priv->outbuf[outlen + 2] == 0xff); | |||||
MPASS(priv->outbuf[outlen + 3] == 0xff); | |||||
/* If we can't compress this packet, send it as-is. */ | /* If we can't compress this packet, send it as-is. */ | ||||
if (outlen > inlen) { | if (outlen > inlen) { | ||||
/* Return original packet uncompressed. */ | /* Return original packet uncompressed. */ | ||||
*resultp = m; | *resultp = m; | ||||
priv->stats.FramesUncomp++; | priv->stats.FramesUncomp++; | ||||
priv->stats.OutOctets+=inlen; | priv->stats.OutOctets+=inlen; | ||||
} else { | } else { | ||||
Show All 23 Lines | |||||
/* | /* | ||||
* Decompress/decrypt packet and put the result in a new mbuf at *resultp. | * Decompress/decrypt packet and put the result in a new mbuf at *resultp. | ||||
* The original mbuf is not free'd. | * The original mbuf is not free'd. | ||||
*/ | */ | ||||
static int | static int | ||||
ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp) | ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp) | ||||
{ | { | ||||
const priv_p priv = NG_NODE_PRIVATE(node); | const priv_p priv = NG_NODE_PRIVATE(node); | ||||
int outlen, inlen; | int outlen, inlen, datalen; | ||||
int rtn; | int rtn; | ||||
uint16_t proto; | uint16_t proto; | ||||
int offset; | int offset; | ||||
uint16_t rseqnum; | uint16_t rseqnum; | ||||
u_char headbuf[5]; | |||||
static u_char EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff }; | |||||
/* Initialize. */ | /* Initialize. */ | ||||
*resultp = NULL; | *resultp = NULL; | ||||
inlen = m->m_pkthdr.len; | inlen = m->m_pkthdr.len; | ||||
if (inlen > DEFLATE_BUF_SIZE) { | if (inlen > DEFLATE_BUF_SIZE) { | ||||
priv->stats.Errors++; | priv->stats.Errors++; | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (proto == PROT_COMPD) { | ||||
/* Prepare to decompress. */ | /* Prepare to decompress. */ | ||||
priv->cx.next_in = priv->inbuf + offset; | priv->cx.next_in = priv->inbuf + offset; | ||||
priv->cx.avail_in = inlen - offset; | priv->cx.avail_in = inlen - offset; | ||||
/* Reserve space for protocol decompression. */ | /* Reserve space for protocol decompression. */ | ||||
priv->cx.next_out = priv->outbuf + 1; | priv->cx.next_out = priv->outbuf + 1; | ||||
priv->cx.avail_out = outlen - 1; | priv->cx.avail_out = outlen - 1; | ||||
/* Decompress. */ | /* Decompress. */ | ||||
rtn = inflate(&priv->cx, Z_PACKET_FLUSH); | rtn = inflate(&priv->cx, Z_SYNC_FLUSH); | ||||
/* Check return value. */ | /* Check return value. */ | ||||
if (rtn != Z_OK && rtn != Z_STREAM_END) { | if (rtn != Z_OK && rtn != Z_STREAM_END) { | ||||
priv->stats.Errors++; | priv->stats.Errors++; | ||||
NG_FREE_M(m); | NG_FREE_M(m); | ||||
priv->seqnum = 0; | priv->seqnum = 0; | ||||
log(LOG_NOTICE, "%s: decompression error: %d (%s)\n", | log(LOG_NOTICE, "%s: decompression error: %d (%s)\n", | ||||
__func__, rtn, priv->cx.msg); | __func__, rtn, priv->cx.msg); | ||||
switch (rtn) { | switch (rtn) { | ||||
case Z_MEM_ERROR: | case Z_MEM_ERROR: | ||||
return (ENOMEM); | return (ENOMEM); | ||||
case Z_DATA_ERROR: | case Z_DATA_ERROR: | ||||
return (EIO); | return (EIO); | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
/* Handle the EMPTY_BLOCK omitted by sender */ | |||||
if (inflateSyncPoint(&priv->cx)) { | |||||
priv->cx.avail_in = 4; | |||||
priv->cx.next_in = EMPTY_BLOCK; | |||||
inflate(&priv->cx, Z_SYNC_FLUSH); | |||||
} | |||||
/* Calculate resulting size. */ | /* Calculate resulting size. */ | ||||
outlen -= priv->cx.avail_out; | outlen -= priv->cx.avail_out; | ||||
/* Decompress protocol. */ | /* Decompress protocol. */ | ||||
if ((priv->outbuf[1] & 0x01) != 0) { | if ((priv->outbuf[1] & 0x01) != 0) { | ||||
priv->outbuf[0] = 0; | priv->outbuf[0] = 0; | ||||
/* Return packet in an mbuf. */ | /* Return packet in an mbuf. */ | ||||
m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); | m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); | ||||
} else { | } else { | ||||
outlen--; | outlen--; | ||||
/* Return packet in an mbuf. */ | /* Return packet in an mbuf. */ | ||||
m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1)); | m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1)); | ||||
} | } | ||||
if (m->m_pkthdr.len < outlen) { | if (m->m_pkthdr.len < outlen) { | ||||
m_freem(m); | m_freem(m); | ||||
priv->stats.Errors++; | priv->stats.Errors++; | ||||
priv->seqnum = 0; | priv->seqnum = 0; | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} else if (outlen < m->m_pkthdr.len) | } else if (outlen < m->m_pkthdr.len) | ||||
m_adj(m, outlen - m->m_pkthdr.len); | m_adj(m, outlen - m->m_pkthdr.len); | ||||
*resultp = m; | *resultp = m; | ||||
priv->stats.FramesPlain++; | priv->stats.FramesPlain++; | ||||
priv->stats.OutOctets+=outlen; | priv->stats.OutOctets+=outlen; | ||||
} else { /* Packet is not compressed, just update dictionary. */ | } else { | ||||
/* Packet is not compressed, just update dictionary. */ | |||||
priv->stats.FramesUncomp++; | priv->stats.FramesUncomp++; | ||||
/* | |||||
* Fake a header for uncompressed data block | |||||
*/ | |||||
datalen = inlen - offset + 1; | |||||
headbuf[0] = 0x80; | |||||
headbuf[1] = datalen & 0xff; | |||||
headbuf[2] = datalen >> 8; | |||||
headbuf[3] = (~datalen) & 0xff; | |||||
headbuf[4] = (~datalen) >> 8; | |||||
priv->cx.next_in = headbuf; | |||||
priv->cx.avail_in = sizeof(headbuf); | |||||
priv->cx.next_out = priv->outbuf; | |||||
priv->cx.avail_out = DEFLATE_BUF_SIZE; | |||||
rtn = inflate(&priv->cx, Z_NO_FLUSH); | |||||
if (priv->inbuf[0] == 0) { | if (priv->inbuf[0] == 0) { | ||||
priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ | priv->cx.next_in = | ||||
priv->inbuf + 1; /* compress protocol */ | |||||
priv->cx.avail_in = inlen - 1; | priv->cx.avail_in = inlen - 1; | ||||
} else { | } else { | ||||
priv->cx.next_in = priv->inbuf; | priv->cx.next_in = priv->inbuf; | ||||
priv->cx.avail_in = inlen; | priv->cx.avail_in = inlen; | ||||
} | } | ||||
priv->cx.next_out = priv->outbuf; | |||||
priv->cx.avail_out = DEFLATE_BUF_SIZE; | |||||
rtn = inflateIncomp(&priv->cx); | rtn = inflate(&priv->cx, Z_SYNC_FLUSH); | ||||
/* Check return value */ | /* Check return value */ | ||||
if (rtn != Z_OK) { | if (rtn != Z_OK) { | ||||
priv->stats.Errors++; | priv->stats.Errors++; | ||||
log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n", | log(LOG_NOTICE, "%s: inflate error: %d (%s)\n", | ||||
__func__, rtn, priv->cx.msg); | __func__, rtn, priv->cx.msg); | ||||
NG_FREE_M(m); | NG_FREE_M(m); | ||||
priv->seqnum = 0; | priv->seqnum = 0; | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
*resultp = m; | *resultp = m; | ||||
priv->stats.FramesPlain++; | priv->stats.FramesPlain++; | ||||
Show All 26 Lines |