]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/netgraph/ng_mppc.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / netgraph / ng_mppc.c
1 /*
2  * ng_mppc.c
3  */
4
5 /*-
6  * Copyright (c) 1996-2000 Whistle Communications, Inc.
7  * All rights reserved.
8  * 
9  * Subject to the following obligations and disclaimer of warranty, use and
10  * redistribution of this software, in source or object code forms, with or
11  * without modifications are expressly permitted by Whistle Communications;
12  * provided, however, that:
13  * 1. Any and all reproductions of the source or object code must include the
14  *    copyright notice above and the following disclaimer of warranties; and
15  * 2. No rights are granted, in any manner or form, to use Whistle
16  *    Communications, Inc. trademarks, including the mark "WHISTLE
17  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18  *    such appears in the above copyright notice or in the software.
19  * 
20  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  *
38  * Author: Archie Cobbs <archie@freebsd.org>
39  *
40  * $Whistle: ng_mppc.c,v 1.4 1999/11/25 00:10:12 archie Exp $
41  * $FreeBSD$
42  */
43
44 /*
45  * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type.
46  *
47  * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or
48  * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful.
49  */
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/kernel.h>
54 #include <sys/mbuf.h>
55 #include <sys/malloc.h>
56 #include <sys/endian.h>
57 #include <sys/errno.h>
58 #include <sys/sysctl.h>
59 #include <sys/syslog.h>
60
61 #include <netgraph/ng_message.h>
62 #include <netgraph/netgraph.h>
63 #include <netgraph/ng_mppc.h>
64
65 #include "opt_netgraph.h"
66
67 #if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION)
68 #ifdef KLD_MODULE
69 /* XXX NETGRAPH_MPPC_COMPRESSION isn't functional yet */
70 #define NETGRAPH_MPPC_ENCRYPTION
71 #else
72 /* This case is indicative of an error in sys/conf files */
73 #error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION
74 #endif
75 #endif
76
77 #ifdef NG_SEPARATE_MALLOC
78 static MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node");
79 #else
80 #define M_NETGRAPH_MPPC M_NETGRAPH
81 #endif
82
83 #ifdef NETGRAPH_MPPC_COMPRESSION
84 /* XXX this file doesn't exist yet, but hopefully someday it will... */
85 #include <net/mppc.h>
86 #endif
87 #ifdef NETGRAPH_MPPC_ENCRYPTION
88 #include <crypto/rc4/rc4.h>
89 #endif
90 #include <crypto/sha1.h>
91
92 /* Decompression blowup */
93 #define MPPC_DECOMP_BUFSIZE     8092            /* allocate buffer this big */
94 #define MPPC_DECOMP_SAFETY      100             /*   plus this much margin */
95
96 /* MPPC/MPPE header length */
97 #define MPPC_HDRLEN             2
98
99 /* Key length */
100 #define KEYLEN(b)               (((b) & MPPE_128) ? 16 : 8)
101
102 /*
103  * When packets are lost with MPPE, we may have to re-key arbitrarily
104  * many times to 'catch up' to the new jumped-ahead sequence number.
105  * Since this can be expensive, we pose a limit on how many re-keyings
106  * we will do at one time to avoid a possible D.O.S. vulnerability.
107  * This should instead be a configurable parameter.
108  */
109 #define MPPE_MAX_REKEY          1000
110
111 SYSCTL_NODE(_net_graph, OID_AUTO, mppe, CTLFLAG_RW, 0, "MPPE");
112
113 static int mppe_block_on_max_rekey = 0;
114 TUNABLE_INT("net.graph.mppe.block_on_max_rekey", &mppe_block_on_max_rekey);
115 SYSCTL_INT(_net_graph_mppe, OID_AUTO, block_on_max_rekey, CTLFLAG_RW,
116     &mppe_block_on_max_rekey, 0, "Block node on max MPPE key re-calculations");
117
118 static int mppe_log_max_rekey = 1;
119 TUNABLE_INT("net.graph.mppe.log_max_rekey", &mppe_log_max_rekey);
120 SYSCTL_INT(_net_graph_mppe, OID_AUTO, log_max_rekey, CTLFLAG_RW,
121     &mppe_log_max_rekey, 0, "Log max MPPE key re-calculations event");
122
123 static int mppe_max_rekey = MPPE_MAX_REKEY;
124 TUNABLE_INT("net.graph.mppe.max_rekey", &mppe_max_rekey);
125 SYSCTL_INT(_net_graph_mppe, OID_AUTO, max_rekey, CTLFLAG_RW,
126     &mppe_max_rekey, 0, "Maximum number of MPPE key re-calculations");
127
128 /* MPPC packet header bits */
129 #define MPPC_FLAG_FLUSHED       0x8000          /* xmitter reset state */
130 #define MPPC_FLAG_RESTART       0x4000          /* compress history restart */
131 #define MPPC_FLAG_COMPRESSED    0x2000          /* packet is compresed */
132 #define MPPC_FLAG_ENCRYPTED     0x1000          /* packet is encrypted */
133 #define MPPC_CCOUNT_MASK        0x0fff          /* sequence number mask */
134
135 #define MPPC_CCOUNT_INC(d)      ((d) = (((d) + 1) & MPPC_CCOUNT_MASK))
136
137 #define MPPE_UPDATE_MASK        0xff            /* coherency count when we're */
138 #define MPPE_UPDATE_FLAG        0xff            /*   supposed to update key */
139
140 #define MPPC_COMP_OK            0x05
141 #define MPPC_DECOMP_OK          0x05
142
143 /* Per direction info */
144 struct ng_mppc_dir {
145         struct ng_mppc_config   cfg;            /* configuration */
146         hook_p                  hook;           /* netgraph hook */
147         u_int16_t               cc:12;          /* coherency count */
148         u_char                  flushed;        /* clean history (xmit only) */
149 #ifdef NETGRAPH_MPPC_COMPRESSION
150         u_char                  *history;       /* compression history */
151 #endif
152 #ifdef NETGRAPH_MPPC_ENCRYPTION
153         u_char                  key[MPPE_KEY_LEN];      /* session key */
154         struct rc4_state        rc4;                    /* rc4 state */
155 #endif
156 };
157
158 /* Node private data */
159 struct ng_mppc_private {
160         struct ng_mppc_dir      xmit;           /* compress/encrypt config */
161         struct ng_mppc_dir      recv;           /* decompress/decrypt config */
162         ng_ID_t                 ctrlnode;       /* path to controlling node */
163 };
164 typedef struct ng_mppc_private *priv_p;
165
166 /* Netgraph node methods */
167 static ng_constructor_t ng_mppc_constructor;
168 static ng_rcvmsg_t      ng_mppc_rcvmsg;
169 static ng_shutdown_t    ng_mppc_shutdown;
170 static ng_newhook_t     ng_mppc_newhook;
171 static ng_rcvdata_t     ng_mppc_rcvdata;
172 static ng_disconnect_t  ng_mppc_disconnect;
173
174 /* Helper functions */
175 static int      ng_mppc_compress(node_p node,
176                         struct mbuf **datap);
177 static int      ng_mppc_decompress(node_p node,
178                         struct mbuf **datap);
179 #ifdef NETGRAPH_MPPC_ENCRYPTION
180 static void     ng_mppc_getkey(const u_char *h, u_char *h2, int len);
181 static void     ng_mppc_updatekey(u_int32_t bits,
182                         u_char *key0, u_char *key, struct rc4_state *rc4);
183 #endif
184 static void     ng_mppc_reset_req(node_p node);
185
186 /* Node type descriptor */
187 static struct ng_type ng_mppc_typestruct = {
188         .version =      NG_ABI_VERSION,
189         .name =         NG_MPPC_NODE_TYPE,
190         .constructor =  ng_mppc_constructor,
191         .rcvmsg =       ng_mppc_rcvmsg,
192         .shutdown =     ng_mppc_shutdown,
193         .newhook =      ng_mppc_newhook,
194         .rcvdata =      ng_mppc_rcvdata,
195         .disconnect =   ng_mppc_disconnect,
196 };
197 NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
198
199 #ifdef NETGRAPH_MPPC_ENCRYPTION
200 /* Depend on separate rc4 module */
201 MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
202 #endif
203
204 /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
205 static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
206
207 #define ERROUT(x)       do { error = (x); goto done; } while (0)
208
209 /************************************************************************
210                         NETGRAPH NODE STUFF
211  ************************************************************************/
212
213 /*
214  * Node type constructor
215  */
216 static int
217 ng_mppc_constructor(node_p node)
218 {
219         priv_p priv;
220
221         /* Allocate private structure */
222         priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_WAITOK | M_ZERO);
223
224         NG_NODE_SET_PRIVATE(node, priv);
225
226         /* This node is not thread safe. */
227         NG_NODE_FORCE_WRITER(node);
228
229         /* Done */
230         return (0);
231 }
232
233 /*
234  * Give our OK for a hook to be added
235  */
236 static int
237 ng_mppc_newhook(node_p node, hook_p hook, const char *name)
238 {
239         const priv_p priv = NG_NODE_PRIVATE(node);
240         hook_p *hookPtr;
241
242         /* Check hook name */
243         if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
244                 hookPtr = &priv->xmit.hook;
245         else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
246                 hookPtr = &priv->recv.hook;
247         else
248                 return (EINVAL);
249
250         /* See if already connected */
251         if (*hookPtr != NULL)
252                 return (EISCONN);
253
254         /* OK */
255         *hookPtr = hook;
256         return (0);
257 }
258
259 /*
260  * Receive a control message
261  */
262 static int
263 ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
264 {
265         const priv_p priv = NG_NODE_PRIVATE(node);
266         struct ng_mesg *resp = NULL;
267         int error = 0;
268         struct ng_mesg *msg;
269
270         NGI_GET_MSG(item, msg);
271         switch (msg->header.typecookie) {
272         case NGM_MPPC_COOKIE:
273                 switch (msg->header.cmd) {
274                 case NGM_MPPC_CONFIG_COMP:
275                 case NGM_MPPC_CONFIG_DECOMP:
276                     {
277                         struct ng_mppc_config *const cfg
278                             = (struct ng_mppc_config *)msg->data;
279                         const int isComp =
280                             msg->header.cmd == NGM_MPPC_CONFIG_COMP;
281                         struct ng_mppc_dir *const d = isComp ?
282                             &priv->xmit : &priv->recv;
283
284                         /* Check configuration */
285                         if (msg->header.arglen != sizeof(*cfg))
286                                 ERROUT(EINVAL);
287                         if (cfg->enable) {
288                                 if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
289                                         ERROUT(EINVAL);
290 #ifndef NETGRAPH_MPPC_COMPRESSION
291                                 if ((cfg->bits & MPPC_BIT) != 0)
292                                         ERROUT(EPROTONOSUPPORT);
293 #endif
294 #ifndef NETGRAPH_MPPC_ENCRYPTION
295                                 if ((cfg->bits & MPPE_BITS) != 0)
296                                         ERROUT(EPROTONOSUPPORT);
297 #endif
298                         } else
299                                 cfg->bits = 0;
300
301                         /* Save return address so we can send reset-req's */
302                         if (!isComp)
303                                 priv->ctrlnode = NGI_RETADDR(item);
304
305                         /* Configuration is OK, reset to it */
306                         d->cfg = *cfg;
307
308 #ifdef NETGRAPH_MPPC_COMPRESSION
309                         /* Initialize state buffers for compression */
310                         if (d->history != NULL) {
311                                 free(d->history, M_NETGRAPH_MPPC);
312                                 d->history = NULL;
313                         }
314                         if ((cfg->bits & MPPC_BIT) != 0) {
315                                 d->history = malloc(isComp ?
316                                     MPPC_SizeOfCompressionHistory() :
317                                     MPPC_SizeOfDecompressionHistory(),
318                                     M_NETGRAPH_MPPC, M_NOWAIT);
319                                 if (d->history == NULL)
320                                         ERROUT(ENOMEM);
321                                 if (isComp)
322                                         MPPC_InitCompressionHistory(d->history);
323                                 else {
324                                         MPPC_InitDecompressionHistory(
325                                             d->history);
326                                 }
327                         }
328 #endif
329
330 #ifdef NETGRAPH_MPPC_ENCRYPTION
331                         /* Generate initial session keys for encryption */
332                         if ((cfg->bits & MPPE_BITS) != 0) {
333                                 const int keylen = KEYLEN(cfg->bits);
334
335                                 bcopy(cfg->startkey, d->key, keylen);
336                                 ng_mppc_getkey(cfg->startkey, d->key, keylen);
337                                 if ((cfg->bits & MPPE_40) != 0)
338                                         bcopy(&ng_mppe_weakenkey, d->key, 3);
339                                 else if ((cfg->bits & MPPE_56) != 0)
340                                         bcopy(&ng_mppe_weakenkey, d->key, 1);
341                                 rc4_init(&d->rc4, d->key, keylen);
342                         }
343 #endif
344
345                         /* Initialize other state */
346                         d->cc = 0;
347                         d->flushed = 0;
348                         break;
349                     }
350
351                 case NGM_MPPC_RESETREQ:
352                         ng_mppc_reset_req(node);
353                         break;
354
355                 default:
356                         error = EINVAL;
357                         break;
358                 }
359                 break;
360         default:
361                 error = EINVAL;
362                 break;
363         }
364 done:
365         NG_RESPOND_MSG(error, node, item, resp);
366         NG_FREE_MSG(msg);
367         return (error);
368 }
369
370 /*
371  * Receive incoming data on our hook.
372  */
373 static int
374 ng_mppc_rcvdata(hook_p hook, item_p item)
375 {
376         const node_p node = NG_HOOK_NODE(hook);
377         const priv_p priv = NG_NODE_PRIVATE(node);
378         int error;
379         struct mbuf *m;
380
381         NGI_GET_M(item, m);
382         /* Compress and/or encrypt */
383         if (hook == priv->xmit.hook) {
384                 if (!priv->xmit.cfg.enable) {
385                         NG_FREE_M(m);
386                         NG_FREE_ITEM(item);
387                         return (ENXIO);
388                 }
389                 if ((error = ng_mppc_compress(node, &m)) != 0) {
390                         NG_FREE_ITEM(item);
391                         return(error);
392                 }
393                 NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
394                 return (error);
395         }
396
397         /* Decompress and/or decrypt */
398         if (hook == priv->recv.hook) {
399                 if (!priv->recv.cfg.enable) {
400                         NG_FREE_M(m);
401                         NG_FREE_ITEM(item);
402                         return (ENXIO);
403                 }
404                 if ((error = ng_mppc_decompress(node, &m)) != 0) {
405                         NG_FREE_ITEM(item);
406                         if (error == EINVAL && priv->ctrlnode != 0) {
407                                 struct ng_mesg *msg;
408
409                                 /* Need to send a reset-request */
410                                 NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
411                                     NGM_MPPC_RESETREQ, 0, M_NOWAIT);
412                                 if (msg == NULL)
413                                         return (error);
414                                 NG_SEND_MSG_ID(error, node, msg,
415                                         priv->ctrlnode, 0); 
416                         }
417                         return (error);
418                 }
419                 NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
420                 return (error);
421         }
422
423         /* Oops */
424         panic("%s: unknown hook", __func__);
425 }
426
427 /*
428  * Destroy node
429  */
430 static int
431 ng_mppc_shutdown(node_p node)
432 {
433         const priv_p priv = NG_NODE_PRIVATE(node);
434
435         /* Take down netgraph node */
436 #ifdef NETGRAPH_MPPC_COMPRESSION
437         if (priv->xmit.history != NULL)
438                 free(priv->xmit.history, M_NETGRAPH_MPPC);
439         if (priv->recv.history != NULL)
440                 free(priv->recv.history, M_NETGRAPH_MPPC);
441 #endif
442         bzero(priv, sizeof(*priv));
443         free(priv, M_NETGRAPH_MPPC);
444         NG_NODE_SET_PRIVATE(node, NULL);
445         NG_NODE_UNREF(node);            /* let the node escape */
446         return (0);
447 }
448
449 /*
450  * Hook disconnection
451  */
452 static int
453 ng_mppc_disconnect(hook_p hook)
454 {
455         const node_p node = NG_HOOK_NODE(hook);
456         const priv_p priv = NG_NODE_PRIVATE(node);
457
458         /* Zero out hook pointer */
459         if (hook == priv->xmit.hook)
460                 priv->xmit.hook = NULL;
461         if (hook == priv->recv.hook)
462                 priv->recv.hook = NULL;
463
464         /* Go away if no longer connected */
465         if ((NG_NODE_NUMHOOKS(node) == 0)
466         && NG_NODE_IS_VALID(node))
467                 ng_rmnode_self(node);
468         return (0);
469 }
470
471 /************************************************************************
472                         HELPER STUFF
473  ************************************************************************/
474
475 /*
476  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
477  * The original mbuf is not free'd.
478  */
479 static int
480 ng_mppc_compress(node_p node, struct mbuf **datap)
481 {
482         const priv_p priv = NG_NODE_PRIVATE(node);
483         struct ng_mppc_dir *const d = &priv->xmit;
484         u_int16_t header;
485         struct mbuf *m = *datap;
486
487         /* We must own the mbuf chain exclusively to modify it. */
488         m = m_unshare(m, M_NOWAIT);
489         if (m == NULL)
490                 return (ENOMEM);
491
492         /* Initialize */
493         header = d->cc;
494
495         /* Always set the flushed bit in stateless mode */
496         if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
497                 header |= MPPC_FLAG_FLUSHED;
498                 d->flushed = 0;
499         }
500
501         /* Compress packet (if compression enabled) */
502 #ifdef NETGRAPH_MPPC_COMPRESSION
503         if ((d->cfg.bits & MPPC_BIT) != 0) {
504                 u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
505                 u_char *inbuf, *outbuf;
506                 int outlen, inlen, ina;
507                 u_char *source, *dest;
508                 u_long sourceCnt, destCnt;
509                 int rtn;
510
511                 /* Work with contiguous regions of memory. */
512                 inlen = m->m_pkthdr.len;
513                 if (m->m_next == NULL) {
514                         inbuf = mtod(m, u_char *);
515                         ina = 0;
516                 } else {
517                         inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
518                         if (inbuf == NULL)
519                                 goto err1;
520                         m_copydata(m, 0, inlen, (caddr_t)inbuf);
521                         ina = 1;
522                 }
523
524                 outlen = MPPC_MAX_BLOWUP(inlen);
525                 outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
526                 if (outbuf == NULL) {
527                         if (ina)
528                                 free(inbuf, M_NETGRAPH_MPPC);
529 err1:
530                         m_freem(m);
531                         MPPC_InitCompressionHistory(d->history);
532                         d->flushed = 1;
533                         return (ENOMEM);
534                 }
535
536                 /* Prepare to compress */
537                 source = inbuf;
538                 sourceCnt = inlen;
539                 dest = outbuf;
540                 destCnt = outlen;
541                 if ((d->cfg.bits & MPPE_STATELESS) == 0)
542                         flags |= MPPC_SAVE_HISTORY;
543
544                 /* Compress */
545                 rtn = MPPC_Compress(&source, &dest, &sourceCnt,
546                         &destCnt, d->history, flags, 0);
547
548                 /* Check return value */
549                 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
550                 if ((rtn & MPPC_EXPANDED) == 0
551                     && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
552                         outlen -= destCnt;     
553                         header |= MPPC_FLAG_COMPRESSED;
554                         if ((rtn & MPPC_RESTART_HISTORY) != 0)
555                                 header |= MPPC_FLAG_RESTART;  
556                                 
557                         /* Replace m by the compresed one. */
558                         m_copyback(m, 0, outlen, (caddr_t)outbuf);
559                         if (m->m_pkthdr.len < outlen) {
560                                 m_freem(m);
561                                 m = NULL;
562                         } else if (outlen < m->m_pkthdr.len)
563                                 m_adj(m, outlen - m->m_pkthdr.len);
564                 }
565                 d->flushed = (rtn & MPPC_EXPANDED) != 0
566                     || (flags & MPPC_SAVE_HISTORY) == 0;
567
568                 if (ina)
569                         free(inbuf, M_NETGRAPH_MPPC);
570                 free(outbuf, M_NETGRAPH_MPPC);
571
572                 /* Check mbuf chain reload result. */
573                 if (m == NULL) {
574                         if (!d->flushed) {
575                                 MPPC_InitCompressionHistory(d->history);
576                                 d->flushed = 1;
577                         }
578                         return (ENOMEM);
579                 }
580         }
581 #endif
582
583         /* Now encrypt packet (if encryption enabled) */
584 #ifdef NETGRAPH_MPPC_ENCRYPTION
585         if ((d->cfg.bits & MPPE_BITS) != 0) {
586                 struct mbuf *m1;
587
588                 /* Set header bits */
589                 header |= MPPC_FLAG_ENCRYPTED;
590
591                 /* Update key if it's time */
592                 if ((d->cfg.bits & MPPE_STATELESS) != 0
593                     || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
594                         ng_mppc_updatekey(d->cfg.bits,
595                             d->cfg.startkey, d->key, &d->rc4);
596                 } else if ((header & MPPC_FLAG_FLUSHED) != 0) {
597                         /* Need to reset key if we say we did 
598                            and ng_mppc_updatekey wasn't called to do it also. */
599                         rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
600                 }
601
602                 /* Encrypt packet */
603                 m1 = m;
604                 while (m1) {
605                         rc4_crypt(&d->rc4, mtod(m1, u_char *),
606                             mtod(m1, u_char *), m1->m_len);
607                         m1 = m1->m_next;
608                 }
609         }
610 #endif
611
612         /* Update coherency count for next time (12 bit arithmetic) */
613         MPPC_CCOUNT_INC(d->cc);
614
615         /* Install header */
616         M_PREPEND(m, MPPC_HDRLEN, M_NOWAIT);
617         if (m != NULL)
618                 be16enc(mtod(m, void *), header);
619
620         *datap = m;
621         return (*datap == NULL ? ENOBUFS : 0);
622 }
623
624 /*
625  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
626  * The original mbuf is not free'd.
627  */
628 static int
629 ng_mppc_decompress(node_p node, struct mbuf **datap)
630 {
631         const priv_p priv = NG_NODE_PRIVATE(node);
632         struct ng_mppc_dir *const d = &priv->recv;
633         u_int16_t header, cc;
634         u_int numLost;
635         struct mbuf *m = *datap;
636
637         /* We must own the mbuf chain exclusively to modify it. */
638         m = m_unshare(m, M_NOWAIT);
639         if (m == NULL)
640                 return (ENOMEM);
641
642         /* Pull off header */
643         if (m->m_pkthdr.len < MPPC_HDRLEN) {
644                 m_freem(m);
645                 return (EINVAL);
646         }
647         header = be16dec(mtod(m, void *));
648         cc = (header & MPPC_CCOUNT_MASK);
649         m_adj(m, MPPC_HDRLEN);
650
651         /* Check for an unexpected jump in the sequence number */
652         numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
653
654         /* If flushed bit set, we can always handle packet */
655         if ((header & MPPC_FLAG_FLUSHED) != 0) {
656 #ifdef NETGRAPH_MPPC_COMPRESSION
657                 if (d->history != NULL)
658                         MPPC_InitDecompressionHistory(d->history);
659 #endif
660 #ifdef NETGRAPH_MPPC_ENCRYPTION
661                 if ((d->cfg.bits & MPPE_BITS) != 0) {
662                         u_int rekey;
663
664                         /* How many times are we going to have to re-key? */
665                         rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
666                             numLost : (numLost / (MPPE_UPDATE_MASK + 1));
667                         if (rekey > mppe_max_rekey) {
668                             if (mppe_block_on_max_rekey) {
669                                 if (mppe_log_max_rekey) {
670                                     log(LOG_ERR, "%s: too many (%d) packets"
671                                         " dropped, disabling node %p!\n",
672                                         __func__, numLost, node);
673                                 }
674                                 priv->recv.cfg.enable = 0;
675                                 goto failed;
676                             } else {
677                                 if (mppe_log_max_rekey) {
678                                     log(LOG_ERR, "%s: %d packets"
679                                         " dropped, node %p\n",
680                                         __func__, numLost, node);
681                                 }
682                                 goto failed;
683                             }
684                         }
685
686                         /* Re-key as necessary to catch up to peer */
687                         while (d->cc != cc) {
688                                 if ((d->cfg.bits & MPPE_STATELESS) != 0
689                                     || (d->cc & MPPE_UPDATE_MASK)
690                                       == MPPE_UPDATE_FLAG) {
691                                         ng_mppc_updatekey(d->cfg.bits,
692                                             d->cfg.startkey, d->key, &d->rc4);
693                                 }
694                                 MPPC_CCOUNT_INC(d->cc);
695                         }
696
697                         /* Reset key (except in stateless mode, see below) */
698                         if ((d->cfg.bits & MPPE_STATELESS) == 0)
699                                 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
700                 }
701 #endif
702                 d->cc = cc;             /* skip over lost seq numbers */
703                 numLost = 0;            /* act like no packets were lost */
704         }
705
706         /* Can't decode non-sequential packets without a flushed bit */
707         if (numLost != 0)
708                 goto failed;
709
710         /* Decrypt packet */
711         if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
712 #ifdef NETGRAPH_MPPC_ENCRYPTION
713                 struct mbuf *m1;
714 #endif
715
716                 /* Are we not expecting encryption? */
717                 if ((d->cfg.bits & MPPE_BITS) == 0) {
718                         log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
719                                 __func__, "encrypted");
720                         goto failed;
721                 }
722
723 #ifdef NETGRAPH_MPPC_ENCRYPTION
724                 /* Update key if it's time (always in stateless mode) */
725                 if ((d->cfg.bits & MPPE_STATELESS) != 0
726                     || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
727                         ng_mppc_updatekey(d->cfg.bits,
728                             d->cfg.startkey, d->key, &d->rc4);
729                 }
730
731                 /* Decrypt packet */
732                 m1 = m;
733                 while (m1 != NULL) {
734                         rc4_crypt(&d->rc4, mtod(m1, u_char *),
735                             mtod(m1, u_char *), m1->m_len);
736                         m1 = m1->m_next;
737                 }
738 #endif
739         } else {
740
741                 /* Are we expecting encryption? */
742                 if ((d->cfg.bits & MPPE_BITS) != 0) {
743                         log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
744                                 __func__, "unencrypted");
745                         goto failed;
746                 }
747         }
748
749         /* Update coherency count for next time (12 bit arithmetic) */
750         MPPC_CCOUNT_INC(d->cc);
751
752         /* Check for unexpected compressed packet */
753         if ((header & MPPC_FLAG_COMPRESSED) != 0
754             && (d->cfg.bits & MPPC_BIT) == 0) {
755                 log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
756                         __func__, "compressed");
757 failed:
758                 m_freem(m);
759                 return (EINVAL);
760         }
761
762 #ifdef NETGRAPH_MPPC_COMPRESSION
763         /* Decompress packet */
764         if ((header & MPPC_FLAG_COMPRESSED) != 0) {
765                 int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
766                 u_char *inbuf, *outbuf;
767                 int inlen, outlen, ina;
768                 u_char *source, *dest;
769                 u_long sourceCnt, destCnt;
770                 int rtn;
771
772                 /* Copy payload into a contiguous region of memory. */
773                 inlen = m->m_pkthdr.len;
774                 if (m->m_next == NULL) {
775                         inbuf = mtod(m, u_char *);
776                         ina = 0;
777                 } else {
778                         inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
779                         if (inbuf == NULL) {
780                                 m_freem(m);
781                                 return (ENOMEM);
782                         }
783                         m_copydata(m, 0, inlen, (caddr_t)inbuf);
784                         ina = 1;
785                 }
786
787                 /* Allocate a buffer for decompressed data */
788                 outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
789                     M_NETGRAPH_MPPC, M_NOWAIT);
790                 if (outbuf == NULL) {
791                         m_freem(m);
792                         if (ina)
793                                 free(inbuf, M_NETGRAPH_MPPC);
794                         return (ENOMEM);
795                 }
796                 outlen = MPPC_DECOMP_BUFSIZE;
797
798                 /* Prepare to decompress */
799                 source = inbuf;
800                 sourceCnt = inlen;
801                 dest = outbuf;
802                 destCnt = outlen;
803                 if ((header & MPPC_FLAG_RESTART) != 0)
804                         flags |= MPPC_RESTART_HISTORY;
805
806                 /* Decompress */
807                 rtn = MPPC_Decompress(&source, &dest,
808                         &sourceCnt, &destCnt, d->history, flags);
809
810                 /* Check return value */
811                 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
812                 if ((rtn & MPPC_DEST_EXHAUSTED) != 0
813                     || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
814                         log(LOG_ERR, "%s: decomp returned 0x%x",
815                             __func__, rtn);
816                         if (ina)
817                                 free(inbuf, M_NETGRAPH_MPPC);
818                         free(outbuf, M_NETGRAPH_MPPC);
819                         goto failed;
820                 }
821
822                 /* Replace compressed data with decompressed data */
823                 if (ina)
824                         free(inbuf, M_NETGRAPH_MPPC);
825                 outlen -= destCnt;
826         
827                 m_copyback(m, 0, outlen, (caddr_t)outbuf);
828                 if (m->m_pkthdr.len < outlen) {
829                         m_freem(m);
830                         m = NULL;
831                 } else if (outlen < m->m_pkthdr.len)
832                         m_adj(m, outlen - m->m_pkthdr.len);
833                 free(outbuf, M_NETGRAPH_MPPC);
834         }
835 #endif
836
837         /* Return result in an mbuf */
838         *datap = m;
839         return (*datap == NULL ? ENOBUFS : 0);
840 }
841
842 /*
843  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
844  */
845 static void
846 ng_mppc_reset_req(node_p node)
847 {   
848         const priv_p priv = NG_NODE_PRIVATE(node);
849         struct ng_mppc_dir *const d = &priv->xmit;
850
851 #ifdef NETGRAPH_MPPC_COMPRESSION
852         if (d->history != NULL)
853                 MPPC_InitCompressionHistory(d->history);
854 #endif
855 #ifdef NETGRAPH_MPPC_ENCRYPTION
856         if ((d->cfg.bits & MPPE_STATELESS) == 0)
857                 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
858 #endif
859         d->flushed = 1;
860 }   
861
862 #ifdef NETGRAPH_MPPC_ENCRYPTION
863 /*
864  * Generate a new encryption key
865  */
866 static void
867 ng_mppc_getkey(const u_char *h, u_char *h2, int len)
868 {
869         static const u_char pad1[40] =
870             { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
871               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
872               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
873               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
874         static const u_char pad2[40] =
875             { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
876               0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
877               0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
878               0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
879         u_char hash[20];
880         SHA1_CTX c;
881
882         SHA1Init(&c);
883         SHA1Update(&c, h, len);
884         SHA1Update(&c, pad1, sizeof(pad1));
885         SHA1Update(&c, h2, len);
886         SHA1Update(&c, pad2, sizeof(pad2));
887         SHA1Final(hash, &c);
888         bcopy(hash, h2, len);
889 }
890
891 /*
892  * Update the encryption key
893  */
894 static void
895 ng_mppc_updatekey(u_int32_t bits,
896         u_char *key0, u_char *key, struct rc4_state *rc4)
897
898         const int keylen = KEYLEN(bits);
899
900         ng_mppc_getkey(key0, key, keylen);
901         rc4_init(rc4, key, keylen);
902         rc4_crypt(rc4, key, key, keylen);
903         if ((bits & MPPE_40) != 0)
904                 bcopy(&ng_mppe_weakenkey, key, 3);
905         else if ((bits & MPPE_56) != 0)
906                 bcopy(&ng_mppe_weakenkey, key, 1);
907         rc4_init(rc4, key, keylen);
908 }
909 #endif
910