2 * Copyright (c) 2006 Alexander Motin <mav@alkar.net>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * $FreeBSD: src/sys/netgraph/ng_deflate.c,v 1.3 2007/01/15 05:55:56 glebius Exp $
28 * $DragonFly: src/sys/netgraph7/ng_deflate.c,v 1.2 2008/06/26 23:05:35 dillon Exp $
32 * Deflate PPP compression netgraph node type.
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/errno.h>
41 #include <sys/syslog.h>
45 #include "ng_message.h"
48 #include "ng_deflate.h"
50 #include "opt_netgraph.h"
52 MALLOC_DEFINE(M_NETGRAPH_DEFLATE
, "netgraph_deflate", "netgraph deflate node ");
54 /* DEFLATE header length */
55 #define DEFLATE_HDRLEN 2
57 #define PROT_COMPD 0x00fd
59 #define DEFLATE_BUF_SIZE 4096
61 /* Node private data */
62 struct ng_deflate_private
{
63 struct ng_deflate_config cfg
; /* configuration */
64 u_char inbuf
[DEFLATE_BUF_SIZE
]; /* input buffer */
65 u_char outbuf
[DEFLATE_BUF_SIZE
]; /* output buffer */
66 z_stream cx
; /* compression context */
67 struct ng_deflate_stats stats
; /* statistics */
68 ng_ID_t ctrlnode
; /* path to controlling node */
69 uint16_t seqnum
; /* sequence number */
70 u_char compress
; /* compress/decompress flag */
72 typedef struct ng_deflate_private
*priv_p
;
74 /* Netgraph node methods */
75 static ng_constructor_t ng_deflate_constructor
;
76 static ng_rcvmsg_t ng_deflate_rcvmsg
;
77 static ng_shutdown_t ng_deflate_shutdown
;
78 static ng_newhook_t ng_deflate_newhook
;
79 static ng_rcvdata_t ng_deflate_rcvdata
;
80 static ng_disconnect_t ng_deflate_disconnect
;
82 /* Helper functions */
83 static void *z_alloc(void *, u_int items
, u_int size
);
84 static void z_free(void *, void *ptr
);
85 static int ng_deflate_compress(node_p node
,
86 struct mbuf
*m
, struct mbuf
**resultp
);
87 static int ng_deflate_decompress(node_p node
,
88 struct mbuf
*m
, struct mbuf
**resultp
);
89 static void ng_deflate_reset_req(node_p node
);
91 /* Parse type for struct ng_deflate_config. */
92 static const struct ng_parse_struct_field ng_deflate_config_type_fields
[]
93 = NG_DEFLATE_CONFIG_INFO
;
94 static const struct ng_parse_type ng_deflate_config_type
= {
95 &ng_parse_struct_type
,
96 ng_deflate_config_type_fields
99 /* Parse type for struct ng_deflate_stat. */
100 static const struct ng_parse_struct_field ng_deflate_stats_type_fields
[]
101 = NG_DEFLATE_STATS_INFO
;
102 static const struct ng_parse_type ng_deflate_stat_type
= {
103 &ng_parse_struct_type
,
104 ng_deflate_stats_type_fields
107 /* List of commands and how to convert arguments to/from ASCII. */
108 static const struct ng_cmdlist ng_deflate_cmds
[] = {
113 &ng_deflate_config_type
,
118 NGM_DEFLATE_RESETREQ
,
125 NGM_DEFLATE_GET_STATS
,
128 &ng_deflate_stat_type
132 NGM_DEFLATE_CLR_STATS
,
139 NGM_DEFLATE_GETCLR_STATS
,
142 &ng_deflate_stat_type
147 /* Node type descriptor */
148 static struct ng_type ng_deflate_typestruct
= {
149 .version
= NG_ABI_VERSION
,
150 .name
= NG_DEFLATE_NODE_TYPE
,
151 .constructor
= ng_deflate_constructor
,
152 .rcvmsg
= ng_deflate_rcvmsg
,
153 .shutdown
= ng_deflate_shutdown
,
154 .newhook
= ng_deflate_newhook
,
155 .rcvdata
= ng_deflate_rcvdata
,
156 .disconnect
= ng_deflate_disconnect
,
157 .cmdlist
= ng_deflate_cmds
,
159 NETGRAPH_INIT(deflate
, &ng_deflate_typestruct
);
161 /* Depend on separate zlib module. */
162 MODULE_DEPEND(ng_deflate
, zlib
, 1, 1, 1);
164 #define ERROUT(x) do { error = (x); goto done; } while (0)
166 /************************************************************************
168 ************************************************************************/
171 * Node type constructor
174 ng_deflate_constructor(node_p node
)
178 /* Allocate private structure. */
179 priv
= kmalloc(sizeof(*priv
), M_NETGRAPH_DEFLATE
, M_WAITOK
| M_ZERO
);
181 NG_NODE_SET_PRIVATE(node
, priv
);
183 /* This node is not thread safe. */
184 NG_NODE_FORCE_WRITER(node
);
191 * Give our OK for a hook to be added.
194 ng_deflate_newhook(node_p node
, hook_p hook
, const char *name
)
196 const priv_p priv
= NG_NODE_PRIVATE(node
);
198 if (NG_NODE_NUMHOOKS(node
) > 0)
201 if (strcmp(name
, NG_DEFLATE_HOOK_COMP
) == 0)
203 else if (strcmp(name
, NG_DEFLATE_HOOK_DECOMP
) == 0)
212 * Receive a control message
215 ng_deflate_rcvmsg(node_p node
, item_p item
, hook_p lasthook
)
217 const priv_p priv
= NG_NODE_PRIVATE(node
);
218 struct ng_mesg
*resp
= NULL
;
222 NGI_GET_MSG(item
, msg
);
224 if (msg
->header
.typecookie
!= NGM_DEFLATE_COOKIE
)
227 switch (msg
->header
.cmd
) {
228 case NGM_DEFLATE_CONFIG
:
230 struct ng_deflate_config
*const cfg
231 = (struct ng_deflate_config
*)msg
->data
;
233 /* Check configuration. */
234 if (msg
->header
.arglen
!= sizeof(*cfg
))
237 if (cfg
->windowBits
< 8 || cfg
->windowBits
> 15)
242 /* Clear previous state. */
243 if (priv
->cfg
.enable
) {
245 deflateEnd(&priv
->cx
);
247 inflateEnd(&priv
->cx
);
248 priv
->cfg
.enable
= 0;
251 /* Configuration is OK, reset to it. */
254 if (priv
->cfg
.enable
) {
255 priv
->cx
.next_in
= NULL
;
256 priv
->cx
.zalloc
= z_alloc
;
257 priv
->cx
.zfree
= z_free
;
259 if (priv
->compress
) {
260 if ((res
= deflateInit2(&priv
->cx
,
261 Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
263 Z_DEFAULT_STRATEGY
)) != Z_OK
) {
265 "deflateInit2: error %d, %s\n",
267 priv
->cfg
.enable
= 0;
271 if ((res
= inflateInit2(&priv
->cx
,
272 -cfg
->windowBits
)) != Z_OK
) {
274 "inflateInit2: error %d, %s\n",
276 priv
->cfg
.enable
= 0;
282 /* Initialize other state. */
285 /* Save return address so we can send reset-req's */
286 priv
->ctrlnode
= NGI_RETADDR(item
);
290 case NGM_DEFLATE_RESETREQ
:
291 ng_deflate_reset_req(node
);
294 case NGM_DEFLATE_GET_STATS
:
295 case NGM_DEFLATE_CLR_STATS
:
296 case NGM_DEFLATE_GETCLR_STATS
:
297 /* Create response if requested. */
298 if (msg
->header
.cmd
!= NGM_DEFLATE_CLR_STATS
) {
299 NG_MKRESPONSE(resp
, msg
,
300 sizeof(struct ng_deflate_stats
), M_WAITOK
| M_NULLOK
);
303 bcopy(&priv
->stats
, resp
->data
,
304 sizeof(struct ng_deflate_stats
));
307 /* Clear stats if requested. */
308 if (msg
->header
.cmd
!= NGM_DEFLATE_GET_STATS
)
310 sizeof(struct ng_deflate_stats
));
318 NG_RESPOND_MSG(error
, node
, item
, resp
);
324 * Receive incoming data on our hook.
327 ng_deflate_rcvdata(hook_p hook
, item_p item
)
329 const node_p node
= NG_HOOK_NODE(hook
);
330 const priv_p priv
= NG_NODE_PRIVATE(node
);
331 struct mbuf
*m
, *out
;
334 if (!priv
->cfg
.enable
) {
341 if (priv
->compress
) {
342 if ((error
= ng_deflate_compress(node
, m
, &out
)) != 0) {
344 log(LOG_NOTICE
, "%s: error: %d\n", __func__
, error
);
348 } else { /* Decompress */
349 if ((error
= ng_deflate_decompress(node
, m
, &out
)) != 0) {
351 log(LOG_NOTICE
, "%s: error: %d\n", __func__
, error
);
352 if (priv
->ctrlnode
!= 0) {
355 /* Need to send a reset-request. */
356 NG_MKMESSAGE(msg
, NGM_DEFLATE_COOKIE
,
357 NGM_DEFLATE_RESETREQ
, 0, M_WAITOK
| M_NULLOK
);
360 NG_SEND_MSG_ID(error
, node
, msg
,
367 NG_FWD_NEW_DATA(error
, item
, hook
, out
);
375 ng_deflate_shutdown(node_p node
)
377 const priv_p priv
= NG_NODE_PRIVATE(node
);
379 /* Take down netgraph node. */
380 if (priv
->cfg
.enable
) {
382 deflateEnd(&priv
->cx
);
384 inflateEnd(&priv
->cx
);
387 kfree(priv
, M_NETGRAPH_DEFLATE
);
388 NG_NODE_SET_PRIVATE(node
, NULL
);
389 NG_NODE_UNREF(node
); /* let the node escape */
397 ng_deflate_disconnect(hook_p hook
)
399 const node_p node
= NG_HOOK_NODE(hook
);
400 const priv_p priv
= NG_NODE_PRIVATE(node
);
402 if (priv
->cfg
.enable
) {
404 deflateEnd(&priv
->cx
);
406 inflateEnd(&priv
->cx
);
407 priv
->cfg
.enable
= 0;
410 /* Go away if no longer connected. */
411 if ((NG_NODE_NUMHOOKS(node
) == 0) && NG_NODE_IS_VALID(node
))
412 ng_rmnode_self(node
);
416 /************************************************************************
418 ************************************************************************/
421 * Space allocation and freeing routines for use by zlib routines.
425 z_alloc(void *notused
, u_int items
, u_int size
)
428 return (kmalloc(items
* size
, M_NETGRAPH_DEFLATE
, M_WAITOK
| M_NULLOK
));
432 z_free(void *notused
, void *ptr
)
435 kfree(ptr
, M_NETGRAPH_DEFLATE
);
439 * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
440 * The original mbuf is not free'd.
443 ng_deflate_compress(node_p node
, struct mbuf
*m
, struct mbuf
**resultp
)
445 const priv_p priv
= NG_NODE_PRIVATE(node
);
452 inlen
= m
->m_pkthdr
.len
;
454 priv
->stats
.FramesPlain
++;
455 priv
->stats
.InOctets
+=inlen
;
457 if (inlen
> DEFLATE_BUF_SIZE
) {
458 priv
->stats
.Errors
++;
463 /* Work with contiguous regions of memory. */
464 m_copydata(m
, 0, inlen
, (caddr_t
)priv
->inbuf
);
465 outlen
= DEFLATE_BUF_SIZE
;
467 /* Compress "inbuf" into "outbuf". */
468 /* Prepare to compress. */
469 if (priv
->inbuf
[0] != 0) {
470 priv
->cx
.next_in
= priv
->inbuf
;
471 priv
->cx
.avail_in
= inlen
;
473 priv
->cx
.next_in
= priv
->inbuf
+ 1; /* compress protocol */
474 priv
->cx
.avail_in
= inlen
- 1;
476 priv
->cx
.next_out
= priv
->outbuf
+ 2 + DEFLATE_HDRLEN
;
477 priv
->cx
.avail_out
= outlen
- 2 - DEFLATE_HDRLEN
;
480 rtn
= deflate(&priv
->cx
, Z_PACKET_FLUSH
);
482 /* Check return value. */
484 priv
->stats
.Errors
++;
485 log(LOG_NOTICE
, "ng_deflate: compression error: %d (%s)\n",
491 /* Calculate resulting size. */
492 outlen
-= priv
->cx
.avail_out
;
494 /* If we can't compress this packet, send it as-is. */
495 if (outlen
> inlen
) {
496 /* Return original packet uncompressed. */
498 priv
->stats
.FramesUncomp
++;
499 priv
->stats
.OutOctets
+=inlen
;
503 /* Install header. */
504 ((u_int16_t
*)priv
->outbuf
)[0] = htons(PROT_COMPD
);
505 ((u_int16_t
*)priv
->outbuf
)[1] = htons(priv
->seqnum
);
507 /* Return packet in an mbuf. */
508 *resultp
= m_devget((caddr_t
)priv
->outbuf
, outlen
, 0, NULL
,
510 if (*resultp
== NULL
) {
511 priv
->stats
.Errors
++;
514 priv
->stats
.FramesComp
++;
515 priv
->stats
.OutOctets
+=outlen
;
518 /* Update sequence number. */
525 * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
526 * The original mbuf is not free'd.
529 ng_deflate_decompress(node_p node
, struct mbuf
*m
, struct mbuf
**resultp
)
531 const priv_p priv
= NG_NODE_PRIVATE(node
);
541 inlen
= m
->m_pkthdr
.len
;
543 if (inlen
> DEFLATE_BUF_SIZE
) {
544 priv
->stats
.Errors
++;
550 /* Work with contiguous regions of memory. */
551 m_copydata(m
, 0, inlen
, (caddr_t
)priv
->inbuf
);
553 /* Separate proto. */
554 if ((priv
->inbuf
[0] & 0x01) != 0) {
555 proto
= priv
->inbuf
[0];
558 proto
= ntohs(((uint16_t *)priv
->inbuf
)[0]);
562 priv
->stats
.InOctets
+= inlen
;
564 /* Packet is compressed, so decompress. */
565 if (proto
== PROT_COMPD
) {
566 priv
->stats
.FramesComp
++;
568 /* Check sequence number. */
569 rseqnum
= ntohs(((uint16_t *)(priv
->inbuf
+ offset
))[0]);
571 if (rseqnum
!= priv
->seqnum
) {
572 priv
->stats
.Errors
++;
573 log(LOG_NOTICE
, "ng_deflate: wrong sequence: %u "
574 "instead of %u\n", rseqnum
, priv
->seqnum
);
580 outlen
= DEFLATE_BUF_SIZE
;
582 /* Decompress "inbuf" into "outbuf". */
583 /* Prepare to decompress. */
584 priv
->cx
.next_in
= priv
->inbuf
+ offset
;
585 priv
->cx
.avail_in
= inlen
- offset
;
586 /* Reserve space for protocol decompression. */
587 priv
->cx
.next_out
= priv
->outbuf
+ 1;
588 priv
->cx
.avail_out
= outlen
- 1;
591 rtn
= inflate(&priv
->cx
, Z_PACKET_FLUSH
);
593 /* Check return value. */
594 if (rtn
!= Z_OK
&& rtn
!= Z_STREAM_END
) {
595 priv
->stats
.Errors
++;
598 log(LOG_NOTICE
, "%s: decompression error: %d (%s)\n",
599 __func__
, rtn
, priv
->cx
.msg
);
611 /* Calculate resulting size. */
612 outlen
-= priv
->cx
.avail_out
;
616 /* Decompress protocol. */
617 if ((priv
->outbuf
[1] & 0x01) != 0) {
619 /* Return packet in an mbuf. */
620 *resultp
= m_devget((caddr_t
)priv
->outbuf
, outlen
, 0,
624 /* Return packet in an mbuf. */
625 *resultp
= m_devget((caddr_t
)(priv
->outbuf
+ 1),
626 outlen
, 0, NULL
, NULL
);
628 if (*resultp
== NULL
) {
629 priv
->stats
.Errors
++;
633 priv
->stats
.FramesPlain
++;
634 priv
->stats
.OutOctets
+=outlen
;
636 } else { /* Packet is not compressed, just update dictionary. */
637 priv
->stats
.FramesUncomp
++;
638 if (priv
->inbuf
[0] == 0) {
639 priv
->cx
.next_in
= priv
->inbuf
+ 1; /* compress protocol */
640 priv
->cx
.avail_in
= inlen
- 1;
642 priv
->cx
.next_in
= priv
->inbuf
;
643 priv
->cx
.avail_in
= inlen
;
646 rtn
= inflateIncomp(&priv
->cx
);
648 /* Check return value */
650 priv
->stats
.Errors
++;
651 log(LOG_NOTICE
, "%s: inflateIncomp error: %d (%s)\n",
652 __func__
, rtn
, priv
->cx
.msg
);
659 priv
->stats
.FramesPlain
++;
660 priv
->stats
.OutOctets
+= inlen
;
663 /* Update sequence number. */
670 * The peer has sent us a CCP ResetRequest, so reset our transmit state.
673 ng_deflate_reset_req(node_p node
)
675 const priv_p priv
= NG_NODE_PRIVATE(node
);
678 if (priv
->cfg
.enable
) {
680 deflateReset(&priv
->cx
);
682 inflateReset(&priv
->cx
);