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