xref: /freebsd/sys/netgraph/ng_mppc.c (revision 2ff63af9b88c7413b7d71715b5532625752a248e)
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  */
42af7ab184SArchie Cobbs 
43af7ab184SArchie Cobbs /*
44af7ab184SArchie Cobbs  * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type.
45af7ab184SArchie Cobbs  *
46af7ab184SArchie Cobbs  * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or
47af7ab184SArchie Cobbs  * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful.
48af7ab184SArchie Cobbs  */
49af7ab184SArchie Cobbs 
50af7ab184SArchie Cobbs #include <sys/param.h>
51af7ab184SArchie Cobbs #include <sys/systm.h>
52af7ab184SArchie Cobbs #include <sys/kernel.h>
53af7ab184SArchie Cobbs #include <sys/mbuf.h>
54af7ab184SArchie Cobbs #include <sys/malloc.h>
5539228864SAlexander Motin #include <sys/endian.h>
56af7ab184SArchie Cobbs #include <sys/errno.h>
57ee652839SAlexander Motin #include <sys/sysctl.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
68ec5753e0SPedro F. Giffuni #define NETGRAPH_MPPC_COMPRESSION
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 #include <net/mppc.h>
84af7ab184SArchie Cobbs #endif
85af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
86af7ab184SArchie Cobbs #include <crypto/rc4/rc4.h>
87af7ab184SArchie Cobbs #endif
88af7ab184SArchie Cobbs #include <crypto/sha1.h>
89af7ab184SArchie Cobbs 
90af7ab184SArchie Cobbs /* Decompression blowup */
91af7ab184SArchie Cobbs #define MPPC_DECOMP_BUFSIZE	8092            /* allocate buffer this big */
92af7ab184SArchie Cobbs #define MPPC_DECOMP_SAFETY	100             /*   plus this much margin */
93af7ab184SArchie Cobbs 
94af7ab184SArchie Cobbs /* MPPC/MPPE header length */
95af7ab184SArchie Cobbs #define MPPC_HDRLEN		2
96af7ab184SArchie Cobbs 
97af7ab184SArchie Cobbs /* Key length */
98af7ab184SArchie Cobbs #define KEYLEN(b)		(((b) & MPPE_128) ? 16 : 8)
99af7ab184SArchie Cobbs 
1005372c30bSGleb Smirnoff /*
1015372c30bSGleb Smirnoff  * When packets are lost with MPPE, we may have to re-key arbitrarily
1025372c30bSGleb Smirnoff  * many times to 'catch up' to the new jumped-ahead sequence number.
1035372c30bSGleb Smirnoff  * Since this can be expensive, we pose a limit on how many re-keyings
1045372c30bSGleb Smirnoff  * we will do at one time to avoid a possible D.O.S. vulnerability.
1055372c30bSGleb Smirnoff  * This should instead be a configurable parameter.
1065372c30bSGleb Smirnoff  */
1075372c30bSGleb Smirnoff #define MPPE_MAX_REKEY		1000
1085372c30bSGleb Smirnoff 
109*7029da5cSPawel Biernacki SYSCTL_NODE(_net_graph, OID_AUTO, mppe, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
110*7029da5cSPawel Biernacki     "MPPE");
111ee652839SAlexander Motin 
112ee652839SAlexander Motin static int mppe_block_on_max_rekey = 0;
113af3b2549SHans Petter Selasky SYSCTL_INT(_net_graph_mppe, OID_AUTO, block_on_max_rekey, CTLFLAG_RWTUN,
114ee652839SAlexander Motin     &mppe_block_on_max_rekey, 0, "Block node on max MPPE key re-calculations");
115ee652839SAlexander Motin 
116ee652839SAlexander Motin static int mppe_log_max_rekey = 1;
117af3b2549SHans Petter Selasky SYSCTL_INT(_net_graph_mppe, OID_AUTO, log_max_rekey, CTLFLAG_RWTUN,
118ee652839SAlexander Motin     &mppe_log_max_rekey, 0, "Log max MPPE key re-calculations event");
119ee652839SAlexander Motin 
120ee652839SAlexander Motin static int mppe_max_rekey = MPPE_MAX_REKEY;
121af3b2549SHans Petter Selasky SYSCTL_INT(_net_graph_mppe, OID_AUTO, max_rekey, CTLFLAG_RWTUN,
122ee652839SAlexander Motin     &mppe_max_rekey, 0, "Maximum number of MPPE key re-calculations");
123ee652839SAlexander Motin 
124af7ab184SArchie Cobbs /* MPPC packet header bits */
125af7ab184SArchie Cobbs #define MPPC_FLAG_FLUSHED	0x8000		/* xmitter reset state */
126af7ab184SArchie Cobbs #define MPPC_FLAG_RESTART	0x4000		/* compress history restart */
127af7ab184SArchie Cobbs #define MPPC_FLAG_COMPRESSED	0x2000		/* packet is compresed */
128af7ab184SArchie Cobbs #define MPPC_FLAG_ENCRYPTED	0x1000		/* packet is encrypted */
129af7ab184SArchie Cobbs #define MPPC_CCOUNT_MASK	0x0fff		/* sequence number mask */
130af7ab184SArchie Cobbs 
131755bc287SAlexander Motin #define MPPC_CCOUNT_INC(d)	((d) = (((d) + 1) & MPPC_CCOUNT_MASK))
132755bc287SAlexander Motin 
133af7ab184SArchie Cobbs #define MPPE_UPDATE_MASK	0xff		/* coherency count when we're */
134af7ab184SArchie Cobbs #define MPPE_UPDATE_FLAG	0xff		/*   supposed to update key */
135af7ab184SArchie Cobbs 
136af7ab184SArchie Cobbs #define MPPC_COMP_OK		0x05
137af7ab184SArchie Cobbs #define MPPC_DECOMP_OK		0x05
138af7ab184SArchie Cobbs 
139af7ab184SArchie Cobbs /* Per direction info */
140af7ab184SArchie Cobbs struct ng_mppc_dir {
141af7ab184SArchie Cobbs 	struct ng_mppc_config	cfg;		/* configuration */
142af7ab184SArchie Cobbs 	hook_p			hook;		/* netgraph hook */
143af7ab184SArchie Cobbs 	u_int16_t		cc:12;		/* coherency count */
144af7ab184SArchie Cobbs 	u_char			flushed;	/* clean history (xmit only) */
145af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
146af7ab184SArchie Cobbs 	u_char			*history;	/* compression history */
147af7ab184SArchie Cobbs #endif
148af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
149af7ab184SArchie Cobbs 	u_char			key[MPPE_KEY_LEN];	/* session key */
150af7ab184SArchie Cobbs 	struct rc4_state	rc4;			/* rc4 state */
151af7ab184SArchie Cobbs #endif
152af7ab184SArchie Cobbs };
153af7ab184SArchie Cobbs 
154af7ab184SArchie Cobbs /* Node private data */
155af7ab184SArchie Cobbs struct ng_mppc_private {
156af7ab184SArchie Cobbs 	struct ng_mppc_dir	xmit;		/* compress/encrypt config */
157af7ab184SArchie Cobbs 	struct ng_mppc_dir	recv;		/* decompress/decrypt config */
158069154d5SJulian Elischer 	ng_ID_t			ctrlnode;	/* path to controlling node */
159af7ab184SArchie Cobbs };
160af7ab184SArchie Cobbs typedef struct ng_mppc_private *priv_p;
161af7ab184SArchie Cobbs 
162af7ab184SArchie Cobbs /* Netgraph node methods */
163af7ab184SArchie Cobbs static ng_constructor_t	ng_mppc_constructor;
164af7ab184SArchie Cobbs static ng_rcvmsg_t	ng_mppc_rcvmsg;
165069154d5SJulian Elischer static ng_shutdown_t	ng_mppc_shutdown;
166af7ab184SArchie Cobbs static ng_newhook_t	ng_mppc_newhook;
167af7ab184SArchie Cobbs static ng_rcvdata_t	ng_mppc_rcvdata;
168af7ab184SArchie Cobbs static ng_disconnect_t	ng_mppc_disconnect;
169af7ab184SArchie Cobbs 
170af7ab184SArchie Cobbs /* Helper functions */
171af7ab184SArchie Cobbs static int	ng_mppc_compress(node_p node,
172ce52e8f4SAlexander Motin 			struct mbuf **datap);
173af7ab184SArchie Cobbs static int	ng_mppc_decompress(node_p node,
174ce52e8f4SAlexander Motin 			struct mbuf **datap);
17506c51e6eSAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
176af7ab184SArchie Cobbs static void	ng_mppc_getkey(const u_char *h, u_char *h2, int len);
177af7ab184SArchie Cobbs static void	ng_mppc_updatekey(u_int32_t bits,
178af7ab184SArchie Cobbs 			u_char *key0, u_char *key, struct rc4_state *rc4);
17906c51e6eSAlexander Motin #endif
180af7ab184SArchie Cobbs static void	ng_mppc_reset_req(node_p node);
181af7ab184SArchie Cobbs 
182af7ab184SArchie Cobbs /* Node type descriptor */
183af7ab184SArchie Cobbs static struct ng_type ng_mppc_typestruct = {
184f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
185f8aae777SJulian Elischer 	.name =		NG_MPPC_NODE_TYPE,
186f8aae777SJulian Elischer 	.constructor =	ng_mppc_constructor,
187f8aae777SJulian Elischer 	.rcvmsg =	ng_mppc_rcvmsg,
188f8aae777SJulian Elischer 	.shutdown =	ng_mppc_shutdown,
189f8aae777SJulian Elischer 	.newhook =	ng_mppc_newhook,
190f8aae777SJulian Elischer 	.rcvdata =	ng_mppc_rcvdata,
191f8aae777SJulian Elischer 	.disconnect =	ng_mppc_disconnect,
192af7ab184SArchie Cobbs };
193af7ab184SArchie Cobbs NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
194af7ab184SArchie Cobbs 
195233896e9SDoug Ambrisko #ifdef NETGRAPH_MPPC_ENCRYPTION
196233896e9SDoug Ambrisko /* Depend on separate rc4 module */
197233896e9SDoug Ambrisko MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
198233896e9SDoug Ambrisko #endif
199233896e9SDoug Ambrisko 
20034fd2381SArchie Cobbs /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
201af7ab184SArchie Cobbs static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
202af7ab184SArchie Cobbs 
203af7ab184SArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
204af7ab184SArchie Cobbs 
205af7ab184SArchie Cobbs /************************************************************************
206af7ab184SArchie Cobbs 			NETGRAPH NODE STUFF
207af7ab184SArchie Cobbs  ************************************************************************/
208af7ab184SArchie Cobbs 
209af7ab184SArchie Cobbs /*
210af7ab184SArchie Cobbs  * Node type constructor
211af7ab184SArchie Cobbs  */
212af7ab184SArchie Cobbs static int
ng_mppc_constructor(node_p node)213069154d5SJulian Elischer ng_mppc_constructor(node_p node)
214af7ab184SArchie Cobbs {
215af7ab184SArchie Cobbs 	priv_p priv;
216af7ab184SArchie Cobbs 
217af7ab184SArchie Cobbs 	/* Allocate private structure */
218674d86bfSGleb Smirnoff 	priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_WAITOK | M_ZERO);
219af7ab184SArchie Cobbs 
22030400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
221af7ab184SArchie Cobbs 
2222f07580bSGleb Smirnoff 	/* This node is not thread safe. */
2232f07580bSGleb Smirnoff 	NG_NODE_FORCE_WRITER(node);
2242f07580bSGleb Smirnoff 
225af7ab184SArchie Cobbs 	/* Done */
226af7ab184SArchie Cobbs 	return (0);
227af7ab184SArchie Cobbs }
228af7ab184SArchie Cobbs 
229af7ab184SArchie Cobbs /*
230af7ab184SArchie Cobbs  * Give our OK for a hook to be added
231af7ab184SArchie Cobbs  */
232af7ab184SArchie Cobbs static int
ng_mppc_newhook(node_p node,hook_p hook,const char * name)233af7ab184SArchie Cobbs ng_mppc_newhook(node_p node, hook_p hook, const char *name)
234af7ab184SArchie Cobbs {
23530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
236af7ab184SArchie Cobbs 	hook_p *hookPtr;
237af7ab184SArchie Cobbs 
238af7ab184SArchie Cobbs 	/* Check hook name */
239af7ab184SArchie Cobbs 	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
240af7ab184SArchie Cobbs 		hookPtr = &priv->xmit.hook;
241af7ab184SArchie Cobbs 	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
242af7ab184SArchie Cobbs 		hookPtr = &priv->recv.hook;
243af7ab184SArchie Cobbs 	else
244af7ab184SArchie Cobbs 		return (EINVAL);
245af7ab184SArchie Cobbs 
246af7ab184SArchie Cobbs 	/* See if already connected */
247af7ab184SArchie Cobbs 	if (*hookPtr != NULL)
248af7ab184SArchie Cobbs 		return (EISCONN);
249af7ab184SArchie Cobbs 
250af7ab184SArchie Cobbs 	/* OK */
251af7ab184SArchie Cobbs 	*hookPtr = hook;
252af7ab184SArchie Cobbs 	return (0);
253af7ab184SArchie Cobbs }
254af7ab184SArchie Cobbs 
255af7ab184SArchie Cobbs /*
256af7ab184SArchie Cobbs  * Receive a control message
257af7ab184SArchie Cobbs  */
258af7ab184SArchie Cobbs static int
ng_mppc_rcvmsg(node_p node,item_p item,hook_p lasthook)259069154d5SJulian Elischer ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
260af7ab184SArchie Cobbs {
26130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
262af7ab184SArchie Cobbs 	struct ng_mesg *resp = NULL;
263af7ab184SArchie Cobbs 	int error = 0;
264069154d5SJulian Elischer 	struct ng_mesg *msg;
265af7ab184SArchie Cobbs 
266069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
267af7ab184SArchie Cobbs 	switch (msg->header.typecookie) {
268af7ab184SArchie Cobbs 	case NGM_MPPC_COOKIE:
269af7ab184SArchie Cobbs 		switch (msg->header.cmd) {
270af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_COMP:
271af7ab184SArchie Cobbs 		case NGM_MPPC_CONFIG_DECOMP:
272af7ab184SArchie Cobbs 		    {
273af7ab184SArchie Cobbs 			struct ng_mppc_config *const cfg
274af7ab184SArchie Cobbs 			    = (struct ng_mppc_config *)msg->data;
275af7ab184SArchie Cobbs 			const int isComp =
276af7ab184SArchie Cobbs 			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
277af7ab184SArchie Cobbs 			struct ng_mppc_dir *const d = isComp ?
278af7ab184SArchie Cobbs 			    &priv->xmit : &priv->recv;
279af7ab184SArchie Cobbs 
280af7ab184SArchie Cobbs 			/* Check configuration */
281af7ab184SArchie Cobbs 			if (msg->header.arglen != sizeof(*cfg))
282af7ab184SArchie Cobbs 				ERROUT(EINVAL);
283af7ab184SArchie Cobbs 			if (cfg->enable) {
284af7ab184SArchie Cobbs 				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
285af7ab184SArchie Cobbs 					ERROUT(EINVAL);
286af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_COMPRESSION
287af7ab184SArchie Cobbs 				if ((cfg->bits & MPPC_BIT) != 0)
288af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
289af7ab184SArchie Cobbs #endif
290af7ab184SArchie Cobbs #ifndef NETGRAPH_MPPC_ENCRYPTION
291af7ab184SArchie Cobbs 				if ((cfg->bits & MPPE_BITS) != 0)
292af7ab184SArchie Cobbs 					ERROUT(EPROTONOSUPPORT);
293af7ab184SArchie Cobbs #endif
294af7ab184SArchie Cobbs 			} else
295af7ab184SArchie Cobbs 				cfg->bits = 0;
296af7ab184SArchie Cobbs 
297af7ab184SArchie Cobbs 			/* Save return address so we can send reset-req's */
298f3059f39SArchie Cobbs 			if (!isComp)
299069154d5SJulian Elischer 				priv->ctrlnode = NGI_RETADDR(item);
300af7ab184SArchie Cobbs 
301af7ab184SArchie Cobbs 			/* Configuration is OK, reset to it */
302af7ab184SArchie Cobbs 			d->cfg = *cfg;
303af7ab184SArchie Cobbs 
304af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
305af7ab184SArchie Cobbs 			/* Initialize state buffers for compression */
306af7ab184SArchie Cobbs 			if (d->history != NULL) {
3071ede983cSDag-Erling Smørgrav 				free(d->history, M_NETGRAPH_MPPC);
308af7ab184SArchie Cobbs 				d->history = NULL;
309af7ab184SArchie Cobbs 			}
310af7ab184SArchie Cobbs 			if ((cfg->bits & MPPC_BIT) != 0) {
311e11e3f18SDag-Erling Smørgrav 				d->history = malloc(isComp ?
312e11e3f18SDag-Erling Smørgrav 				    MPPC_SizeOfCompressionHistory() :
313af7ab184SArchie Cobbs 				    MPPC_SizeOfDecompressionHistory(),
3149c8c302fSJulian Elischer 				    M_NETGRAPH_MPPC, M_NOWAIT);
315af7ab184SArchie Cobbs 				if (d->history == NULL)
316af7ab184SArchie Cobbs 					ERROUT(ENOMEM);
317af7ab184SArchie Cobbs 				if (isComp)
318af7ab184SArchie Cobbs 					MPPC_InitCompressionHistory(d->history);
319af7ab184SArchie Cobbs 				else {
320af7ab184SArchie Cobbs 					MPPC_InitDecompressionHistory(
321af7ab184SArchie Cobbs 					    d->history);
322af7ab184SArchie Cobbs 				}
323af7ab184SArchie Cobbs 			}
324af7ab184SArchie Cobbs #endif
325af7ab184SArchie Cobbs 
326af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
327af7ab184SArchie Cobbs 			/* Generate initial session keys for encryption */
328af7ab184SArchie Cobbs 			if ((cfg->bits & MPPE_BITS) != 0) {
329af7ab184SArchie Cobbs 				const int keylen = KEYLEN(cfg->bits);
330af7ab184SArchie Cobbs 
331af7ab184SArchie Cobbs 				bcopy(cfg->startkey, d->key, keylen);
332af7ab184SArchie Cobbs 				ng_mppc_getkey(cfg->startkey, d->key, keylen);
33334fd2381SArchie Cobbs 				if ((cfg->bits & MPPE_40) != 0)
33434fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 3);
33534fd2381SArchie Cobbs 				else if ((cfg->bits & MPPE_56) != 0)
33634fd2381SArchie Cobbs 					bcopy(&ng_mppe_weakenkey, d->key, 1);
337af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, keylen);
338af7ab184SArchie Cobbs 			}
339af7ab184SArchie Cobbs #endif
340af7ab184SArchie Cobbs 
341af7ab184SArchie Cobbs 			/* Initialize other state */
342af7ab184SArchie Cobbs 			d->cc = 0;
343af7ab184SArchie Cobbs 			d->flushed = 0;
344af7ab184SArchie Cobbs 			break;
345af7ab184SArchie Cobbs 		    }
346af7ab184SArchie Cobbs 
347af7ab184SArchie Cobbs 		case NGM_MPPC_RESETREQ:
348af7ab184SArchie Cobbs 			ng_mppc_reset_req(node);
349af7ab184SArchie Cobbs 			break;
350af7ab184SArchie Cobbs 
351af7ab184SArchie Cobbs 		default:
352af7ab184SArchie Cobbs 			error = EINVAL;
353af7ab184SArchie Cobbs 			break;
354af7ab184SArchie Cobbs 		}
355af7ab184SArchie Cobbs 		break;
356af7ab184SArchie Cobbs 	default:
357af7ab184SArchie Cobbs 		error = EINVAL;
358af7ab184SArchie Cobbs 		break;
359af7ab184SArchie Cobbs 	}
360af7ab184SArchie Cobbs done:
361069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
362069154d5SJulian Elischer 	NG_FREE_MSG(msg);
363af7ab184SArchie Cobbs 	return (error);
364af7ab184SArchie Cobbs }
365af7ab184SArchie Cobbs 
366af7ab184SArchie Cobbs /*
367af7ab184SArchie Cobbs  * Receive incoming data on our hook.
368af7ab184SArchie Cobbs  */
369af7ab184SArchie Cobbs static int
ng_mppc_rcvdata(hook_p hook,item_p item)370069154d5SJulian Elischer ng_mppc_rcvdata(hook_p hook, item_p item)
371af7ab184SArchie Cobbs {
37230400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
37330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
374af7ab184SArchie Cobbs 	int error;
375069154d5SJulian Elischer 	struct mbuf *m;
376af7ab184SArchie Cobbs 
377069154d5SJulian Elischer 	NGI_GET_M(item, m);
378af7ab184SArchie Cobbs 	/* Compress and/or encrypt */
379af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook) {
380af7ab184SArchie Cobbs 		if (!priv->xmit.cfg.enable) {
381069154d5SJulian Elischer 			NG_FREE_M(m);
382069154d5SJulian Elischer 			NG_FREE_ITEM(item);
383af7ab184SArchie Cobbs 			return (ENXIO);
384af7ab184SArchie Cobbs 		}
385ce52e8f4SAlexander Motin 		if ((error = ng_mppc_compress(node, &m)) != 0) {
386069154d5SJulian Elischer 			NG_FREE_ITEM(item);
387af7ab184SArchie Cobbs 			return(error);
388af7ab184SArchie Cobbs 		}
389ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
390af7ab184SArchie Cobbs 		return (error);
391af7ab184SArchie Cobbs 	}
392af7ab184SArchie Cobbs 
393af7ab184SArchie Cobbs 	/* Decompress and/or decrypt */
394af7ab184SArchie Cobbs 	if (hook == priv->recv.hook) {
395af7ab184SArchie Cobbs 		if (!priv->recv.cfg.enable) {
396069154d5SJulian Elischer 			NG_FREE_M(m);
397069154d5SJulian Elischer 			NG_FREE_ITEM(item);
398af7ab184SArchie Cobbs 			return (ENXIO);
399af7ab184SArchie Cobbs 		}
400ce52e8f4SAlexander Motin 		if ((error = ng_mppc_decompress(node, &m)) != 0) {
401069154d5SJulian Elischer 			NG_FREE_ITEM(item);
402facfd889SArchie Cobbs 			if (error == EINVAL && priv->ctrlnode != 0) {
403af7ab184SArchie Cobbs 				struct ng_mesg *msg;
404af7ab184SArchie Cobbs 
405af7ab184SArchie Cobbs 				/* Need to send a reset-request */
406af7ab184SArchie Cobbs 				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
407af7ab184SArchie Cobbs 				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
408af7ab184SArchie Cobbs 				if (msg == NULL)
409af7ab184SArchie Cobbs 					return (error);
410069154d5SJulian Elischer 				NG_SEND_MSG_ID(error, node, msg,
411facfd889SArchie Cobbs 					priv->ctrlnode, 0);
412af7ab184SArchie Cobbs 			}
413af7ab184SArchie Cobbs 			return (error);
414af7ab184SArchie Cobbs 		}
415ce52e8f4SAlexander Motin 		NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
416af7ab184SArchie Cobbs 		return (error);
417af7ab184SArchie Cobbs 	}
418af7ab184SArchie Cobbs 
419af7ab184SArchie Cobbs 	/* Oops */
4206e551fb6SDavid E. O'Brien 	panic("%s: unknown hook", __func__);
421af7ab184SArchie Cobbs }
422af7ab184SArchie Cobbs 
423af7ab184SArchie Cobbs /*
424af7ab184SArchie Cobbs  * Destroy node
425af7ab184SArchie Cobbs  */
426af7ab184SArchie Cobbs static int
ng_mppc_shutdown(node_p node)427069154d5SJulian Elischer ng_mppc_shutdown(node_p node)
428af7ab184SArchie Cobbs {
42930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
430af7ab184SArchie Cobbs 
431af7ab184SArchie Cobbs 	/* Take down netgraph node */
432af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
433af7ab184SArchie Cobbs 	if (priv->xmit.history != NULL)
4341ede983cSDag-Erling Smørgrav 		free(priv->xmit.history, M_NETGRAPH_MPPC);
435af7ab184SArchie Cobbs 	if (priv->recv.history != NULL)
4361ede983cSDag-Erling Smørgrav 		free(priv->recv.history, M_NETGRAPH_MPPC);
437af7ab184SArchie Cobbs #endif
438af7ab184SArchie Cobbs 	bzero(priv, sizeof(*priv));
4391ede983cSDag-Erling Smørgrav 	free(priv, M_NETGRAPH_MPPC);
44030400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
44130400f03SJulian Elischer 	NG_NODE_UNREF(node);		/* let the node escape */
442af7ab184SArchie Cobbs 	return (0);
443af7ab184SArchie Cobbs }
444af7ab184SArchie Cobbs 
445af7ab184SArchie Cobbs /*
446af7ab184SArchie Cobbs  * Hook disconnection
447af7ab184SArchie Cobbs  */
448af7ab184SArchie Cobbs static int
ng_mppc_disconnect(hook_p hook)449af7ab184SArchie Cobbs ng_mppc_disconnect(hook_p hook)
450af7ab184SArchie Cobbs {
45130400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
45230400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
453af7ab184SArchie Cobbs 
454af7ab184SArchie Cobbs 	/* Zero out hook pointer */
455af7ab184SArchie Cobbs 	if (hook == priv->xmit.hook)
456af7ab184SArchie Cobbs 		priv->xmit.hook = NULL;
457af7ab184SArchie Cobbs 	if (hook == priv->recv.hook)
458af7ab184SArchie Cobbs 		priv->recv.hook = NULL;
459af7ab184SArchie Cobbs 
460af7ab184SArchie Cobbs 	/* Go away if no longer connected */
46130400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(node) == 0)
46230400f03SJulian Elischer 	&& NG_NODE_IS_VALID(node))
463069154d5SJulian Elischer 		ng_rmnode_self(node);
464af7ab184SArchie Cobbs 	return (0);
465af7ab184SArchie Cobbs }
466af7ab184SArchie Cobbs 
467af7ab184SArchie Cobbs /************************************************************************
468af7ab184SArchie Cobbs 			HELPER STUFF
469af7ab184SArchie Cobbs  ************************************************************************/
470af7ab184SArchie Cobbs 
471af7ab184SArchie Cobbs /*
472af7ab184SArchie Cobbs  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
473af7ab184SArchie Cobbs  * The original mbuf is not free'd.
474af7ab184SArchie Cobbs  */
475af7ab184SArchie Cobbs static int
ng_mppc_compress(node_p node,struct mbuf ** datap)476ce52e8f4SAlexander Motin ng_mppc_compress(node_p node, struct mbuf **datap)
477af7ab184SArchie Cobbs {
47830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
479af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
480af7ab184SArchie Cobbs 	u_int16_t header;
481ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
482af7ab184SArchie Cobbs 
483e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
484eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
485e4651e05SAlexander Motin 	if (m == NULL)
486e4651e05SAlexander Motin 		return (ENOMEM);
487e4651e05SAlexander Motin 
488af7ab184SArchie Cobbs 	/* Initialize */
489af7ab184SArchie Cobbs 	header = d->cc;
490adecf751SAlexander Motin 
491adecf751SAlexander Motin 	/* Always set the flushed bit in stateless mode */
492adecf751SAlexander Motin 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
493af7ab184SArchie Cobbs 		header |= MPPC_FLAG_FLUSHED;
494af7ab184SArchie Cobbs 		d->flushed = 0;
495af7ab184SArchie Cobbs 	}
496af7ab184SArchie Cobbs 
497ce52e8f4SAlexander Motin 	/* Compress packet (if compression enabled) */
498af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
499af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPC_BIT) != 0) {
500af7ab184SArchie Cobbs 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
501ce52e8f4SAlexander Motin 		u_char *inbuf, *outbuf;
50267898a23SAlexander Motin 		int outlen, inlen, ina;
503af7ab184SArchie Cobbs 		u_char *source, *dest;
504af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
505af7ab184SArchie Cobbs 		int rtn;
506af7ab184SArchie Cobbs 
507ce52e8f4SAlexander Motin 		/* Work with contiguous regions of memory. */
508ce52e8f4SAlexander Motin 		inlen = m->m_pkthdr.len;
50967898a23SAlexander Motin 		if (m->m_next == NULL) {
51067898a23SAlexander Motin 			inbuf = mtod(m, u_char *);
51167898a23SAlexander Motin 			ina = 0;
51267898a23SAlexander Motin 		} else {
513ce52e8f4SAlexander Motin 			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
51411d1cadeSAlexander Motin 			if (inbuf == NULL)
51511d1cadeSAlexander Motin 				goto err1;
516ce52e8f4SAlexander Motin 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
51767898a23SAlexander Motin 			ina = 1;
51867898a23SAlexander Motin 		}
519ce52e8f4SAlexander Motin 
520ce52e8f4SAlexander Motin 		outlen = MPPC_MAX_BLOWUP(inlen);
521ce52e8f4SAlexander Motin 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
522ce52e8f4SAlexander Motin 		if (outbuf == NULL) {
52367898a23SAlexander Motin 			if (ina)
524ce52e8f4SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
52511d1cadeSAlexander Motin err1:
52611d1cadeSAlexander Motin 			m_freem(m);
52711d1cadeSAlexander Motin 			MPPC_InitCompressionHistory(d->history);
52811d1cadeSAlexander Motin 			d->flushed = 1;
529ce52e8f4SAlexander Motin 			return (ENOMEM);
530ce52e8f4SAlexander Motin 		}
531ce52e8f4SAlexander Motin 
532af7ab184SArchie Cobbs 		/* Prepare to compress */
533af7ab184SArchie Cobbs 		source = inbuf;
534af7ab184SArchie Cobbs 		sourceCnt = inlen;
535ce52e8f4SAlexander Motin 		dest = outbuf;
536ce52e8f4SAlexander Motin 		destCnt = outlen;
537af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
538af7ab184SArchie Cobbs 			flags |= MPPC_SAVE_HISTORY;
539af7ab184SArchie Cobbs 
540af7ab184SArchie Cobbs 		/* Compress */
541af7ab184SArchie Cobbs 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
542af7ab184SArchie Cobbs 			&destCnt, d->history, flags, 0);
543af7ab184SArchie Cobbs 
544af7ab184SArchie Cobbs 		/* Check return value */
545ec5753e0SPedro F. Giffuni 		/* KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); */
546af7ab184SArchie Cobbs 		if ((rtn & MPPC_EXPANDED) == 0
547af7ab184SArchie Cobbs 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
548af7ab184SArchie Cobbs 			outlen -= destCnt;
549af7ab184SArchie Cobbs 			header |= MPPC_FLAG_COMPRESSED;
550af7ab184SArchie Cobbs 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
551af7ab184SArchie Cobbs 				header |= MPPC_FLAG_RESTART;
552ce52e8f4SAlexander Motin 
553ce52e8f4SAlexander Motin 			/* Replace m by the compresed one. */
554e4651e05SAlexander Motin 			m_copyback(m, 0, outlen, (caddr_t)outbuf);
555e4651e05SAlexander Motin 			if (m->m_pkthdr.len < outlen) {
556ce52e8f4SAlexander Motin 				m_freem(m);
557e4651e05SAlexander Motin 				m = NULL;
558e4651e05SAlexander Motin 			} else if (outlen < m->m_pkthdr.len)
559e4651e05SAlexander Motin 				m_adj(m, outlen - m->m_pkthdr.len);
560af7ab184SArchie Cobbs 		}
561af7ab184SArchie Cobbs 		d->flushed = (rtn & MPPC_EXPANDED) != 0
562af7ab184SArchie Cobbs 		    || (flags & MPPC_SAVE_HISTORY) == 0;
563ce52e8f4SAlexander Motin 
56467898a23SAlexander Motin 		if (ina)
565ce52e8f4SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
566ce52e8f4SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
567ce52e8f4SAlexander Motin 
568e4651e05SAlexander Motin 		/* Check mbuf chain reload result. */
56911d1cadeSAlexander Motin 		if (m == NULL) {
57011d1cadeSAlexander Motin 			if (!d->flushed) {
57111d1cadeSAlexander Motin 				MPPC_InitCompressionHistory(d->history);
57211d1cadeSAlexander Motin 				d->flushed = 1;
57311d1cadeSAlexander Motin 			}
574ce52e8f4SAlexander Motin 			return (ENOMEM);
575af7ab184SArchie Cobbs 		}
57611d1cadeSAlexander Motin 	}
577af7ab184SArchie Cobbs #endif
578af7ab184SArchie Cobbs 
579af7ab184SArchie Cobbs 	/* Now encrypt packet (if encryption enabled) */
580af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
581af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_BITS) != 0) {
582ce52e8f4SAlexander Motin 		struct mbuf *m1;
583af7ab184SArchie Cobbs 
5846370fd6bSAlexander Motin 		/* Set header bits */
585af7ab184SArchie Cobbs 		header |= MPPC_FLAG_ENCRYPTED;
586af7ab184SArchie Cobbs 
587af7ab184SArchie Cobbs 		/* Update key if it's time */
588af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
589af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
590af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
591af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
5926370fd6bSAlexander Motin 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
5936370fd6bSAlexander Motin 			/* Need to reset key if we say we did
5946370fd6bSAlexander Motin 			   and ng_mppc_updatekey wasn't called to do it also. */
5956370fd6bSAlexander Motin 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
596af7ab184SArchie Cobbs 		}
597af7ab184SArchie Cobbs 
598af7ab184SArchie Cobbs 		/* Encrypt packet */
599ce52e8f4SAlexander Motin 		m1 = m;
600ce52e8f4SAlexander Motin 		while (m1) {
601ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
602ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
603ce52e8f4SAlexander Motin 			m1 = m1->m_next;
604ce52e8f4SAlexander Motin 		}
605af7ab184SArchie Cobbs 	}
606af7ab184SArchie Cobbs #endif
607af7ab184SArchie Cobbs 
608755bc287SAlexander Motin 	/* Update coherency count for next time (12 bit arithmetic) */
609755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
610af7ab184SArchie Cobbs 
611af7ab184SArchie Cobbs 	/* Install header */
612eb1b1807SGleb Smirnoff 	M_PREPEND(m, MPPC_HDRLEN, M_NOWAIT);
613ce52e8f4SAlexander Motin 	if (m != NULL)
61439228864SAlexander Motin 		be16enc(mtod(m, void *), header);
615af7ab184SArchie Cobbs 
616ce52e8f4SAlexander Motin 	*datap = m;
617ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
618af7ab184SArchie Cobbs }
619af7ab184SArchie Cobbs 
620af7ab184SArchie Cobbs /*
621af7ab184SArchie Cobbs  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
622af7ab184SArchie Cobbs  * The original mbuf is not free'd.
623af7ab184SArchie Cobbs  */
624af7ab184SArchie Cobbs static int
ng_mppc_decompress(node_p node,struct mbuf ** datap)625ce52e8f4SAlexander Motin ng_mppc_decompress(node_p node, struct mbuf **datap)
626af7ab184SArchie Cobbs {
62730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
628af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->recv;
629f3059f39SArchie Cobbs 	u_int16_t header, cc;
630f3059f39SArchie Cobbs 	u_int numLost;
631ce52e8f4SAlexander Motin 	struct mbuf *m = *datap;
632af7ab184SArchie Cobbs 
633e4651e05SAlexander Motin 	/* We must own the mbuf chain exclusively to modify it. */
634eb1b1807SGleb Smirnoff 	m = m_unshare(m, M_NOWAIT);
635e4651e05SAlexander Motin 	if (m == NULL)
636e4651e05SAlexander Motin 		return (ENOMEM);
637e4651e05SAlexander Motin 
638af7ab184SArchie Cobbs 	/* Pull off header */
639ce52e8f4SAlexander Motin 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
640ce52e8f4SAlexander Motin 		m_freem(m);
641af7ab184SArchie Cobbs 		return (EINVAL);
642ce52e8f4SAlexander Motin 	}
64339228864SAlexander Motin 	header = be16dec(mtod(m, void *));
644af7ab184SArchie Cobbs 	cc = (header & MPPC_CCOUNT_MASK);
645ce52e8f4SAlexander Motin 	m_adj(m, MPPC_HDRLEN);
646af7ab184SArchie Cobbs 
647f3059f39SArchie Cobbs 	/* Check for an unexpected jump in the sequence number */
648af7ab184SArchie Cobbs 	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
649af7ab184SArchie Cobbs 
650af7ab184SArchie Cobbs 	/* If flushed bit set, we can always handle packet */
651af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_FLUSHED) != 0) {
652af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
653af7ab184SArchie Cobbs 		if (d->history != NULL)
654af7ab184SArchie Cobbs 			MPPC_InitDecompressionHistory(d->history);
655af7ab184SArchie Cobbs #endif
656af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
657af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
658f3059f39SArchie Cobbs 			u_int rekey;
659af7ab184SArchie Cobbs 
660f3059f39SArchie Cobbs 			/* How many times are we going to have to re-key? */
661f3059f39SArchie Cobbs 			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
662f3059f39SArchie Cobbs 			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
663ee652839SAlexander Motin 			if (rekey > mppe_max_rekey) {
664ee652839SAlexander Motin 			    if (mppe_block_on_max_rekey) {
665ee652839SAlexander Motin 				if (mppe_log_max_rekey) {
6665372c30bSGleb Smirnoff 				    log(LOG_ERR, "%s: too many (%d) packets"
667ee652839SAlexander Motin 					" dropped, disabling node %p!\n",
6685372c30bSGleb Smirnoff 					__func__, numLost, node);
669ee652839SAlexander Motin 				}
6705372c30bSGleb Smirnoff 				priv->recv.cfg.enable = 0;
6715372c30bSGleb Smirnoff 				goto failed;
672ee652839SAlexander Motin 			    } else {
673ee652839SAlexander Motin 				if (mppe_log_max_rekey) {
674ee652839SAlexander Motin 				    log(LOG_ERR, "%s: %d packets"
675ee652839SAlexander Motin 					" dropped, node %p\n",
676ee652839SAlexander Motin 					__func__, numLost, node);
677ee652839SAlexander Motin 				}
678ee652839SAlexander Motin 				goto failed;
679ee652839SAlexander Motin 			    }
6805372c30bSGleb Smirnoff 			}
681f3059f39SArchie Cobbs 
6825372c30bSGleb Smirnoff 			/* Re-key as necessary to catch up to peer */
683af7ab184SArchie Cobbs 			while (d->cc != cc) {
684f3059f39SArchie Cobbs 				if ((d->cfg.bits & MPPE_STATELESS) != 0
685af7ab184SArchie Cobbs 				    || (d->cc & MPPE_UPDATE_MASK)
686af7ab184SArchie Cobbs 				      == MPPE_UPDATE_FLAG) {
687af7ab184SArchie Cobbs 					ng_mppc_updatekey(d->cfg.bits,
688af7ab184SArchie Cobbs 					    d->cfg.startkey, d->key, &d->rc4);
689af7ab184SArchie Cobbs 				}
690755bc287SAlexander Motin 				MPPC_CCOUNT_INC(d->cc);
691af7ab184SArchie Cobbs 			}
692af7ab184SArchie Cobbs 
693af7ab184SArchie Cobbs 			/* Reset key (except in stateless mode, see below) */
694af7ab184SArchie Cobbs 			if ((d->cfg.bits & MPPE_STATELESS) == 0)
695af7ab184SArchie Cobbs 				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
696af7ab184SArchie Cobbs 		}
697af7ab184SArchie Cobbs #endif
698af7ab184SArchie Cobbs 		d->cc = cc;		/* skip over lost seq numbers */
699af7ab184SArchie Cobbs 		numLost = 0;		/* act like no packets were lost */
700af7ab184SArchie Cobbs 	}
701af7ab184SArchie Cobbs 
702af7ab184SArchie Cobbs 	/* Can't decode non-sequential packets without a flushed bit */
703af7ab184SArchie Cobbs 	if (numLost != 0)
704af7ab184SArchie Cobbs 		goto failed;
705af7ab184SArchie Cobbs 
706af7ab184SArchie Cobbs 	/* Decrypt packet */
707af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
708ce52e8f4SAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
709ce52e8f4SAlexander Motin 		struct mbuf *m1;
710ce52e8f4SAlexander Motin #endif
711af7ab184SArchie Cobbs 
712af7ab184SArchie Cobbs 		/* Are we not 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__, "encrypted");
716af7ab184SArchie Cobbs 			goto failed;
717af7ab184SArchie Cobbs 		}
718af7ab184SArchie Cobbs 
719af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
720af7ab184SArchie Cobbs 		/* Update key if it's time (always in stateless mode) */
721af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_STATELESS) != 0
722af7ab184SArchie Cobbs 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
723af7ab184SArchie Cobbs 			ng_mppc_updatekey(d->cfg.bits,
724af7ab184SArchie Cobbs 			    d->cfg.startkey, d->key, &d->rc4);
725af7ab184SArchie Cobbs 		}
726af7ab184SArchie Cobbs 
727af7ab184SArchie Cobbs 		/* Decrypt packet */
728ce52e8f4SAlexander Motin 		m1 = m;
729ce52e8f4SAlexander Motin 		while (m1 != NULL) {
730ce52e8f4SAlexander Motin 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
731ce52e8f4SAlexander Motin 			    mtod(m1, u_char *), m1->m_len);
732ce52e8f4SAlexander Motin 			m1 = m1->m_next;
733ce52e8f4SAlexander Motin 		}
734af7ab184SArchie Cobbs #endif
735af7ab184SArchie Cobbs 	} else {
736af7ab184SArchie Cobbs 		/* Are we expecting encryption? */
737af7ab184SArchie Cobbs 		if ((d->cfg.bits & MPPE_BITS) != 0) {
738af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7396e551fb6SDavid E. O'Brien 				__func__, "unencrypted");
740af7ab184SArchie Cobbs 			goto failed;
741af7ab184SArchie Cobbs 		}
742af7ab184SArchie Cobbs 	}
743af7ab184SArchie Cobbs 
744af7ab184SArchie Cobbs 	/* Update coherency count for next time (12 bit arithmetic) */
745755bc287SAlexander Motin 	MPPC_CCOUNT_INC(d->cc);
746af7ab184SArchie Cobbs 
747af7ab184SArchie Cobbs 	/* Check for unexpected compressed packet */
748af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0
749af7ab184SArchie Cobbs 	    && (d->cfg.bits & MPPC_BIT) == 0) {
750af7ab184SArchie Cobbs 		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
7516e551fb6SDavid E. O'Brien 			__func__, "compressed");
752af7ab184SArchie Cobbs failed:
753ce52e8f4SAlexander Motin 		m_freem(m);
754af7ab184SArchie Cobbs 		return (EINVAL);
755af7ab184SArchie Cobbs 	}
756af7ab184SArchie Cobbs 
757af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
758af7ab184SArchie Cobbs 	/* Decompress packet */
759af7ab184SArchie Cobbs 	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
760af7ab184SArchie Cobbs 		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
76167898a23SAlexander Motin 		u_char *inbuf, *outbuf;
76267898a23SAlexander Motin 		int inlen, outlen, ina;
76367898a23SAlexander Motin 		u_char *source, *dest;
764af7ab184SArchie Cobbs 		u_long sourceCnt, destCnt;
76567898a23SAlexander Motin 		int rtn;
766ce52e8f4SAlexander Motin 
767ce52e8f4SAlexander Motin 		/* Copy payload into a contiguous region of memory. */
76867898a23SAlexander Motin 		inlen = m->m_pkthdr.len;
76967898a23SAlexander Motin 		if (m->m_next == NULL) {
77067898a23SAlexander Motin                 	inbuf = mtod(m, u_char *);
77167898a23SAlexander Motin 			ina = 0;
77267898a23SAlexander Motin 		} else {
77367898a23SAlexander Motin 		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
77467898a23SAlexander Motin 			if (inbuf == NULL) {
775ce52e8f4SAlexander Motin 				m_freem(m);
776ce52e8f4SAlexander Motin 				return (ENOMEM);
777ce52e8f4SAlexander Motin 			}
77867898a23SAlexander Motin 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
77967898a23SAlexander Motin 			ina = 1;
78067898a23SAlexander Motin 		}
781af7ab184SArchie Cobbs 
782af7ab184SArchie Cobbs 		/* Allocate a buffer for decompressed data */
78367898a23SAlexander Motin 		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
784ce52e8f4SAlexander Motin 		    M_NETGRAPH_MPPC, M_NOWAIT);
78567898a23SAlexander Motin 		if (outbuf == NULL) {
786ce52e8f4SAlexander Motin 			m_freem(m);
78767898a23SAlexander Motin 			if (ina)
78867898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
789af7ab184SArchie Cobbs 			return (ENOMEM);
790af7ab184SArchie Cobbs 		}
79167898a23SAlexander Motin 		outlen = MPPC_DECOMP_BUFSIZE;
792af7ab184SArchie Cobbs 
793af7ab184SArchie Cobbs 		/* Prepare to decompress */
79467898a23SAlexander Motin 		source = inbuf;
79567898a23SAlexander Motin 		sourceCnt = inlen;
79667898a23SAlexander Motin 		dest = outbuf;
79767898a23SAlexander Motin 		destCnt = outlen;
798af7ab184SArchie Cobbs 		if ((header & MPPC_FLAG_RESTART) != 0)
799af7ab184SArchie Cobbs 			flags |= MPPC_RESTART_HISTORY;
800af7ab184SArchie Cobbs 
801af7ab184SArchie Cobbs 		/* Decompress */
802af7ab184SArchie Cobbs 		rtn = MPPC_Decompress(&source, &dest,
803af7ab184SArchie Cobbs 			&sourceCnt, &destCnt, d->history, flags);
804af7ab184SArchie Cobbs 
805af7ab184SArchie Cobbs 		/* Check return value */
806ec5753e0SPedro F. Giffuni 		/* KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); */
807af7ab184SArchie Cobbs 		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
808af7ab184SArchie Cobbs 		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
809af7ab184SArchie Cobbs 			log(LOG_ERR, "%s: decomp returned 0x%x",
8106e551fb6SDavid E. O'Brien 			    __func__, rtn);
81167898a23SAlexander Motin 			if (ina)
81267898a23SAlexander Motin 				free(inbuf, M_NETGRAPH_MPPC);
81367898a23SAlexander Motin 			free(outbuf, M_NETGRAPH_MPPC);
814af7ab184SArchie Cobbs 			goto failed;
815af7ab184SArchie Cobbs 		}
816af7ab184SArchie Cobbs 
817af7ab184SArchie Cobbs 		/* Replace compressed data with decompressed data */
81867898a23SAlexander Motin 		if (ina)
81967898a23SAlexander Motin 			free(inbuf, M_NETGRAPH_MPPC);
82067898a23SAlexander Motin 		outlen -= destCnt;
821ce52e8f4SAlexander Motin 
82267898a23SAlexander Motin 		m_copyback(m, 0, outlen, (caddr_t)outbuf);
82367898a23SAlexander Motin 		if (m->m_pkthdr.len < outlen) {
824ce52e8f4SAlexander Motin 			m_freem(m);
825e4651e05SAlexander Motin 			m = NULL;
82667898a23SAlexander Motin 		} else if (outlen < m->m_pkthdr.len)
82767898a23SAlexander Motin 			m_adj(m, outlen - m->m_pkthdr.len);
82867898a23SAlexander Motin 		free(outbuf, M_NETGRAPH_MPPC);
829af7ab184SArchie Cobbs 	}
830af7ab184SArchie Cobbs #endif
831af7ab184SArchie Cobbs 
832af7ab184SArchie Cobbs 	/* Return result in an mbuf */
833ce52e8f4SAlexander Motin 	*datap = m;
834ce52e8f4SAlexander Motin 	return (*datap == NULL ? ENOBUFS : 0);
835af7ab184SArchie Cobbs }
836af7ab184SArchie Cobbs 
837af7ab184SArchie Cobbs /*
838af7ab184SArchie Cobbs  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
839af7ab184SArchie Cobbs  */
840af7ab184SArchie Cobbs static void
ng_mppc_reset_req(node_p node)841af7ab184SArchie Cobbs ng_mppc_reset_req(node_p node)
842af7ab184SArchie Cobbs {
84330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
844af7ab184SArchie Cobbs 	struct ng_mppc_dir *const d = &priv->xmit;
845af7ab184SArchie Cobbs 
846af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_COMPRESSION
847af7ab184SArchie Cobbs 	if (d->history != NULL)
848af7ab184SArchie Cobbs 		MPPC_InitCompressionHistory(d->history);
849af7ab184SArchie Cobbs #endif
850af7ab184SArchie Cobbs #ifdef NETGRAPH_MPPC_ENCRYPTION
851af7ab184SArchie Cobbs 	if ((d->cfg.bits & MPPE_STATELESS) == 0)
852af7ab184SArchie Cobbs 		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
853af7ab184SArchie Cobbs #endif
854af7ab184SArchie Cobbs 	d->flushed = 1;
855af7ab184SArchie Cobbs }
856af7ab184SArchie Cobbs 
85706c51e6eSAlexander Motin #ifdef NETGRAPH_MPPC_ENCRYPTION
858af7ab184SArchie Cobbs /*
859af7ab184SArchie Cobbs  * Generate a new encryption key
860af7ab184SArchie Cobbs  */
861af7ab184SArchie Cobbs static void
ng_mppc_getkey(const u_char * h,u_char * h2,int len)862af7ab184SArchie Cobbs ng_mppc_getkey(const u_char *h, u_char *h2, int len)
863af7ab184SArchie Cobbs {
864b3d298b9SAlexander Motin 	static const u_char pad1[40] =
865b3d298b9SAlexander Motin 	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
866b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
867b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
868b3d298b9SAlexander Motin 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
869b3d298b9SAlexander Motin 	static const u_char pad2[40] =
870b3d298b9SAlexander Motin 	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
871b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
872b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
873b3d298b9SAlexander Motin 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
874af7ab184SArchie Cobbs 	u_char hash[20];
875af7ab184SArchie Cobbs 	SHA1_CTX c;
876af7ab184SArchie Cobbs 
877af7ab184SArchie Cobbs 	SHA1Init(&c);
878af7ab184SArchie Cobbs 	SHA1Update(&c, h, len);
879592009a3SAlexander Motin 	SHA1Update(&c, pad1, sizeof(pad1));
880af7ab184SArchie Cobbs 	SHA1Update(&c, h2, len);
881af7ab184SArchie Cobbs 	SHA1Update(&c, pad2, sizeof(pad2));
882af7ab184SArchie Cobbs 	SHA1Final(hash, &c);
883af7ab184SArchie Cobbs 	bcopy(hash, h2, len);
884af7ab184SArchie Cobbs }
885af7ab184SArchie Cobbs 
886af7ab184SArchie Cobbs /*
887af7ab184SArchie Cobbs  * Update the encryption key
888af7ab184SArchie Cobbs  */
889af7ab184SArchie Cobbs static void
ng_mppc_updatekey(u_int32_t bits,u_char * key0,u_char * key,struct rc4_state * rc4)890af7ab184SArchie Cobbs ng_mppc_updatekey(u_int32_t bits,
891af7ab184SArchie Cobbs 	u_char *key0, u_char *key, struct rc4_state *rc4)
892af7ab184SArchie Cobbs {
893af7ab184SArchie Cobbs 	const int keylen = KEYLEN(bits);
894af7ab184SArchie Cobbs 
895af7ab184SArchie Cobbs 	ng_mppc_getkey(key0, key, keylen);
896af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
897af7ab184SArchie Cobbs 	rc4_crypt(rc4, key, key, keylen);
89834fd2381SArchie Cobbs 	if ((bits & MPPE_40) != 0)
89934fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 3);
90034fd2381SArchie Cobbs 	else if ((bits & MPPE_56) != 0)
90134fd2381SArchie Cobbs 		bcopy(&ng_mppe_weakenkey, key, 1);
902af7ab184SArchie Cobbs 	rc4_init(rc4, key, keylen);
903af7ab184SArchie Cobbs }
90406c51e6eSAlexander Motin #endif
905