154331Sarchie/*
254331Sarchie * ng_pptpgre.c
3139823Simp */
4139823Simp
5139823Simp/*-
654331Sarchie * Copyright (c) 1996-1999 Whistle Communications, Inc.
754331Sarchie * All rights reserved.
854331Sarchie *
954331Sarchie * Subject to the following obligations and disclaimer of warranty, use and
1054331Sarchie * redistribution of this software, in source or object code forms, with or
1154331Sarchie * without modifications are expressly permitted by Whistle Communications;
1254331Sarchie * provided, however, that:
1354331Sarchie * 1. Any and all reproductions of the source or object code must include the
1454331Sarchie *    copyright notice above and the following disclaimer of warranties; and
1554331Sarchie * 2. No rights are granted, in any manner or form, to use Whistle
1654331Sarchie *    Communications, Inc. trademarks, including the mark "WHISTLE
1754331Sarchie *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1854331Sarchie *    such appears in the above copyright notice or in the software.
1954331Sarchie *
2054331Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2154331Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2254331Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2354331Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2454331Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2554331Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2654331Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2754331Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2854331Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2954331Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3054331Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3154331Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3254331Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3354331Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3454331Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3554331Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3654331Sarchie * OF SUCH DAMAGE.
3754331Sarchie *
3867506Sjulian * Author: Archie Cobbs <archie@freebsd.org>
3954331Sarchie *
4054331Sarchie * $FreeBSD$
4154331Sarchie * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
4254331Sarchie */
4354331Sarchie
4454331Sarchie/*
4554331Sarchie * PPTP/GRE netgraph node type.
4654331Sarchie *
4754331Sarchie * This node type does the GRE encapsulation as specified for the PPTP
4854331Sarchie * protocol (RFC 2637, section 4).  This includes sequencing and
4954331Sarchie * retransmission of frames, but not the actual packet delivery nor
5054331Sarchie * any of the TCP control stream protocol.
5154331Sarchie *
5254331Sarchie * The "upper" hook of this node is suitable for attaching to a "ppp"
5354331Sarchie * node link hook.  The "lower" hook of this node is suitable for attaching
5454331Sarchie * to a "ksocket" node on hook "inet/raw/gre".
5554331Sarchie */
5654331Sarchie
5754331Sarchie#include <sys/param.h>
5854331Sarchie#include <sys/systm.h>
5954331Sarchie#include <sys/kernel.h>
6054331Sarchie#include <sys/time.h>
61149615Sglebius#include <sys/lock.h>
62149615Sglebius#include <sys/malloc.h>
6354331Sarchie#include <sys/mbuf.h>
64149615Sglebius#include <sys/mutex.h>
65206050Smav#include <sys/endian.h>
6654331Sarchie#include <sys/errno.h>
6754331Sarchie
6854331Sarchie#include <netinet/in.h>
6954331Sarchie#include <netinet/in_systm.h>
7054331Sarchie#include <netinet/ip.h>
7154331Sarchie
7254331Sarchie#include <netgraph/ng_message.h>
7354331Sarchie#include <netgraph/netgraph.h>
7454331Sarchie#include <netgraph/ng_parse.h>
7554331Sarchie#include <netgraph/ng_pptpgre.h>
7654331Sarchie
7754331Sarchie/* GRE packet format, as used by PPTP */
7854331Sarchiestruct greheader {
7954331Sarchie#if BYTE_ORDER == LITTLE_ENDIAN
8054331Sarchie	u_char		recursion:3;		/* recursion control */
8154331Sarchie	u_char		ssr:1;			/* strict source route */
8254331Sarchie	u_char		hasSeq:1;		/* sequence number present */
8354331Sarchie	u_char		hasKey:1;		/* key present */
8454331Sarchie	u_char		hasRoute:1;		/* routing present */
8554331Sarchie	u_char		hasSum:1;		/* checksum present */
8654331Sarchie	u_char		vers:3;			/* version */
8754331Sarchie	u_char		flags:4;		/* flags */
8854331Sarchie	u_char		hasAck:1;		/* acknowlege number present */
8954331Sarchie#elif BYTE_ORDER == BIG_ENDIAN
9054331Sarchie	u_char		hasSum:1;		/* checksum present */
9154331Sarchie	u_char		hasRoute:1;		/* routing present */
9254331Sarchie	u_char		hasKey:1;		/* key present */
9354331Sarchie	u_char		hasSeq:1;		/* sequence number present */
9454331Sarchie	u_char		ssr:1;			/* strict source route */
9554331Sarchie	u_char		recursion:3;		/* recursion control */
9654331Sarchie	u_char		hasAck:1;		/* acknowlege number present */
9754331Sarchie	u_char		flags:4;		/* flags */
9854331Sarchie	u_char		vers:3;			/* version */
9954331Sarchie#else
10054331Sarchie#error BYTE_ORDER is not defined properly
10154331Sarchie#endif
10254331Sarchie	u_int16_t	proto;			/* protocol (ethertype) */
10354331Sarchie	u_int16_t	length;			/* payload length */
10454331Sarchie	u_int16_t	cid;			/* call id */
10554331Sarchie	u_int32_t	data[0];		/* opt. seq, ack, then data */
10654331Sarchie};
10754331Sarchie
10854331Sarchie/* The PPTP protocol ID used in the GRE 'proto' field */
10954331Sarchie#define PPTP_GRE_PROTO		0x880b
11054331Sarchie
11154331Sarchie/* Bits that must be set a certain way in all PPTP/GRE packets */
11254331Sarchie#define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
11354331Sarchie#define PPTP_INIT_MASK		0xef7fffff
11454331Sarchie
11554331Sarchie/* Min and max packet length */
11654331Sarchie#define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
11754331Sarchie
11854331Sarchie/* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
119177646Smav#define PPTP_TIME_SCALE		1024			/* milliseconds */
12063822Sarchietypedef u_int64_t		pptptime_t;
12154331Sarchie
12254331Sarchie/* Acknowledgment timeout parameters and functions */
12362222Sarchie#define PPTP_XMIT_WIN		16			/* max xmit window */
12494667Sarchie#define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
125134865Sglebius#define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 seconds */
12654331Sarchie
12763852Sarchie/* When we recieve a packet, we wait to see if there's an outgoing packet
12863852Sarchie   we can piggy-back the ACK off of. These parameters determine the mimimum
12963852Sarchie   and maxmimum length of time we're willing to wait in order to do that.
13063852Sarchie   These have no effect unless "enableDelayedAck" is turned on. */
13163852Sarchie#define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
13263852Sarchie#define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
13363852Sarchie
13462222Sarchie/* See RFC 2637 section 4.4 */
135166424Sglebius#define PPTP_ACK_ALPHA(x)	(((x) + 4) >> 3)	/* alpha = 0.125 */
136166424Sglebius#define PPTP_ACK_BETA(x)	(((x) + 2) >> 2)	/* beta = 0.25 */
13754331Sarchie#define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
13854331Sarchie#define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
13954331Sarchie
14060009Sarchie#define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
14160009Sarchie
142177587Smav#define SESSHASHSIZE		0x0020
143177587Smav#define SESSHASH(x)		(((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
144177587Smav
14554331Sarchie/* We keep packet retransmit and acknowlegement state in this struct */
146177587Smavstruct ng_pptpgre_sess {
147177587Smav	node_p			node;		/* this node pointer */
148177587Smav	hook_p			hook;		/* hook to upper layers */
149177587Smav	struct ng_pptpgre_conf	conf;		/* configuration info */
150177587Smav	struct mtx		mtx;		/* session mutex */
151177587Smav	u_int32_t		recvSeq;	/* last seq # we rcv'd */
152177587Smav	u_int32_t		xmitSeq;	/* last seq # we sent */
153177587Smav	u_int32_t		recvAck;	/* last seq # peer ack'd */
154177587Smav	u_int32_t		xmitAck;	/* last seq # we ack'd */
15554331Sarchie	int32_t			ato;		/* adaptive time-out value */
15654331Sarchie	int32_t			rtt;		/* round trip time estimate */
15754331Sarchie	int32_t			dev;		/* deviation estimate */
15854331Sarchie	u_int16_t		xmitWin;	/* size of xmit window */
15994667Sarchie	struct callout		sackTimer;	/* send ack timer */
16094667Sarchie	struct callout		rackTimer;	/* recv ack timer */
16162129Sarchie	u_int32_t		winAck;		/* seq when xmitWin will grow */
16254331Sarchie	pptptime_t		timeSent[PPTP_XMIT_WIN];
163177587Smav	LIST_ENTRY(ng_pptpgre_sess) sessions;
16454331Sarchie};
165177587Smavtypedef struct ng_pptpgre_sess *hpriv_p;
16654331Sarchie
16754331Sarchie/* Node private data */
16854331Sarchiestruct ng_pptpgre_private {
16954331Sarchie	hook_p			upper;		/* hook to upper layers */
17054331Sarchie	hook_p			lower;		/* hook to lower layers */
171177587Smav	struct ng_pptpgre_sess	uppersess;	/* default session for compat */
172177587Smav	LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
17360009Sarchie	struct ng_pptpgre_stats	stats;		/* node statistics */
17454331Sarchie};
17554331Sarchietypedef struct ng_pptpgre_private *priv_p;
17654331Sarchie
17754331Sarchie/* Netgraph node methods */
17854331Sarchiestatic ng_constructor_t	ng_pptpgre_constructor;
17954331Sarchiestatic ng_rcvmsg_t	ng_pptpgre_rcvmsg;
18070700Sjulianstatic ng_shutdown_t	ng_pptpgre_shutdown;
18154331Sarchiestatic ng_newhook_t	ng_pptpgre_newhook;
18254331Sarchiestatic ng_rcvdata_t	ng_pptpgre_rcvdata;
183177587Smavstatic ng_rcvdata_t	ng_pptpgre_rcvdata_lower;
18454331Sarchiestatic ng_disconnect_t	ng_pptpgre_disconnect;
18554331Sarchie
18654331Sarchie/* Helper functions */
187177587Smavstatic int	ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
188177646Smavstatic void	ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
189177587Smavstatic void	ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
190138618Sglebiusstatic void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
191138618Sglebius		    void *arg1, int arg2);
192138618Sglebiusstatic void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
193138618Sglebius		    void *arg1, int arg2);
194177587Smavstatic hpriv_p	ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
195177587Smavstatic void	ng_pptpgre_reset(hpriv_p hpriv);
196177587Smavstatic pptptime_t ng_pptpgre_time(void);
19754331Sarchie
19854331Sarchie/* Parse type for struct ng_pptpgre_conf */
19997685Sarchiestatic const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
20097685Sarchie	= NG_PPTPGRE_CONF_TYPE_INFO;
20154331Sarchiestatic const struct ng_parse_type ng_pptpgre_conf_type = {
20254331Sarchie	&ng_parse_struct_type,
20397685Sarchie	&ng_pptpgre_conf_type_fields,
20454331Sarchie};
20554331Sarchie
20660009Sarchie/* Parse type for struct ng_pptpgre_stats */
20797685Sarchiestatic const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
20897685Sarchie	= NG_PPTPGRE_STATS_TYPE_INFO;
20960009Sarchiestatic const struct ng_parse_type ng_pptp_stats_type = {
21060009Sarchie	&ng_parse_struct_type,
21197685Sarchie	&ng_pptpgre_stats_type_fields
21260009Sarchie};
21360009Sarchie
21454331Sarchie/* List of commands and how to convert arguments to/from ASCII */
21554331Sarchiestatic const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
21654331Sarchie	{
21754331Sarchie	  NGM_PPTPGRE_COOKIE,
21854331Sarchie	  NGM_PPTPGRE_SET_CONFIG,
21954331Sarchie	  "setconfig",
22054331Sarchie	  &ng_pptpgre_conf_type,
22154331Sarchie	  NULL
22254331Sarchie	},
22354331Sarchie	{
22454331Sarchie	  NGM_PPTPGRE_COOKIE,
22554331Sarchie	  NGM_PPTPGRE_GET_CONFIG,
22654331Sarchie	  "getconfig",
227177587Smav	  &ng_parse_hint16_type,
22854331Sarchie	  &ng_pptpgre_conf_type
22954331Sarchie	},
23060009Sarchie	{
23160009Sarchie	  NGM_PPTPGRE_COOKIE,
23260009Sarchie	  NGM_PPTPGRE_GET_STATS,
23360009Sarchie	  "getstats",
23460009Sarchie	  NULL,
23560009Sarchie	  &ng_pptp_stats_type
23660009Sarchie	},
23760009Sarchie	{
23860009Sarchie	  NGM_PPTPGRE_COOKIE,
23960009Sarchie	  NGM_PPTPGRE_CLR_STATS,
24060009Sarchie	  "clrstats",
24160009Sarchie	  NULL,
24260009Sarchie	  NULL
24360009Sarchie	},
24460009Sarchie	{
24560009Sarchie	  NGM_PPTPGRE_COOKIE,
24660009Sarchie	  NGM_PPTPGRE_GETCLR_STATS,
24760009Sarchie	  "getclrstats",
24860009Sarchie	  NULL,
24960009Sarchie	  &ng_pptp_stats_type
25060009Sarchie	},
25154331Sarchie	{ 0 }
25254331Sarchie};
25354331Sarchie
25454331Sarchie/* Node type descriptor */
25554331Sarchiestatic struct ng_type ng_pptpgre_typestruct = {
256129823Sjulian	.version =	NG_ABI_VERSION,
257129823Sjulian	.name =		NG_PPTPGRE_NODE_TYPE,
258129823Sjulian	.constructor =	ng_pptpgre_constructor,
259129823Sjulian	.rcvmsg =	ng_pptpgre_rcvmsg,
260129823Sjulian	.shutdown =	ng_pptpgre_shutdown,
261129823Sjulian	.newhook =	ng_pptpgre_newhook,
262129823Sjulian	.rcvdata =	ng_pptpgre_rcvdata,
263129823Sjulian	.disconnect =	ng_pptpgre_disconnect,
264129823Sjulian	.cmdlist =	ng_pptpgre_cmdlist,
26554331Sarchie};
26654331SarchieNETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
26754331Sarchie
26854331Sarchie#define ERROUT(x)	do { error = (x); goto done; } while (0)
26954331Sarchie
27054331Sarchie/************************************************************************
27154331Sarchie			NETGRAPH NODE STUFF
27254331Sarchie ************************************************************************/
27354331Sarchie
27454331Sarchie/*
27554331Sarchie * Node type constructor
27654331Sarchie */
27754331Sarchiestatic int
27870700Sjulianng_pptpgre_constructor(node_p node)
27954331Sarchie{
28054331Sarchie	priv_p priv;
281177587Smav	int i;
28254331Sarchie
28354331Sarchie	/* Allocate private structure */
284220768Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
28554331Sarchie
28670784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
28754331Sarchie
28854331Sarchie	/* Initialize state */
289177587Smav	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
290177587Smav	ng_callout_init(&priv->uppersess.sackTimer);
291177587Smav	ng_callout_init(&priv->uppersess.rackTimer);
292177587Smav	priv->uppersess.node = node;
29354331Sarchie
294177587Smav	for (i = 0; i < SESSHASHSIZE; i++)
295177587Smav	    LIST_INIT(&priv->sesshash[i]);
296177587Smav
297177587Smav	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
298177587Smav
29954331Sarchie	/* Done */
30054331Sarchie	return (0);
30154331Sarchie}
30254331Sarchie
30354331Sarchie/*
30454331Sarchie * Give our OK for a hook to be added.
30554331Sarchie */
30654331Sarchiestatic int
30754331Sarchieng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
30854331Sarchie{
30970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
31054331Sarchie
31154331Sarchie	/* Check hook name */
312177587Smav	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
313177587Smav		priv->upper = hook;
314177587Smav		priv->uppersess.hook = hook;
315177587Smav		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
316177587Smav	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
317177587Smav		priv->lower = hook;
318177587Smav		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
319177587Smav	} else {
320177587Smav		static const char hexdig[16] = "0123456789abcdef";
321177587Smav		const char *hex;
322177587Smav		hpriv_p hpriv;
323177587Smav		int i, j;
324177587Smav		uint16_t cid, hash;
32554331Sarchie
326177587Smav		/* Parse hook name to get session ID */
327177587Smav		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
328177587Smav		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
329177587Smav			return (EINVAL);
330177587Smav		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
331177587Smav		for (cid = i = 0; i < 4; i++) {
332177587Smav			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
333177587Smav			if (j == 16)
334177587Smav				return (EINVAL);
335177587Smav			cid = (cid << 4) | j;
336177587Smav		}
337177587Smav		if (hex[i] != '\0')
338177587Smav			return (EINVAL);
33954331Sarchie
340177587Smav		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
341177587Smav		if (hpriv == NULL)
342177587Smav			return (ENOMEM);
343177587Smav
344177587Smav		/* Initialize state */
345177587Smav		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
346177587Smav		ng_callout_init(&hpriv->sackTimer);
347177587Smav		ng_callout_init(&hpriv->rackTimer);
348177587Smav		hpriv->conf.cid = cid;
349177587Smav		hpriv->node = node;
350177587Smav		hpriv->hook = hook;
351177587Smav		NG_HOOK_SET_PRIVATE(hook, hpriv);
352177587Smav
353177587Smav		hash = SESSHASH(cid);
354177587Smav		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
355177587Smav	}
356177587Smav
35754331Sarchie	return (0);
35854331Sarchie}
35954331Sarchie
36054331Sarchie/*
36154331Sarchie * Receive a control message.
36254331Sarchie */
36354331Sarchiestatic int
36470700Sjulianng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
36554331Sarchie{
36670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
36754331Sarchie	struct ng_mesg *resp = NULL;
36854331Sarchie	int error = 0;
36970700Sjulian	struct ng_mesg *msg;
37054331Sarchie
37170700Sjulian	NGI_GET_MSG(item, msg);
37254331Sarchie	switch (msg->header.typecookie) {
37354331Sarchie	case NGM_PPTPGRE_COOKIE:
37454331Sarchie		switch (msg->header.cmd) {
37554331Sarchie		case NGM_PPTPGRE_SET_CONFIG:
37654331Sarchie		    {
37754331Sarchie			struct ng_pptpgre_conf *const newConf =
37854331Sarchie				(struct ng_pptpgre_conf *) msg->data;
379177587Smav			hpriv_p hpriv;
380177587Smav			uint16_t hash;
38154331Sarchie
38254331Sarchie			/* Check for invalid or illegal config */
38354331Sarchie			if (msg->header.arglen != sizeof(*newConf))
38454331Sarchie				ERROUT(EINVAL);
385177587Smav			/* Try to find session by cid. */
386177587Smav			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
387177587Smav			/* If not present - use upper. */
388177587Smav			if (hpriv == NULL) {
389177587Smav				hpriv = &priv->uppersess;
390177587Smav				LIST_REMOVE(hpriv, sessions);
391177587Smav				hash = SESSHASH(newConf->cid);
392177587Smav				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
393177587Smav				    sessions);
394177587Smav			}
395177587Smav			ng_pptpgre_reset(hpriv);	/* reset on configure */
396177587Smav			hpriv->conf = *newConf;
39754331Sarchie			break;
39854331Sarchie		    }
39954331Sarchie		case NGM_PPTPGRE_GET_CONFIG:
400177587Smav		    {
401177587Smav			hpriv_p hpriv;
402177587Smav
403177587Smav			if (msg->header.arglen == 2) {
404177587Smav				/* Try to find session by cid. */
405177587Smav	    			hpriv = ng_pptpgre_find_session(priv,
406177587Smav				    *((uint16_t *)msg->data));
407177587Smav				if (hpriv == NULL)
408177587Smav					ERROUT(EINVAL);
409177587Smav			} else if (msg->header.arglen == 0) {
410177587Smav				/* Use upper. */
411177587Smav				hpriv = &priv->uppersess;
412177587Smav			} else
413177587Smav				ERROUT(EINVAL);
414177587Smav			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
41554331Sarchie			if (resp == NULL)
41654331Sarchie				ERROUT(ENOMEM);
417177587Smav			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
41854331Sarchie			break;
419177587Smav		    }
42060009Sarchie		case NGM_PPTPGRE_GET_STATS:
42160009Sarchie		case NGM_PPTPGRE_CLR_STATS:
42260009Sarchie		case NGM_PPTPGRE_GETCLR_STATS:
42360009Sarchie		    {
42460009Sarchie			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
42560009Sarchie				NG_MKRESPONSE(resp, msg,
42660009Sarchie				    sizeof(priv->stats), M_NOWAIT);
42760009Sarchie				if (resp == NULL)
42860009Sarchie					ERROUT(ENOMEM);
42960009Sarchie				bcopy(&priv->stats,
43060009Sarchie				    resp->data, sizeof(priv->stats));
43160009Sarchie			}
43260009Sarchie			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
43360009Sarchie				bzero(&priv->stats, sizeof(priv->stats));
43460009Sarchie			break;
43560009Sarchie		    }
43654331Sarchie		default:
43754331Sarchie			error = EINVAL;
43854331Sarchie			break;
43954331Sarchie		}
44054331Sarchie		break;
44154331Sarchie	default:
44254331Sarchie		error = EINVAL;
44354331Sarchie		break;
44454331Sarchie	}
44570159Sjuliandone:
44670700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
44770700Sjulian	NG_FREE_MSG(msg);
44854331Sarchie	return (error);
44954331Sarchie}
45054331Sarchie
45154331Sarchie/*
45254331Sarchie * Receive incoming data on a hook.
45354331Sarchie */
45454331Sarchiestatic int
45570700Sjulianng_pptpgre_rcvdata(hook_p hook, item_p item)
45654331Sarchie{
457177587Smav	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
458149615Sglebius	int rval;
45954331Sarchie
46054331Sarchie	/* If not configured, reject */
461177587Smav	if (!hpriv->conf.enabled) {
46270700Sjulian		NG_FREE_ITEM(item);
46354331Sarchie		return (ENXIO);
46454331Sarchie	}
46554331Sarchie
466177587Smav	mtx_lock(&hpriv->mtx);
467149615Sglebius
468177587Smav	rval = ng_pptpgre_xmit(hpriv, item);
469149615Sglebius
470177587Smav	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
471149615Sglebius
472149615Sglebius	return (rval);
47354331Sarchie}
47454331Sarchie
47554331Sarchie/*
476177587Smav * Hook disconnection
47754331Sarchie */
47854331Sarchiestatic int
479177587Smavng_pptpgre_disconnect(hook_p hook)
48054331Sarchie{
481177587Smav	const node_p node = NG_HOOK_NODE(hook);
48270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
483177587Smav	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
48454331Sarchie
485177587Smav	/* Zero out hook pointer */
486177587Smav	if (hook == priv->upper) {
487177587Smav		priv->upper = NULL;
488177587Smav		priv->uppersess.hook = NULL;
489177587Smav	} else if (hook == priv->lower) {
490177587Smav		priv->lower = NULL;
491177587Smav	} else {
492177587Smav		/* Reset node (stops timers) */
493177587Smav		ng_pptpgre_reset(hpriv);
49454331Sarchie
495177587Smav		LIST_REMOVE(hpriv, sessions);
496177587Smav		mtx_destroy(&hpriv->mtx);
497177587Smav		free(hpriv, M_NETGRAPH);
498177587Smav	}
499149615Sglebius
500177587Smav	/* Go away if no longer connected to anything */
501177587Smav	if ((NG_NODE_NUMHOOKS(node) == 0)
502177587Smav	&& (NG_NODE_IS_VALID(node)))
503177587Smav		ng_rmnode_self(node);
50454331Sarchie	return (0);
50554331Sarchie}
50654331Sarchie
50754331Sarchie/*
508177587Smav * Destroy node
50954331Sarchie */
51054331Sarchiestatic int
511177587Smavng_pptpgre_shutdown(node_p node)
51254331Sarchie{
51370784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
51454331Sarchie
515177587Smav	/* Reset node (stops timers) */
516177587Smav	ng_pptpgre_reset(&priv->uppersess);
51754331Sarchie
518177587Smav	LIST_REMOVE(&priv->uppersess, sessions);
519177587Smav	mtx_destroy(&priv->uppersess.mtx);
520177587Smav
521184205Sdes	free(priv, M_NETGRAPH);
522177587Smav
523177587Smav	/* Decrement ref count */
524177587Smav	NG_NODE_UNREF(node);
52554331Sarchie	return (0);
52654331Sarchie}
52754331Sarchie
52854331Sarchie/*************************************************************************
52954331Sarchie		    TRANSMIT AND RECEIVE FUNCTIONS
53054331Sarchie*************************************************************************/
53154331Sarchie
53254331Sarchie/*
53354331Sarchie * Transmit an outgoing frame, or just an ack if m is NULL.
53454331Sarchie */
53554331Sarchiestatic int
536177587Smavng_pptpgre_xmit(hpriv_p hpriv, item_p item)
53754331Sarchie{
538177587Smav	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
53954331Sarchie	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
54054331Sarchie	struct greheader *const gre = (struct greheader *)buf;
54154331Sarchie	int grelen, error;
54270700Sjulian	struct mbuf *m;
54354331Sarchie
544177587Smav	mtx_assert(&hpriv->mtx, MA_OWNED);
545149880Sglebius
54670700Sjulian	if (item) {
54770700Sjulian		NGI_GET_M(item, m);
54870700Sjulian	} else {
54970700Sjulian		m = NULL;
55070700Sjulian	}
55160009Sarchie	/* Check if there's data */
55260009Sarchie	if (m != NULL) {
55354331Sarchie
554128657Sarchie		/* Check if windowing is enabled */
555177587Smav		if (hpriv->conf.enableWindowing) {
556128657Sarchie			/* Is our transmit window full? */
557177587Smav			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
558177587Smav			    hpriv->recvAck) >= hpriv->xmitWin) {
559128657Sarchie				priv->stats.xmitDrops++;
560149880Sglebius				ERROUT(ENOBUFS);
561128657Sarchie			}
56260009Sarchie		}
56354331Sarchie
56460009Sarchie		/* Sanity check frame length */
565239007Smav		if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
56660009Sarchie			priv->stats.xmitTooBig++;
567149880Sglebius			ERROUT(EMSGSIZE);
56860009Sarchie		}
56970700Sjulian	} else {
57060009Sarchie		priv->stats.xmitLoneAcks++;
57170700Sjulian	}
57260009Sarchie
57354331Sarchie	/* Build GRE header */
574206050Smav	be32enc(gre, PPTP_INIT_VALUE);
575206050Smav	be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
576206050Smav	be16enc(&gre->cid, hpriv->conf.peerCid);
57754331Sarchie
57854331Sarchie	/* Include sequence number if packet contains any data */
57954331Sarchie	if (m != NULL) {
58054331Sarchie		gre->hasSeq = 1;
581177587Smav		if (hpriv->conf.enableWindowing) {
582177587Smav			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
583177587Smav			    = ng_pptpgre_time();
584128657Sarchie		}
585177587Smav		hpriv->xmitSeq++;
586206050Smav		be32enc(&gre->data[0], hpriv->xmitSeq);
58754331Sarchie	}
58854331Sarchie
58954331Sarchie	/* Include acknowledgement (and stop send ack timer) if needed */
590177587Smav	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
59154331Sarchie		gre->hasAck = 1;
592206050Smav		be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
593177587Smav		hpriv->xmitAck = hpriv->recvSeq;
594177646Smav		if (hpriv->conf.enableDelayedAck)
595177646Smav			ng_uncallout(&hpriv->sackTimer, hpriv->node);
59654331Sarchie	}
59754331Sarchie
59854331Sarchie	/* Prepend GRE header to outgoing frame */
59954331Sarchie	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
60054331Sarchie	if (m == NULL) {
601243882Sglebius		MGETHDR(m, M_NOWAIT, MT_DATA);
60254331Sarchie		if (m == NULL) {
60363822Sarchie			priv->stats.memoryFailures++;
604149880Sglebius			ERROUT(ENOBUFS);
60554331Sarchie		}
60654331Sarchie		m->m_len = m->m_pkthdr.len = grelen;
60754331Sarchie		m->m_pkthdr.rcvif = NULL;
60854331Sarchie	} else {
609243882Sglebius		M_PREPEND(m, grelen, M_NOWAIT);
61054331Sarchie		if (m == NULL || (m->m_len < grelen
61154331Sarchie		    && (m = m_pullup(m, grelen)) == NULL)) {
61263822Sarchie			priv->stats.memoryFailures++;
613149880Sglebius			ERROUT(ENOBUFS);
61454331Sarchie		}
61554331Sarchie	}
61654331Sarchie	bcopy(gre, mtod(m, u_char *), grelen);
61754331Sarchie
61860009Sarchie	/* Update stats */
61960009Sarchie	priv->stats.xmitPackets++;
62060009Sarchie	priv->stats.xmitOctets += m->m_pkthdr.len;
62160009Sarchie
622149880Sglebius	/*
623149880Sglebius	 * XXX: we should reset timer only after an item has been sent
624149880Sglebius	 * successfully.
625149880Sglebius	 */
626177587Smav	if (hpriv->conf.enableWindowing &&
627177587Smav	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
628177587Smav		ng_pptpgre_start_recv_ack_timer(hpriv);
629149880Sglebius
630177587Smav	mtx_unlock(&hpriv->mtx);
631149880Sglebius
63254331Sarchie	/* Deliver packet */
63370700Sjulian	if (item) {
63470700Sjulian		NG_FWD_NEW_DATA(error, item, priv->lower, m);
63570700Sjulian	} else {
63670700Sjulian		NG_SEND_DATA_ONLY(error, priv->lower, m);
63770700Sjulian	}
63863822Sarchie
639149880Sglebius	return (error);
64070700Sjulian
641149880Sglebiusdone:
642177587Smav	mtx_unlock(&hpriv->mtx);
643149880Sglebius	NG_FREE_M(m);
644149880Sglebius	if (item)
645149880Sglebius		NG_FREE_ITEM(item);
64654331Sarchie	return (error);
64754331Sarchie}
64854331Sarchie
64954331Sarchie/*
65054331Sarchie * Handle an incoming packet.  The packet includes the IP header.
65154331Sarchie */
65254331Sarchiestatic int
653177587Smavng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
65454331Sarchie{
655177587Smav	hpriv_p hpriv;
656177587Smav	node_p node = NG_HOOK_NODE(hook);
65770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
65854331Sarchie	int iphlen, grelen, extralen;
65997897Sarchie	const struct greheader *gre;
66097897Sarchie	const struct ip *ip;
66154331Sarchie	int error = 0;
66270700Sjulian	struct mbuf *m;
66354331Sarchie
66470700Sjulian	NGI_GET_M(item, m);
66560009Sarchie	/* Update stats */
66660009Sarchie	priv->stats.recvPackets++;
66760009Sarchie	priv->stats.recvOctets += m->m_pkthdr.len;
66860009Sarchie
66954331Sarchie	/* Sanity check packet length */
67054331Sarchie	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
67160009Sarchie		priv->stats.recvRunts++;
672149880Sglebius		ERROUT(EINVAL);
67354331Sarchie	}
67454331Sarchie
67554331Sarchie	/* Safely pull up the complete IP+GRE headers */
67654331Sarchie	if (m->m_len < sizeof(*ip) + sizeof(*gre)
67754331Sarchie	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
67863822Sarchie		priv->stats.memoryFailures++;
679149880Sglebius		ERROUT(ENOBUFS);
68054331Sarchie	}
68197897Sarchie	ip = mtod(m, const struct ip *);
68254331Sarchie	iphlen = ip->ip_hl << 2;
68354331Sarchie	if (m->m_len < iphlen + sizeof(*gre)) {
68454331Sarchie		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
68563822Sarchie			priv->stats.memoryFailures++;
686149880Sglebius			ERROUT(ENOBUFS);
68754331Sarchie		}
68897897Sarchie		ip = mtod(m, const struct ip *);
68954331Sarchie	}
69097897Sarchie	gre = (const struct greheader *)((const u_char *)ip + iphlen);
69154331Sarchie	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
69260009Sarchie	if (m->m_pkthdr.len < iphlen + grelen) {
69360009Sarchie		priv->stats.recvRunts++;
694149880Sglebius		ERROUT(EINVAL);
69560009Sarchie	}
69654331Sarchie	if (m->m_len < iphlen + grelen) {
69754331Sarchie		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
69863822Sarchie			priv->stats.memoryFailures++;
699149880Sglebius			ERROUT(ENOBUFS);
70054331Sarchie		}
70197897Sarchie		ip = mtod(m, const struct ip *);
70297897Sarchie		gre = (const struct greheader *)((const u_char *)ip + iphlen);
70354331Sarchie	}
70454331Sarchie
70554331Sarchie	/* Sanity check packet length and GRE header bits */
70654331Sarchie	extralen = m->m_pkthdr.len
707206050Smav	    - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
70860009Sarchie	if (extralen < 0) {
70960009Sarchie		priv->stats.recvBadGRE++;
710149880Sglebius		ERROUT(EINVAL);
71160009Sarchie	}
712206050Smav	if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
71360009Sarchie		priv->stats.recvBadGRE++;
714149880Sglebius		ERROUT(EINVAL);
71560009Sarchie	}
716177587Smav
717206050Smav	hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
718177587Smav	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
71960009Sarchie		priv->stats.recvBadCID++;
720149880Sglebius		ERROUT(EINVAL);
72160009Sarchie	}
722177587Smav	mtx_lock(&hpriv->mtx);
72354331Sarchie
72454331Sarchie	/* Look for peer ack */
72554331Sarchie	if (gre->hasAck) {
726206050Smav		const u_int32_t	ack = be32dec(&gre->data[gre->hasSeq]);
727177587Smav		const int index = ack - hpriv->recvAck - 1;
72873998Sarchie		long sample;
72954331Sarchie		long diff;
73054331Sarchie
73154331Sarchie		/* Sanity check ack value */
732177587Smav		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
73360009Sarchie			priv->stats.recvBadAcks++;
73460009Sarchie			goto badAck;		/* we never sent it! */
73560009Sarchie		}
736177587Smav		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
73760009Sarchie			goto badAck;		/* ack already timed out */
738177587Smav		hpriv->recvAck = ack;
73954331Sarchie
74054331Sarchie		/* Update adaptive timeout stuff */
741177587Smav		if (hpriv->conf.enableWindowing) {
742177587Smav			sample = ng_pptpgre_time() - hpriv->timeSent[index];
743177587Smav			diff = sample - hpriv->rtt;
744177587Smav			hpriv->rtt += PPTP_ACK_ALPHA(diff);
745128657Sarchie			if (diff < 0)
746128657Sarchie				diff = -diff;
747177587Smav			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
748166424Sglebius			    /* +2 to compensate low precision of int math */
749177587Smav			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
750177587Smav			if (hpriv->ato > PPTP_MAX_TIMEOUT)
751177587Smav				hpriv->ato = PPTP_MAX_TIMEOUT;
752177587Smav			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
753177587Smav				hpriv->ato = PPTP_MIN_TIMEOUT;
75462222Sarchie
755128657Sarchie			/* Shift packet transmit times in our transmit window */
756177587Smav			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
757177587Smav			    sizeof(*hpriv->timeSent)
758128657Sarchie			      * (PPTP_XMIT_WIN - (index + 1)));
75962222Sarchie
760128657Sarchie			/* If we sent an entire window, increase window size */
761177587Smav			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
762177587Smav			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
763177587Smav				hpriv->xmitWin++;
764177587Smav				hpriv->winAck = ack + hpriv->xmitWin;
765128657Sarchie			}
766128657Sarchie
767128657Sarchie			/* Stop/(re)start receive ACK timer as necessary */
768177646Smav			ng_uncallout(&hpriv->rackTimer, hpriv->node);
769177587Smav			if (hpriv->recvAck != hpriv->xmitSeq)
770177587Smav				ng_pptpgre_start_recv_ack_timer(hpriv);
77154331Sarchie		}
77254331Sarchie	}
77360009SarchiebadAck:
77454331Sarchie
77554331Sarchie	/* See if frame contains any data */
77654331Sarchie	if (gre->hasSeq) {
777206050Smav		const u_int32_t seq = be32dec(&gre->data[0]);
77854331Sarchie
77954331Sarchie		/* Sanity check sequence number */
780177587Smav		if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) {
781177587Smav			if (seq == hpriv->recvSeq)
78260009Sarchie				priv->stats.recvDuplicates++;
78360009Sarchie			else
78460009Sarchie				priv->stats.recvOutOfOrder++;
785177587Smav			mtx_unlock(&hpriv->mtx);
786149880Sglebius			ERROUT(EINVAL);
78760009Sarchie		}
788177587Smav		hpriv->recvSeq = seq;
78954331Sarchie
79054331Sarchie		/* We need to acknowledge this packet; do it soon... */
791177587Smav		if (!(callout_pending(&hpriv->sackTimer))) {
79263822Sarchie			/* If delayed ACK is disabled, send it now */
793177587Smav			if (!hpriv->conf.enableDelayedAck) {	/* ack now */
794177587Smav				ng_pptpgre_xmit(hpriv, NULL);
795177587Smav				/* ng_pptpgre_xmit() drops the mutex */
796149880Sglebius			} else {				/* ack later */
797177646Smav				ng_pptpgre_start_send_ack_timer(hpriv);
798177587Smav				mtx_unlock(&hpriv->mtx);
79954331Sarchie			}
800177587Smav		} else
801177587Smav			mtx_unlock(&hpriv->mtx);
80254331Sarchie
80354331Sarchie		/* Trim mbuf down to internal payload */
80454331Sarchie		m_adj(m, iphlen + grelen);
80554331Sarchie		if (extralen > 0)
80654331Sarchie			m_adj(m, -extralen);
80754331Sarchie
808177587Smav		mtx_assert(&hpriv->mtx, MA_NOTOWNED);
809177587Smav
81054331Sarchie		/* Deliver frame to upper layers */
811177587Smav		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
81260009Sarchie	} else {
81360009Sarchie		priv->stats.recvLoneAcks++;
814177587Smav		mtx_unlock(&hpriv->mtx);
81570700Sjulian		NG_FREE_ITEM(item);
81670700Sjulian		NG_FREE_M(m);		/* no data to deliver */
81760009Sarchie	}
818149880Sglebius
81954331Sarchie	return (error);
820149880Sglebius
821149880Sglebiusdone:
822149880Sglebius	NG_FREE_ITEM(item);
823149880Sglebius	NG_FREE_M(m);
824149880Sglebius	return (error);
82554331Sarchie}
82654331Sarchie
82754331Sarchie/*************************************************************************
82854331Sarchie		    TIMER RELATED FUNCTIONS
82954331Sarchie*************************************************************************/
83054331Sarchie
83154331Sarchie/*
83260009Sarchie * Start a timer for the peer's acknowledging our oldest unacknowledged
83354331Sarchie * sequence number.  If we get an ack for this sequence number before
83454331Sarchie * the timer goes off, we cancel the timer.  Resets currently running
83554331Sarchie * recv ack timer, if any.
83654331Sarchie */
83754331Sarchiestatic void
838177587Smavng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
83954331Sarchie{
84063822Sarchie	int remain, ticks;
84154331Sarchie
84254331Sarchie	/* Compute how long until oldest unack'd packet times out,
84354331Sarchie	   and reset the timer to that time. */
844177587Smav	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
84554331Sarchie	if (remain < 0)
84654331Sarchie		remain = 0;
84760009Sarchie
84894667Sarchie	/* Be conservative: timeout can happen up to 1 tick early */
84963822Sarchie	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
850177587Smav	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
851177587Smav	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
85254331Sarchie}
85354331Sarchie
85454331Sarchie/*
85554331Sarchie * The peer has failed to acknowledge the oldest unacknowledged sequence
85654331Sarchie * number within the time allotted.  Update our adaptive timeout parameters
85754331Sarchie * and reset/restart the recv ack timer.
85854331Sarchie */
85954331Sarchiestatic void
860138618Sglebiusng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
86154331Sarchie{
86270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
863177587Smav	const hpriv_p hpriv = arg1;
86454331Sarchie
86554331Sarchie	/* Update adaptive timeout stuff */
86660009Sarchie	priv->stats.recvAckTimeouts++;
867177587Smav	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
868177587Smav	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
869177587Smav	if (hpriv->ato > PPTP_MAX_TIMEOUT)
870177587Smav		hpriv->ato = PPTP_MAX_TIMEOUT;
871177587Smav	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
872177587Smav		hpriv->ato = PPTP_MIN_TIMEOUT;
87362222Sarchie
87462222Sarchie	/* Reset ack and sliding window */
875177587Smav	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
876177587Smav	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
877177587Smav	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
87854331Sarchie}
87954331Sarchie
88054331Sarchie/*
88160009Sarchie * Start the send ack timer. This assumes the timer is not
88260009Sarchie * already running.
88360009Sarchie */
88460009Sarchiestatic void
885177646Smavng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
88660009Sarchie{
887177646Smav	int ackTimeout, ticks;
88860009Sarchie
889177646Smav	/* Take 1/4 of the estimated round trip time */
890177646Smav	ackTimeout = (hpriv->rtt >> 2);
891177646Smav	if (ackTimeout < PPTP_MIN_ACK_DELAY)
892177646Smav		ackTimeout = PPTP_MIN_ACK_DELAY;
893177646Smav	else if (ackTimeout > PPTP_MAX_ACK_DELAY)
894177646Smav		ackTimeout = PPTP_MAX_ACK_DELAY;
895177646Smav
89694667Sarchie	/* Be conservative: timeout can happen up to 1 tick early */
89763822Sarchie	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
898177587Smav	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
899177587Smav	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
90060009Sarchie}
90160009Sarchie
90260009Sarchie/*
90354331Sarchie * We've waited as long as we're willing to wait before sending an
90454331Sarchie * acknowledgement to the peer for received frames. We had hoped to
90554331Sarchie * be able to piggy back our acknowledgement on an outgoing data frame,
90654331Sarchie * but apparently there haven't been any since. So send the ack now.
90754331Sarchie */
90854331Sarchiestatic void
909138618Sglebiusng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
91054331Sarchie{
911177587Smav	const hpriv_p hpriv = arg1;
912149880Sglebius
913177587Smav	mtx_lock(&hpriv->mtx);
91454331Sarchie	/* Send a frame with an ack but no payload */
915177587Smav  	ng_pptpgre_xmit(hpriv, NULL);
916177587Smav	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
91754331Sarchie}
91854331Sarchie
91954331Sarchie/*************************************************************************
92054331Sarchie		    MISC FUNCTIONS
92154331Sarchie*************************************************************************/
92254331Sarchie
92354331Sarchie/*
924177587Smav * Find the hook with a given session ID.
92554331Sarchie */
926177587Smavstatic hpriv_p
927177587Smavng_pptpgre_find_session(priv_p privp, u_int16_t cid)
92854331Sarchie{
929177587Smav	uint16_t	hash = SESSHASH(cid);
930177587Smav	hpriv_p	hpriv = NULL;
93154331Sarchie
932177587Smav	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
933177587Smav		if (hpriv->conf.cid == cid)
934177587Smav			break;
935177587Smav	}
936149615Sglebius
937177587Smav	return (hpriv);
938177587Smav}
939177587Smav
940177587Smav/*
941177587Smav * Reset state (must be called with lock held or from writer)
942177587Smav */
943177587Smavstatic void
944177587Smavng_pptpgre_reset(hpriv_p hpriv)
945177587Smav{
94654331Sarchie	/* Reset adaptive timeout state */
947177587Smav	hpriv->ato = PPTP_MAX_TIMEOUT;
948177646Smav	hpriv->rtt = PPTP_TIME_SCALE / 10;
949177646Smav	if (hpriv->conf.peerPpd > 1)	/* ppd = 0 treat as = 1 */
950177646Smav		hpriv->rtt *= hpriv->conf.peerPpd;
951177587Smav	hpriv->dev = 0;
952177587Smav	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
953177587Smav	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
954177587Smav		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
955177587Smav	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
956177587Smav		hpriv->xmitWin = PPTP_XMIT_WIN;
957177587Smav	hpriv->winAck = hpriv->xmitWin;
95854331Sarchie
95954331Sarchie	/* Reset sequence numbers */
960177587Smav	hpriv->recvSeq = ~0;
961177587Smav	hpriv->recvAck = ~0;
962177587Smav	hpriv->xmitSeq = ~0;
963177587Smav	hpriv->xmitAck = ~0;
96454331Sarchie
96594667Sarchie	/* Stop timers */
966177646Smav	ng_uncallout(&hpriv->sackTimer, hpriv->node);
967177646Smav	ng_uncallout(&hpriv->rackTimer, hpriv->node);
96854331Sarchie}
96954331Sarchie
97054331Sarchie/*
97154331Sarchie * Return the current time scaled & translated to our internally used format.
97254331Sarchie */
97354331Sarchiestatic pptptime_t
974177587Smavng_pptpgre_time(void)
97554331Sarchie{
97654331Sarchie	struct timeval tv;
97763822Sarchie	pptptime_t t;
97854331Sarchie
97963822Sarchie	microuptime(&tv);
98063822Sarchie	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
981177646Smav	t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
98263822Sarchie	return(t);
98354331Sarchie}
984