xref: /freebsd/sys/netgraph/ng_mppc.c (revision ee652839f696bd529f355c801cb7db3e8ff3b17a)
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>
58*ee652839SAlexander Motin #include <sys/sysctl.h>
59af7ab184SArchie Cobbs #include <sys/syslog.h>
60af7ab184SArchie Cobbs 
61af7ab184SArchie Cobbs #include <netgraph/ng_message.h>
62af7ab184SArchie Cobbs #include <netgraph/netgraph.h>
63af7ab184SArchie Cobbs #include <netgraph/ng_mppc.h>
64af7ab184SArchie Cobbs 
65af7ab184SArchie Cobbs #include "opt_netgraph.h"
66af7ab184SArchie Cobbs 
67af7ab184SArchie Cobbs #if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION)
6810d645b7SYaroslav Tykhiy #ifdef KLD_MODULE
6910d645b7SYaroslav Tykhiy /* XXX NETGRAPH_MPPC_COMPRESSION isn't functional yet */
7010d645b7SYaroslav Tykhiy #define NETGRAPH_MPPC_ENCRYPTION
7110d645b7SYaroslav Tykhiy #else
7210d645b7SYaroslav Tykhiy /* This case is indicative of an error in sys/conf files */
73af7ab184SArchie Cobbs #error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION
74af7ab184SArchie Cobbs #endif
7510d645b7SYaroslav Tykhiy #endif
76af7ab184SArchie Cobbs 
779c8c302fSJulian Elischer #ifdef NG_SEPARATE_MALLOC
78d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node");
799c8c302fSJulian Elischer #else
809c8c302fSJulian Elischer #define M_NETGRAPH_MPPC M_NETGRAPH
819c8c302fSJulian Elischer #endif
829c8c302fSJulian Elischer 
83af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
84af7ab184SArchie Cobbs /* XXX this file doesn't exist yet, but hopefully someday it will... */
85af7ab184SArchie Cobbs #include <net/mppc.h>
86af7ab184SArchie Cobbs #endif
87af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
88af7ab184SArchie Cobbs #include <crypto/rc4/rc4.h>
89af7ab184SArchie Cobbs #endif
90af7ab184SArchie Cobbs #include <crypto/sha1.h>
91af7ab184SArchie Cobbs 
92af7ab184SArchie Cobbs /* Decompression blowup */
93af7ab184SArchie Cobbs #define MPPC_DECOMP_BUFSIZE	8092            /* allocate buffer this big */
94af7ab184SArchie Cobbs #define MPPC_DECOMP_SAFETY	100             /*   plus this much margin */
95af7ab184SArchie Cobbs 
96af7ab184SArchie Cobbs /* MPPC/MPPE header length */
97af7ab184SArchie Cobbs #define MPPC_HDRLEN		2
98af7ab184SArchie Cobbs 
99af7ab184SArchie Cobbs /* Key length */
100af7ab184SArchie Cobbs #define KEYLEN(b)		(((b) & MPPE_128) ? 16 : 8)
101af7ab184SArchie Cobbs 
1025372c30bSGleb Smirnoff /*
1035372c30bSGleb Smirnoff  * When packets are lost with MPPE, we may have to re-key arbitrarily
1045372c30bSGleb Smirnoff  * many times to 'catch up' to the new jumped-ahead sequence number.
1055372c30bSGleb Smirnoff  * Since this can be expensive, we pose a limit on how many re-keyings
1065372c30bSGleb Smirnoff  * we will do at one time to avoid a possible D.O.S. vulnerability.
1075372c30bSGleb Smirnoff  * This should instead be a configurable parameter.
1085372c30bSGleb Smirnoff  */
1095372c30bSGleb Smirnoff #define MPPE_MAX_REKEY		1000
1105372c30bSGleb Smirnoff 
111*ee652839SAlexander Motin SYSCTL_NODE(_net_graph, OID_AUTO, mppe, CTLFLAG_RW, 0, "MPPE");
112*ee652839SAlexander Motin 
113*ee652839SAlexander Motin static int mppe_block_on_max_rekey = 0;
114*ee652839SAlexander Motin TUNABLE_INT("net.graph.mppe.block_on_max_rekey", &mppe_block_on_max_rekey);
115*ee652839SAlexander Motin SYSCTL_INT(_net_graph_mppe, OID_AUTO, block_on_max_rekey, CTLFLAG_RW,
116*ee652839SAlexander Motin     &mppe_block_on_max_rekey, 0, "Block node on max MPPE key re-calculations");
117*ee652839SAlexander Motin 
118*ee652839SAlexander Motin static int mppe_log_max_rekey = 1;
119*ee652839SAlexander Motin TUNABLE_INT("net.graph.mppe.log_max_rekey", &mppe_log_max_rekey);
120*ee652839SAlexander Motin SYSCTL_INT(_net_graph_mppe, OID_AUTO, log_max_rekey, CTLFLAG_RW,
121*ee652839SAlexander Motin     &mppe_log_max_rekey, 0, "Log max MPPE key re-calculations event");
122*ee652839SAlexander Motin 
123*ee652839SAlexander Motin static int mppe_max_rekey = MPPE_MAX_REKEY;
124*ee652839SAlexander Motin TUNABLE_INT("net.graph.mppe.max_rekey", &mppe_max_rekey);
125*ee652839SAlexander Motin SYSCTL_INT(_net_graph_mppe, OID_AUTO, max_rekey, CTLFLAG_RW,
126*ee652839SAlexander Motin     &mppe_max_rekey, 0, "Maximum number of MPPE key re-calculations");
127*ee652839SAlexander Motin 
128af7ab184SArchie Cobbs /* MPPC packet header bits */
129af7ab184SArchie Cobbs #define MPPC_FLAG_FLUSHED	0x8000		/* xmitter reset state */
130af7ab184SArchie Cobbs #define MPPC_FLAG_RESTART	0x4000		/* compress history restart */
131af7ab184SArchie Cobbs #define MPPC_FLAG_COMPRESSED	0x2000		/* packet is compresed */
132af7ab184SArchie Cobbs #define MPPC_FLAG_ENCRYPTED	0x1000		/* packet is encrypted */
133af7ab184SArchie Cobbs #define MPPC_CCOUNT_MASK	0x0fff		/* sequence number mask */
134af7ab184SArchie Cobbs 
135755bc287SAlexander Motin #define MPPC_CCOUNT_INC(d)	((d) = (((d) + 1) & MPPC_CCOUNT_MASK))
136755bc287SAlexander Motin 
137af7ab184SArchie Cobbs #define MPPE_UPDATE_MASK	0xff		/* coherency count when we're */
138af7ab184SArchie Cobbs #define MPPE_UPDATE_FLAG	0xff		/*   supposed to update key */
139af7ab184SArchie Cobbs 
140af7ab184SArchie Cobbs #define MPPC_COMP_OK		0x05
141af7ab184SArchie Cobbs #define MPPC_DECOMP_OK		0x05
142af7ab184SArchie Cobbs 
143af7ab184SArchie Cobbs /* Per direction info */
144af7ab184SArchie Cobbs struct ng_mppc_dir {
145af7ab184SArchie Cobbs 	struct ng_mppc_config	cfg;		/* configuration */
146af7ab184SArchie Cobbs 	hook_p			hook;		/* netgraph hook */
147af7ab184SArchie Cobbs 	u_int16_t		cc:12;		/* coherency count */
148af7ab184SArchie Cobbs 	u_char			flushed;	/* clean history (xmit only) */
149af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
150af7ab184SArchie Cobbs 	u_char			*history;	/* compression history */
151af7ab184SArchie Cobbs #endif
152af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
153af7ab184SArchie Cobbs 	u_char			key[MPPE_KEY_LEN];	/* session key */
154af7ab184SArchie Cobbs 	struct rc4_state	rc4;			/* rc4 state */
155af7ab184SArchie Cobbs #endif
156af7ab184SArchie Cobbs };
157af7ab184SArchie Cobbs 
158af7ab184SArchie Cobbs /* Node private data */
159af7ab184SArchie Cobbs struct ng_mppc_private {
160af7ab184SArchie Cobbs 	struct ng_mppc_dir	xmit;		/* compress/encrypt config */
161af7ab184SArchie Cobbs 	struct ng_mppc_dir	recv;		/* decompress/decrypt config */
162069154d5SJulian Elischer 	ng_ID_t			ctrlnode;	/* path to controlling node */
163af7ab184SArchie Cobbs };
164af7ab184SArchie Cobbs typedef struct ng_mppc_private *priv_p;
165af7ab184SArchie Cobbs 
166af7ab184SArchie Cobbs /* Netgraph node methods */
167af7ab184SArchie Cobbs static ng_constructor_t	ng_mppc_constructor;
168af7ab184SArchie Cobbs static ng_rcvmsg_t	ng_mppc_rcvmsg;
169069154d5SJulian Elischer static ng_shutdown_t	ng_mppc_shutdown;
170af7ab184SArchie Cobbs static ng_newhook_t	ng_mppc_newhook;
171af7ab184SArchie Cobbs static ng_rcvdata_t	ng_mppc_rcvdata;
172af7ab184SArchie Cobbs static ng_disconnect_t	ng_mppc_disconnect;
173af7ab184SArchie Cobbs 
174af7ab184SArchie Cobbs /* Helper functions */
175af7ab184SArchie Cobbs static int	ng_mppc_compress(node_p node,
176ce52e8f4SAlexander Motin 			struct mbuf **datap);
177af7ab184SArchie Cobbs static int	ng_mppc_decompress(node_p node,
178ce52e8f4SAlexander Motin 			struct mbuf **datap);
17906c51e6eSAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
180af7ab184SArchie Cobbs static void	ng_mppc_getkey(const u_char *h, u_char *h2, int len);
181af7ab184SArchie Cobbs static void	ng_mppc_updatekey(u_int32_t bits,
182af7ab184SArchie Cobbs 			u_char *key0, u_char *key, struct rc4_state *rc4);
18306c51e6eSAlexander Motin #endif
184af7ab184SArchie Cobbs static void	ng_mppc_reset_req(node_p node);
185af7ab184SArchie Cobbs 
186af7ab184SArchie Cobbs /* Node type descriptor */
187af7ab184SArchie Cobbs static struct ng_type ng_mppc_typestruct = {
188f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
189f8aae777SJulian Elischer 	.name =		NG_MPPC_NODE_TYPE,
190f8aae777SJulian Elischer 	.constructor =	ng_mppc_constructor,
191f8aae777SJulian Elischer 	.rcvmsg =	ng_mppc_rcvmsg,
192f8aae777SJulian Elischer 	.shutdown =	ng_mppc_shutdown,
193f8aae777SJulian Elischer 	.newhook =	ng_mppc_newhook,
194f8aae777SJulian Elischer 	.rcvdata =	ng_mppc_rcvdata,
195f8aae777SJulian Elischer 	.disconnect =	ng_mppc_disconnect,
196af7ab184SArchie Cobbs };
197af7ab184SArchie Cobbs NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
198af7ab184SArchie Cobbs 
199233896e9SDoug Ambrisko #ifdef NETGRAPH_MPPC_ENCRYPTION
200233896e9SDoug Ambrisko /* Depend on separate rc4 module */
201233896e9SDoug Ambrisko MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
202233896e9SDoug Ambrisko #endif
203233896e9SDoug Ambrisko 
20434fd2381SArchie Cobbs /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
205af7ab184SArchie Cobbs static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
206af7ab184SArchie Cobbs 
207af7ab184SArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
208af7ab184SArchie Cobbs 
209af7ab184SArchie Cobbs /************************************************************************
210af7ab184SArchie Cobbs 			NETGRAPH NODE STUFF
211af7ab184SArchie Cobbs  ************************************************************************/
212af7ab184SArchie Cobbs 
213af7ab184SArchie Cobbs /*
214af7ab184SArchie Cobbs  * Node type constructor
215af7ab184SArchie Cobbs  */
216af7ab184SArchie Cobbs static int
217069154d5SJulian Elischer ng_mppc_constructor(node_p node)
218af7ab184SArchie Cobbs {
219af7ab184SArchie Cobbs 	priv_p priv;
220af7ab184SArchie Cobbs 
221af7ab184SArchie Cobbs 	/* Allocate private structure */
222674d86bfSGleb Smirnoff 	priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_WAITOK | M_ZERO);
223af7ab184SArchie Cobbs 
22430400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
225af7ab184SArchie Cobbs 
2262f07580bSGleb Smirnoff 	/* This node is not thread safe. */
2272f07580bSGleb Smirnoff 	NG_NODE_FORCE_WRITER(node);
2282f07580bSGleb Smirnoff 
229af7ab184SArchie Cobbs 	/* Done */
230af7ab184SArchie Cobbs 	return (0);
231af7ab184SArchie Cobbs }
232af7ab184SArchie Cobbs 
233af7ab184SArchie Cobbs /*
234af7ab184SArchie Cobbs  * Give our OK for a hook to be added
235af7ab184SArchie Cobbs  */
236af7ab184SArchie Cobbs static int
237af7ab184SArchie Cobbs ng_mppc_newhook(node_p node, hook_p hook, const char *name)
238af7ab184SArchie Cobbs {
23930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
240af7ab184SArchie Cobbs 	hook_p *hookPtr;
241af7ab184SArchie Cobbs 
242af7ab184SArchie Cobbs 	/* Check hook name */
243af7ab184SArchie Cobbs 	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
244af7ab184SArchie Cobbs 		hookPtr = &priv->xmit.hook;
245af7ab184SArchie Cobbs 	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
246af7ab184SArchie Cobbs 		hookPtr = &priv->recv.hook;
247af7ab184SArchie Cobbs 	else
248af7ab184SArchie Cobbs 		return (EINVAL);
249af7ab184SArchie Cobbs 
250af7ab184SArchie Cobbs 	/* See if already connected */
251af7ab184SArchie Cobbs 	if (*hookPtr != NULL)
252af7ab184SArchie Cobbs 		return (EISCONN);
253af7ab184SArchie Cobbs 
254af7ab184SArchie Cobbs 	/* OK */
255af7ab184SArchie Cobbs 	*hookPtr = hook;
256af7ab184SArchie Cobbs 	return (0);
257af7ab184SArchie Cobbs }
258af7ab184SArchie Cobbs 
259af7ab184SArchie Cobbs /*
260af7ab184SArchie Cobbs  * Receive a control message
261af7ab184SArchie Cobbs  */
262af7ab184SArchie Cobbs static int
263069154d5SJulian Elischer ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
264af7ab184SArchie Cobbs {
26530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
266af7ab184SArchie Cobbs 	struct ng_mesg *resp = NULL;
267af7ab184SArchie Cobbs 	int error = 0;
268069154d5SJulian Elischer 	struct ng_mesg *msg;
269af7ab184SArchie Cobbs 
270069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
271af7ab184SArchie Cobbs 	switch (msg->header.typecookie) {
272af7ab184SArchie Cobbs 	case NGM_MPPC_COOKIE:
273af7ab184SArchie Cobbs 		switch (msg->header.cmd) {
274af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_COMP:
275af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_DECOMP:
276af7ab184SArchie Cobbs 		    {
277af7ab184SArchie Cobbs 			struct ng_mppc_config *const cfg
278af7ab184SArchie Cobbs 			    = (struct ng_mppc_config *)msg->data;
279af7ab184SArchie Cobbs 			const int isComp =
280af7ab184SArchie Cobbs 			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
281af7ab184SArchie Cobbs 			struct ng_mppc_dir *const d = isComp ?
282af7ab184SArchie Cobbs 			    &priv->xmit : &priv->recv;
283af7ab184SArchie Cobbs 
284af7ab184SArchie Cobbs 			/* Check configuration */
285af7ab184SArchie Cobbs 			if (msg->header.arglen != sizeof(*cfg))
286af7ab184SArchie Cobbs 				ERROUT(EINVAL);
287af7ab184SArchie Cobbs 			if (cfg->enable) {
288af7ab184SArchie Cobbs 				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
289af7ab184SArchie Cobbs 					ERROUT(EINVAL);
290af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_COMPRESSION
291af7ab184SArchie Cobbs 				if ((cfg->bits & MPPC_BIT) != 0)
292af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
293af7ab184SArchie Cobbs #endif
294af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_ENCRYPTION
295af7ab184SArchie Cobbs 				if ((cfg->bits & MPPE_BITS) != 0)
296af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
297af7ab184SArchie Cobbs #endif
298af7ab184SArchie Cobbs 			} else
299af7ab184SArchie Cobbs 				cfg->bits = 0;
300af7ab184SArchie Cobbs 
301af7ab184SArchie Cobbs 			/* Save return address so we can send reset-req's */
302f3059f39SArchie Cobbs 			if (!isComp)
303069154d5SJulian Elischer 				priv->ctrlnode = NGI_RETADDR(item);
304af7ab184SArchie Cobbs 
305af7ab184SArchie Cobbs 			/* Configuration is OK, reset to it */
306af7ab184SArchie Cobbs 			d->cfg = *cfg;
307af7ab184SArchie Cobbs 
308af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
309af7ab184SArchie Cobbs 			/* Initialize state buffers for compression */
310af7ab184SArchie Cobbs 			if (d->history != NULL) {
3111ede983cSDag-Erling Smørgrav 				free(d->history, M_NETGRAPH_MPPC);
312af7ab184SArchie Cobbs 				d->history = NULL;
313af7ab184SArchie Cobbs 			}
314af7ab184SArchie Cobbs 			if ((cfg->bits & MPPC_BIT) != 0) {
315e11e3f18SDag-Erling Smørgrav 				d->history = malloc(isComp ?
316e11e3f18SDag-Erling Smørgrav 				    MPPC_SizeOfCompressionHistory() :
317af7ab184SArchie Cobbs 				    MPPC_SizeOfDecompressionHistory(),
3189c8c302fSJulian Elischer 				    M_NETGRAPH_MPPC, M_NOWAIT);
319af7ab184SArchie Cobbs 				if (d->history == NULL)
320af7ab184SArchie Cobbs 					ERROUT(ENOMEM);
321af7ab184SArchie Cobbs 				if (isComp)
322af7ab184SArchie Cobbs 					MPPC_InitCompressionHistory(d->history);
323af7ab184SArchie Cobbs 				else {
324af7ab184SArchie Cobbs 					MPPC_InitDecompressionHistory(
325af7ab184SArchie Cobbs 					    d->history);
326af7ab184SArchie Cobbs 				}
327af7ab184SArchie Cobbs 			}
328af7ab184SArchie Cobbs #endif
329af7ab184SArchie Cobbs 
330af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
331af7ab184SArchie Cobbs 			/* Generate initial session keys for encryption */
332af7ab184SArchie Cobbs 			if ((cfg->bits & MPPE_BITS) != 0) {
333af7ab184SArchie Cobbs 				const int keylen = KEYLEN(cfg->bits);
334af7ab184SArchie Cobbs 
335af7ab184SArchie Cobbs 				bcopy(cfg->startkey, d->key, keylen);
336af7ab184SArchie Cobbs 				ng_mppc_getkey(cfg->startkey, d->key, keylen);
33734fd2381SArchie Cobbs 				if ((cfg->bits & MPPE_40) != 0)
33834fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 3);
33934fd2381SArchie Cobbs 				else if ((cfg->bits & MPPE_56) != 0)
34034fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 1);
341af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, keylen);
342af7ab184SArchie Cobbs 			}
343af7ab184SArchie Cobbs #endif
344af7ab184SArchie Cobbs 
345af7ab184SArchie Cobbs 			/* Initialize other state */
346af7ab184SArchie Cobbs 			d->cc = 0;
347af7ab184SArchie Cobbs 			d->flushed = 0;
348af7ab184SArchie Cobbs 			break;
349af7ab184SArchie Cobbs 		    }
350af7ab184SArchie Cobbs 
351af7ab184SArchie Cobbs 		case NGM_MPPC_RESETREQ:
352af7ab184SArchie Cobbs 			ng_mppc_reset_req(node);
353af7ab184SArchie Cobbs 			break;
354af7ab184SArchie Cobbs 
355af7ab184SArchie Cobbs 		default:
356af7ab184SArchie Cobbs 			error = EINVAL;
357af7ab184SArchie Cobbs 			break;
358af7ab184SArchie Cobbs 		}
359af7ab184SArchie Cobbs 		break;
360af7ab184SArchie Cobbs 	default:
361af7ab184SArchie Cobbs 		error = EINVAL;
362af7ab184SArchie Cobbs 		break;
363af7ab184SArchie Cobbs 	}
364af7ab184SArchie Cobbs done:
365069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
366069154d5SJulian Elischer 	NG_FREE_MSG(msg);
367af7ab184SArchie Cobbs 	return (error);
368af7ab184SArchie Cobbs }
369af7ab184SArchie Cobbs 
370af7ab184SArchie Cobbs /*
371af7ab184SArchie Cobbs  * Receive incoming data on our hook.
372af7ab184SArchie Cobbs  */
373af7ab184SArchie Cobbs static int
374069154d5SJulian Elischer ng_mppc_rcvdata(hook_p hook, item_p item)
375af7ab184SArchie Cobbs {
37630400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
37730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
378af7ab184SArchie Cobbs 	int error;
379069154d5SJulian Elischer 	struct mbuf *m;
380af7ab184SArchie Cobbs 
381069154d5SJulian Elischer 	NGI_GET_M(item, m);
382af7ab184SArchie Cobbs 	/* Compress and/or encrypt */
383af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook) {
384af7ab184SArchie Cobbs 		if (!priv->xmit.cfg.enable) {
385069154d5SJulian Elischer 			NG_FREE_M(m);
386069154d5SJulian Elischer 			NG_FREE_ITEM(item);
387af7ab184SArchie Cobbs 			return (ENXIO);
388af7ab184SArchie Cobbs 		}
389ce52e8f4SAlexander Motin 		if ((error = ng_mppc_compress(node, &m)) != 0) {
390069154d5SJulian Elischer 			NG_FREE_ITEM(item);
391af7ab184SArchie Cobbs 			return(error);
392af7ab184SArchie Cobbs 		}
393ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
394af7ab184SArchie Cobbs 		return (error);
395af7ab184SArchie Cobbs 	}
396af7ab184SArchie Cobbs 
397af7ab184SArchie Cobbs 	/* Decompress and/or decrypt */
398af7ab184SArchie Cobbs 	if (hook == priv->recv.hook) {
399af7ab184SArchie Cobbs 		if (!priv->recv.cfg.enable) {
400069154d5SJulian Elischer 			NG_FREE_M(m);
401069154d5SJulian Elischer 			NG_FREE_ITEM(item);
402af7ab184SArchie Cobbs 			return (ENXIO);
403af7ab184SArchie Cobbs 		}
404ce52e8f4SAlexander Motin 		if ((error = ng_mppc_decompress(node, &m)) != 0) {
405069154d5SJulian Elischer 			NG_FREE_ITEM(item);
406facfd889SArchie Cobbs 			if (error == EINVAL && priv->ctrlnode != 0) {
407af7ab184SArchie Cobbs 				struct ng_mesg *msg;
408af7ab184SArchie Cobbs 
409af7ab184SArchie Cobbs 				/* Need to send a reset-request */
410af7ab184SArchie Cobbs 				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
411af7ab184SArchie Cobbs 				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
412af7ab184SArchie Cobbs 				if (msg == NULL)
413af7ab184SArchie Cobbs 					return (error);
414069154d5SJulian Elischer 				NG_SEND_MSG_ID(error, node, msg,
415facfd889SArchie Cobbs 					priv->ctrlnode, 0);
416af7ab184SArchie Cobbs 			}
417af7ab184SArchie Cobbs 			return (error);
418af7ab184SArchie Cobbs 		}
419ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
420af7ab184SArchie Cobbs 		return (error);
421af7ab184SArchie Cobbs 	}
422af7ab184SArchie Cobbs 
423af7ab184SArchie Cobbs 	/* Oops */
4246e551fb6SDavid E. O'Brien 	panic("%s: unknown hook", __func__);
425af7ab184SArchie Cobbs }
426af7ab184SArchie Cobbs 
427af7ab184SArchie Cobbs /*
428af7ab184SArchie Cobbs  * Destroy node
429af7ab184SArchie Cobbs  */
430af7ab184SArchie Cobbs static int
431069154d5SJulian Elischer ng_mppc_shutdown(node_p node)
432af7ab184SArchie Cobbs {
43330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
434af7ab184SArchie Cobbs 
435af7ab184SArchie Cobbs 	/* Take down netgraph node */
436af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
437af7ab184SArchie Cobbs 	if (priv->xmit.history != NULL)
4381ede983cSDag-Erling Smørgrav 		free(priv->xmit.history, M_NETGRAPH_MPPC);
439af7ab184SArchie Cobbs 	if (priv->recv.history != NULL)
4401ede983cSDag-Erling Smørgrav 		free(priv->recv.history, M_NETGRAPH_MPPC);
441af7ab184SArchie Cobbs #endif
442af7ab184SArchie Cobbs 	bzero(priv, sizeof(*priv));
4431ede983cSDag-Erling Smørgrav 	free(priv, M_NETGRAPH_MPPC);
44430400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
44530400f03SJulian Elischer 	NG_NODE_UNREF(node);		/* let the node escape */
446af7ab184SArchie Cobbs 	return (0);
447af7ab184SArchie Cobbs }
448af7ab184SArchie Cobbs 
449af7ab184SArchie Cobbs /*
450af7ab184SArchie Cobbs  * Hook disconnection
451af7ab184SArchie Cobbs  */
452af7ab184SArchie Cobbs static int
453af7ab184SArchie Cobbs ng_mppc_disconnect(hook_p hook)
454af7ab184SArchie Cobbs {
45530400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
45630400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
457af7ab184SArchie Cobbs 
458af7ab184SArchie Cobbs 	/* Zero out hook pointer */
459af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook)
460af7ab184SArchie Cobbs 		priv->xmit.hook = NULL;
461af7ab184SArchie Cobbs 	if (hook == priv->recv.hook)
462af7ab184SArchie Cobbs 		priv->recv.hook = NULL;
463af7ab184SArchie Cobbs 
464af7ab184SArchie Cobbs 	/* Go away if no longer connected */
46530400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(node) == 0)
46630400f03SJulian Elischer 	&& NG_NODE_IS_VALID(node))
467069154d5SJulian Elischer 		ng_rmnode_self(node);
468af7ab184SArchie Cobbs 	return (0);
469af7ab184SArchie Cobbs }
470af7ab184SArchie Cobbs 
471af7ab184SArchie Cobbs /************************************************************************
472af7ab184SArchie Cobbs 			HELPER STUFF
473af7ab184SArchie Cobbs  ************************************************************************/
474af7ab184SArchie Cobbs 
475af7ab184SArchie Cobbs /*
476af7ab184SArchie Cobbs  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
477af7ab184SArchie Cobbs  * The original mbuf is not free'd.
478af7ab184SArchie Cobbs  */
479af7ab184SArchie Cobbs static int
480ce52e8f4SAlexander Motin ng_mppc_compress(node_p node, struct mbuf **datap)
481af7ab184SArchie Cobbs {
48230400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
483af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
484af7ab184SArchie Cobbs 	u_int16_t header;
485ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
486af7ab184SArchie Cobbs 
487e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
488eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
489e4651e05SAlexander Motin 	if (m == NULL)
490e4651e05SAlexander Motin 		return (ENOMEM);
491e4651e05SAlexander Motin 
492af7ab184SArchie Cobbs 	/* Initialize */
493af7ab184SArchie Cobbs 	header = d->cc;
494adecf751SAlexander Motin 
495adecf751SAlexander Motin 	/* Always set the flushed bit in stateless mode */
496adecf751SAlexander Motin 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
497af7ab184SArchie Cobbs 		header |= MPPC_FLAG_FLUSHED;
498af7ab184SArchie Cobbs 		d->flushed = 0;
499af7ab184SArchie Cobbs 	}
500af7ab184SArchie Cobbs 
501ce52e8f4SAlexander Motin 	/* Compress packet (if compression enabled) */
502af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
503af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPC_BIT) != 0) {
504af7ab184SArchie Cobbs 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
505ce52e8f4SAlexander Motin 		u_char *inbuf, *outbuf;
50667898a23SAlexander Motin 		int outlen, inlen, ina;
507af7ab184SArchie Cobbs 		u_char *source, *dest;
508af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
509af7ab184SArchie Cobbs 		int rtn;
510af7ab184SArchie Cobbs 
511ce52e8f4SAlexander Motin 		/* Work with contiguous regions of memory. */
512ce52e8f4SAlexander Motin 		inlen = m->m_pkthdr.len;
51367898a23SAlexander Motin 		if (m->m_next == NULL) {
51467898a23SAlexander Motin 			inbuf = mtod(m, u_char *);
51567898a23SAlexander Motin 			ina = 0;
51667898a23SAlexander Motin 		} else {
517ce52e8f4SAlexander Motin 			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
51811d1cadeSAlexander Motin 			if (inbuf == NULL)
51911d1cadeSAlexander Motin 				goto err1;
520ce52e8f4SAlexander Motin 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
52167898a23SAlexander Motin 			ina = 1;
52267898a23SAlexander Motin 		}
523ce52e8f4SAlexander Motin 
524ce52e8f4SAlexander Motin 		outlen = MPPC_MAX_BLOWUP(inlen);
525ce52e8f4SAlexander Motin 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
526ce52e8f4SAlexander Motin 		if (outbuf == NULL) {
52767898a23SAlexander Motin 			if (ina)
528ce52e8f4SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
52911d1cadeSAlexander Motin err1:
53011d1cadeSAlexander Motin 			m_freem(m);
53111d1cadeSAlexander Motin 			MPPC_InitCompressionHistory(d->history);
53211d1cadeSAlexander Motin 			d->flushed = 1;
533ce52e8f4SAlexander Motin 			return (ENOMEM);
534ce52e8f4SAlexander Motin 		}
535ce52e8f4SAlexander Motin 
536af7ab184SArchie Cobbs 		/* Prepare to compress */
537af7ab184SArchie Cobbs 		source = inbuf;
538af7ab184SArchie Cobbs 		sourceCnt = inlen;
539ce52e8f4SAlexander Motin 		dest = outbuf;
540ce52e8f4SAlexander Motin 		destCnt = outlen;
541af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
542af7ab184SArchie Cobbs 			flags |= MPPC_SAVE_HISTORY;
543af7ab184SArchie Cobbs 
544af7ab184SArchie Cobbs 		/* Compress */
545af7ab184SArchie Cobbs 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
546af7ab184SArchie Cobbs 			&destCnt, d->history, flags, 0);
547af7ab184SArchie Cobbs 
548af7ab184SArchie Cobbs 		/* Check return value */
5496e551fb6SDavid E. O'Brien 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
550af7ab184SArchie Cobbs 		if ((rtn & MPPC_EXPANDED) == 0
551af7ab184SArchie Cobbs 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
552af7ab184SArchie Cobbs 			outlen -= destCnt;
553af7ab184SArchie Cobbs 			header |= MPPC_FLAG_COMPRESSED;
554af7ab184SArchie Cobbs 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
555af7ab184SArchie Cobbs 				header |= MPPC_FLAG_RESTART;
556ce52e8f4SAlexander Motin 
557ce52e8f4SAlexander Motin 			/* Replace m by the compresed one. */
558e4651e05SAlexander Motin 			m_copyback(m, 0, outlen, (caddr_t)outbuf);
559e4651e05SAlexander Motin 			if (m->m_pkthdr.len < outlen) {
560ce52e8f4SAlexander Motin 				m_freem(m);
561e4651e05SAlexander Motin 				m = NULL;
562e4651e05SAlexander Motin 			} else if (outlen < m->m_pkthdr.len)
563e4651e05SAlexander Motin 				m_adj(m, outlen - m->m_pkthdr.len);
564af7ab184SArchie Cobbs 		}
565af7ab184SArchie Cobbs 		d->flushed = (rtn & MPPC_EXPANDED) != 0
566af7ab184SArchie Cobbs 		    || (flags & MPPC_SAVE_HISTORY) == 0;
567ce52e8f4SAlexander Motin 
56867898a23SAlexander Motin 		if (ina)
569ce52e8f4SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
570ce52e8f4SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
571ce52e8f4SAlexander Motin 
572e4651e05SAlexander Motin 		/* Check mbuf chain reload result. */
57311d1cadeSAlexander Motin 		if (m == NULL) {
57411d1cadeSAlexander Motin 			if (!d->flushed) {
57511d1cadeSAlexander Motin 				MPPC_InitCompressionHistory(d->history);
57611d1cadeSAlexander Motin 				d->flushed = 1;
57711d1cadeSAlexander Motin 			}
578ce52e8f4SAlexander Motin 			return (ENOMEM);
579af7ab184SArchie Cobbs 		}
58011d1cadeSAlexander Motin 	}
581af7ab184SArchie Cobbs #endif
582af7ab184SArchie Cobbs 
583af7ab184SArchie Cobbs 	/* Now encrypt packet (if encryption enabled) */
584af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
585af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_BITS) != 0) {
586ce52e8f4SAlexander Motin 		struct mbuf *m1;
587af7ab184SArchie Cobbs 
5886370fd6bSAlexander Motin 		/* Set header bits */
589af7ab184SArchie Cobbs 		header |= MPPC_FLAG_ENCRYPTED;
590af7ab184SArchie Cobbs 
591af7ab184SArchie Cobbs 		/* Update key if it's time */
592af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
593af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
594af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
595af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
5966370fd6bSAlexander Motin 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
5976370fd6bSAlexander Motin 			/* Need to reset key if we say we did
5986370fd6bSAlexander Motin 			   and ng_mppc_updatekey wasn't called to do it also. */
5996370fd6bSAlexander Motin 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
600af7ab184SArchie Cobbs 		}
601af7ab184SArchie Cobbs 
602af7ab184SArchie Cobbs 		/* Encrypt packet */
603ce52e8f4SAlexander Motin 		m1 = m;
604ce52e8f4SAlexander Motin 		while (m1) {
605ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
606ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
607ce52e8f4SAlexander Motin 			m1 = m1->m_next;
608ce52e8f4SAlexander Motin 		}
609af7ab184SArchie Cobbs 	}
610af7ab184SArchie Cobbs #endif
611af7ab184SArchie Cobbs 
612755bc287SAlexander Motin 	/* Update coherency count for next time (12 bit arithmetic) */
613755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
614af7ab184SArchie Cobbs 
615af7ab184SArchie Cobbs 	/* Install header */
616eb1b1807SGleb Smirnoff 	M_PREPEND(m, MPPC_HDRLEN, M_NOWAIT);
617ce52e8f4SAlexander Motin 	if (m != NULL)
61839228864SAlexander Motin 		be16enc(mtod(m, void *), header);
619af7ab184SArchie Cobbs 
620ce52e8f4SAlexander Motin 	*datap = m;
621ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
622af7ab184SArchie Cobbs }
623af7ab184SArchie Cobbs 
624af7ab184SArchie Cobbs /*
625af7ab184SArchie Cobbs  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
626af7ab184SArchie Cobbs  * The original mbuf is not free'd.
627af7ab184SArchie Cobbs  */
628af7ab184SArchie Cobbs static int
629ce52e8f4SAlexander Motin ng_mppc_decompress(node_p node, struct mbuf **datap)
630af7ab184SArchie Cobbs {
63130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
632af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->recv;
633f3059f39SArchie Cobbs 	u_int16_t header, cc;
634f3059f39SArchie Cobbs 	u_int numLost;
635ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
636af7ab184SArchie Cobbs 
637e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
638eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
639e4651e05SAlexander Motin 	if (m == NULL)
640e4651e05SAlexander Motin 		return (ENOMEM);
641e4651e05SAlexander Motin 
642af7ab184SArchie Cobbs 	/* Pull off header */
643ce52e8f4SAlexander Motin 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
644ce52e8f4SAlexander Motin 		m_freem(m);
645af7ab184SArchie Cobbs 		return (EINVAL);
646ce52e8f4SAlexander Motin 	}
64739228864SAlexander Motin 	header = be16dec(mtod(m, void *));
648af7ab184SArchie Cobbs 	cc = (header & MPPC_CCOUNT_MASK);
649ce52e8f4SAlexander Motin 	m_adj(m, MPPC_HDRLEN);
650af7ab184SArchie Cobbs 
651f3059f39SArchie Cobbs 	/* Check for an unexpected jump in the sequence number */
652af7ab184SArchie Cobbs 	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
653af7ab184SArchie Cobbs 
654af7ab184SArchie Cobbs 	/* If flushed bit set, we can always handle packet */
655af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_FLUSHED) != 0) {
656af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
657af7ab184SArchie Cobbs 		if (d->history != NULL)
658af7ab184SArchie Cobbs 			MPPC_InitDecompressionHistory(d->history);
659af7ab184SArchie Cobbs #endif
660af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
661af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
662f3059f39SArchie Cobbs 			u_int rekey;
663af7ab184SArchie Cobbs 
664f3059f39SArchie Cobbs 			/* How many times are we going to have to re-key? */
665f3059f39SArchie Cobbs 			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
666f3059f39SArchie Cobbs 			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
667*ee652839SAlexander Motin 			if (rekey > mppe_max_rekey) {
668*ee652839SAlexander Motin 			    if (mppe_block_on_max_rekey) {
669*ee652839SAlexander Motin 				if (mppe_log_max_rekey) {
6705372c30bSGleb Smirnoff 				    log(LOG_ERR, "%s: too many (%d) packets"
671*ee652839SAlexander Motin 					" dropped, disabling node %p!\n",
6725372c30bSGleb Smirnoff 					__func__, numLost, node);
673*ee652839SAlexander Motin 				}
6745372c30bSGleb Smirnoff 				priv->recv.cfg.enable = 0;
6755372c30bSGleb Smirnoff 				goto failed;
676*ee652839SAlexander Motin 			    } else {
677*ee652839SAlexander Motin 				if (mppe_log_max_rekey) {
678*ee652839SAlexander Motin 				    log(LOG_ERR, "%s: %d packets"
679*ee652839SAlexander Motin 					" dropped, node %p\n",
680*ee652839SAlexander Motin 					__func__, numLost, node);
681*ee652839SAlexander Motin 				}
682*ee652839SAlexander Motin 				goto failed;
683*ee652839SAlexander Motin 			    }
6845372c30bSGleb Smirnoff 			}
685f3059f39SArchie Cobbs 
6865372c30bSGleb Smirnoff 			/* Re-key as necessary to catch up to peer */
687af7ab184SArchie Cobbs 			while (d->cc != cc) {
688f3059f39SArchie Cobbs 				if ((d->cfg.bits & MPPE_STATELESS) != 0
689af7ab184SArchie Cobbs 				    || (d->cc & MPPE_UPDATE_MASK)
690af7ab184SArchie Cobbs 				      == MPPE_UPDATE_FLAG) {
691af7ab184SArchie Cobbs 					ng_mppc_updatekey(d->cfg.bits,
692af7ab184SArchie Cobbs 					    d->cfg.startkey, d->key, &d->rc4);
693af7ab184SArchie Cobbs 				}
694755bc287SAlexander Motin 				MPPC_CCOUNT_INC(d->cc);
695af7ab184SArchie Cobbs 			}
696af7ab184SArchie Cobbs 
697af7ab184SArchie Cobbs 			/* Reset key (except in stateless mode, see below) */
698af7ab184SArchie Cobbs 			if ((d->cfg.bits & MPPE_STATELESS) == 0)
699af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
700af7ab184SArchie Cobbs 		}
701af7ab184SArchie Cobbs #endif
702af7ab184SArchie Cobbs 		d->cc = cc;		/* skip over lost seq numbers */
703af7ab184SArchie Cobbs 		numLost = 0;		/* act like no packets were lost */
704af7ab184SArchie Cobbs 	}
705af7ab184SArchie Cobbs 
706af7ab184SArchie Cobbs 	/* Can't decode non-sequential packets without a flushed bit */
707af7ab184SArchie Cobbs 	if (numLost != 0)
708af7ab184SArchie Cobbs 		goto failed;
709af7ab184SArchie Cobbs 
710af7ab184SArchie Cobbs 	/* Decrypt packet */
711af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
712ce52e8f4SAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
713ce52e8f4SAlexander Motin 		struct mbuf *m1;
714ce52e8f4SAlexander Motin #endif
715af7ab184SArchie Cobbs 
716af7ab184SArchie Cobbs 		/* Are we not expecting encryption? */
717af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) == 0) {
718af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7196e551fb6SDavid E. O'Brien 				__func__, "encrypted");
720af7ab184SArchie Cobbs 			goto failed;
721af7ab184SArchie Cobbs 		}
722af7ab184SArchie Cobbs 
723af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
724af7ab184SArchie Cobbs 		/* Update key if it's time (always in stateless mode) */
725af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
726af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
727af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
728af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
729af7ab184SArchie Cobbs 		}
730af7ab184SArchie Cobbs 
731af7ab184SArchie Cobbs 		/* Decrypt packet */
732ce52e8f4SAlexander Motin 		m1 = m;
733ce52e8f4SAlexander Motin 		while (m1 != NULL) {
734ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
735ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
736ce52e8f4SAlexander Motin 			m1 = m1->m_next;
737ce52e8f4SAlexander Motin 		}
738af7ab184SArchie Cobbs #endif
739af7ab184SArchie Cobbs 	} else {
740af7ab184SArchie Cobbs 
741af7ab184SArchie Cobbs 		/* Are we expecting encryption? */
742af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
743af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7446e551fb6SDavid E. O'Brien 				__func__, "unencrypted");
745af7ab184SArchie Cobbs 			goto failed;
746af7ab184SArchie Cobbs 		}
747af7ab184SArchie Cobbs 	}
748af7ab184SArchie Cobbs 
749af7ab184SArchie Cobbs 	/* Update coherency count for next time (12 bit arithmetic) */
750755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
751af7ab184SArchie Cobbs 
752af7ab184SArchie Cobbs 	/* Check for unexpected compressed packet */
753af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0
754af7ab184SArchie Cobbs 	    && (d->cfg.bits & MPPC_BIT) == 0) {
755af7ab184SArchie Cobbs 		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7566e551fb6SDavid E. O'Brien 			__func__, "compressed");
757af7ab184SArchie Cobbs failed:
758ce52e8f4SAlexander Motin 		m_freem(m);
759af7ab184SArchie Cobbs 		return (EINVAL);
760af7ab184SArchie Cobbs 	}
761af7ab184SArchie Cobbs 
762af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
763af7ab184SArchie Cobbs 	/* Decompress packet */
764af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
765af7ab184SArchie Cobbs 		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
76667898a23SAlexander Motin 		u_char *inbuf, *outbuf;
76767898a23SAlexander Motin 		int inlen, outlen, ina;
76867898a23SAlexander Motin 		u_char *source, *dest;
769af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
77067898a23SAlexander Motin 		int rtn;
771ce52e8f4SAlexander Motin 
772ce52e8f4SAlexander Motin 		/* Copy payload into a contiguous region of memory. */
77367898a23SAlexander Motin 		inlen = m->m_pkthdr.len;
77467898a23SAlexander Motin 		if (m->m_next == NULL) {
77567898a23SAlexander Motin                 	inbuf = mtod(m, u_char *);
77667898a23SAlexander Motin 			ina = 0;
77767898a23SAlexander Motin 		} else {
77867898a23SAlexander Motin 		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
77967898a23SAlexander Motin 			if (inbuf == NULL) {
780ce52e8f4SAlexander Motin 				m_freem(m);
781ce52e8f4SAlexander Motin 				return (ENOMEM);
782ce52e8f4SAlexander Motin 			}
78367898a23SAlexander Motin 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
78467898a23SAlexander Motin 			ina = 1;
78567898a23SAlexander Motin 		}
786af7ab184SArchie Cobbs 
787af7ab184SArchie Cobbs 		/* Allocate a buffer for decompressed data */
78867898a23SAlexander Motin 		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
789ce52e8f4SAlexander Motin 		    M_NETGRAPH_MPPC, M_NOWAIT);
79067898a23SAlexander Motin 		if (outbuf == NULL) {
791ce52e8f4SAlexander Motin 			m_freem(m);
79267898a23SAlexander Motin 			if (ina)
79367898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
794af7ab184SArchie Cobbs 			return (ENOMEM);
795af7ab184SArchie Cobbs 		}
79667898a23SAlexander Motin 		outlen = MPPC_DECOMP_BUFSIZE;
797af7ab184SArchie Cobbs 
798af7ab184SArchie Cobbs 		/* Prepare to decompress */
79967898a23SAlexander Motin 		source = inbuf;
80067898a23SAlexander Motin 		sourceCnt = inlen;
80167898a23SAlexander Motin 		dest = outbuf;
80267898a23SAlexander Motin 		destCnt = outlen;
803af7ab184SArchie Cobbs 		if ((header & MPPC_FLAG_RESTART) != 0)
804af7ab184SArchie Cobbs 			flags |= MPPC_RESTART_HISTORY;
805af7ab184SArchie Cobbs 
806af7ab184SArchie Cobbs 		/* Decompress */
807af7ab184SArchie Cobbs 		rtn = MPPC_Decompress(&source, &dest,
808af7ab184SArchie Cobbs 			&sourceCnt, &destCnt, d->history, flags);
809af7ab184SArchie Cobbs 
810af7ab184SArchie Cobbs 		/* Check return value */
8116e551fb6SDavid E. O'Brien 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
812af7ab184SArchie Cobbs 		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
813af7ab184SArchie Cobbs 		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
814af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: decomp returned 0x%x",
8156e551fb6SDavid E. O'Brien 			    __func__, rtn);
81667898a23SAlexander Motin 			if (ina)
81767898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
81867898a23SAlexander Motin 			free(outbuf, M_NETGRAPH_MPPC);
819af7ab184SArchie Cobbs 			goto failed;
820af7ab184SArchie Cobbs 		}
821af7ab184SArchie Cobbs 
822af7ab184SArchie Cobbs 		/* Replace compressed data with decompressed data */
82367898a23SAlexander Motin 		if (ina)
82467898a23SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
82567898a23SAlexander Motin 		outlen -= destCnt;
826ce52e8f4SAlexander Motin 
82767898a23SAlexander Motin 		m_copyback(m, 0, outlen, (caddr_t)outbuf);
82867898a23SAlexander Motin 		if (m->m_pkthdr.len < outlen) {
829ce52e8f4SAlexander Motin 			m_freem(m);
830e4651e05SAlexander Motin 			m = NULL;
83167898a23SAlexander Motin 		} else if (outlen < m->m_pkthdr.len)
83267898a23SAlexander Motin 			m_adj(m, outlen - m->m_pkthdr.len);
83367898a23SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
834af7ab184SArchie Cobbs 	}
835af7ab184SArchie Cobbs #endif
836af7ab184SArchie Cobbs 
837af7ab184SArchie Cobbs 	/* Return result in an mbuf */
838ce52e8f4SAlexander Motin 	*datap = m;
839ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
840af7ab184SArchie Cobbs }
841af7ab184SArchie Cobbs 
842af7ab184SArchie Cobbs /*
843af7ab184SArchie Cobbs  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
844af7ab184SArchie Cobbs  */
845af7ab184SArchie Cobbs static void
846af7ab184SArchie Cobbs ng_mppc_reset_req(node_p node)
847af7ab184SArchie Cobbs {
84830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
849af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
850af7ab184SArchie Cobbs 
851af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
852af7ab184SArchie Cobbs 	if (d->history != NULL)
853af7ab184SArchie Cobbs 		MPPC_InitCompressionHistory(d->history);
854af7ab184SArchie Cobbs #endif
855af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
856af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_STATELESS) == 0)
857af7ab184SArchie Cobbs 		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
858af7ab184SArchie Cobbs #endif
859af7ab184SArchie Cobbs 	d->flushed = 1;
860af7ab184SArchie Cobbs }
861af7ab184SArchie Cobbs 
86206c51e6eSAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
863af7ab184SArchie Cobbs /*
864af7ab184SArchie Cobbs  * Generate a new encryption key
865af7ab184SArchie Cobbs  */
866af7ab184SArchie Cobbs static void
867af7ab184SArchie Cobbs ng_mppc_getkey(const u_char *h, u_char *h2, int len)
868af7ab184SArchie Cobbs {
869b3d298b9SAlexander Motin 	static const u_char pad1[40] =
870b3d298b9SAlexander Motin 	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
871b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
872b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
873b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
874b3d298b9SAlexander Motin 	static const u_char pad2[40] =
875b3d298b9SAlexander Motin 	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
876b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
877b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
878b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
879af7ab184SArchie Cobbs 	u_char hash[20];
880af7ab184SArchie Cobbs 	SHA1_CTX c;
881af7ab184SArchie Cobbs 
882af7ab184SArchie Cobbs 	SHA1Init(&c);
883af7ab184SArchie Cobbs 	SHA1Update(&c, h, len);
884592009a3SAlexander Motin 	SHA1Update(&c, pad1, sizeof(pad1));
885af7ab184SArchie Cobbs 	SHA1Update(&c, h2, len);
886af7ab184SArchie Cobbs 	SHA1Update(&c, pad2, sizeof(pad2));
887af7ab184SArchie Cobbs 	SHA1Final(hash, &c);
888af7ab184SArchie Cobbs 	bcopy(hash, h2, len);
889af7ab184SArchie Cobbs }
890af7ab184SArchie Cobbs 
891af7ab184SArchie Cobbs /*
892af7ab184SArchie Cobbs  * Update the encryption key
893af7ab184SArchie Cobbs  */
894af7ab184SArchie Cobbs static void
895af7ab184SArchie Cobbs ng_mppc_updatekey(u_int32_t bits,
896af7ab184SArchie Cobbs 	u_char *key0, u_char *key, struct rc4_state *rc4)
897af7ab184SArchie Cobbs {
898af7ab184SArchie Cobbs 	const int keylen = KEYLEN(bits);
899af7ab184SArchie Cobbs 
900af7ab184SArchie Cobbs 	ng_mppc_getkey(key0, key, keylen);
901af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
902af7ab184SArchie Cobbs 	rc4_crypt(rc4, key, key, keylen);
90334fd2381SArchie Cobbs 	if ((bits & MPPE_40) != 0)
90434fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 3);
90534fd2381SArchie Cobbs 	else if ((bits & MPPE_56) != 0)
90634fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 1);
907af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
908af7ab184SArchie Cobbs }
90906c51e6eSAlexander Motin #endif
910af7ab184SArchie Cobbs 
911