xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1 /*
2  * ng_pptpgre.c
3  */
4 
5 /*-
6  * Copyright (c) 1996-1999 Whistle Communications, Inc.
7  * All rights reserved.
8  *
9  * Subject to the following obligations and disclaimer of warranty, use and
10  * redistribution of this software, in source or object code forms, with or
11  * without modifications are expressly permitted by Whistle Communications;
12  * provided, however, that:
13  * 1. Any and all reproductions of the source or object code must include the
14  *    copyright notice above and the following disclaimer of warranties; and
15  * 2. No rights are granted, in any manner or form, to use Whistle
16  *    Communications, Inc. trademarks, including the mark "WHISTLE
17  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18  *    such appears in the above copyright notice or in the software.
19  *
20  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  *
38  * Author: Archie Cobbs <archie@freebsd.org>
39  *
40  * $FreeBSD$
41  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
42  */
43 
44 /*
45  * PPTP/GRE netgraph node type.
46  *
47  * This node type does the GRE encapsulation as specified for the PPTP
48  * protocol (RFC 2637, section 4).  This includes sequencing and
49  * retransmission of frames, but not the actual packet delivery nor
50  * any of the TCP control stream protocol.
51  *
52  * The "upper" hook of this node is suitable for attaching to a "ppp"
53  * node link hook.  The "lower" hook of this node is suitable for attaching
54  * to a "ksocket" node on hook "inet/raw/gre".
55  */
56 
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/kernel.h>
60 #include <sys/time.h>
61 #include <sys/lock.h>
62 #include <sys/malloc.h>
63 #include <sys/mbuf.h>
64 #include <sys/mutex.h>
65 #include <sys/endian.h>
66 #include <sys/errno.h>
67 #include <sys/sysctl.h>
68 
69 #include <netinet/in.h>
70 #include <netinet/in_systm.h>
71 #include <netinet/ip.h>
72 
73 #include <netgraph/ng_message.h>
74 #include <netgraph/netgraph.h>
75 #include <netgraph/ng_parse.h>
76 #include <netgraph/ng_pptpgre.h>
77 
78 /* GRE packet format, as used by PPTP */
79 struct greheader {
80 #if BYTE_ORDER == LITTLE_ENDIAN
81 	u_char		recursion:3;		/* recursion control */
82 	u_char		ssr:1;			/* strict source route */
83 	u_char		hasSeq:1;		/* sequence number present */
84 	u_char		hasKey:1;		/* key present */
85 	u_char		hasRoute:1;		/* routing present */
86 	u_char		hasSum:1;		/* checksum present */
87 	u_char		vers:3;			/* version */
88 	u_char		flags:4;		/* flags */
89 	u_char		hasAck:1;		/* acknowlege number present */
90 #elif BYTE_ORDER == BIG_ENDIAN
91 	u_char		hasSum:1;		/* checksum present */
92 	u_char		hasRoute:1;		/* routing present */
93 	u_char		hasKey:1;		/* key present */
94 	u_char		hasSeq:1;		/* sequence number present */
95 	u_char		ssr:1;			/* strict source route */
96 	u_char		recursion:3;		/* recursion control */
97 	u_char		hasAck:1;		/* acknowlege number present */
98 	u_char		flags:4;		/* flags */
99 	u_char		vers:3;			/* version */
100 #else
101 #error BYTE_ORDER is not defined properly
102 #endif
103 	u_int16_t	proto;			/* protocol (ethertype) */
104 	u_int16_t	length;			/* payload length */
105 	u_int16_t	cid;			/* call id */
106 	u_int32_t	data[0];		/* opt. seq, ack, then data */
107 };
108 
109 /* The PPTP protocol ID used in the GRE 'proto' field */
110 #define PPTP_GRE_PROTO		0x880b
111 
112 /* Bits that must be set a certain way in all PPTP/GRE packets */
113 #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
114 #define PPTP_INIT_MASK		0xef7fffff
115 
116 /* Min and max packet length */
117 #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
118 
119 /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
120 #define PPTP_TIME_SCALE		1024			/* milliseconds */
121 typedef u_int64_t		pptptime_t;
122 
123 /* Acknowledgment timeout parameters and functions */
124 #define PPTP_XMIT_WIN		16			/* max xmit window */
125 #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
126 #define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 seconds */
127 
128 #define PPTP_REORDER_TIMEOUT	1
129 
130 /* When we receive a packet, we wait to see if there's an outgoing packet
131    we can piggy-back the ACK off of. These parameters determine the mimimum
132    and maxmimum length of time we're willing to wait in order to do that.
133    These have no effect unless "enableDelayedAck" is turned on. */
134 #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
135 #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
136 
137 /* See RFC 2637 section 4.4 */
138 #define PPTP_ACK_ALPHA(x)	(((x) + 4) >> 3)	/* alpha = 0.125 */
139 #define PPTP_ACK_BETA(x)	(((x) + 2) >> 2)	/* beta = 0.25 */
140 #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
141 #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
142 
143 #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
144 
145 #define SESSHASHSIZE		0x0020
146 #define SESSHASH(x)		(((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
147 
148 SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
149     "PPTPGRE");
150 
151 /*
152  * Reorder queue maximum length. Zero disables reorder.
153  *
154  * The node may keep reorder_max queue entries per session
155  * if reorder is enabled, plus allocate one more for short time.
156  *
157  * Be conservative in memory consumption by default.
158  * Lots of sessions with large queues can overflow M_NETGRAPH zone.
159  */
160 static int reorder_max = 1; /* reorder up to two swapped packets in a row */
161 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
162 	&reorder_max, 0, "Reorder queue maximum length");
163 
164 static int reorder_timeout = PPTP_REORDER_TIMEOUT;
165 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
166 	&reorder_timeout, 0, "Reorder timeout is milliseconds");
167 
168 /* Packet reorder FIFO queue */
169 struct ng_pptpgre_roq {
170 	SLIST_ENTRY(ng_pptpgre_roq)  next;	/* next entry of the queue */
171 	item_p			item;		/* netgraph item */
172 	u_int32_t		seq;		/* packet sequence number */
173 };
174 SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
175 typedef struct ng_pptpgre_roq_head roqh;
176 
177 /* We keep packet retransmit and acknowlegement state in this struct */
178 struct ng_pptpgre_sess {
179 	node_p			node;		/* this node pointer */
180 	hook_p			hook;		/* hook to upper layers */
181 	struct ng_pptpgre_conf	conf;		/* configuration info */
182 	struct mtx		mtx;		/* session mutex */
183 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
184 	u_int32_t		xmitSeq;	/* last seq # we sent */
185 	u_int32_t		recvAck;	/* last seq # peer ack'd */
186 	u_int32_t		xmitAck;	/* last seq # we ack'd */
187 	int32_t			ato;		/* adaptive time-out value */
188 	int32_t			rtt;		/* round trip time estimate */
189 	int32_t			dev;		/* deviation estimate */
190 	u_int16_t		xmitWin;	/* size of xmit window */
191 	struct callout		sackTimer;	/* send ack timer */
192 	struct callout		rackTimer;	/* recv ack timer */
193 	u_int32_t		winAck;		/* seq when xmitWin will grow */
194 	pptptime_t		timeSent[PPTP_XMIT_WIN];
195 	LIST_ENTRY(ng_pptpgre_sess) sessions;
196 	roqh			roq;		/* reorder queue head */
197 	u_int8_t		roq_len;	/* reorder queue length */
198 	struct callout		reorderTimer;	/* reorder timeout handler */
199 };
200 typedef struct ng_pptpgre_sess *hpriv_p;
201 
202 /* Node private data */
203 struct ng_pptpgre_private {
204 	hook_p			upper;		/* hook to upper layers */
205 	hook_p			lower;		/* hook to lower layers */
206 	struct ng_pptpgre_sess	uppersess;	/* default session for compat */
207 	LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
208 	struct ng_pptpgre_stats	stats;		/* node statistics */
209 };
210 typedef struct ng_pptpgre_private *priv_p;
211 
212 /* Netgraph node methods */
213 static ng_constructor_t	ng_pptpgre_constructor;
214 static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
215 static ng_shutdown_t	ng_pptpgre_shutdown;
216 static ng_newhook_t	ng_pptpgre_newhook;
217 static ng_rcvdata_t	ng_pptpgre_rcvdata;
218 static ng_rcvdata_t	ng_pptpgre_rcvdata_lower;
219 static ng_disconnect_t	ng_pptpgre_disconnect;
220 
221 /* Helper functions */
222 static int	ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
223 static void	ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
224 static void	ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
225 static void	ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
226 static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
227 		    void *arg1, int arg2);
228 static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
229 		    void *arg1, int arg2);
230 static void	ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
231 		    void *arg1, int arg2);
232 static hpriv_p	ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
233 static void	ng_pptpgre_reset(hpriv_p hpriv);
234 static pptptime_t ng_pptpgre_time(void);
235 static void	ng_pptpgre_ack(const hpriv_p hpriv);
236 static int	ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
237 		    const struct ng_pptpgre_roq *st);
238 
239 /* Parse type for struct ng_pptpgre_conf */
240 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
241 	= NG_PPTPGRE_CONF_TYPE_INFO;
242 static const struct ng_parse_type ng_pptpgre_conf_type = {
243 	&ng_parse_struct_type,
244 	&ng_pptpgre_conf_type_fields,
245 };
246 
247 /* Parse type for struct ng_pptpgre_stats */
248 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
249 	= NG_PPTPGRE_STATS_TYPE_INFO;
250 static const struct ng_parse_type ng_pptp_stats_type = {
251 	&ng_parse_struct_type,
252 	&ng_pptpgre_stats_type_fields
253 };
254 
255 /* List of commands and how to convert arguments to/from ASCII */
256 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
257 	{
258 	  NGM_PPTPGRE_COOKIE,
259 	  NGM_PPTPGRE_SET_CONFIG,
260 	  "setconfig",
261 	  &ng_pptpgre_conf_type,
262 	  NULL
263 	},
264 	{
265 	  NGM_PPTPGRE_COOKIE,
266 	  NGM_PPTPGRE_GET_CONFIG,
267 	  "getconfig",
268 	  &ng_parse_hint16_type,
269 	  &ng_pptpgre_conf_type
270 	},
271 	{
272 	  NGM_PPTPGRE_COOKIE,
273 	  NGM_PPTPGRE_GET_STATS,
274 	  "getstats",
275 	  NULL,
276 	  &ng_pptp_stats_type
277 	},
278 	{
279 	  NGM_PPTPGRE_COOKIE,
280 	  NGM_PPTPGRE_CLR_STATS,
281 	  "clrstats",
282 	  NULL,
283 	  NULL
284 	},
285 	{
286 	  NGM_PPTPGRE_COOKIE,
287 	  NGM_PPTPGRE_GETCLR_STATS,
288 	  "getclrstats",
289 	  NULL,
290 	  &ng_pptp_stats_type
291 	},
292 	{ 0 }
293 };
294 
295 /* Node type descriptor */
296 static struct ng_type ng_pptpgre_typestruct = {
297 	.version =	NG_ABI_VERSION,
298 	.name =		NG_PPTPGRE_NODE_TYPE,
299 	.constructor =	ng_pptpgre_constructor,
300 	.rcvmsg =	ng_pptpgre_rcvmsg,
301 	.shutdown =	ng_pptpgre_shutdown,
302 	.newhook =	ng_pptpgre_newhook,
303 	.rcvdata =	ng_pptpgre_rcvdata,
304 	.disconnect =	ng_pptpgre_disconnect,
305 	.cmdlist =	ng_pptpgre_cmdlist,
306 };
307 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
308 
309 #define ERROUT(x)	do { error = (x); goto done; } while (0)
310 
311 /************************************************************************
312 			NETGRAPH NODE STUFF
313  ************************************************************************/
314 
315 /*
316  * Node type constructor
317  */
318 static int
319 ng_pptpgre_constructor(node_p node)
320 {
321 	priv_p priv;
322 	int i;
323 
324 	/* Allocate private structure */
325 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
326 
327 	NG_NODE_SET_PRIVATE(node, priv);
328 
329 	/* Initialize state */
330 	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
331 	ng_callout_init(&priv->uppersess.sackTimer);
332 	ng_callout_init(&priv->uppersess.rackTimer);
333 	priv->uppersess.node = node;
334 
335 	SLIST_INIT(&priv->uppersess.roq);
336 	priv->uppersess.roq_len = 0;
337 	ng_callout_init(&priv->uppersess.reorderTimer);
338 
339 	for (i = 0; i < SESSHASHSIZE; i++)
340 	    LIST_INIT(&priv->sesshash[i]);
341 
342 	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
343 
344 	/* Done */
345 	return (0);
346 }
347 
348 /*
349  * Give our OK for a hook to be added.
350  */
351 static int
352 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
353 {
354 	const priv_p priv = NG_NODE_PRIVATE(node);
355 
356 	/* Check hook name */
357 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
358 		priv->upper = hook;
359 		priv->uppersess.hook = hook;
360 		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
361 	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
362 		priv->lower = hook;
363 		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
364 	} else {
365 		static const char hexdig[16] = "0123456789abcdef";
366 		const char *hex;
367 		hpriv_p hpriv;
368 		int i, j;
369 		uint16_t cid, hash;
370 
371 		/* Parse hook name to get session ID */
372 		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
373 		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
374 			return (EINVAL);
375 		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
376 		for (cid = i = 0; i < 4; i++) {
377 			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
378 			if (j == 16)
379 				return (EINVAL);
380 			cid = (cid << 4) | j;
381 		}
382 		if (hex[i] != '\0')
383 			return (EINVAL);
384 
385 		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
386 		if (hpriv == NULL)
387 			return (ENOMEM);
388 
389 		/* Initialize state */
390 		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
391 		ng_callout_init(&hpriv->sackTimer);
392 		ng_callout_init(&hpriv->rackTimer);
393 		hpriv->conf.cid = cid;
394 		hpriv->node = node;
395 		hpriv->hook = hook;
396 
397 		SLIST_INIT(&hpriv->roq);
398 		hpriv->roq_len = 0;
399 		ng_callout_init(&hpriv->reorderTimer);
400 
401 		NG_HOOK_SET_PRIVATE(hook, hpriv);
402 
403 		hash = SESSHASH(cid);
404 		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
405 	}
406 
407 	return (0);
408 }
409 
410 /*
411  * Receive a control message.
412  */
413 static int
414 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
415 {
416 	const priv_p priv = NG_NODE_PRIVATE(node);
417 	struct ng_mesg *resp = NULL;
418 	int error = 0;
419 	struct ng_mesg *msg;
420 
421 	NGI_GET_MSG(item, msg);
422 	switch (msg->header.typecookie) {
423 	case NGM_PPTPGRE_COOKIE:
424 		switch (msg->header.cmd) {
425 		case NGM_PPTPGRE_SET_CONFIG:
426 		    {
427 			struct ng_pptpgre_conf *const newConf =
428 				(struct ng_pptpgre_conf *) msg->data;
429 			hpriv_p hpriv;
430 			uint16_t hash;
431 
432 			/* Check for invalid or illegal config */
433 			if (msg->header.arglen != sizeof(*newConf))
434 				ERROUT(EINVAL);
435 			/* Try to find session by cid. */
436 			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
437 			/* If not present - use upper. */
438 			if (hpriv == NULL) {
439 				hpriv = &priv->uppersess;
440 				LIST_REMOVE(hpriv, sessions);
441 				hash = SESSHASH(newConf->cid);
442 				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
443 				    sessions);
444 			}
445 			ng_pptpgre_reset(hpriv);	/* reset on configure */
446 			hpriv->conf = *newConf;
447 			break;
448 		    }
449 		case NGM_PPTPGRE_GET_CONFIG:
450 		    {
451 			hpriv_p hpriv;
452 
453 			if (msg->header.arglen == 2) {
454 				/* Try to find session by cid. */
455 	    			hpriv = ng_pptpgre_find_session(priv,
456 				    *((uint16_t *)msg->data));
457 				if (hpriv == NULL)
458 					ERROUT(EINVAL);
459 			} else if (msg->header.arglen == 0) {
460 				/* Use upper. */
461 				hpriv = &priv->uppersess;
462 			} else
463 				ERROUT(EINVAL);
464 			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
465 			if (resp == NULL)
466 				ERROUT(ENOMEM);
467 			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
468 			break;
469 		    }
470 		case NGM_PPTPGRE_GET_STATS:
471 		case NGM_PPTPGRE_CLR_STATS:
472 		case NGM_PPTPGRE_GETCLR_STATS:
473 		    {
474 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
475 				NG_MKRESPONSE(resp, msg,
476 				    sizeof(priv->stats), M_NOWAIT);
477 				if (resp == NULL)
478 					ERROUT(ENOMEM);
479 				bcopy(&priv->stats,
480 				    resp->data, sizeof(priv->stats));
481 			}
482 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
483 				bzero(&priv->stats, sizeof(priv->stats));
484 			break;
485 		    }
486 		default:
487 			error = EINVAL;
488 			break;
489 		}
490 		break;
491 	default:
492 		error = EINVAL;
493 		break;
494 	}
495 done:
496 	NG_RESPOND_MSG(error, node, item, resp);
497 	NG_FREE_MSG(msg);
498 	return (error);
499 }
500 
501 /*
502  * Receive incoming data on a hook.
503  */
504 static int
505 ng_pptpgre_rcvdata(hook_p hook, item_p item)
506 {
507 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
508 	int rval;
509 
510 	/* If not configured, reject */
511 	if (!hpriv->conf.enabled) {
512 		NG_FREE_ITEM(item);
513 		return (ENXIO);
514 	}
515 
516 	mtx_lock(&hpriv->mtx);
517 
518 	rval = ng_pptpgre_xmit(hpriv, item);
519 
520 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
521 
522 	return (rval);
523 }
524 
525 /*
526  * Hook disconnection
527  */
528 static int
529 ng_pptpgre_disconnect(hook_p hook)
530 {
531 	const node_p node = NG_HOOK_NODE(hook);
532 	const priv_p priv = NG_NODE_PRIVATE(node);
533 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
534 
535 	/* Zero out hook pointer */
536 	if (hook == priv->upper) {
537 		priv->upper = NULL;
538 		priv->uppersess.hook = NULL;
539 	} else if (hook == priv->lower) {
540 		priv->lower = NULL;
541 	} else {
542 		/* Reset node (stops timers) */
543 		ng_pptpgre_reset(hpriv);
544 
545 		LIST_REMOVE(hpriv, sessions);
546 		mtx_destroy(&hpriv->mtx);
547 		free(hpriv, M_NETGRAPH);
548 	}
549 
550 	/* Go away if no longer connected to anything */
551 	if ((NG_NODE_NUMHOOKS(node) == 0)
552 	&& (NG_NODE_IS_VALID(node)))
553 		ng_rmnode_self(node);
554 	return (0);
555 }
556 
557 /*
558  * Destroy node
559  */
560 static int
561 ng_pptpgre_shutdown(node_p node)
562 {
563 	const priv_p priv = NG_NODE_PRIVATE(node);
564 
565 	/* Reset node (stops timers) */
566 	ng_pptpgre_reset(&priv->uppersess);
567 
568 	LIST_REMOVE(&priv->uppersess, sessions);
569 	mtx_destroy(&priv->uppersess.mtx);
570 
571 	free(priv, M_NETGRAPH);
572 
573 	/* Decrement ref count */
574 	NG_NODE_UNREF(node);
575 	return (0);
576 }
577 
578 /*************************************************************************
579 		    TRANSMIT AND RECEIVE FUNCTIONS
580 *************************************************************************/
581 
582 /*
583  * Transmit an outgoing frame, or just an ack if m is NULL.
584  */
585 static int
586 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
587 {
588 	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
589 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
590 	struct greheader *const gre = (struct greheader *)buf;
591 	int grelen, error;
592 	struct mbuf *m;
593 
594 	mtx_assert(&hpriv->mtx, MA_OWNED);
595 
596 	if (item) {
597 		NGI_GET_M(item, m);
598 	} else {
599 		m = NULL;
600 	}
601 	/* Check if there's data */
602 	if (m != NULL) {
603 
604 		/* Check if windowing is enabled */
605 		if (hpriv->conf.enableWindowing) {
606 			/* Is our transmit window full? */
607 			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
608 			    hpriv->recvAck) >= hpriv->xmitWin) {
609 				priv->stats.xmitDrops++;
610 				ERROUT(ENOBUFS);
611 			}
612 		}
613 
614 		/* Sanity check frame length */
615 		if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
616 			priv->stats.xmitTooBig++;
617 			ERROUT(EMSGSIZE);
618 		}
619 	} else {
620 		priv->stats.xmitLoneAcks++;
621 	}
622 
623 	/* Build GRE header */
624 	be32enc(gre, PPTP_INIT_VALUE);
625 	be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
626 	be16enc(&gre->cid, hpriv->conf.peerCid);
627 
628 	/* Include sequence number if packet contains any data */
629 	if (m != NULL) {
630 		gre->hasSeq = 1;
631 		if (hpriv->conf.enableWindowing) {
632 			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
633 			    = ng_pptpgre_time();
634 		}
635 		hpriv->xmitSeq++;
636 		be32enc(&gre->data[0], hpriv->xmitSeq);
637 	}
638 
639 	/* Include acknowledgement (and stop send ack timer) if needed */
640 	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
641 		gre->hasAck = 1;
642 		be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
643 		hpriv->xmitAck = hpriv->recvSeq;
644 		if (hpriv->conf.enableDelayedAck)
645 			ng_uncallout(&hpriv->sackTimer, hpriv->node);
646 	}
647 
648 	/* Prepend GRE header to outgoing frame */
649 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
650 	if (m == NULL) {
651 		MGETHDR(m, M_NOWAIT, MT_DATA);
652 		if (m == NULL) {
653 			priv->stats.memoryFailures++;
654 			ERROUT(ENOBUFS);
655 		}
656 		m->m_len = m->m_pkthdr.len = grelen;
657 		m->m_pkthdr.rcvif = NULL;
658 	} else {
659 		M_PREPEND(m, grelen, M_NOWAIT);
660 		if (m == NULL || (m->m_len < grelen
661 		    && (m = m_pullup(m, grelen)) == NULL)) {
662 			priv->stats.memoryFailures++;
663 			ERROUT(ENOBUFS);
664 		}
665 	}
666 	bcopy(gre, mtod(m, u_char *), grelen);
667 
668 	/* Update stats */
669 	priv->stats.xmitPackets++;
670 	priv->stats.xmitOctets += m->m_pkthdr.len;
671 
672 	/*
673 	 * XXX: we should reset timer only after an item has been sent
674 	 * successfully.
675 	 */
676 	if (hpriv->conf.enableWindowing &&
677 	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
678 		ng_pptpgre_start_recv_ack_timer(hpriv);
679 
680 	mtx_unlock(&hpriv->mtx);
681 
682 	/* Deliver packet */
683 	if (item) {
684 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
685 	} else {
686 		NG_SEND_DATA_ONLY(error, priv->lower, m);
687 	}
688 
689 	return (error);
690 
691 done:
692 	mtx_unlock(&hpriv->mtx);
693 	NG_FREE_M(m);
694 	if (item)
695 		NG_FREE_ITEM(item);
696 	return (error);
697 }
698 
699 static void
700 ng_pptpgre_ack(const hpriv_p hpriv)
701 {
702 	mtx_assert(&hpriv->mtx, MA_OWNED);
703 	if (!(callout_pending(&hpriv->sackTimer))) {
704 		/* If delayed ACK is disabled, send it now */
705 		if (!hpriv->conf.enableDelayedAck) {	/* ack now */
706 			ng_pptpgre_xmit(hpriv, NULL);
707 			/* ng_pptpgre_xmit() drops the mutex */
708 			return;
709 		}
710 		/* ack later */
711 		ng_pptpgre_start_send_ack_timer(hpriv);
712 		mtx_unlock(&hpriv->mtx);
713 		return;
714 	}
715 	mtx_unlock(&hpriv->mtx);
716 }
717 
718 /*
719  * Delivers packets from the queue "q" to upper layers. Frees delivered
720  * entries with the exception of one equal to "st" that is allocated
721  * on caller's stack and not on the heap.
722  */
723 static int
724 ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
725 {
726 	struct ng_pptpgre_roq *np;
727 	struct mbuf *m;
728 	int error = 0;
729 
730 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
731 	while (!SLIST_EMPTY(q)) {
732 		np = SLIST_FIRST(q);
733 		SLIST_REMOVE_HEAD(q, next);
734 		NGI_GET_M(np->item, m);
735 		NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
736 		if (np != st)
737 			free(np, M_NETGRAPH);
738 	}
739 	return (error);
740 }
741 
742 /*
743  * Handle an incoming packet.  The packet includes the IP header.
744  */
745 static int
746 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
747 {
748 	hpriv_p hpriv;
749 	node_p node = NG_HOOK_NODE(hook);
750 	const priv_p priv = NG_NODE_PRIVATE(node);
751 	int iphlen, grelen, extralen;
752 	const struct greheader *gre;
753 	const struct ip *ip;
754 	int error = 0;
755 	struct mbuf *m;
756 
757 	roqh sendq = SLIST_HEAD_INITIALIZER(sendq);  /* send queue on stack */
758 	struct ng_pptpgre_roq *last = NULL;	/* last packet in the sendq */
759 	struct ng_pptpgre_roq *np, *prev;
760 	struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
761 	long diff;
762 	u_int32_t seq;
763 
764 	m = NGI_M(item);
765 	/* Update stats */
766 	priv->stats.recvPackets++;
767 	priv->stats.recvOctets += m->m_pkthdr.len;
768 
769 	/* Sanity check packet length */
770 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
771 		priv->stats.recvRunts++;
772 		ERROUT(EINVAL);
773 	}
774 
775 	/* Safely pull up the complete IP+GRE headers */
776 	if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
777 		if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
778 			priv->stats.memoryFailures++;
779 			_NGI_M(item) = NULL;
780 			ERROUT(ENOBUFS);
781 		}
782 		_NGI_M(item) = m;
783 	}
784 	ip = mtod(m, const struct ip *);
785 	iphlen = ip->ip_hl << 2;
786 	if (m->m_len < iphlen + sizeof(*gre)) {
787 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
788 			priv->stats.memoryFailures++;
789 			_NGI_M(item) = NULL;
790 			ERROUT(ENOBUFS);
791 		}
792 		_NGI_M(item) = m;
793 		ip = mtod(m, const struct ip *);
794 	}
795 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
796 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
797 	if (m->m_pkthdr.len < iphlen + grelen) {
798 		priv->stats.recvRunts++;
799 		ERROUT(EINVAL);
800 	}
801 	if (m->m_len < iphlen + grelen) {
802 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
803 			priv->stats.memoryFailures++;
804 			_NGI_M(item) = NULL;
805 			ERROUT(ENOBUFS);
806 		}
807 		_NGI_M(item) = m;
808 		ip = mtod(m, const struct ip *);
809 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
810 	}
811 
812 	/* Sanity check packet length and GRE header bits */
813 	extralen = m->m_pkthdr.len
814 	    - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
815 	if (extralen < 0) {
816 		priv->stats.recvBadGRE++;
817 		ERROUT(EINVAL);
818 	}
819 	if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
820 		priv->stats.recvBadGRE++;
821 		ERROUT(EINVAL);
822 	}
823 
824 	hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
825 	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
826 		priv->stats.recvBadCID++;
827 		ERROUT(EINVAL);
828 	}
829 	mtx_lock(&hpriv->mtx);
830 
831 	/* Look for peer ack */
832 	if (gre->hasAck) {
833 		const u_int32_t	ack = be32dec(&gre->data[gre->hasSeq]);
834 		const int index = ack - hpriv->recvAck - 1;
835 		long sample;
836 
837 		/* Sanity check ack value */
838 		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
839 			priv->stats.recvBadAcks++;
840 			goto badAck;		/* we never sent it! */
841 		}
842 		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
843 			goto badAck;		/* ack already timed out */
844 		hpriv->recvAck = ack;
845 
846 		/* Update adaptive timeout stuff */
847 		if (hpriv->conf.enableWindowing) {
848 			sample = ng_pptpgre_time() - hpriv->timeSent[index];
849 			diff = sample - hpriv->rtt;
850 			hpriv->rtt += PPTP_ACK_ALPHA(diff);
851 			if (diff < 0)
852 				diff = -diff;
853 			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
854 			    /* +2 to compensate low precision of int math */
855 			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
856 			if (hpriv->ato > PPTP_MAX_TIMEOUT)
857 				hpriv->ato = PPTP_MAX_TIMEOUT;
858 			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
859 				hpriv->ato = PPTP_MIN_TIMEOUT;
860 
861 			/* Shift packet transmit times in our transmit window */
862 			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
863 			    sizeof(*hpriv->timeSent)
864 			      * (PPTP_XMIT_WIN - (index + 1)));
865 
866 			/* If we sent an entire window, increase window size */
867 			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
868 			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
869 				hpriv->xmitWin++;
870 				hpriv->winAck = ack + hpriv->xmitWin;
871 			}
872 
873 			/* Stop/(re)start receive ACK timer as necessary */
874 			ng_uncallout(&hpriv->rackTimer, hpriv->node);
875 			if (hpriv->recvAck != hpriv->xmitSeq)
876 				ng_pptpgre_start_recv_ack_timer(hpriv);
877 		}
878 	}
879 badAck:
880 
881 	/* See if frame contains any data */
882 	if (!gre->hasSeq) {		/* no data to deliver */
883 		priv->stats.recvLoneAcks++;
884 		mtx_unlock(&hpriv->mtx);
885 		ERROUT(0);
886 	}
887 
888 	seq = be32dec(&gre->data[0]);
889 
890 	diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
891 	if (diff <= 0) {			/* late or duplicate packet */
892 		if (diff < 0 && reorder_max == 0)	/* reorder disabled */
893 			priv->stats.recvOutOfOrder++;	/* late */
894 		else
895 			priv->stats.recvDuplicates++;	/* duplicate */
896 		mtx_unlock(&hpriv->mtx);
897 		ERROUT(EINVAL);
898 	}
899 
900 	/* Trim mbuf down to internal payload */
901 	m_adj(m, iphlen + grelen);
902 	if (extralen > 0)
903 		m_adj(m, -extralen);
904 
905 #define INIT_SENDQ(t) do {				\
906 		t.item = item;				\
907 		t.seq = seq;				\
908 		SLIST_INSERT_HEAD(&sendq, &t, next);	\
909 		last = &t;				\
910 		hpriv->recvSeq = seq;			\
911 		goto deliver;				\
912 	} while(0)
913 
914 	if (diff == 1)
915 		/* the packet came in order, place it at the start of sendq */
916 		INIT_SENDQ(temp);
917 
918 	/* The packet came too early, try to enqueue it.
919 	 *
920 	 * Check for duplicate in the queue. After this loop, "prev" will be
921 	 * NULL if the packet should become new head of the queue,
922 	 * or else it should be inserted after the "prev".
923 	 */
924 	prev = SLIST_FIRST(&hpriv->roq);
925 	SLIST_FOREACH(np, &hpriv->roq, next) {
926 		diff = PPTP_SEQ_DIFF(np->seq, seq);
927 		if (diff == 0) { /* do not add duplicate, drop it */
928 			priv->stats.recvDuplicates++;
929 			mtx_unlock(&hpriv->mtx);
930 			ERROUT(EINVAL);
931 		}
932 		if (diff > 0) {		/* we found newer packet */
933 			if (np == prev)	/* that is the head of the queue */
934 			    prev = NULL; /* put current packet to the head */
935 			break;
936 		}
937 		prev = np;
938 	}
939 
940 	priv->stats.recvOutOfOrder++;	/* duplicate not found */
941 	if (hpriv->roq_len < reorder_max)
942 		goto enqueue;	/* reorder enabled and there is a room */
943 
944 	/*
945 	 * There is no room in the queue or reorder disabled.
946 	 *
947 	 * It the latter case, we may still have non-empty reorder queue
948 	 * if reorder was disabled in process of reordering.
949 	 * Then we correctly deliver the queue without growing it.
950 	 *
951 	 * In both cases, no malloc()'s until the queue is shortened.
952 	 */
953 	priv->stats.recvReorderOverflow++;
954 	if (prev == NULL) {	  /* new packet goes before the head      */
955 		INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
956 	}
957 #undef INIT_SENDQ
958 
959 	/*
960 	 * Current packet goes after the head of reorder queue.
961 	 * Move the head to sendq to make room for current packet.
962 	 */
963 	np = SLIST_FIRST(&hpriv->roq);
964 	if (prev == np)
965 		prev = NULL;
966 	SLIST_REMOVE_HEAD(&hpriv->roq, next);
967 	hpriv->roq_len--;	/* we are allowed to use malloc() now */
968 	SLIST_INSERT_HEAD(&sendq, np, next);
969 	last = np;
970 	hpriv->recvSeq = np->seq;
971 
972 enqueue:
973 	np = malloc(sizeof(*np), M_NETGRAPH, M_NOWAIT | M_ZERO);
974 	if (np == NULL) {
975 		priv->stats.memoryFailures++;
976 		/*
977 		 * Emergency: we cannot save new data.
978 		 * Flush the queue delivering all queued packets preceeding
979 		 * current one despite of gaps.
980 		 */
981 		while (!SLIST_EMPTY(&hpriv->roq)) {
982 			np = SLIST_FIRST(&hpriv->roq);
983 			if (np->seq > seq)
984 				break;
985 			SLIST_REMOVE_HEAD(&hpriv->roq, next);
986 			hpriv->roq_len--;
987 			if (last == NULL)
988 				SLIST_INSERT_HEAD(&sendq, np, next);
989 			else
990 				SLIST_INSERT_AFTER(last, np, next);
991 			last = np;
992 		}
993 
994 		/*
995 		 * Pretend we got all packets till the current one
996 		 * and acknowledge it.
997 		 */
998 		hpriv->recvSeq = seq;
999 		ng_pptpgre_ack(hpriv);	/* drops lock */
1000 		ng_pptpgre_sendq(hpriv, &sendq, &temp);
1001 		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1002 		ERROUT(ENOMEM);
1003 	}
1004 
1005 	/* Add current (early) packet to the reorder queue. */
1006 	np->item = item;
1007 	np->seq = seq;
1008 	if (prev == NULL)
1009 		SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1010 	else
1011 		SLIST_INSERT_AFTER(prev, np, next);
1012 	hpriv->roq_len++;
1013 
1014 deliver:
1015 	/* Look if we have some packets in sequence after sendq. */
1016 	while (!SLIST_EMPTY(&hpriv->roq)) {
1017 		np = SLIST_FIRST(&hpriv->roq);
1018 		if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1019 			break; /* the gap in the sequence */
1020 
1021 		/* "np" is in sequence, move it to the sendq. */
1022 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1023 		hpriv->roq_len--;
1024 		hpriv->recvSeq = np->seq;
1025 
1026 		if (last == NULL)
1027 			SLIST_INSERT_HEAD(&sendq, np, next);
1028 		else
1029 			SLIST_INSERT_AFTER(last, np, next);
1030 		last = np;
1031 	}
1032 
1033 	if (SLIST_EMPTY(&hpriv->roq)) {
1034 		if (callout_pending(&hpriv->reorderTimer))
1035 			ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1036 	} else {
1037 		if (!callout_pending(&hpriv->reorderTimer))
1038 			ng_pptpgre_start_reorder_timer(hpriv);
1039 	}
1040 
1041 	if (SLIST_EMPTY(&sendq)) {
1042 		/* Current packet has been queued, nothing to free/deliver. */
1043 		mtx_unlock(&hpriv->mtx);
1044 		return (error);
1045 	}
1046 
1047 	/* We need to acknowledge last packet; do it soon... */
1048 	ng_pptpgre_ack(hpriv);		/* drops lock */
1049 	ng_pptpgre_sendq(hpriv, &sendq, &temp);
1050 	return (error);
1051 
1052 done:
1053 	NG_FREE_ITEM(item);
1054 	return (error);
1055 }
1056 
1057 /*************************************************************************
1058 		    TIMER RELATED FUNCTIONS
1059 *************************************************************************/
1060 
1061 /*
1062  * Start a timer for the peer's acknowledging our oldest unacknowledged
1063  * sequence number.  If we get an ack for this sequence number before
1064  * the timer goes off, we cancel the timer.  Resets currently running
1065  * recv ack timer, if any.
1066  */
1067 static void
1068 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1069 {
1070 	int remain, ticks;
1071 
1072 	/* Compute how long until oldest unack'd packet times out,
1073 	   and reset the timer to that time. */
1074 	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1075 	if (remain < 0)
1076 		remain = 0;
1077 
1078 	/* Be conservative: timeout can happen up to 1 tick early */
1079 	ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1080 	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1081 	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
1082 }
1083 
1084 /*
1085  * The peer has failed to acknowledge the oldest unacknowledged sequence
1086  * number within the time allotted.  Update our adaptive timeout parameters
1087  * and reset/restart the recv ack timer.
1088  */
1089 static void
1090 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1091 {
1092 	const priv_p priv = NG_NODE_PRIVATE(node);
1093 	const hpriv_p hpriv = arg1;
1094 
1095 	/* Update adaptive timeout stuff */
1096 	priv->stats.recvAckTimeouts++;
1097 	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1098 	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1099 	if (hpriv->ato > PPTP_MAX_TIMEOUT)
1100 		hpriv->ato = PPTP_MAX_TIMEOUT;
1101 	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1102 		hpriv->ato = PPTP_MIN_TIMEOUT;
1103 
1104 	/* Reset ack and sliding window */
1105 	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
1106 	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
1107 	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
1108 }
1109 
1110 /*
1111  * Start the send ack timer. This assumes the timer is not
1112  * already running.
1113  */
1114 static void
1115 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
1116 {
1117 	int ackTimeout, ticks;
1118 
1119 	/* Take 1/4 of the estimated round trip time */
1120 	ackTimeout = (hpriv->rtt >> 2);
1121 	if (ackTimeout < PPTP_MIN_ACK_DELAY)
1122 		ackTimeout = PPTP_MIN_ACK_DELAY;
1123 	else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1124 		ackTimeout = PPTP_MAX_ACK_DELAY;
1125 
1126 	/* Be conservative: timeout can happen up to 1 tick early */
1127 	ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1128 	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1129 	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
1130 }
1131 
1132 /*
1133  * We've waited as long as we're willing to wait before sending an
1134  * acknowledgement to the peer for received frames. We had hoped to
1135  * be able to piggy back our acknowledgement on an outgoing data frame,
1136  * but apparently there haven't been any since. So send the ack now.
1137  */
1138 static void
1139 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1140 {
1141 	const hpriv_p hpriv = arg1;
1142 
1143 	mtx_lock(&hpriv->mtx);
1144 	/* Send a frame with an ack but no payload */
1145   	ng_pptpgre_xmit(hpriv, NULL);
1146 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1147 }
1148 
1149 /*
1150  * Start a timer for the reorder queue. This assumes the timer is not
1151  * already running.
1152  */
1153 static void
1154 ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1155 {
1156 	int ticks;
1157 
1158 	/* Be conservative: timeout can happen up to 1 tick early */
1159 	ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1160 	ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1161 		ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1162 }
1163 
1164 /*
1165  * The oldest packet spent too much time in the reorder queue.
1166  * Deliver it and next packets in sequence, if any.
1167  */
1168 static void
1169 ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1170 {
1171 	const priv_p priv = NG_NODE_PRIVATE(node);
1172 	const hpriv_p hpriv = arg1;
1173 	roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1174 	struct ng_pptpgre_roq *np, *last = NULL;
1175 
1176 	priv->stats.recvReorderTimeouts++;
1177 	mtx_lock(&hpriv->mtx);
1178 	if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1179 		mtx_unlock(&hpriv->mtx);
1180 		return;
1181 	}
1182 
1183 	last = np = SLIST_FIRST(&hpriv->roq);
1184 	hpriv->roq_len--;
1185 	SLIST_REMOVE_HEAD(&hpriv->roq, next);
1186 	SLIST_INSERT_HEAD(&sendq, np, next);
1187 
1188 	/* Look if we have more packets in sequence */
1189 	while (!SLIST_EMPTY(&hpriv->roq)) {
1190 		np = SLIST_FIRST(&hpriv->roq);
1191 		if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1192 			break; /* the gap in the sequence */
1193 
1194 		/* Next packet is in sequence, move it to the sendq. */
1195 		hpriv->roq_len--;
1196 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1197 		SLIST_INSERT_AFTER(last, np, next);
1198 		last = np;
1199 	}
1200 
1201 	hpriv->recvSeq = last->seq;
1202 	if (!SLIST_EMPTY(&hpriv->roq))
1203 		ng_pptpgre_start_reorder_timer(hpriv);
1204 
1205 	/* We need to acknowledge last packet; do it soon... */
1206 	ng_pptpgre_ack(hpriv);		/* drops lock */
1207 	ng_pptpgre_sendq(hpriv, &sendq, NULL);
1208 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1209 }
1210 
1211 /*************************************************************************
1212 		    MISC FUNCTIONS
1213 *************************************************************************/
1214 
1215 /*
1216  * Find the hook with a given session ID.
1217  */
1218 static hpriv_p
1219 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1220 {
1221 	uint16_t	hash = SESSHASH(cid);
1222 	hpriv_p	hpriv = NULL;
1223 
1224 	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1225 		if (hpriv->conf.cid == cid)
1226 			break;
1227 	}
1228 
1229 	return (hpriv);
1230 }
1231 
1232 /*
1233  * Reset state (must be called with lock held or from writer)
1234  */
1235 static void
1236 ng_pptpgre_reset(hpriv_p hpriv)
1237 {
1238 	struct ng_pptpgre_roq *np;
1239 
1240 	/* Reset adaptive timeout state */
1241 	hpriv->ato = PPTP_MAX_TIMEOUT;
1242 	hpriv->rtt = PPTP_TIME_SCALE / 10;
1243 	if (hpriv->conf.peerPpd > 1)	/* ppd = 0 treat as = 1 */
1244 		hpriv->rtt *= hpriv->conf.peerPpd;
1245 	hpriv->dev = 0;
1246 	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1247 	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
1248 		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
1249 	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1250 		hpriv->xmitWin = PPTP_XMIT_WIN;
1251 	hpriv->winAck = hpriv->xmitWin;
1252 
1253 	/* Reset sequence numbers */
1254 	hpriv->recvSeq = ~0;
1255 	hpriv->recvAck = ~0;
1256 	hpriv->xmitSeq = ~0;
1257 	hpriv->xmitAck = ~0;
1258 
1259 	/* Stop timers */
1260 	ng_uncallout(&hpriv->sackTimer, hpriv->node);
1261 	ng_uncallout(&hpriv->rackTimer, hpriv->node);
1262 	ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1263 
1264 	/* Clear reorder queue */
1265 	while (!SLIST_EMPTY(&hpriv->roq)) {
1266 		np = SLIST_FIRST(&hpriv->roq);
1267 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1268 		NG_FREE_ITEM(np->item);
1269 		free(np, M_NETGRAPH);
1270 	}
1271 	hpriv->roq_len = 0;
1272 }
1273 
1274 /*
1275  * Return the current time scaled & translated to our internally used format.
1276  */
1277 static pptptime_t
1278 ng_pptpgre_time(void)
1279 {
1280 	struct timeval tv;
1281 	pptptime_t t;
1282 
1283 	microuptime(&tv);
1284 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1285 	t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1286 	return(t);
1287 }
1288