xref: /freebsd/sys/netgraph/ng_mppc.c (revision 39228864fd92381b652b08667fe43f0aa03cf969)
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>
5639228864SAlexander Motin #include <sys/endian.h>
57af7ab184SArchie Cobbs #include <sys/errno.h>
58af7ab184SArchie Cobbs #include <sys/syslog.h>
59af7ab184SArchie Cobbs 
60af7ab184SArchie Cobbs #include <netgraph/ng_message.h>
61af7ab184SArchie Cobbs #include <netgraph/netgraph.h>
62af7ab184SArchie Cobbs #include <netgraph/ng_mppc.h>
63af7ab184SArchie Cobbs 
64af7ab184SArchie Cobbs #include "opt_netgraph.h"
65af7ab184SArchie Cobbs 
66af7ab184SArchie Cobbs #if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION)
6710d645b7SYaroslav Tykhiy #ifdef KLD_MODULE
6810d645b7SYaroslav Tykhiy /* XXX NETGRAPH_MPPC_COMPRESSION isn't functional yet */
6910d645b7SYaroslav Tykhiy #define NETGRAPH_MPPC_ENCRYPTION
7010d645b7SYaroslav Tykhiy #else
7110d645b7SYaroslav Tykhiy /* This case is indicative of an error in sys/conf files */
72af7ab184SArchie Cobbs #error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION
73af7ab184SArchie Cobbs #endif
7410d645b7SYaroslav Tykhiy #endif
75af7ab184SArchie Cobbs 
769c8c302fSJulian Elischer #ifdef NG_SEPARATE_MALLOC
779c8c302fSJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node ");
789c8c302fSJulian Elischer #else
799c8c302fSJulian Elischer #define M_NETGRAPH_MPPC M_NETGRAPH
809c8c302fSJulian Elischer #endif
819c8c302fSJulian Elischer 
82af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
83af7ab184SArchie Cobbs /* XXX this file doesn't exist yet, but hopefully someday it will... */
84af7ab184SArchie Cobbs #include <net/mppc.h>
85af7ab184SArchie Cobbs #endif
86af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
87af7ab184SArchie Cobbs #include <crypto/rc4/rc4.h>
88af7ab184SArchie Cobbs #endif
89af7ab184SArchie Cobbs #include <crypto/sha1.h>
90af7ab184SArchie Cobbs 
91af7ab184SArchie Cobbs /* Decompression blowup */
92af7ab184SArchie Cobbs #define MPPC_DECOMP_BUFSIZE	8092            /* allocate buffer this big */
93af7ab184SArchie Cobbs #define MPPC_DECOMP_SAFETY	100             /*   plus this much margin */
94af7ab184SArchie Cobbs 
95af7ab184SArchie Cobbs /* MPPC/MPPE header length */
96af7ab184SArchie Cobbs #define MPPC_HDRLEN		2
97af7ab184SArchie Cobbs 
98af7ab184SArchie Cobbs /* Key length */
99af7ab184SArchie Cobbs #define KEYLEN(b)		(((b) & MPPE_128) ? 16 : 8)
100af7ab184SArchie Cobbs 
101f3059f39SArchie Cobbs /*
102f3059f39SArchie Cobbs  * When packets are lost with MPPE, we may have to re-key arbitrarily
103f3059f39SArchie Cobbs  * many times to 'catch up' to the new jumped-ahead sequence number.
104f3059f39SArchie Cobbs  * Since this can be expensive, we pose a limit on how many re-keyings
105f3059f39SArchie Cobbs  * we will do at one time to avoid a possible D.O.S. vulnerability.
106f3059f39SArchie Cobbs  * This should instead be a configurable parameter.
107f3059f39SArchie Cobbs  */
108f3059f39SArchie Cobbs #define MPPE_MAX_REKEY		1000
109af7ab184SArchie Cobbs 
110af7ab184SArchie Cobbs /* MPPC packet header bits */
111af7ab184SArchie Cobbs #define MPPC_FLAG_FLUSHED	0x8000		/* xmitter reset state */
112af7ab184SArchie Cobbs #define MPPC_FLAG_RESTART	0x4000		/* compress history restart */
113af7ab184SArchie Cobbs #define MPPC_FLAG_COMPRESSED	0x2000		/* packet is compresed */
114af7ab184SArchie Cobbs #define MPPC_FLAG_ENCRYPTED	0x1000		/* packet is encrypted */
115af7ab184SArchie Cobbs #define MPPC_CCOUNT_MASK	0x0fff		/* sequence number mask */
116af7ab184SArchie Cobbs 
117755bc287SAlexander Motin #define MPPC_CCOUNT_INC(d)	((d) = (((d) + 1) & MPPC_CCOUNT_MASK))
118755bc287SAlexander Motin 
119af7ab184SArchie Cobbs #define MPPE_UPDATE_MASK	0xff		/* coherency count when we're */
120af7ab184SArchie Cobbs #define MPPE_UPDATE_FLAG	0xff		/*   supposed to update key */
121af7ab184SArchie Cobbs 
122af7ab184SArchie Cobbs #define MPPC_COMP_OK		0x05
123af7ab184SArchie Cobbs #define MPPC_DECOMP_OK		0x05
124af7ab184SArchie Cobbs 
125af7ab184SArchie Cobbs /* Per direction info */
126af7ab184SArchie Cobbs struct ng_mppc_dir {
127af7ab184SArchie Cobbs 	struct ng_mppc_config	cfg;		/* configuration */
128af7ab184SArchie Cobbs 	hook_p			hook;		/* netgraph hook */
129af7ab184SArchie Cobbs 	u_int16_t		cc:12;		/* coherency count */
130af7ab184SArchie Cobbs 	u_char			flushed;	/* clean history (xmit only) */
131af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
132af7ab184SArchie Cobbs 	u_char			*history;	/* compression history */
133af7ab184SArchie Cobbs #endif
134af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
135af7ab184SArchie Cobbs 	u_char			key[MPPE_KEY_LEN];	/* session key */
136af7ab184SArchie Cobbs 	struct rc4_state	rc4;			/* rc4 state */
137af7ab184SArchie Cobbs #endif
138af7ab184SArchie Cobbs };
139af7ab184SArchie Cobbs 
140af7ab184SArchie Cobbs /* Node private data */
141af7ab184SArchie Cobbs struct ng_mppc_private {
142af7ab184SArchie Cobbs 	struct ng_mppc_dir	xmit;		/* compress/encrypt config */
143af7ab184SArchie Cobbs 	struct ng_mppc_dir	recv;		/* decompress/decrypt config */
144069154d5SJulian Elischer 	ng_ID_t			ctrlnode;	/* path to controlling node */
145af7ab184SArchie Cobbs };
146af7ab184SArchie Cobbs typedef struct ng_mppc_private *priv_p;
147af7ab184SArchie Cobbs 
148af7ab184SArchie Cobbs /* Netgraph node methods */
149af7ab184SArchie Cobbs static ng_constructor_t	ng_mppc_constructor;
150af7ab184SArchie Cobbs static ng_rcvmsg_t	ng_mppc_rcvmsg;
151069154d5SJulian Elischer static ng_shutdown_t	ng_mppc_shutdown;
152af7ab184SArchie Cobbs static ng_newhook_t	ng_mppc_newhook;
153af7ab184SArchie Cobbs static ng_rcvdata_t	ng_mppc_rcvdata;
154af7ab184SArchie Cobbs static ng_disconnect_t	ng_mppc_disconnect;
155af7ab184SArchie Cobbs 
156af7ab184SArchie Cobbs /* Helper functions */
157af7ab184SArchie Cobbs static int	ng_mppc_compress(node_p node,
158ce52e8f4SAlexander Motin 			struct mbuf **datap);
159af7ab184SArchie Cobbs static int	ng_mppc_decompress(node_p node,
160ce52e8f4SAlexander Motin 			struct mbuf **datap);
16106c51e6eSAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
162af7ab184SArchie Cobbs static void	ng_mppc_getkey(const u_char *h, u_char *h2, int len);
163af7ab184SArchie Cobbs static void	ng_mppc_updatekey(u_int32_t bits,
164af7ab184SArchie Cobbs 			u_char *key0, u_char *key, struct rc4_state *rc4);
16506c51e6eSAlexander Motin #endif
166af7ab184SArchie Cobbs static void	ng_mppc_reset_req(node_p node);
167af7ab184SArchie Cobbs 
168af7ab184SArchie Cobbs /* Node type descriptor */
169af7ab184SArchie Cobbs static struct ng_type ng_mppc_typestruct = {
170f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
171f8aae777SJulian Elischer 	.name =		NG_MPPC_NODE_TYPE,
172f8aae777SJulian Elischer 	.constructor =	ng_mppc_constructor,
173f8aae777SJulian Elischer 	.rcvmsg =	ng_mppc_rcvmsg,
174f8aae777SJulian Elischer 	.shutdown =	ng_mppc_shutdown,
175f8aae777SJulian Elischer 	.newhook =	ng_mppc_newhook,
176f8aae777SJulian Elischer 	.rcvdata =	ng_mppc_rcvdata,
177f8aae777SJulian Elischer 	.disconnect =	ng_mppc_disconnect,
178af7ab184SArchie Cobbs };
179af7ab184SArchie Cobbs NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
180af7ab184SArchie Cobbs 
181233896e9SDoug Ambrisko #ifdef NETGRAPH_MPPC_ENCRYPTION
182233896e9SDoug Ambrisko /* Depend on separate rc4 module */
183233896e9SDoug Ambrisko MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
184233896e9SDoug Ambrisko #endif
185233896e9SDoug Ambrisko 
18634fd2381SArchie Cobbs /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
187af7ab184SArchie Cobbs static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
188af7ab184SArchie Cobbs 
189af7ab184SArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
190af7ab184SArchie Cobbs 
191af7ab184SArchie Cobbs /************************************************************************
192af7ab184SArchie Cobbs 			NETGRAPH NODE STUFF
193af7ab184SArchie Cobbs  ************************************************************************/
194af7ab184SArchie Cobbs 
195af7ab184SArchie Cobbs /*
196af7ab184SArchie Cobbs  * Node type constructor
197af7ab184SArchie Cobbs  */
198af7ab184SArchie Cobbs static int
199069154d5SJulian Elischer ng_mppc_constructor(node_p node)
200af7ab184SArchie Cobbs {
201af7ab184SArchie Cobbs 	priv_p priv;
202af7ab184SArchie Cobbs 
203af7ab184SArchie Cobbs 	/* Allocate private structure */
2041ede983cSDag-Erling Smørgrav 	priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_NOWAIT | M_ZERO);
205af7ab184SArchie Cobbs 	if (priv == NULL)
206af7ab184SArchie Cobbs 		return (ENOMEM);
207af7ab184SArchie Cobbs 
20830400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
209af7ab184SArchie Cobbs 
2102f07580bSGleb Smirnoff 	/* This node is not thread safe. */
2112f07580bSGleb Smirnoff 	NG_NODE_FORCE_WRITER(node);
2122f07580bSGleb Smirnoff 
213af7ab184SArchie Cobbs 	/* Done */
214af7ab184SArchie Cobbs 	return (0);
215af7ab184SArchie Cobbs }
216af7ab184SArchie Cobbs 
217af7ab184SArchie Cobbs /*
218af7ab184SArchie Cobbs  * Give our OK for a hook to be added
219af7ab184SArchie Cobbs  */
220af7ab184SArchie Cobbs static int
221af7ab184SArchie Cobbs ng_mppc_newhook(node_p node, hook_p hook, const char *name)
222af7ab184SArchie Cobbs {
22330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
224af7ab184SArchie Cobbs 	hook_p *hookPtr;
225af7ab184SArchie Cobbs 
226af7ab184SArchie Cobbs 	/* Check hook name */
227af7ab184SArchie Cobbs 	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
228af7ab184SArchie Cobbs 		hookPtr = &priv->xmit.hook;
229af7ab184SArchie Cobbs 	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
230af7ab184SArchie Cobbs 		hookPtr = &priv->recv.hook;
231af7ab184SArchie Cobbs 	else
232af7ab184SArchie Cobbs 		return (EINVAL);
233af7ab184SArchie Cobbs 
234af7ab184SArchie Cobbs 	/* See if already connected */
235af7ab184SArchie Cobbs 	if (*hookPtr != NULL)
236af7ab184SArchie Cobbs 		return (EISCONN);
237af7ab184SArchie Cobbs 
238af7ab184SArchie Cobbs 	/* OK */
239af7ab184SArchie Cobbs 	*hookPtr = hook;
240af7ab184SArchie Cobbs 	return (0);
241af7ab184SArchie Cobbs }
242af7ab184SArchie Cobbs 
243af7ab184SArchie Cobbs /*
244af7ab184SArchie Cobbs  * Receive a control message
245af7ab184SArchie Cobbs  */
246af7ab184SArchie Cobbs static int
247069154d5SJulian Elischer ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
248af7ab184SArchie Cobbs {
24930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
250af7ab184SArchie Cobbs 	struct ng_mesg *resp = NULL;
251af7ab184SArchie Cobbs 	int error = 0;
252069154d5SJulian Elischer 	struct ng_mesg *msg;
253af7ab184SArchie Cobbs 
254069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
255af7ab184SArchie Cobbs 	switch (msg->header.typecookie) {
256af7ab184SArchie Cobbs 	case NGM_MPPC_COOKIE:
257af7ab184SArchie Cobbs 		switch (msg->header.cmd) {
258af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_COMP:
259af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_DECOMP:
260af7ab184SArchie Cobbs 		    {
261af7ab184SArchie Cobbs 			struct ng_mppc_config *const cfg
262af7ab184SArchie Cobbs 			    = (struct ng_mppc_config *)msg->data;
263af7ab184SArchie Cobbs 			const int isComp =
264af7ab184SArchie Cobbs 			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
265af7ab184SArchie Cobbs 			struct ng_mppc_dir *const d = isComp ?
266af7ab184SArchie Cobbs 			    &priv->xmit : &priv->recv;
267af7ab184SArchie Cobbs 
268af7ab184SArchie Cobbs 			/* Check configuration */
269af7ab184SArchie Cobbs 			if (msg->header.arglen != sizeof(*cfg))
270af7ab184SArchie Cobbs 				ERROUT(EINVAL);
271af7ab184SArchie Cobbs 			if (cfg->enable) {
272af7ab184SArchie Cobbs 				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
273af7ab184SArchie Cobbs 					ERROUT(EINVAL);
274af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_COMPRESSION
275af7ab184SArchie Cobbs 				if ((cfg->bits & MPPC_BIT) != 0)
276af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
277af7ab184SArchie Cobbs #endif
278af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_ENCRYPTION
279af7ab184SArchie Cobbs 				if ((cfg->bits & MPPE_BITS) != 0)
280af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
281af7ab184SArchie Cobbs #endif
282af7ab184SArchie Cobbs 			} else
283af7ab184SArchie Cobbs 				cfg->bits = 0;
284af7ab184SArchie Cobbs 
285af7ab184SArchie Cobbs 			/* Save return address so we can send reset-req's */
286f3059f39SArchie Cobbs 			if (!isComp)
287069154d5SJulian Elischer 				priv->ctrlnode = NGI_RETADDR(item);
288af7ab184SArchie Cobbs 
289af7ab184SArchie Cobbs 			/* Configuration is OK, reset to it */
290af7ab184SArchie Cobbs 			d->cfg = *cfg;
291af7ab184SArchie Cobbs 
292af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
293af7ab184SArchie Cobbs 			/* Initialize state buffers for compression */
294af7ab184SArchie Cobbs 			if (d->history != NULL) {
2951ede983cSDag-Erling Smørgrav 				free(d->history, M_NETGRAPH_MPPC);
296af7ab184SArchie Cobbs 				d->history = NULL;
297af7ab184SArchie Cobbs 			}
298af7ab184SArchie Cobbs 			if ((cfg->bits & MPPC_BIT) != 0) {
299e11e3f18SDag-Erling Smørgrav 				d->history = malloc(isComp ?
300e11e3f18SDag-Erling Smørgrav 				    MPPC_SizeOfCompressionHistory() :
301af7ab184SArchie Cobbs 				    MPPC_SizeOfDecompressionHistory(),
3029c8c302fSJulian Elischer 				    M_NETGRAPH_MPPC, M_NOWAIT);
303af7ab184SArchie Cobbs 				if (d->history == NULL)
304af7ab184SArchie Cobbs 					ERROUT(ENOMEM);
305af7ab184SArchie Cobbs 				if (isComp)
306af7ab184SArchie Cobbs 					MPPC_InitCompressionHistory(d->history);
307af7ab184SArchie Cobbs 				else {
308af7ab184SArchie Cobbs 					MPPC_InitDecompressionHistory(
309af7ab184SArchie Cobbs 					    d->history);
310af7ab184SArchie Cobbs 				}
311af7ab184SArchie Cobbs 			}
312af7ab184SArchie Cobbs #endif
313af7ab184SArchie Cobbs 
314af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
315af7ab184SArchie Cobbs 			/* Generate initial session keys for encryption */
316af7ab184SArchie Cobbs 			if ((cfg->bits & MPPE_BITS) != 0) {
317af7ab184SArchie Cobbs 				const int keylen = KEYLEN(cfg->bits);
318af7ab184SArchie Cobbs 
319af7ab184SArchie Cobbs 				bcopy(cfg->startkey, d->key, keylen);
320af7ab184SArchie Cobbs 				ng_mppc_getkey(cfg->startkey, d->key, keylen);
32134fd2381SArchie Cobbs 				if ((cfg->bits & MPPE_40) != 0)
32234fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 3);
32334fd2381SArchie Cobbs 				else if ((cfg->bits & MPPE_56) != 0)
32434fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 1);
325af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, keylen);
326af7ab184SArchie Cobbs 			}
327af7ab184SArchie Cobbs #endif
328af7ab184SArchie Cobbs 
329af7ab184SArchie Cobbs 			/* Initialize other state */
330af7ab184SArchie Cobbs 			d->cc = 0;
331af7ab184SArchie Cobbs 			d->flushed = 0;
332af7ab184SArchie Cobbs 			break;
333af7ab184SArchie Cobbs 		    }
334af7ab184SArchie Cobbs 
335af7ab184SArchie Cobbs 		case NGM_MPPC_RESETREQ:
336af7ab184SArchie Cobbs 			ng_mppc_reset_req(node);
337af7ab184SArchie Cobbs 			break;
338af7ab184SArchie Cobbs 
339af7ab184SArchie Cobbs 		default:
340af7ab184SArchie Cobbs 			error = EINVAL;
341af7ab184SArchie Cobbs 			break;
342af7ab184SArchie Cobbs 		}
343af7ab184SArchie Cobbs 		break;
344af7ab184SArchie Cobbs 	default:
345af7ab184SArchie Cobbs 		error = EINVAL;
346af7ab184SArchie Cobbs 		break;
347af7ab184SArchie Cobbs 	}
348af7ab184SArchie Cobbs done:
349069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
350069154d5SJulian Elischer 	NG_FREE_MSG(msg);
351af7ab184SArchie Cobbs 	return (error);
352af7ab184SArchie Cobbs }
353af7ab184SArchie Cobbs 
354af7ab184SArchie Cobbs /*
355af7ab184SArchie Cobbs  * Receive incoming data on our hook.
356af7ab184SArchie Cobbs  */
357af7ab184SArchie Cobbs static int
358069154d5SJulian Elischer ng_mppc_rcvdata(hook_p hook, item_p item)
359af7ab184SArchie Cobbs {
36030400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
36130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
362af7ab184SArchie Cobbs 	int error;
363069154d5SJulian Elischer 	struct mbuf *m;
364af7ab184SArchie Cobbs 
365069154d5SJulian Elischer 	NGI_GET_M(item, m);
366af7ab184SArchie Cobbs 	/* Compress and/or encrypt */
367af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook) {
368af7ab184SArchie Cobbs 		if (!priv->xmit.cfg.enable) {
369069154d5SJulian Elischer 			NG_FREE_M(m);
370069154d5SJulian Elischer 			NG_FREE_ITEM(item);
371af7ab184SArchie Cobbs 			return (ENXIO);
372af7ab184SArchie Cobbs 		}
373ce52e8f4SAlexander Motin 		if ((error = ng_mppc_compress(node, &m)) != 0) {
374069154d5SJulian Elischer 			NG_FREE_ITEM(item);
375af7ab184SArchie Cobbs 			return(error);
376af7ab184SArchie Cobbs 		}
377ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
378af7ab184SArchie Cobbs 		return (error);
379af7ab184SArchie Cobbs 	}
380af7ab184SArchie Cobbs 
381af7ab184SArchie Cobbs 	/* Decompress and/or decrypt */
382af7ab184SArchie Cobbs 	if (hook == priv->recv.hook) {
383af7ab184SArchie Cobbs 		if (!priv->recv.cfg.enable) {
384069154d5SJulian Elischer 			NG_FREE_M(m);
385069154d5SJulian Elischer 			NG_FREE_ITEM(item);
386af7ab184SArchie Cobbs 			return (ENXIO);
387af7ab184SArchie Cobbs 		}
388ce52e8f4SAlexander Motin 		if ((error = ng_mppc_decompress(node, &m)) != 0) {
389069154d5SJulian Elischer 			NG_FREE_ITEM(item);
390facfd889SArchie Cobbs 			if (error == EINVAL && priv->ctrlnode != 0) {
391af7ab184SArchie Cobbs 				struct ng_mesg *msg;
392af7ab184SArchie Cobbs 
393af7ab184SArchie Cobbs 				/* Need to send a reset-request */
394af7ab184SArchie Cobbs 				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
395af7ab184SArchie Cobbs 				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
396af7ab184SArchie Cobbs 				if (msg == NULL)
397af7ab184SArchie Cobbs 					return (error);
398069154d5SJulian Elischer 				NG_SEND_MSG_ID(error, node, msg,
399facfd889SArchie Cobbs 					priv->ctrlnode, 0);
400af7ab184SArchie Cobbs 			}
401af7ab184SArchie Cobbs 			return (error);
402af7ab184SArchie Cobbs 		}
403ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
404af7ab184SArchie Cobbs 		return (error);
405af7ab184SArchie Cobbs 	}
406af7ab184SArchie Cobbs 
407af7ab184SArchie Cobbs 	/* Oops */
4086e551fb6SDavid E. O'Brien 	panic("%s: unknown hook", __func__);
409b40ce416SJulian Elischer #ifdef RESTARTABLE_PANICS
410b40ce416SJulian Elischer 	return (EINVAL);
411b40ce416SJulian Elischer #endif
412af7ab184SArchie Cobbs }
413af7ab184SArchie Cobbs 
414af7ab184SArchie Cobbs /*
415af7ab184SArchie Cobbs  * Destroy node
416af7ab184SArchie Cobbs  */
417af7ab184SArchie Cobbs static int
418069154d5SJulian Elischer ng_mppc_shutdown(node_p node)
419af7ab184SArchie Cobbs {
42030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
421af7ab184SArchie Cobbs 
422af7ab184SArchie Cobbs 	/* Take down netgraph node */
423af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
424af7ab184SArchie Cobbs 	if (priv->xmit.history != NULL)
4251ede983cSDag-Erling Smørgrav 		free(priv->xmit.history, M_NETGRAPH_MPPC);
426af7ab184SArchie Cobbs 	if (priv->recv.history != NULL)
4271ede983cSDag-Erling Smørgrav 		free(priv->recv.history, M_NETGRAPH_MPPC);
428af7ab184SArchie Cobbs #endif
429af7ab184SArchie Cobbs 	bzero(priv, sizeof(*priv));
4301ede983cSDag-Erling Smørgrav 	free(priv, M_NETGRAPH_MPPC);
43130400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
43230400f03SJulian Elischer 	NG_NODE_UNREF(node);		/* let the node escape */
433af7ab184SArchie Cobbs 	return (0);
434af7ab184SArchie Cobbs }
435af7ab184SArchie Cobbs 
436af7ab184SArchie Cobbs /*
437af7ab184SArchie Cobbs  * Hook disconnection
438af7ab184SArchie Cobbs  */
439af7ab184SArchie Cobbs static int
440af7ab184SArchie Cobbs ng_mppc_disconnect(hook_p hook)
441af7ab184SArchie Cobbs {
44230400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
44330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
444af7ab184SArchie Cobbs 
445af7ab184SArchie Cobbs 	/* Zero out hook pointer */
446af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook)
447af7ab184SArchie Cobbs 		priv->xmit.hook = NULL;
448af7ab184SArchie Cobbs 	if (hook == priv->recv.hook)
449af7ab184SArchie Cobbs 		priv->recv.hook = NULL;
450af7ab184SArchie Cobbs 
451af7ab184SArchie Cobbs 	/* Go away if no longer connected */
45230400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(node) == 0)
45330400f03SJulian Elischer 	&& NG_NODE_IS_VALID(node))
454069154d5SJulian Elischer 		ng_rmnode_self(node);
455af7ab184SArchie Cobbs 	return (0);
456af7ab184SArchie Cobbs }
457af7ab184SArchie Cobbs 
458af7ab184SArchie Cobbs /************************************************************************
459af7ab184SArchie Cobbs 			HELPER STUFF
460af7ab184SArchie Cobbs  ************************************************************************/
461af7ab184SArchie Cobbs 
462af7ab184SArchie Cobbs /*
463af7ab184SArchie Cobbs  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
464af7ab184SArchie Cobbs  * The original mbuf is not free'd.
465af7ab184SArchie Cobbs  */
466af7ab184SArchie Cobbs static int
467ce52e8f4SAlexander Motin ng_mppc_compress(node_p node, struct mbuf **datap)
468af7ab184SArchie Cobbs {
46930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
470af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
471af7ab184SArchie Cobbs 	u_int16_t header;
472ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
473af7ab184SArchie Cobbs 
474e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
475e4651e05SAlexander Motin 	m = m_unshare(m, M_DONTWAIT);
476e4651e05SAlexander Motin 	if (m == NULL)
477e4651e05SAlexander Motin 		return (ENOMEM);
478e4651e05SAlexander Motin 
479af7ab184SArchie Cobbs 	/* Initialize */
480af7ab184SArchie Cobbs 	header = d->cc;
481adecf751SAlexander Motin 
482adecf751SAlexander Motin 	/* Always set the flushed bit in stateless mode */
483adecf751SAlexander Motin 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
484af7ab184SArchie Cobbs 		header |= MPPC_FLAG_FLUSHED;
485af7ab184SArchie Cobbs 		d->flushed = 0;
486af7ab184SArchie Cobbs 	}
487af7ab184SArchie Cobbs 
488ce52e8f4SAlexander Motin 	/* Compress packet (if compression enabled) */
489af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
490af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPC_BIT) != 0) {
491af7ab184SArchie Cobbs 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
492ce52e8f4SAlexander Motin 		u_char *inbuf, *outbuf;
49367898a23SAlexander Motin 		int outlen, inlen, ina;
494af7ab184SArchie Cobbs 		u_char *source, *dest;
495af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
496af7ab184SArchie Cobbs 		int rtn;
497af7ab184SArchie Cobbs 
498ce52e8f4SAlexander Motin 		/* Work with contiguous regions of memory. */
499ce52e8f4SAlexander Motin 		inlen = m->m_pkthdr.len;
50067898a23SAlexander Motin 		if (m->m_next == NULL) {
50167898a23SAlexander Motin 			inbuf = mtod(m, u_char *);
50267898a23SAlexander Motin 			ina = 0;
50367898a23SAlexander Motin 		} else {
504ce52e8f4SAlexander Motin 			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
50511d1cadeSAlexander Motin 			if (inbuf == NULL)
50611d1cadeSAlexander Motin 				goto err1;
507ce52e8f4SAlexander Motin 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
50867898a23SAlexander Motin 			ina = 1;
50967898a23SAlexander Motin 		}
510ce52e8f4SAlexander Motin 
511ce52e8f4SAlexander Motin 		outlen = MPPC_MAX_BLOWUP(inlen);
512ce52e8f4SAlexander Motin 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
513ce52e8f4SAlexander Motin 		if (outbuf == NULL) {
51467898a23SAlexander Motin 			if (ina)
515ce52e8f4SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
51611d1cadeSAlexander Motin err1:
51711d1cadeSAlexander Motin 			m_freem(m);
51811d1cadeSAlexander Motin 			MPPC_InitCompressionHistory(d->history);
51911d1cadeSAlexander Motin 			d->flushed = 1;
520ce52e8f4SAlexander Motin 			return (ENOMEM);
521ce52e8f4SAlexander Motin 		}
522ce52e8f4SAlexander Motin 
523af7ab184SArchie Cobbs 		/* Prepare to compress */
524af7ab184SArchie Cobbs 		source = inbuf;
525af7ab184SArchie Cobbs 		sourceCnt = inlen;
526ce52e8f4SAlexander Motin 		dest = outbuf;
527ce52e8f4SAlexander Motin 		destCnt = outlen;
528af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
529af7ab184SArchie Cobbs 			flags |= MPPC_SAVE_HISTORY;
530af7ab184SArchie Cobbs 
531af7ab184SArchie Cobbs 		/* Compress */
532af7ab184SArchie Cobbs 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
533af7ab184SArchie Cobbs 			&destCnt, d->history, flags, 0);
534af7ab184SArchie Cobbs 
535af7ab184SArchie Cobbs 		/* Check return value */
5366e551fb6SDavid E. O'Brien 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
537af7ab184SArchie Cobbs 		if ((rtn & MPPC_EXPANDED) == 0
538af7ab184SArchie Cobbs 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
539af7ab184SArchie Cobbs 			outlen -= destCnt;
540af7ab184SArchie Cobbs 			header |= MPPC_FLAG_COMPRESSED;
541af7ab184SArchie Cobbs 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
542af7ab184SArchie Cobbs 				header |= MPPC_FLAG_RESTART;
543ce52e8f4SAlexander Motin 
544ce52e8f4SAlexander Motin 			/* Replace m by the compresed one. */
545e4651e05SAlexander Motin 			m_copyback(m, 0, outlen, (caddr_t)outbuf);
546e4651e05SAlexander Motin 			if (m->m_pkthdr.len < outlen) {
547ce52e8f4SAlexander Motin 				m_freem(m);
548e4651e05SAlexander Motin 				m = NULL;
549e4651e05SAlexander Motin 			} else if (outlen < m->m_pkthdr.len)
550e4651e05SAlexander Motin 				m_adj(m, outlen - m->m_pkthdr.len);
551af7ab184SArchie Cobbs 		}
552af7ab184SArchie Cobbs 		d->flushed = (rtn & MPPC_EXPANDED) != 0
553af7ab184SArchie Cobbs 		    || (flags & MPPC_SAVE_HISTORY) == 0;
554ce52e8f4SAlexander Motin 
55567898a23SAlexander Motin 		if (ina)
556ce52e8f4SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
557ce52e8f4SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
558ce52e8f4SAlexander Motin 
559e4651e05SAlexander Motin 		/* Check mbuf chain reload result. */
56011d1cadeSAlexander Motin 		if (m == NULL) {
56111d1cadeSAlexander Motin 			if (!d->flushed) {
56211d1cadeSAlexander Motin 				MPPC_InitCompressionHistory(d->history);
56311d1cadeSAlexander Motin 				d->flushed = 1;
56411d1cadeSAlexander Motin 			}
565ce52e8f4SAlexander Motin 			return (ENOMEM);
566af7ab184SArchie Cobbs 		}
56711d1cadeSAlexander Motin 	}
568af7ab184SArchie Cobbs #endif
569af7ab184SArchie Cobbs 
570af7ab184SArchie Cobbs 	/* Now encrypt packet (if encryption enabled) */
571af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
572af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_BITS) != 0) {
573ce52e8f4SAlexander Motin 		struct mbuf *m1;
574af7ab184SArchie Cobbs 
5756370fd6bSAlexander Motin 		/* Set header bits */
576af7ab184SArchie Cobbs 		header |= MPPC_FLAG_ENCRYPTED;
577af7ab184SArchie Cobbs 
578af7ab184SArchie Cobbs 		/* Update key if it's time */
579af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
580af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
581af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
582af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
5836370fd6bSAlexander Motin 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
5846370fd6bSAlexander Motin 			/* Need to reset key if we say we did
5856370fd6bSAlexander Motin 			   and ng_mppc_updatekey wasn't called to do it also. */
5866370fd6bSAlexander Motin 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
587af7ab184SArchie Cobbs 		}
588af7ab184SArchie Cobbs 
589af7ab184SArchie Cobbs 		/* Encrypt packet */
590ce52e8f4SAlexander Motin 		m1 = m;
591ce52e8f4SAlexander Motin 		while (m1) {
592ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
593ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
594ce52e8f4SAlexander Motin 			m1 = m1->m_next;
595ce52e8f4SAlexander Motin 		}
596af7ab184SArchie Cobbs 	}
597af7ab184SArchie Cobbs #endif
598af7ab184SArchie Cobbs 
599755bc287SAlexander Motin 	/* Update coherency count for next time (12 bit arithmetic) */
600755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
601af7ab184SArchie Cobbs 
602af7ab184SArchie Cobbs 	/* Install header */
603ce52e8f4SAlexander Motin 	M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT);
604ce52e8f4SAlexander Motin 	if (m != NULL)
60539228864SAlexander Motin 		be16enc(mtod(m, void *), header);
606af7ab184SArchie Cobbs 
607ce52e8f4SAlexander Motin 	*datap = m;
608ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
609af7ab184SArchie Cobbs }
610af7ab184SArchie Cobbs 
611af7ab184SArchie Cobbs /*
612af7ab184SArchie Cobbs  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
613af7ab184SArchie Cobbs  * The original mbuf is not free'd.
614af7ab184SArchie Cobbs  */
615af7ab184SArchie Cobbs static int
616ce52e8f4SAlexander Motin ng_mppc_decompress(node_p node, struct mbuf **datap)
617af7ab184SArchie Cobbs {
61830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
619af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->recv;
620f3059f39SArchie Cobbs 	u_int16_t header, cc;
621f3059f39SArchie Cobbs 	u_int numLost;
622ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
623af7ab184SArchie Cobbs 
624e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
625e4651e05SAlexander Motin 	m = m_unshare(m, M_DONTWAIT);
626e4651e05SAlexander Motin 	if (m == NULL)
627e4651e05SAlexander Motin 		return (ENOMEM);
628e4651e05SAlexander Motin 
629af7ab184SArchie Cobbs 	/* Pull off header */
630ce52e8f4SAlexander Motin 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
631ce52e8f4SAlexander Motin 		m_freem(m);
632af7ab184SArchie Cobbs 		return (EINVAL);
633ce52e8f4SAlexander Motin 	}
63439228864SAlexander Motin 	header = be16dec(mtod(m, void *));
635af7ab184SArchie Cobbs 	cc = (header & MPPC_CCOUNT_MASK);
636ce52e8f4SAlexander Motin 	m_adj(m, MPPC_HDRLEN);
637af7ab184SArchie Cobbs 
638f3059f39SArchie Cobbs 	/* Check for an unexpected jump in the sequence number */
639af7ab184SArchie Cobbs 	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
640af7ab184SArchie Cobbs 
641af7ab184SArchie Cobbs 	/* If flushed bit set, we can always handle packet */
642af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_FLUSHED) != 0) {
643af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
644af7ab184SArchie Cobbs 		if (d->history != NULL)
645af7ab184SArchie Cobbs 			MPPC_InitDecompressionHistory(d->history);
646af7ab184SArchie Cobbs #endif
647af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
648af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
649f3059f39SArchie Cobbs 			u_int rekey;
650af7ab184SArchie Cobbs 
651f3059f39SArchie Cobbs 			/* How many times are we going to have to re-key? */
652f3059f39SArchie Cobbs 			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
653f3059f39SArchie Cobbs 			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
654f3059f39SArchie Cobbs 			if (rekey > MPPE_MAX_REKEY) {
655f3059f39SArchie Cobbs 				log(LOG_ERR, "%s: too many (%d) packets"
656f3059f39SArchie Cobbs 				    " dropped, disabling node %p!",
657f3059f39SArchie Cobbs 				    __func__, numLost, node);
658f3059f39SArchie Cobbs 				priv->recv.cfg.enable = 0;
659f3059f39SArchie Cobbs 				goto failed;
660f3059f39SArchie Cobbs 			}
661f3059f39SArchie Cobbs 
662f3059f39SArchie Cobbs 			/* Re-key as necessary to catch up to peer */
663af7ab184SArchie Cobbs 			while (d->cc != cc) {
664f3059f39SArchie Cobbs 				if ((d->cfg.bits & MPPE_STATELESS) != 0
665af7ab184SArchie Cobbs 				    || (d->cc & MPPE_UPDATE_MASK)
666af7ab184SArchie Cobbs 				      == MPPE_UPDATE_FLAG) {
667af7ab184SArchie Cobbs 					ng_mppc_updatekey(d->cfg.bits,
668af7ab184SArchie Cobbs 					    d->cfg.startkey, d->key, &d->rc4);
669af7ab184SArchie Cobbs 				}
670755bc287SAlexander Motin 				MPPC_CCOUNT_INC(d->cc);
671af7ab184SArchie Cobbs 			}
672af7ab184SArchie Cobbs 
673af7ab184SArchie Cobbs 			/* Reset key (except in stateless mode, see below) */
674af7ab184SArchie Cobbs 			if ((d->cfg.bits & MPPE_STATELESS) == 0)
675af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
676af7ab184SArchie Cobbs 		}
677af7ab184SArchie Cobbs #endif
678af7ab184SArchie Cobbs 		d->cc = cc;		/* skip over lost seq numbers */
679af7ab184SArchie Cobbs 		numLost = 0;		/* act like no packets were lost */
680af7ab184SArchie Cobbs 	}
681af7ab184SArchie Cobbs 
682af7ab184SArchie Cobbs 	/* Can't decode non-sequential packets without a flushed bit */
683af7ab184SArchie Cobbs 	if (numLost != 0)
684af7ab184SArchie Cobbs 		goto failed;
685af7ab184SArchie Cobbs 
686af7ab184SArchie Cobbs 	/* Decrypt packet */
687af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
688ce52e8f4SAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
689ce52e8f4SAlexander Motin 		struct mbuf *m1;
690ce52e8f4SAlexander Motin #endif
691af7ab184SArchie Cobbs 
692af7ab184SArchie Cobbs 		/* Are we not expecting encryption? */
693af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) == 0) {
694af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
6956e551fb6SDavid E. O'Brien 				__func__, "encrypted");
696af7ab184SArchie Cobbs 			goto failed;
697af7ab184SArchie Cobbs 		}
698af7ab184SArchie Cobbs 
699af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
700af7ab184SArchie Cobbs 		/* Update key if it's time (always in stateless mode) */
701af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
702af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
703af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
704af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
705af7ab184SArchie Cobbs 		}
706af7ab184SArchie Cobbs 
707af7ab184SArchie Cobbs 		/* Decrypt packet */
708ce52e8f4SAlexander Motin 		m1 = m;
709ce52e8f4SAlexander Motin 		while (m1 != NULL) {
710ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
711ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
712ce52e8f4SAlexander Motin 			m1 = m1->m_next;
713ce52e8f4SAlexander Motin 		}
714af7ab184SArchie Cobbs #endif
715af7ab184SArchie Cobbs 	} else {
716af7ab184SArchie Cobbs 
717af7ab184SArchie Cobbs 		/* Are we expecting encryption? */
718af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
719af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7206e551fb6SDavid E. O'Brien 				__func__, "unencrypted");
721af7ab184SArchie Cobbs 			goto failed;
722af7ab184SArchie Cobbs 		}
723af7ab184SArchie Cobbs 	}
724af7ab184SArchie Cobbs 
725af7ab184SArchie Cobbs 	/* Update coherency count for next time (12 bit arithmetic) */
726755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
727af7ab184SArchie Cobbs 
728af7ab184SArchie Cobbs 	/* Check for unexpected compressed packet */
729af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0
730af7ab184SArchie Cobbs 	    && (d->cfg.bits & MPPC_BIT) == 0) {
731af7ab184SArchie Cobbs 		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7326e551fb6SDavid E. O'Brien 			__func__, "compressed");
733af7ab184SArchie Cobbs failed:
734ce52e8f4SAlexander Motin 		m_freem(m);
735af7ab184SArchie Cobbs 		return (EINVAL);
736af7ab184SArchie Cobbs 	}
737af7ab184SArchie Cobbs 
738af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
739af7ab184SArchie Cobbs 	/* Decompress packet */
740af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
741af7ab184SArchie Cobbs 		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
74267898a23SAlexander Motin 		u_char *inbuf, *outbuf;
74367898a23SAlexander Motin 		int inlen, outlen, ina;
74467898a23SAlexander Motin 		u_char *source, *dest;
745af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
74667898a23SAlexander Motin 		int rtn;
747ce52e8f4SAlexander Motin 
748ce52e8f4SAlexander Motin 		/* Copy payload into a contiguous region of memory. */
74967898a23SAlexander Motin 		inlen = m->m_pkthdr.len;
75067898a23SAlexander Motin 		if (m->m_next == NULL) {
75167898a23SAlexander Motin                 	inbuf = mtod(m, u_char *);
75267898a23SAlexander Motin 			ina = 0;
75367898a23SAlexander Motin 		} else {
75467898a23SAlexander Motin 		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
75567898a23SAlexander Motin 			if (inbuf == NULL) {
756ce52e8f4SAlexander Motin 				m_freem(m);
757ce52e8f4SAlexander Motin 				return (ENOMEM);
758ce52e8f4SAlexander Motin 			}
75967898a23SAlexander Motin 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
76067898a23SAlexander Motin 			ina = 1;
76167898a23SAlexander Motin 		}
762af7ab184SArchie Cobbs 
763af7ab184SArchie Cobbs 		/* Allocate a buffer for decompressed data */
76467898a23SAlexander Motin 		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
765ce52e8f4SAlexander Motin 		    M_NETGRAPH_MPPC, M_NOWAIT);
76667898a23SAlexander Motin 		if (outbuf == NULL) {
767ce52e8f4SAlexander Motin 			m_freem(m);
76867898a23SAlexander Motin 			if (ina)
76967898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
770af7ab184SArchie Cobbs 			return (ENOMEM);
771af7ab184SArchie Cobbs 		}
77267898a23SAlexander Motin 		outlen = MPPC_DECOMP_BUFSIZE;
773af7ab184SArchie Cobbs 
774af7ab184SArchie Cobbs 		/* Prepare to decompress */
77567898a23SAlexander Motin 		source = inbuf;
77667898a23SAlexander Motin 		sourceCnt = inlen;
77767898a23SAlexander Motin 		dest = outbuf;
77867898a23SAlexander Motin 		destCnt = outlen;
779af7ab184SArchie Cobbs 		if ((header & MPPC_FLAG_RESTART) != 0)
780af7ab184SArchie Cobbs 			flags |= MPPC_RESTART_HISTORY;
781af7ab184SArchie Cobbs 
782af7ab184SArchie Cobbs 		/* Decompress */
783af7ab184SArchie Cobbs 		rtn = MPPC_Decompress(&source, &dest,
784af7ab184SArchie Cobbs 			&sourceCnt, &destCnt, d->history, flags);
785af7ab184SArchie Cobbs 
786af7ab184SArchie Cobbs 		/* Check return value */
7876e551fb6SDavid E. O'Brien 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
788af7ab184SArchie Cobbs 		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
789af7ab184SArchie Cobbs 		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
790af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: decomp returned 0x%x",
7916e551fb6SDavid E. O'Brien 			    __func__, rtn);
79267898a23SAlexander Motin 			if (ina)
79367898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
79467898a23SAlexander Motin 			free(outbuf, M_NETGRAPH_MPPC);
795af7ab184SArchie Cobbs 			goto failed;
796af7ab184SArchie Cobbs 		}
797af7ab184SArchie Cobbs 
798af7ab184SArchie Cobbs 		/* Replace compressed data with decompressed data */
79967898a23SAlexander Motin 		if (ina)
80067898a23SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
80167898a23SAlexander Motin 		outlen -= destCnt;
802ce52e8f4SAlexander Motin 
80367898a23SAlexander Motin 		m_copyback(m, 0, outlen, (caddr_t)outbuf);
80467898a23SAlexander Motin 		if (m->m_pkthdr.len < outlen) {
805ce52e8f4SAlexander Motin 			m_freem(m);
806e4651e05SAlexander Motin 			m = NULL;
80767898a23SAlexander Motin 		} else if (outlen < m->m_pkthdr.len)
80867898a23SAlexander Motin 			m_adj(m, outlen - m->m_pkthdr.len);
80967898a23SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
810af7ab184SArchie Cobbs 	}
811af7ab184SArchie Cobbs #endif
812af7ab184SArchie Cobbs 
813af7ab184SArchie Cobbs 	/* Return result in an mbuf */
814ce52e8f4SAlexander Motin 	*datap = m;
815ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
816af7ab184SArchie Cobbs }
817af7ab184SArchie Cobbs 
818af7ab184SArchie Cobbs /*
819af7ab184SArchie Cobbs  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
820af7ab184SArchie Cobbs  */
821af7ab184SArchie Cobbs static void
822af7ab184SArchie Cobbs ng_mppc_reset_req(node_p node)
823af7ab184SArchie Cobbs {
82430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
825af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
826af7ab184SArchie Cobbs 
827af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
828af7ab184SArchie Cobbs 	if (d->history != NULL)
829af7ab184SArchie Cobbs 		MPPC_InitCompressionHistory(d->history);
830af7ab184SArchie Cobbs #endif
831af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
832af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_STATELESS) == 0)
833af7ab184SArchie Cobbs 		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
834af7ab184SArchie Cobbs #endif
835af7ab184SArchie Cobbs 	d->flushed = 1;
836af7ab184SArchie Cobbs }
837af7ab184SArchie Cobbs 
83806c51e6eSAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
839af7ab184SArchie Cobbs /*
840af7ab184SArchie Cobbs  * Generate a new encryption key
841af7ab184SArchie Cobbs  */
842af7ab184SArchie Cobbs static void
843af7ab184SArchie Cobbs ng_mppc_getkey(const u_char *h, u_char *h2, int len)
844af7ab184SArchie Cobbs {
845b3d298b9SAlexander Motin 	static const u_char pad1[40] =
846b3d298b9SAlexander Motin 	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
847b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
848b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
849b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
850b3d298b9SAlexander Motin 	static const u_char pad2[40] =
851b3d298b9SAlexander Motin 	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
852b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
853b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
854b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
855af7ab184SArchie Cobbs 	u_char hash[20];
856af7ab184SArchie Cobbs 	SHA1_CTX c;
857af7ab184SArchie Cobbs 
858af7ab184SArchie Cobbs 	SHA1Init(&c);
859af7ab184SArchie Cobbs 	SHA1Update(&c, h, len);
860592009a3SAlexander Motin 	SHA1Update(&c, pad1, sizeof(pad1));
861af7ab184SArchie Cobbs 	SHA1Update(&c, h2, len);
862af7ab184SArchie Cobbs 	SHA1Update(&c, pad2, sizeof(pad2));
863af7ab184SArchie Cobbs 	SHA1Final(hash, &c);
864af7ab184SArchie Cobbs 	bcopy(hash, h2, len);
865af7ab184SArchie Cobbs }
866af7ab184SArchie Cobbs 
867af7ab184SArchie Cobbs /*
868af7ab184SArchie Cobbs  * Update the encryption key
869af7ab184SArchie Cobbs  */
870af7ab184SArchie Cobbs static void
871af7ab184SArchie Cobbs ng_mppc_updatekey(u_int32_t bits,
872af7ab184SArchie Cobbs 	u_char *key0, u_char *key, struct rc4_state *rc4)
873af7ab184SArchie Cobbs {
874af7ab184SArchie Cobbs 	const int keylen = KEYLEN(bits);
875af7ab184SArchie Cobbs 
876af7ab184SArchie Cobbs 	ng_mppc_getkey(key0, key, keylen);
877af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
878af7ab184SArchie Cobbs 	rc4_crypt(rc4, key, key, keylen);
87934fd2381SArchie Cobbs 	if ((bits & MPPE_40) != 0)
88034fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 3);
88134fd2381SArchie Cobbs 	else if ((bits & MPPE_56) != 0)
88234fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 1);
883af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
884af7ab184SArchie Cobbs }
88506c51e6eSAlexander Motin #endif
886af7ab184SArchie Cobbs 
887