xref: /freebsd/sys/netgraph/ng_mppc.c (revision eb1b1807afd1266445720b768b1bdbcdf7655a0a)
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
77d745c852SEd Schouten static 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 
1015372c30bSGleb Smirnoff /*
1025372c30bSGleb Smirnoff  * When packets are lost with MPPE, we may have to re-key arbitrarily
1035372c30bSGleb Smirnoff  * many times to 'catch up' to the new jumped-ahead sequence number.
1045372c30bSGleb Smirnoff  * Since this can be expensive, we pose a limit on how many re-keyings
1055372c30bSGleb Smirnoff  * we will do at one time to avoid a possible D.O.S. vulnerability.
1065372c30bSGleb Smirnoff  * This should instead be a configurable parameter.
1075372c30bSGleb Smirnoff  */
1085372c30bSGleb Smirnoff #define MPPE_MAX_REKEY		1000
1095372c30bSGleb Smirnoff 
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 */
204674d86bfSGleb Smirnoff 	priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_WAITOK | M_ZERO);
205af7ab184SArchie Cobbs 
20630400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
207af7ab184SArchie Cobbs 
2082f07580bSGleb Smirnoff 	/* This node is not thread safe. */
2092f07580bSGleb Smirnoff 	NG_NODE_FORCE_WRITER(node);
2102f07580bSGleb Smirnoff 
211af7ab184SArchie Cobbs 	/* Done */
212af7ab184SArchie Cobbs 	return (0);
213af7ab184SArchie Cobbs }
214af7ab184SArchie Cobbs 
215af7ab184SArchie Cobbs /*
216af7ab184SArchie Cobbs  * Give our OK for a hook to be added
217af7ab184SArchie Cobbs  */
218af7ab184SArchie Cobbs static int
219af7ab184SArchie Cobbs ng_mppc_newhook(node_p node, hook_p hook, const char *name)
220af7ab184SArchie Cobbs {
22130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
222af7ab184SArchie Cobbs 	hook_p *hookPtr;
223af7ab184SArchie Cobbs 
224af7ab184SArchie Cobbs 	/* Check hook name */
225af7ab184SArchie Cobbs 	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
226af7ab184SArchie Cobbs 		hookPtr = &priv->xmit.hook;
227af7ab184SArchie Cobbs 	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
228af7ab184SArchie Cobbs 		hookPtr = &priv->recv.hook;
229af7ab184SArchie Cobbs 	else
230af7ab184SArchie Cobbs 		return (EINVAL);
231af7ab184SArchie Cobbs 
232af7ab184SArchie Cobbs 	/* See if already connected */
233af7ab184SArchie Cobbs 	if (*hookPtr != NULL)
234af7ab184SArchie Cobbs 		return (EISCONN);
235af7ab184SArchie Cobbs 
236af7ab184SArchie Cobbs 	/* OK */
237af7ab184SArchie Cobbs 	*hookPtr = hook;
238af7ab184SArchie Cobbs 	return (0);
239af7ab184SArchie Cobbs }
240af7ab184SArchie Cobbs 
241af7ab184SArchie Cobbs /*
242af7ab184SArchie Cobbs  * Receive a control message
243af7ab184SArchie Cobbs  */
244af7ab184SArchie Cobbs static int
245069154d5SJulian Elischer ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
246af7ab184SArchie Cobbs {
24730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
248af7ab184SArchie Cobbs 	struct ng_mesg *resp = NULL;
249af7ab184SArchie Cobbs 	int error = 0;
250069154d5SJulian Elischer 	struct ng_mesg *msg;
251af7ab184SArchie Cobbs 
252069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
253af7ab184SArchie Cobbs 	switch (msg->header.typecookie) {
254af7ab184SArchie Cobbs 	case NGM_MPPC_COOKIE:
255af7ab184SArchie Cobbs 		switch (msg->header.cmd) {
256af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_COMP:
257af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_DECOMP:
258af7ab184SArchie Cobbs 		    {
259af7ab184SArchie Cobbs 			struct ng_mppc_config *const cfg
260af7ab184SArchie Cobbs 			    = (struct ng_mppc_config *)msg->data;
261af7ab184SArchie Cobbs 			const int isComp =
262af7ab184SArchie Cobbs 			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
263af7ab184SArchie Cobbs 			struct ng_mppc_dir *const d = isComp ?
264af7ab184SArchie Cobbs 			    &priv->xmit : &priv->recv;
265af7ab184SArchie Cobbs 
266af7ab184SArchie Cobbs 			/* Check configuration */
267af7ab184SArchie Cobbs 			if (msg->header.arglen != sizeof(*cfg))
268af7ab184SArchie Cobbs 				ERROUT(EINVAL);
269af7ab184SArchie Cobbs 			if (cfg->enable) {
270af7ab184SArchie Cobbs 				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
271af7ab184SArchie Cobbs 					ERROUT(EINVAL);
272af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_COMPRESSION
273af7ab184SArchie Cobbs 				if ((cfg->bits & MPPC_BIT) != 0)
274af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
275af7ab184SArchie Cobbs #endif
276af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_ENCRYPTION
277af7ab184SArchie Cobbs 				if ((cfg->bits & MPPE_BITS) != 0)
278af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
279af7ab184SArchie Cobbs #endif
280af7ab184SArchie Cobbs 			} else
281af7ab184SArchie Cobbs 				cfg->bits = 0;
282af7ab184SArchie Cobbs 
283af7ab184SArchie Cobbs 			/* Save return address so we can send reset-req's */
284f3059f39SArchie Cobbs 			if (!isComp)
285069154d5SJulian Elischer 				priv->ctrlnode = NGI_RETADDR(item);
286af7ab184SArchie Cobbs 
287af7ab184SArchie Cobbs 			/* Configuration is OK, reset to it */
288af7ab184SArchie Cobbs 			d->cfg = *cfg;
289af7ab184SArchie Cobbs 
290af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
291af7ab184SArchie Cobbs 			/* Initialize state buffers for compression */
292af7ab184SArchie Cobbs 			if (d->history != NULL) {
2931ede983cSDag-Erling Smørgrav 				free(d->history, M_NETGRAPH_MPPC);
294af7ab184SArchie Cobbs 				d->history = NULL;
295af7ab184SArchie Cobbs 			}
296af7ab184SArchie Cobbs 			if ((cfg->bits & MPPC_BIT) != 0) {
297e11e3f18SDag-Erling Smørgrav 				d->history = malloc(isComp ?
298e11e3f18SDag-Erling Smørgrav 				    MPPC_SizeOfCompressionHistory() :
299af7ab184SArchie Cobbs 				    MPPC_SizeOfDecompressionHistory(),
3009c8c302fSJulian Elischer 				    M_NETGRAPH_MPPC, M_NOWAIT);
301af7ab184SArchie Cobbs 				if (d->history == NULL)
302af7ab184SArchie Cobbs 					ERROUT(ENOMEM);
303af7ab184SArchie Cobbs 				if (isComp)
304af7ab184SArchie Cobbs 					MPPC_InitCompressionHistory(d->history);
305af7ab184SArchie Cobbs 				else {
306af7ab184SArchie Cobbs 					MPPC_InitDecompressionHistory(
307af7ab184SArchie Cobbs 					    d->history);
308af7ab184SArchie Cobbs 				}
309af7ab184SArchie Cobbs 			}
310af7ab184SArchie Cobbs #endif
311af7ab184SArchie Cobbs 
312af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
313af7ab184SArchie Cobbs 			/* Generate initial session keys for encryption */
314af7ab184SArchie Cobbs 			if ((cfg->bits & MPPE_BITS) != 0) {
315af7ab184SArchie Cobbs 				const int keylen = KEYLEN(cfg->bits);
316af7ab184SArchie Cobbs 
317af7ab184SArchie Cobbs 				bcopy(cfg->startkey, d->key, keylen);
318af7ab184SArchie Cobbs 				ng_mppc_getkey(cfg->startkey, d->key, keylen);
31934fd2381SArchie Cobbs 				if ((cfg->bits & MPPE_40) != 0)
32034fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 3);
32134fd2381SArchie Cobbs 				else if ((cfg->bits & MPPE_56) != 0)
32234fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 1);
323af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, keylen);
324af7ab184SArchie Cobbs 			}
325af7ab184SArchie Cobbs #endif
326af7ab184SArchie Cobbs 
327af7ab184SArchie Cobbs 			/* Initialize other state */
328af7ab184SArchie Cobbs 			d->cc = 0;
329af7ab184SArchie Cobbs 			d->flushed = 0;
330af7ab184SArchie Cobbs 			break;
331af7ab184SArchie Cobbs 		    }
332af7ab184SArchie Cobbs 
333af7ab184SArchie Cobbs 		case NGM_MPPC_RESETREQ:
334af7ab184SArchie Cobbs 			ng_mppc_reset_req(node);
335af7ab184SArchie Cobbs 			break;
336af7ab184SArchie Cobbs 
337af7ab184SArchie Cobbs 		default:
338af7ab184SArchie Cobbs 			error = EINVAL;
339af7ab184SArchie Cobbs 			break;
340af7ab184SArchie Cobbs 		}
341af7ab184SArchie Cobbs 		break;
342af7ab184SArchie Cobbs 	default:
343af7ab184SArchie Cobbs 		error = EINVAL;
344af7ab184SArchie Cobbs 		break;
345af7ab184SArchie Cobbs 	}
346af7ab184SArchie Cobbs done:
347069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
348069154d5SJulian Elischer 	NG_FREE_MSG(msg);
349af7ab184SArchie Cobbs 	return (error);
350af7ab184SArchie Cobbs }
351af7ab184SArchie Cobbs 
352af7ab184SArchie Cobbs /*
353af7ab184SArchie Cobbs  * Receive incoming data on our hook.
354af7ab184SArchie Cobbs  */
355af7ab184SArchie Cobbs static int
356069154d5SJulian Elischer ng_mppc_rcvdata(hook_p hook, item_p item)
357af7ab184SArchie Cobbs {
35830400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
35930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
360af7ab184SArchie Cobbs 	int error;
361069154d5SJulian Elischer 	struct mbuf *m;
362af7ab184SArchie Cobbs 
363069154d5SJulian Elischer 	NGI_GET_M(item, m);
364af7ab184SArchie Cobbs 	/* Compress and/or encrypt */
365af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook) {
366af7ab184SArchie Cobbs 		if (!priv->xmit.cfg.enable) {
367069154d5SJulian Elischer 			NG_FREE_M(m);
368069154d5SJulian Elischer 			NG_FREE_ITEM(item);
369af7ab184SArchie Cobbs 			return (ENXIO);
370af7ab184SArchie Cobbs 		}
371ce52e8f4SAlexander Motin 		if ((error = ng_mppc_compress(node, &m)) != 0) {
372069154d5SJulian Elischer 			NG_FREE_ITEM(item);
373af7ab184SArchie Cobbs 			return(error);
374af7ab184SArchie Cobbs 		}
375ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
376af7ab184SArchie Cobbs 		return (error);
377af7ab184SArchie Cobbs 	}
378af7ab184SArchie Cobbs 
379af7ab184SArchie Cobbs 	/* Decompress and/or decrypt */
380af7ab184SArchie Cobbs 	if (hook == priv->recv.hook) {
381af7ab184SArchie Cobbs 		if (!priv->recv.cfg.enable) {
382069154d5SJulian Elischer 			NG_FREE_M(m);
383069154d5SJulian Elischer 			NG_FREE_ITEM(item);
384af7ab184SArchie Cobbs 			return (ENXIO);
385af7ab184SArchie Cobbs 		}
386ce52e8f4SAlexander Motin 		if ((error = ng_mppc_decompress(node, &m)) != 0) {
387069154d5SJulian Elischer 			NG_FREE_ITEM(item);
388facfd889SArchie Cobbs 			if (error == EINVAL && priv->ctrlnode != 0) {
389af7ab184SArchie Cobbs 				struct ng_mesg *msg;
390af7ab184SArchie Cobbs 
391af7ab184SArchie Cobbs 				/* Need to send a reset-request */
392af7ab184SArchie Cobbs 				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
393af7ab184SArchie Cobbs 				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
394af7ab184SArchie Cobbs 				if (msg == NULL)
395af7ab184SArchie Cobbs 					return (error);
396069154d5SJulian Elischer 				NG_SEND_MSG_ID(error, node, msg,
397facfd889SArchie Cobbs 					priv->ctrlnode, 0);
398af7ab184SArchie Cobbs 			}
399af7ab184SArchie Cobbs 			return (error);
400af7ab184SArchie Cobbs 		}
401ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
402af7ab184SArchie Cobbs 		return (error);
403af7ab184SArchie Cobbs 	}
404af7ab184SArchie Cobbs 
405af7ab184SArchie Cobbs 	/* Oops */
4066e551fb6SDavid E. O'Brien 	panic("%s: unknown hook", __func__);
407af7ab184SArchie Cobbs }
408af7ab184SArchie Cobbs 
409af7ab184SArchie Cobbs /*
410af7ab184SArchie Cobbs  * Destroy node
411af7ab184SArchie Cobbs  */
412af7ab184SArchie Cobbs static int
413069154d5SJulian Elischer ng_mppc_shutdown(node_p node)
414af7ab184SArchie Cobbs {
41530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
416af7ab184SArchie Cobbs 
417af7ab184SArchie Cobbs 	/* Take down netgraph node */
418af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
419af7ab184SArchie Cobbs 	if (priv->xmit.history != NULL)
4201ede983cSDag-Erling Smørgrav 		free(priv->xmit.history, M_NETGRAPH_MPPC);
421af7ab184SArchie Cobbs 	if (priv->recv.history != NULL)
4221ede983cSDag-Erling Smørgrav 		free(priv->recv.history, M_NETGRAPH_MPPC);
423af7ab184SArchie Cobbs #endif
424af7ab184SArchie Cobbs 	bzero(priv, sizeof(*priv));
4251ede983cSDag-Erling Smørgrav 	free(priv, M_NETGRAPH_MPPC);
42630400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
42730400f03SJulian Elischer 	NG_NODE_UNREF(node);		/* let the node escape */
428af7ab184SArchie Cobbs 	return (0);
429af7ab184SArchie Cobbs }
430af7ab184SArchie Cobbs 
431af7ab184SArchie Cobbs /*
432af7ab184SArchie Cobbs  * Hook disconnection
433af7ab184SArchie Cobbs  */
434af7ab184SArchie Cobbs static int
435af7ab184SArchie Cobbs ng_mppc_disconnect(hook_p hook)
436af7ab184SArchie Cobbs {
43730400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
43830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
439af7ab184SArchie Cobbs 
440af7ab184SArchie Cobbs 	/* Zero out hook pointer */
441af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook)
442af7ab184SArchie Cobbs 		priv->xmit.hook = NULL;
443af7ab184SArchie Cobbs 	if (hook == priv->recv.hook)
444af7ab184SArchie Cobbs 		priv->recv.hook = NULL;
445af7ab184SArchie Cobbs 
446af7ab184SArchie Cobbs 	/* Go away if no longer connected */
44730400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(node) == 0)
44830400f03SJulian Elischer 	&& NG_NODE_IS_VALID(node))
449069154d5SJulian Elischer 		ng_rmnode_self(node);
450af7ab184SArchie Cobbs 	return (0);
451af7ab184SArchie Cobbs }
452af7ab184SArchie Cobbs 
453af7ab184SArchie Cobbs /************************************************************************
454af7ab184SArchie Cobbs 			HELPER STUFF
455af7ab184SArchie Cobbs  ************************************************************************/
456af7ab184SArchie Cobbs 
457af7ab184SArchie Cobbs /*
458af7ab184SArchie Cobbs  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
459af7ab184SArchie Cobbs  * The original mbuf is not free'd.
460af7ab184SArchie Cobbs  */
461af7ab184SArchie Cobbs static int
462ce52e8f4SAlexander Motin ng_mppc_compress(node_p node, struct mbuf **datap)
463af7ab184SArchie Cobbs {
46430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
465af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
466af7ab184SArchie Cobbs 	u_int16_t header;
467ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
468af7ab184SArchie Cobbs 
469e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
470*eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
471e4651e05SAlexander Motin 	if (m == NULL)
472e4651e05SAlexander Motin 		return (ENOMEM);
473e4651e05SAlexander Motin 
474af7ab184SArchie Cobbs 	/* Initialize */
475af7ab184SArchie Cobbs 	header = d->cc;
476adecf751SAlexander Motin 
477adecf751SAlexander Motin 	/* Always set the flushed bit in stateless mode */
478adecf751SAlexander Motin 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
479af7ab184SArchie Cobbs 		header |= MPPC_FLAG_FLUSHED;
480af7ab184SArchie Cobbs 		d->flushed = 0;
481af7ab184SArchie Cobbs 	}
482af7ab184SArchie Cobbs 
483ce52e8f4SAlexander Motin 	/* Compress packet (if compression enabled) */
484af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
485af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPC_BIT) != 0) {
486af7ab184SArchie Cobbs 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
487ce52e8f4SAlexander Motin 		u_char *inbuf, *outbuf;
48867898a23SAlexander Motin 		int outlen, inlen, ina;
489af7ab184SArchie Cobbs 		u_char *source, *dest;
490af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
491af7ab184SArchie Cobbs 		int rtn;
492af7ab184SArchie Cobbs 
493ce52e8f4SAlexander Motin 		/* Work with contiguous regions of memory. */
494ce52e8f4SAlexander Motin 		inlen = m->m_pkthdr.len;
49567898a23SAlexander Motin 		if (m->m_next == NULL) {
49667898a23SAlexander Motin 			inbuf = mtod(m, u_char *);
49767898a23SAlexander Motin 			ina = 0;
49867898a23SAlexander Motin 		} else {
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);
50367898a23SAlexander Motin 			ina = 1;
50467898a23SAlexander Motin 		}
505ce52e8f4SAlexander Motin 
506ce52e8f4SAlexander Motin 		outlen = MPPC_MAX_BLOWUP(inlen);
507ce52e8f4SAlexander Motin 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
508ce52e8f4SAlexander Motin 		if (outbuf == NULL) {
50967898a23SAlexander Motin 			if (ina)
510ce52e8f4SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
51111d1cadeSAlexander Motin err1:
51211d1cadeSAlexander Motin 			m_freem(m);
51311d1cadeSAlexander Motin 			MPPC_InitCompressionHistory(d->history);
51411d1cadeSAlexander Motin 			d->flushed = 1;
515ce52e8f4SAlexander Motin 			return (ENOMEM);
516ce52e8f4SAlexander Motin 		}
517ce52e8f4SAlexander Motin 
518af7ab184SArchie Cobbs 		/* Prepare to compress */
519af7ab184SArchie Cobbs 		source = inbuf;
520af7ab184SArchie Cobbs 		sourceCnt = inlen;
521ce52e8f4SAlexander Motin 		dest = outbuf;
522ce52e8f4SAlexander Motin 		destCnt = outlen;
523af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
524af7ab184SArchie Cobbs 			flags |= MPPC_SAVE_HISTORY;
525af7ab184SArchie Cobbs 
526af7ab184SArchie Cobbs 		/* Compress */
527af7ab184SArchie Cobbs 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
528af7ab184SArchie Cobbs 			&destCnt, d->history, flags, 0);
529af7ab184SArchie Cobbs 
530af7ab184SArchie Cobbs 		/* Check return value */
5316e551fb6SDavid E. O'Brien 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
532af7ab184SArchie Cobbs 		if ((rtn & MPPC_EXPANDED) == 0
533af7ab184SArchie Cobbs 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
534af7ab184SArchie Cobbs 			outlen -= destCnt;
535af7ab184SArchie Cobbs 			header |= MPPC_FLAG_COMPRESSED;
536af7ab184SArchie Cobbs 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
537af7ab184SArchie Cobbs 				header |= MPPC_FLAG_RESTART;
538ce52e8f4SAlexander Motin 
539ce52e8f4SAlexander Motin 			/* Replace m by the compresed one. */
540e4651e05SAlexander Motin 			m_copyback(m, 0, outlen, (caddr_t)outbuf);
541e4651e05SAlexander Motin 			if (m->m_pkthdr.len < outlen) {
542ce52e8f4SAlexander Motin 				m_freem(m);
543e4651e05SAlexander Motin 				m = NULL;
544e4651e05SAlexander Motin 			} else if (outlen < m->m_pkthdr.len)
545e4651e05SAlexander Motin 				m_adj(m, outlen - m->m_pkthdr.len);
546af7ab184SArchie Cobbs 		}
547af7ab184SArchie Cobbs 		d->flushed = (rtn & MPPC_EXPANDED) != 0
548af7ab184SArchie Cobbs 		    || (flags & MPPC_SAVE_HISTORY) == 0;
549ce52e8f4SAlexander Motin 
55067898a23SAlexander Motin 		if (ina)
551ce52e8f4SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
552ce52e8f4SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
553ce52e8f4SAlexander Motin 
554e4651e05SAlexander Motin 		/* Check mbuf chain reload result. */
55511d1cadeSAlexander Motin 		if (m == NULL) {
55611d1cadeSAlexander Motin 			if (!d->flushed) {
55711d1cadeSAlexander Motin 				MPPC_InitCompressionHistory(d->history);
55811d1cadeSAlexander Motin 				d->flushed = 1;
55911d1cadeSAlexander Motin 			}
560ce52e8f4SAlexander Motin 			return (ENOMEM);
561af7ab184SArchie Cobbs 		}
56211d1cadeSAlexander Motin 	}
563af7ab184SArchie Cobbs #endif
564af7ab184SArchie Cobbs 
565af7ab184SArchie Cobbs 	/* Now encrypt packet (if encryption enabled) */
566af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
567af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_BITS) != 0) {
568ce52e8f4SAlexander Motin 		struct mbuf *m1;
569af7ab184SArchie Cobbs 
5706370fd6bSAlexander Motin 		/* Set header bits */
571af7ab184SArchie Cobbs 		header |= MPPC_FLAG_ENCRYPTED;
572af7ab184SArchie Cobbs 
573af7ab184SArchie Cobbs 		/* Update key if it's time */
574af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
575af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
576af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
577af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
5786370fd6bSAlexander Motin 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
5796370fd6bSAlexander Motin 			/* Need to reset key if we say we did
5806370fd6bSAlexander Motin 			   and ng_mppc_updatekey wasn't called to do it also. */
5816370fd6bSAlexander Motin 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
582af7ab184SArchie Cobbs 		}
583af7ab184SArchie Cobbs 
584af7ab184SArchie Cobbs 		/* Encrypt packet */
585ce52e8f4SAlexander Motin 		m1 = m;
586ce52e8f4SAlexander Motin 		while (m1) {
587ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
588ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
589ce52e8f4SAlexander Motin 			m1 = m1->m_next;
590ce52e8f4SAlexander Motin 		}
591af7ab184SArchie Cobbs 	}
592af7ab184SArchie Cobbs #endif
593af7ab184SArchie Cobbs 
594755bc287SAlexander Motin 	/* Update coherency count for next time (12 bit arithmetic) */
595755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
596af7ab184SArchie Cobbs 
597af7ab184SArchie Cobbs 	/* Install header */
598*eb1b1807SGleb Smirnoff 	M_PREPEND(m, MPPC_HDRLEN, M_NOWAIT);
599ce52e8f4SAlexander Motin 	if (m != NULL)
60039228864SAlexander Motin 		be16enc(mtod(m, void *), header);
601af7ab184SArchie Cobbs 
602ce52e8f4SAlexander Motin 	*datap = m;
603ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
604af7ab184SArchie Cobbs }
605af7ab184SArchie Cobbs 
606af7ab184SArchie Cobbs /*
607af7ab184SArchie Cobbs  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
608af7ab184SArchie Cobbs  * The original mbuf is not free'd.
609af7ab184SArchie Cobbs  */
610af7ab184SArchie Cobbs static int
611ce52e8f4SAlexander Motin ng_mppc_decompress(node_p node, struct mbuf **datap)
612af7ab184SArchie Cobbs {
61330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
614af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->recv;
615f3059f39SArchie Cobbs 	u_int16_t header, cc;
616f3059f39SArchie Cobbs 	u_int numLost;
617ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
618af7ab184SArchie Cobbs 
619e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
620*eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
621e4651e05SAlexander Motin 	if (m == NULL)
622e4651e05SAlexander Motin 		return (ENOMEM);
623e4651e05SAlexander Motin 
624af7ab184SArchie Cobbs 	/* Pull off header */
625ce52e8f4SAlexander Motin 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
626ce52e8f4SAlexander Motin 		m_freem(m);
627af7ab184SArchie Cobbs 		return (EINVAL);
628ce52e8f4SAlexander Motin 	}
62939228864SAlexander Motin 	header = be16dec(mtod(m, void *));
630af7ab184SArchie Cobbs 	cc = (header & MPPC_CCOUNT_MASK);
631ce52e8f4SAlexander Motin 	m_adj(m, MPPC_HDRLEN);
632af7ab184SArchie Cobbs 
633f3059f39SArchie Cobbs 	/* Check for an unexpected jump in the sequence number */
634af7ab184SArchie Cobbs 	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
635af7ab184SArchie Cobbs 
636af7ab184SArchie Cobbs 	/* If flushed bit set, we can always handle packet */
637af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_FLUSHED) != 0) {
638af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
639af7ab184SArchie Cobbs 		if (d->history != NULL)
640af7ab184SArchie Cobbs 			MPPC_InitDecompressionHistory(d->history);
641af7ab184SArchie Cobbs #endif
642af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
643af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
644f3059f39SArchie Cobbs 			u_int rekey;
645af7ab184SArchie Cobbs 
646f3059f39SArchie Cobbs 			/* How many times are we going to have to re-key? */
647f3059f39SArchie Cobbs 			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
648f3059f39SArchie Cobbs 			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
6495372c30bSGleb Smirnoff 			if (rekey > MPPE_MAX_REKEY) {
6505372c30bSGleb Smirnoff 				log(LOG_ERR, "%s: too many (%d) packets"
6515372c30bSGleb Smirnoff 				    " dropped, disabling node %p!",
6525372c30bSGleb Smirnoff 				    __func__, numLost, node);
6535372c30bSGleb Smirnoff 				priv->recv.cfg.enable = 0;
6545372c30bSGleb Smirnoff 				goto failed;
6555372c30bSGleb Smirnoff 			}
656f3059f39SArchie Cobbs 
6575372c30bSGleb Smirnoff 			/* Re-key as necessary to catch up to peer */
658af7ab184SArchie Cobbs 			while (d->cc != cc) {
659f3059f39SArchie Cobbs 				if ((d->cfg.bits & MPPE_STATELESS) != 0
660af7ab184SArchie Cobbs 				    || (d->cc & MPPE_UPDATE_MASK)
661af7ab184SArchie Cobbs 				      == MPPE_UPDATE_FLAG) {
662af7ab184SArchie Cobbs 					ng_mppc_updatekey(d->cfg.bits,
663af7ab184SArchie Cobbs 					    d->cfg.startkey, d->key, &d->rc4);
664af7ab184SArchie Cobbs 				}
665755bc287SAlexander Motin 				MPPC_CCOUNT_INC(d->cc);
666af7ab184SArchie Cobbs 			}
667af7ab184SArchie Cobbs 
668af7ab184SArchie Cobbs 			/* Reset key (except in stateless mode, see below) */
669af7ab184SArchie Cobbs 			if ((d->cfg.bits & MPPE_STATELESS) == 0)
670af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
671af7ab184SArchie Cobbs 		}
672af7ab184SArchie Cobbs #endif
673af7ab184SArchie Cobbs 		d->cc = cc;		/* skip over lost seq numbers */
674af7ab184SArchie Cobbs 		numLost = 0;		/* act like no packets were lost */
675af7ab184SArchie Cobbs 	}
676af7ab184SArchie Cobbs 
677af7ab184SArchie Cobbs 	/* Can't decode non-sequential packets without a flushed bit */
678af7ab184SArchie Cobbs 	if (numLost != 0)
679af7ab184SArchie Cobbs 		goto failed;
680af7ab184SArchie Cobbs 
681af7ab184SArchie Cobbs 	/* Decrypt packet */
682af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
683ce52e8f4SAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
684ce52e8f4SAlexander Motin 		struct mbuf *m1;
685ce52e8f4SAlexander Motin #endif
686af7ab184SArchie Cobbs 
687af7ab184SArchie Cobbs 		/* Are we not expecting encryption? */
688af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) == 0) {
689af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
6906e551fb6SDavid E. O'Brien 				__func__, "encrypted");
691af7ab184SArchie Cobbs 			goto failed;
692af7ab184SArchie Cobbs 		}
693af7ab184SArchie Cobbs 
694af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
695af7ab184SArchie Cobbs 		/* Update key if it's time (always in stateless mode) */
696af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
697af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
698af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
699af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
700af7ab184SArchie Cobbs 		}
701af7ab184SArchie Cobbs 
702af7ab184SArchie Cobbs 		/* Decrypt packet */
703ce52e8f4SAlexander Motin 		m1 = m;
704ce52e8f4SAlexander Motin 		while (m1 != NULL) {
705ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
706ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
707ce52e8f4SAlexander Motin 			m1 = m1->m_next;
708ce52e8f4SAlexander Motin 		}
709af7ab184SArchie Cobbs #endif
710af7ab184SArchie Cobbs 	} else {
711af7ab184SArchie Cobbs 
712af7ab184SArchie Cobbs 		/* Are we expecting encryption? */
713af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
714af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7156e551fb6SDavid E. O'Brien 				__func__, "unencrypted");
716af7ab184SArchie Cobbs 			goto failed;
717af7ab184SArchie Cobbs 		}
718af7ab184SArchie Cobbs 	}
719af7ab184SArchie Cobbs 
720af7ab184SArchie Cobbs 	/* Update coherency count for next time (12 bit arithmetic) */
721755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
722af7ab184SArchie Cobbs 
723af7ab184SArchie Cobbs 	/* Check for unexpected compressed packet */
724af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0
725af7ab184SArchie Cobbs 	    && (d->cfg.bits & MPPC_BIT) == 0) {
726af7ab184SArchie Cobbs 		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7276e551fb6SDavid E. O'Brien 			__func__, "compressed");
728af7ab184SArchie Cobbs failed:
729ce52e8f4SAlexander Motin 		m_freem(m);
730af7ab184SArchie Cobbs 		return (EINVAL);
731af7ab184SArchie Cobbs 	}
732af7ab184SArchie Cobbs 
733af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
734af7ab184SArchie Cobbs 	/* Decompress packet */
735af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
736af7ab184SArchie Cobbs 		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
73767898a23SAlexander Motin 		u_char *inbuf, *outbuf;
73867898a23SAlexander Motin 		int inlen, outlen, ina;
73967898a23SAlexander Motin 		u_char *source, *dest;
740af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
74167898a23SAlexander Motin 		int rtn;
742ce52e8f4SAlexander Motin 
743ce52e8f4SAlexander Motin 		/* Copy payload into a contiguous region of memory. */
74467898a23SAlexander Motin 		inlen = m->m_pkthdr.len;
74567898a23SAlexander Motin 		if (m->m_next == NULL) {
74667898a23SAlexander Motin                 	inbuf = mtod(m, u_char *);
74767898a23SAlexander Motin 			ina = 0;
74867898a23SAlexander Motin 		} else {
74967898a23SAlexander Motin 		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
75067898a23SAlexander Motin 			if (inbuf == NULL) {
751ce52e8f4SAlexander Motin 				m_freem(m);
752ce52e8f4SAlexander Motin 				return (ENOMEM);
753ce52e8f4SAlexander Motin 			}
75467898a23SAlexander Motin 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
75567898a23SAlexander Motin 			ina = 1;
75667898a23SAlexander Motin 		}
757af7ab184SArchie Cobbs 
758af7ab184SArchie Cobbs 		/* Allocate a buffer for decompressed data */
75967898a23SAlexander Motin 		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
760ce52e8f4SAlexander Motin 		    M_NETGRAPH_MPPC, M_NOWAIT);
76167898a23SAlexander Motin 		if (outbuf == NULL) {
762ce52e8f4SAlexander Motin 			m_freem(m);
76367898a23SAlexander Motin 			if (ina)
76467898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
765af7ab184SArchie Cobbs 			return (ENOMEM);
766af7ab184SArchie Cobbs 		}
76767898a23SAlexander Motin 		outlen = MPPC_DECOMP_BUFSIZE;
768af7ab184SArchie Cobbs 
769af7ab184SArchie Cobbs 		/* Prepare to decompress */
77067898a23SAlexander Motin 		source = inbuf;
77167898a23SAlexander Motin 		sourceCnt = inlen;
77267898a23SAlexander Motin 		dest = outbuf;
77367898a23SAlexander Motin 		destCnt = outlen;
774af7ab184SArchie Cobbs 		if ((header & MPPC_FLAG_RESTART) != 0)
775af7ab184SArchie Cobbs 			flags |= MPPC_RESTART_HISTORY;
776af7ab184SArchie Cobbs 
777af7ab184SArchie Cobbs 		/* Decompress */
778af7ab184SArchie Cobbs 		rtn = MPPC_Decompress(&source, &dest,
779af7ab184SArchie Cobbs 			&sourceCnt, &destCnt, d->history, flags);
780af7ab184SArchie Cobbs 
781af7ab184SArchie Cobbs 		/* Check return value */
7826e551fb6SDavid E. O'Brien 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
783af7ab184SArchie Cobbs 		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
784af7ab184SArchie Cobbs 		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
785af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: decomp returned 0x%x",
7866e551fb6SDavid E. O'Brien 			    __func__, rtn);
78767898a23SAlexander Motin 			if (ina)
78867898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
78967898a23SAlexander Motin 			free(outbuf, M_NETGRAPH_MPPC);
790af7ab184SArchie Cobbs 			goto failed;
791af7ab184SArchie Cobbs 		}
792af7ab184SArchie Cobbs 
793af7ab184SArchie Cobbs 		/* Replace compressed data with decompressed data */
79467898a23SAlexander Motin 		if (ina)
79567898a23SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
79667898a23SAlexander Motin 		outlen -= destCnt;
797ce52e8f4SAlexander Motin 
79867898a23SAlexander Motin 		m_copyback(m, 0, outlen, (caddr_t)outbuf);
79967898a23SAlexander Motin 		if (m->m_pkthdr.len < outlen) {
800ce52e8f4SAlexander Motin 			m_freem(m);
801e4651e05SAlexander Motin 			m = NULL;
80267898a23SAlexander Motin 		} else if (outlen < m->m_pkthdr.len)
80367898a23SAlexander Motin 			m_adj(m, outlen - m->m_pkthdr.len);
80467898a23SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
805af7ab184SArchie Cobbs 	}
806af7ab184SArchie Cobbs #endif
807af7ab184SArchie Cobbs 
808af7ab184SArchie Cobbs 	/* Return result in an mbuf */
809ce52e8f4SAlexander Motin 	*datap = m;
810ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
811af7ab184SArchie Cobbs }
812af7ab184SArchie Cobbs 
813af7ab184SArchie Cobbs /*
814af7ab184SArchie Cobbs  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
815af7ab184SArchie Cobbs  */
816af7ab184SArchie Cobbs static void
817af7ab184SArchie Cobbs ng_mppc_reset_req(node_p node)
818af7ab184SArchie Cobbs {
81930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
820af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
821af7ab184SArchie Cobbs 
822af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
823af7ab184SArchie Cobbs 	if (d->history != NULL)
824af7ab184SArchie Cobbs 		MPPC_InitCompressionHistory(d->history);
825af7ab184SArchie Cobbs #endif
826af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
827af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_STATELESS) == 0)
828af7ab184SArchie Cobbs 		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
829af7ab184SArchie Cobbs #endif
830af7ab184SArchie Cobbs 	d->flushed = 1;
831af7ab184SArchie Cobbs }
832af7ab184SArchie Cobbs 
83306c51e6eSAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
834af7ab184SArchie Cobbs /*
835af7ab184SArchie Cobbs  * Generate a new encryption key
836af7ab184SArchie Cobbs  */
837af7ab184SArchie Cobbs static void
838af7ab184SArchie Cobbs ng_mppc_getkey(const u_char *h, u_char *h2, int len)
839af7ab184SArchie Cobbs {
840b3d298b9SAlexander Motin 	static const u_char pad1[40] =
841b3d298b9SAlexander Motin 	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
842b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
843b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
844b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
845b3d298b9SAlexander Motin 	static const u_char pad2[40] =
846b3d298b9SAlexander Motin 	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
847b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
848b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
849b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
850af7ab184SArchie Cobbs 	u_char hash[20];
851af7ab184SArchie Cobbs 	SHA1_CTX c;
852af7ab184SArchie Cobbs 
853af7ab184SArchie Cobbs 	SHA1Init(&c);
854af7ab184SArchie Cobbs 	SHA1Update(&c, h, len);
855592009a3SAlexander Motin 	SHA1Update(&c, pad1, sizeof(pad1));
856af7ab184SArchie Cobbs 	SHA1Update(&c, h2, len);
857af7ab184SArchie Cobbs 	SHA1Update(&c, pad2, sizeof(pad2));
858af7ab184SArchie Cobbs 	SHA1Final(hash, &c);
859af7ab184SArchie Cobbs 	bcopy(hash, h2, len);
860af7ab184SArchie Cobbs }
861af7ab184SArchie Cobbs 
862af7ab184SArchie Cobbs /*
863af7ab184SArchie Cobbs  * Update the encryption key
864af7ab184SArchie Cobbs  */
865af7ab184SArchie Cobbs static void
866af7ab184SArchie Cobbs ng_mppc_updatekey(u_int32_t bits,
867af7ab184SArchie Cobbs 	u_char *key0, u_char *key, struct rc4_state *rc4)
868af7ab184SArchie Cobbs {
869af7ab184SArchie Cobbs 	const int keylen = KEYLEN(bits);
870af7ab184SArchie Cobbs 
871af7ab184SArchie Cobbs 	ng_mppc_getkey(key0, key, keylen);
872af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
873af7ab184SArchie Cobbs 	rc4_crypt(rc4, key, key, keylen);
87434fd2381SArchie Cobbs 	if ((bits & MPPE_40) != 0)
87534fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 3);
87634fd2381SArchie Cobbs 	else if ((bits & MPPE_56) != 0)
87734fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 1);
878af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
879af7ab184SArchie Cobbs }
88006c51e6eSAlexander Motin #endif
881af7ab184SArchie Cobbs 
882