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