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