]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/netgraph/ng_deflate.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / sys / netgraph / ng_deflate.c
1 /*-
2  * Copyright (c) 2006 Alexander Motin <mav@alkar.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
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.
14  *
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
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 /*
31  * Deflate PPP compression netgraph node type.
32  */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/mbuf.h>
38 #include <sys/malloc.h>
39 #include <sys/errno.h>
40 #include <sys/syslog.h>
41
42 #include <net/zlib.h>
43
44 #include <netgraph/ng_message.h>
45 #include <netgraph/netgraph.h>
46 #include <netgraph/ng_parse.h>
47 #include <netgraph/ng_deflate.h>
48
49 #include "opt_netgraph.h"
50
51 MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate", "netgraph deflate node ");
52
53 /* DEFLATE header length */
54 #define DEFLATE_HDRLEN          2
55
56 #define PROT_COMPD              0x00fd
57
58 #define DEFLATE_BUF_SIZE        4096
59
60 /* Node private data */
61 struct ng_deflate_private {
62         struct ng_deflate_config cfg;           /* configuration */
63         u_char          inbuf[DEFLATE_BUF_SIZE];        /* input buffer */
64         u_char          outbuf[DEFLATE_BUF_SIZE];       /* output buffer */
65         z_stream        cx;                     /* compression context */
66         struct ng_deflate_stats stats;          /* statistics */
67         ng_ID_t         ctrlnode;               /* path to controlling node */
68         uint16_t        seqnum;                 /* sequence number */
69         u_char          compress;               /* compress/decompress flag */
70 };
71 typedef struct ng_deflate_private *priv_p;
72
73 /* Netgraph node methods */
74 static ng_constructor_t ng_deflate_constructor;
75 static ng_rcvmsg_t      ng_deflate_rcvmsg;
76 static ng_shutdown_t    ng_deflate_shutdown;
77 static ng_newhook_t     ng_deflate_newhook;
78 static ng_rcvdata_t     ng_deflate_rcvdata;
79 static ng_disconnect_t  ng_deflate_disconnect;
80
81 /* Helper functions */
82 static void     *z_alloc(void *, u_int items, u_int size);
83 static void     z_free(void *, void *ptr);
84 static int      ng_deflate_compress(node_p node,
85                     struct mbuf *m, struct mbuf **resultp);
86 static int      ng_deflate_decompress(node_p node,
87                     struct mbuf *m, struct mbuf **resultp);
88 static void     ng_deflate_reset_req(node_p node);
89
90 /* Parse type for struct ng_deflate_config. */
91 static const struct ng_parse_struct_field ng_deflate_config_type_fields[]
92         = NG_DEFLATE_CONFIG_INFO;
93 static const struct ng_parse_type ng_deflate_config_type = {
94         &ng_parse_struct_type,
95         ng_deflate_config_type_fields
96 };
97
98 /* Parse type for struct ng_deflate_stat. */
99 static const struct ng_parse_struct_field ng_deflate_stats_type_fields[]
100         = NG_DEFLATE_STATS_INFO;
101 static const struct ng_parse_type ng_deflate_stat_type = {
102         &ng_parse_struct_type,
103         ng_deflate_stats_type_fields
104 };
105
106 /* List of commands and how to convert arguments to/from ASCII. */
107 static const struct ng_cmdlist ng_deflate_cmds[] = {
108         {
109           NGM_DEFLATE_COOKIE,
110           NGM_DEFLATE_CONFIG,
111           "config",
112           &ng_deflate_config_type,
113           NULL
114         },
115         {
116           NGM_DEFLATE_COOKIE,
117           NGM_DEFLATE_RESETREQ,
118           "resetreq",
119           NULL,
120           NULL
121         },
122         {
123           NGM_DEFLATE_COOKIE,
124           NGM_DEFLATE_GET_STATS,
125           "getstats",
126           NULL,
127           &ng_deflate_stat_type
128         },
129         {
130           NGM_DEFLATE_COOKIE,
131           NGM_DEFLATE_CLR_STATS,
132           "clrstats",
133           NULL,
134           NULL
135         },
136         {
137           NGM_DEFLATE_COOKIE,
138           NGM_DEFLATE_GETCLR_STATS,
139           "getclrstats",
140           NULL,
141           &ng_deflate_stat_type
142         },
143         { 0 }
144 };
145
146 /* Node type descriptor */
147 static struct ng_type ng_deflate_typestruct = {
148         .version =      NG_ABI_VERSION,
149         .name =         NG_DEFLATE_NODE_TYPE,
150         .constructor =  ng_deflate_constructor,
151         .rcvmsg =       ng_deflate_rcvmsg,
152         .shutdown =     ng_deflate_shutdown,
153         .newhook =      ng_deflate_newhook,
154         .rcvdata =      ng_deflate_rcvdata,
155         .disconnect =   ng_deflate_disconnect,
156         .cmdlist =      ng_deflate_cmds,
157 };
158 NETGRAPH_INIT(deflate, &ng_deflate_typestruct);
159
160 /* Depend on separate zlib module. */
161 MODULE_DEPEND(ng_deflate, zlib, 1, 1, 1);
162
163 #define ERROUT(x)       do { error = (x); goto done; } while (0)
164
165 /************************************************************************
166                         NETGRAPH NODE STUFF
167  ************************************************************************/
168
169 /*
170  * Node type constructor
171  */
172 static int
173 ng_deflate_constructor(node_p node)
174 {
175         priv_p priv;
176
177         /* Allocate private structure. */
178         priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO);
179
180         NG_NODE_SET_PRIVATE(node, priv);
181
182         /* This node is not thread safe. */
183         NG_NODE_FORCE_WRITER(node);
184
185         /* Done */
186         return (0);
187 }
188
189 /*
190  * Give our OK for a hook to be added.
191  */
192 static int
193 ng_deflate_newhook(node_p node, hook_p hook, const char *name)
194 {
195         const priv_p priv = NG_NODE_PRIVATE(node);
196
197         if (NG_NODE_NUMHOOKS(node) > 0)
198                 return (EINVAL);
199
200         if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0)
201                 priv->compress = 1;
202         else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0)
203                 priv->compress = 0;
204         else
205                 return (EINVAL);
206
207         return (0);
208 }
209
210 /*
211  * Receive a control message
212  */
213 static int
214 ng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook)
215 {
216         const priv_p priv = NG_NODE_PRIVATE(node);
217         struct ng_mesg *resp = NULL;
218         int error = 0;
219         struct ng_mesg *msg;
220
221         NGI_GET_MSG(item, msg);
222
223         if (msg->header.typecookie != NGM_DEFLATE_COOKIE)
224                 ERROUT(EINVAL);
225
226         switch (msg->header.cmd) {
227         case NGM_DEFLATE_CONFIG:
228             {
229                 struct ng_deflate_config *const cfg
230                     = (struct ng_deflate_config *)msg->data;
231
232                 /* Check configuration. */
233                 if (msg->header.arglen != sizeof(*cfg))
234                         ERROUT(EINVAL);
235                 if (cfg->enable) {
236                     if (cfg->windowBits < 8 || cfg->windowBits > 15)
237                         ERROUT(EINVAL);
238                 } else
239                     cfg->windowBits = 0;
240
241                 /* Clear previous state. */
242                 if (priv->cfg.enable) {
243                         if (priv->compress)
244                                 deflateEnd(&priv->cx);
245                         else
246                                 inflateEnd(&priv->cx);
247                         priv->cfg.enable = 0;
248                 }
249
250                 /* Configuration is OK, reset to it. */
251                 priv->cfg = *cfg;
252
253                 if (priv->cfg.enable) {
254                         priv->cx.next_in = NULL;
255                         priv->cx.zalloc = z_alloc;
256                         priv->cx.zfree = z_free;
257                         int res;
258                         if (priv->compress) {
259                                 if ((res = deflateInit2(&priv->cx,
260                                     Z_DEFAULT_COMPRESSION, Z_DEFLATED,
261                                     -cfg->windowBits, 8,
262                                     Z_DEFAULT_STRATEGY)) != Z_OK) {
263                                         log(LOG_NOTICE,
264                                             "deflateInit2: error %d, %s\n",
265                                             res, priv->cx.msg);
266                                         priv->cfg.enable = 0;
267                                         ERROUT(ENOMEM);
268                                 }
269                         } else {
270                                 if ((res = inflateInit2(&priv->cx,
271                                     -cfg->windowBits)) != Z_OK) {
272                                         log(LOG_NOTICE,
273                                             "inflateInit2: error %d, %s\n",
274                                             res, priv->cx.msg);
275                                         priv->cfg.enable = 0;
276                                         ERROUT(ENOMEM);
277                                 }
278                         }
279                 }
280
281                 /* Initialize other state. */
282                 priv->seqnum = 0;
283
284                 /* Save return address so we can send reset-req's */
285                 priv->ctrlnode = NGI_RETADDR(item);
286                 break;
287             }
288
289         case NGM_DEFLATE_RESETREQ:
290                 ng_deflate_reset_req(node);
291                 break;
292
293         case NGM_DEFLATE_GET_STATS:
294         case NGM_DEFLATE_CLR_STATS:
295         case NGM_DEFLATE_GETCLR_STATS:
296                 /* Create response if requested. */
297                 if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) {
298                         NG_MKRESPONSE(resp, msg,
299                             sizeof(struct ng_deflate_stats), M_NOWAIT);
300                         if (resp == NULL)
301                                 ERROUT(ENOMEM);
302                         bcopy(&priv->stats, resp->data,
303                             sizeof(struct ng_deflate_stats));
304                 }
305
306                 /* Clear stats if requested. */
307                 if (msg->header.cmd != NGM_DEFLATE_GET_STATS)
308                         bzero(&priv->stats,
309                             sizeof(struct ng_deflate_stats));
310                 break;
311
312         default:
313                 error = EINVAL;
314                 break;
315         }
316 done:
317         NG_RESPOND_MSG(error, node, item, resp);
318         NG_FREE_MSG(msg);
319         return (error);
320 }
321
322 /*
323  * Receive incoming data on our hook.
324  */
325 static int
326 ng_deflate_rcvdata(hook_p hook, item_p item)
327 {
328         const node_p node = NG_HOOK_NODE(hook);
329         const priv_p priv = NG_NODE_PRIVATE(node);
330         struct mbuf *m, *out;
331         int error;
332
333         if (!priv->cfg.enable) {
334                 NG_FREE_ITEM(item);
335                 return (ENXIO);
336         }
337
338         NGI_GET_M(item, m);
339         /* Compress */
340         if (priv->compress) {
341                 if ((error = ng_deflate_compress(node, m, &out)) != 0) {
342                         NG_FREE_ITEM(item);
343                         log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
344                         return (error);
345                 }
346
347         } else { /* Decompress */
348                 if ((error = ng_deflate_decompress(node, m, &out)) != 0) {
349                         NG_FREE_ITEM(item);
350                         log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
351                         if (priv->ctrlnode != 0) {
352                                 struct ng_mesg *msg;
353
354                                 /* Need to send a reset-request. */
355                                 NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE,
356                                     NGM_DEFLATE_RESETREQ, 0, M_NOWAIT);
357                                 if (msg == NULL)
358                                         return (error);
359                                 NG_SEND_MSG_ID(error, node, msg,
360                                         priv->ctrlnode, 0);
361                         }
362                         return (error);
363                 }
364         }
365
366         NG_FWD_NEW_DATA(error, item, hook, out);
367         return (error);
368 }
369
370 /*
371  * Destroy node.
372  */
373 static int
374 ng_deflate_shutdown(node_p node)
375 {
376         const priv_p priv = NG_NODE_PRIVATE(node);
377
378         /* Take down netgraph node. */
379         if (priv->cfg.enable) {
380             if (priv->compress)
381                 deflateEnd(&priv->cx);
382             else
383                 inflateEnd(&priv->cx);
384         }
385
386         free(priv, M_NETGRAPH_DEFLATE);
387         NG_NODE_SET_PRIVATE(node, NULL);
388         NG_NODE_UNREF(node);            /* let the node escape */
389         return (0);
390 }
391
392 /*
393  * Hook disconnection
394  */
395 static int
396 ng_deflate_disconnect(hook_p hook)
397 {
398         const node_p node = NG_HOOK_NODE(hook);
399         const priv_p priv = NG_NODE_PRIVATE(node);
400
401         if (priv->cfg.enable) {
402             if (priv->compress)
403                 deflateEnd(&priv->cx);
404             else
405                 inflateEnd(&priv->cx);
406             priv->cfg.enable = 0;
407         }
408
409         /* Go away if no longer connected. */
410         if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node))
411                 ng_rmnode_self(node);
412         return (0);
413 }
414
415 /************************************************************************
416                         HELPER STUFF
417  ************************************************************************/
418
419 /*
420  * Space allocation and freeing routines for use by zlib routines.
421  */
422
423 static void *
424 z_alloc(void *notused, u_int items, u_int size)
425 {
426
427         return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT));
428 }
429
430 static void
431 z_free(void *notused, void *ptr)
432 {
433
434         free(ptr, M_NETGRAPH_DEFLATE);
435 }
436
437 /*
438  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
439  * The original mbuf is not free'd.
440  */
441 static int
442 ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
443 {
444         const priv_p    priv = NG_NODE_PRIVATE(node);
445         int             outlen, inlen;
446         int             rtn;
447
448         /* Initialize. */
449         *resultp = NULL;
450
451         inlen = m->m_pkthdr.len;
452
453         priv->stats.FramesPlain++;
454         priv->stats.InOctets+=inlen;
455
456         if (inlen > DEFLATE_BUF_SIZE) {
457                 priv->stats.Errors++;
458                 NG_FREE_M(m);
459                 return (ENOMEM);
460         }
461
462         /* Work with contiguous regions of memory. */
463         m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
464         outlen = DEFLATE_BUF_SIZE;
465
466         /* Compress "inbuf" into "outbuf". */
467         /* Prepare to compress. */
468         if (priv->inbuf[0] != 0) {
469                 priv->cx.next_in = priv->inbuf;
470                 priv->cx.avail_in = inlen;
471         } else {
472                 priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
473                 priv->cx.avail_in = inlen - 1;
474         }
475         priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN;
476         priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN;
477
478         /* Compress. */
479         rtn = deflate(&priv->cx, Z_PACKET_FLUSH);
480
481         /* Check return value. */
482         if (rtn != Z_OK) {
483                 priv->stats.Errors++;
484                 log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n",
485                     rtn, priv->cx.msg);
486                 NG_FREE_M(m);
487                 return (EINVAL);
488         }
489
490         /* Calculate resulting size. */
491         outlen -= priv->cx.avail_out;
492
493         /* If we can't compress this packet, send it as-is. */
494         if (outlen > inlen) {
495                 /* Return original packet uncompressed. */
496                 *resultp = m;
497                 priv->stats.FramesUncomp++;
498                 priv->stats.OutOctets+=inlen;
499         } else {
500                 NG_FREE_M(m);
501
502                 /* Install header. */
503                 ((u_int16_t *)priv->outbuf)[0] = htons(PROT_COMPD);
504                 ((u_int16_t *)priv->outbuf)[1] = htons(priv->seqnum);
505
506                 /* Return packet in an mbuf. */
507                 *resultp = m_devget((caddr_t)priv->outbuf, outlen, 0, NULL,
508                     NULL);
509                 if (*resultp == NULL) {
510                         priv->stats.Errors++;
511                         return (ENOMEM);
512                 };
513                 priv->stats.FramesComp++;
514                 priv->stats.OutOctets+=outlen;
515         }
516
517         /* Update sequence number. */
518         priv->seqnum++;
519
520         return (0);
521 }
522
523 /*
524  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
525  * The original mbuf is not free'd.
526  */
527 static int
528 ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp)
529 {
530         const priv_p    priv = NG_NODE_PRIVATE(node);
531         int             outlen, inlen;
532         int             rtn;
533         uint16_t        proto;
534         int             offset;
535         uint16_t        rseqnum;
536
537         /* Initialize. */
538         *resultp = NULL;
539
540         inlen = m->m_pkthdr.len;
541
542         if (inlen > DEFLATE_BUF_SIZE) {
543                 priv->stats.Errors++;
544                 NG_FREE_M(m);
545                 priv->seqnum = 0;
546                 return (ENOMEM);
547         }
548
549         /* Work with contiguous regions of memory. */
550         m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
551
552         /* Separate proto. */
553         if ((priv->inbuf[0] & 0x01) != 0) {
554                 proto = priv->inbuf[0];
555                 offset = 1;
556         } else {
557                 proto = ntohs(((uint16_t *)priv->inbuf)[0]);
558                 offset = 2;
559         }
560
561         priv->stats.InOctets += inlen;
562
563         /* Packet is compressed, so decompress. */
564         if (proto == PROT_COMPD) {
565                 priv->stats.FramesComp++;
566
567                 /* Check sequence number. */
568                 rseqnum = ntohs(((uint16_t *)(priv->inbuf + offset))[0]);
569                 offset += 2;
570                 if (rseqnum != priv->seqnum) {
571                         priv->stats.Errors++;
572                         log(LOG_NOTICE, "ng_deflate: wrong sequence: %u "
573                             "instead of %u\n", rseqnum, priv->seqnum);
574                         NG_FREE_M(m);
575                         priv->seqnum = 0;
576                         return (EPIPE);
577                 }
578
579                 outlen = DEFLATE_BUF_SIZE;
580
581                 /* Decompress "inbuf" into "outbuf". */
582                 /* Prepare to decompress. */
583                 priv->cx.next_in = priv->inbuf + offset;
584                 priv->cx.avail_in = inlen - offset;
585                 /* Reserve space for protocol decompression. */
586                 priv->cx.next_out = priv->outbuf + 1;
587                 priv->cx.avail_out = outlen - 1;
588
589                 /* Decompress. */
590                 rtn = inflate(&priv->cx, Z_PACKET_FLUSH);
591
592                 /* Check return value. */
593                 if (rtn != Z_OK && rtn != Z_STREAM_END) {
594                         priv->stats.Errors++;
595                         NG_FREE_M(m);
596                         priv->seqnum = 0;
597                         log(LOG_NOTICE, "%s: decompression error: %d (%s)\n",
598                             __func__, rtn, priv->cx.msg);
599
600                         switch (rtn) {
601                         case Z_MEM_ERROR:
602                                 return (ENOMEM);
603                         case Z_DATA_ERROR:
604                                 return (EIO);
605                         default:
606                                 return (EINVAL);
607                         }
608                 }
609
610                 /* Calculate resulting size. */
611                 outlen -= priv->cx.avail_out;
612
613                 NG_FREE_M(m);
614
615                 /* Decompress protocol. */
616                 if ((priv->outbuf[1] & 0x01) != 0) {
617                         priv->outbuf[0] = 0;
618                         /* Return packet in an mbuf. */
619                         *resultp = m_devget((caddr_t)priv->outbuf, outlen, 0,
620                             NULL, NULL);
621                 } else {
622                         outlen--;
623                         /* Return packet in an mbuf. */
624                         *resultp = m_devget((caddr_t)(priv->outbuf + 1),
625                             outlen, 0, NULL, NULL);
626                 }
627                 if (*resultp == NULL) {
628                         priv->stats.Errors++;
629                         priv->seqnum = 0;
630                         return (ENOMEM);
631                 };
632                 priv->stats.FramesPlain++;
633                 priv->stats.OutOctets+=outlen;
634
635         } else { /* Packet is not compressed, just update dictionary. */
636                 priv->stats.FramesUncomp++;
637                 if (priv->inbuf[0] == 0) {
638                     priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
639                     priv->cx.avail_in = inlen - 1;
640                 } else {
641                     priv->cx.next_in = priv->inbuf;
642                     priv->cx.avail_in = inlen;
643                 }
644
645                 rtn = inflateIncomp(&priv->cx);
646
647                 /* Check return value */
648                 if (rtn != Z_OK) {
649                         priv->stats.Errors++;
650                         log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n",
651                             __func__, rtn, priv->cx.msg);
652                         NG_FREE_M(m);
653                         priv->seqnum = 0;
654                         return (EINVAL);
655                 }
656
657                 *resultp = m;
658                 priv->stats.FramesPlain++;
659                 priv->stats.OutOctets += inlen;
660         }
661
662         /* Update sequence number. */
663         priv->seqnum++;
664
665         return (0);
666 }
667
668 /*
669  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
670  */
671 static void
672 ng_deflate_reset_req(node_p node)
673 {
674         const priv_p priv = NG_NODE_PRIVATE(node);
675
676         priv->seqnum = 0;
677         if (priv->cfg.enable) {
678             if (priv->compress)
679                 deflateReset(&priv->cx);
680             else
681                 inflateReset(&priv->cx);
682         }
683 }
684