xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision 816b834f1422775bca3e40de0fe8343ed6dfd1cf)
1add85a1dSArchie Cobbs 
2add85a1dSArchie Cobbs /*
3add85a1dSArchie Cobbs  * ng_pptpgre.c
4add85a1dSArchie Cobbs  *
5add85a1dSArchie Cobbs  * Copyright (c) 1996-1999 Whistle Communications, Inc.
6add85a1dSArchie Cobbs  * All rights reserved.
7add85a1dSArchie Cobbs  *
8add85a1dSArchie Cobbs  * Subject to the following obligations and disclaimer of warranty, use and
9add85a1dSArchie Cobbs  * redistribution of this software, in source or object code forms, with or
10add85a1dSArchie Cobbs  * without modifications are expressly permitted by Whistle Communications;
11add85a1dSArchie Cobbs  * provided, however, that:
12add85a1dSArchie Cobbs  * 1. Any and all reproductions of the source or object code must include the
13add85a1dSArchie Cobbs  *    copyright notice above and the following disclaimer of warranties; and
14add85a1dSArchie Cobbs  * 2. No rights are granted, in any manner or form, to use Whistle
15add85a1dSArchie Cobbs  *    Communications, Inc. trademarks, including the mark "WHISTLE
16add85a1dSArchie Cobbs  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17add85a1dSArchie Cobbs  *    such appears in the above copyright notice or in the software.
18add85a1dSArchie Cobbs  *
19add85a1dSArchie Cobbs  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20add85a1dSArchie Cobbs  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21add85a1dSArchie Cobbs  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22add85a1dSArchie Cobbs  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23add85a1dSArchie Cobbs  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24add85a1dSArchie Cobbs  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25add85a1dSArchie Cobbs  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26add85a1dSArchie Cobbs  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27add85a1dSArchie Cobbs  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28add85a1dSArchie Cobbs  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29add85a1dSArchie Cobbs  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30add85a1dSArchie Cobbs  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31add85a1dSArchie Cobbs  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32add85a1dSArchie Cobbs  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33add85a1dSArchie Cobbs  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34add85a1dSArchie Cobbs  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35add85a1dSArchie Cobbs  * OF SUCH DAMAGE.
36add85a1dSArchie Cobbs  *
37cc3bbd68SJulian Elischer  * Author: Archie Cobbs <archie@freebsd.org>
38add85a1dSArchie Cobbs  *
39add85a1dSArchie Cobbs  * $FreeBSD$
40add85a1dSArchie Cobbs  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
41add85a1dSArchie Cobbs  */
42add85a1dSArchie Cobbs 
43add85a1dSArchie Cobbs /*
44add85a1dSArchie Cobbs  * PPTP/GRE netgraph node type.
45add85a1dSArchie Cobbs  *
46add85a1dSArchie Cobbs  * This node type does the GRE encapsulation as specified for the PPTP
47add85a1dSArchie Cobbs  * protocol (RFC 2637, section 4).  This includes sequencing and
48add85a1dSArchie Cobbs  * retransmission of frames, but not the actual packet delivery nor
49add85a1dSArchie Cobbs  * any of the TCP control stream protocol.
50add85a1dSArchie Cobbs  *
51add85a1dSArchie Cobbs  * The "upper" hook of this node is suitable for attaching to a "ppp"
52add85a1dSArchie Cobbs  * node link hook.  The "lower" hook of this node is suitable for attaching
53add85a1dSArchie Cobbs  * to a "ksocket" node on hook "inet/raw/gre".
54add85a1dSArchie Cobbs  */
55add85a1dSArchie Cobbs 
56add85a1dSArchie Cobbs #include <sys/param.h>
57add85a1dSArchie Cobbs #include <sys/systm.h>
58add85a1dSArchie Cobbs #include <sys/kernel.h>
59add85a1dSArchie Cobbs #include <sys/time.h>
60add85a1dSArchie Cobbs #include <sys/mbuf.h>
61add85a1dSArchie Cobbs #include <sys/malloc.h>
62add85a1dSArchie Cobbs #include <sys/errno.h>
63add85a1dSArchie Cobbs 
64add85a1dSArchie Cobbs #include <netinet/in.h>
65add85a1dSArchie Cobbs #include <netinet/in_systm.h>
66add85a1dSArchie Cobbs #include <netinet/ip.h>
67add85a1dSArchie Cobbs 
68add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
69add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
70add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
71add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
72add85a1dSArchie Cobbs 
73add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
74add85a1dSArchie Cobbs struct greheader {
75add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
76add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
77add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
78add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
79add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
80add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
81add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
82add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
83add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
84add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
85add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
86add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
87add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
88add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
89add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
90add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
91add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
92add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
93add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
94add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
95add85a1dSArchie Cobbs #else
96add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
97add85a1dSArchie Cobbs #endif
98add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
99add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
100add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
101add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
102add85a1dSArchie Cobbs };
103add85a1dSArchie Cobbs 
104add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
105add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
106add85a1dSArchie Cobbs 
107add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
108add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
109add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
110add85a1dSArchie Cobbs 
111add85a1dSArchie Cobbs /* Min and max packet length */
112add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
113add85a1dSArchie Cobbs 
114add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
115e962a823SArchie Cobbs #define PPTP_TIME_SCALE		1000			/* milliseconds */
116678f9e33SArchie Cobbs typedef u_int64_t		pptptime_t;
117add85a1dSArchie Cobbs 
118add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
119e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
120e962a823SArchie Cobbs #define PPTP_MIN_RTT		(PPTP_TIME_SCALE / 10)	/* 100 milliseconds */
1214a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
122add85a1dSArchie Cobbs #define PPTP_MAX_TIMEOUT	(10 * PPTP_TIME_SCALE)	/* 10 seconds */
123add85a1dSArchie Cobbs 
124da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet
125da010626SArchie Cobbs    we can piggy-back the ACK off of. These parameters determine the mimimum
126da010626SArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that.
127da010626SArchie Cobbs    These have no effect unless "enableDelayedAck" is turned on. */
128da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
129da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
130da010626SArchie Cobbs 
131e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
132add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x)	((x) >> 3)	/* alpha = 0.125 */
133add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x)	((x) >> 2)	/* beta = 0.25 */
134add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
135add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
136add85a1dSArchie Cobbs 
1379bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
1389bee7adfSArchie Cobbs 
139add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
140add85a1dSArchie Cobbs struct ng_pptpgre_ackp {
141add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
142add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
143add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
144add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
1454a48abb2SArchie Cobbs 	struct callout		sackTimer;	/* send ack timer */
1464a48abb2SArchie Cobbs 	struct callout		rackTimer;	/* recv ack timer */
1473cd7db22SArchie Cobbs 	node_p			*sackTimerPtr;	/* send ack timer pointer */
1483cd7db22SArchie Cobbs 	node_p			*rackTimerPtr;	/* recv ack timer pointer */
1493cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
150add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
151678f9e33SArchie Cobbs #ifdef DEBUG_RAT
152da010626SArchie Cobbs 	pptptime_t		timerStart;	/* when rackTimer started */
153da010626SArchie Cobbs 	pptptime_t		timerLength;	/* rackTimer duration */
154678f9e33SArchie Cobbs #endif
155add85a1dSArchie Cobbs };
156add85a1dSArchie Cobbs 
157add85a1dSArchie Cobbs /* Node private data */
158add85a1dSArchie Cobbs struct ng_pptpgre_private {
159add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
160add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
161add85a1dSArchie Cobbs 	struct ng_pptpgre_conf	conf;		/* configuration info */
162add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp	ackp;		/* packet transmit ack state */
163add85a1dSArchie Cobbs 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
164add85a1dSArchie Cobbs 	u_int32_t		xmitSeq;	/* last seq # we sent */
165add85a1dSArchie Cobbs 	u_int32_t		recvAck;	/* last seq # peer ack'd */
166add85a1dSArchie Cobbs 	u_int32_t		xmitAck;	/* last seq # we ack'd */
1674a48abb2SArchie Cobbs 	u_int			timers;		/* number of pending timers */
168add85a1dSArchie Cobbs 	struct timeval		startTime;	/* time node was created */
1699bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
170add85a1dSArchie Cobbs };
171add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
172add85a1dSArchie Cobbs 
173add85a1dSArchie Cobbs /* Netgraph node methods */
174add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
175add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
176069154d5SJulian Elischer static ng_shutdown_t	ng_pptpgre_shutdown;
177add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
178add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
179add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
180add85a1dSArchie Cobbs 
181add85a1dSArchie Cobbs /* Helper functions */
182069154d5SJulian Elischer static int	ng_pptpgre_xmit(node_p node, item_p item);
183069154d5SJulian Elischer static int	ng_pptpgre_recv(node_p node, item_p item);
184da010626SArchie Cobbs static void	ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout);
1854a48abb2SArchie Cobbs static void	ng_pptpgre_stop_send_ack_timer(node_p node);
186add85a1dSArchie Cobbs static void	ng_pptpgre_start_recv_ack_timer(node_p node);
1874a48abb2SArchie Cobbs static void	ng_pptpgre_stop_recv_ack_timer(node_p node);
188add85a1dSArchie Cobbs static void	ng_pptpgre_recv_ack_timeout(void *arg);
189add85a1dSArchie Cobbs static void	ng_pptpgre_send_ack_timeout(void *arg);
190add85a1dSArchie Cobbs static void	ng_pptpgre_reset(node_p node);
191add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node);
192add85a1dSArchie Cobbs 
193add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
194f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
195f0184ff8SArchie Cobbs 	= NG_PPTPGRE_CONF_TYPE_INFO;
196add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
197add85a1dSArchie Cobbs 	&ng_parse_struct_type,
198f0184ff8SArchie Cobbs 	&ng_pptpgre_conf_type_fields,
199add85a1dSArchie Cobbs };
200add85a1dSArchie Cobbs 
2019bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
202f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
203f0184ff8SArchie Cobbs 	= NG_PPTPGRE_STATS_TYPE_INFO;
2049bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2059bee7adfSArchie Cobbs 	&ng_parse_struct_type,
206f0184ff8SArchie Cobbs 	&ng_pptpgre_stats_type_fields
2079bee7adfSArchie Cobbs };
2089bee7adfSArchie Cobbs 
209add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
210add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
211add85a1dSArchie Cobbs 	{
212add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
213add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
214add85a1dSArchie Cobbs 	  "setconfig",
215add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
216add85a1dSArchie Cobbs 	  NULL
217add85a1dSArchie Cobbs 	},
218add85a1dSArchie Cobbs 	{
219add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
220add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
221add85a1dSArchie Cobbs 	  "getconfig",
222add85a1dSArchie Cobbs 	  NULL,
223add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
224add85a1dSArchie Cobbs 	},
2259bee7adfSArchie Cobbs 	{
2269bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2279bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2289bee7adfSArchie Cobbs 	  "getstats",
2299bee7adfSArchie Cobbs 	  NULL,
2309bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2319bee7adfSArchie Cobbs 	},
2329bee7adfSArchie Cobbs 	{
2339bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2349bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2359bee7adfSArchie Cobbs 	  "clrstats",
2369bee7adfSArchie Cobbs 	  NULL,
2379bee7adfSArchie Cobbs 	  NULL
2389bee7adfSArchie Cobbs 	},
2399bee7adfSArchie Cobbs 	{
2409bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2419bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2429bee7adfSArchie Cobbs 	  "getclrstats",
2439bee7adfSArchie Cobbs 	  NULL,
2449bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2459bee7adfSArchie Cobbs 	},
246add85a1dSArchie Cobbs 	{ 0 }
247add85a1dSArchie Cobbs };
248add85a1dSArchie Cobbs 
249add85a1dSArchie Cobbs /* Node type descriptor */
250add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
251589f6ed8SJulian Elischer 	NG_ABI_VERSION,
252add85a1dSArchie Cobbs 	NG_PPTPGRE_NODE_TYPE,
253add85a1dSArchie Cobbs 	NULL,
254add85a1dSArchie Cobbs 	ng_pptpgre_constructor,
255add85a1dSArchie Cobbs 	ng_pptpgre_rcvmsg,
256069154d5SJulian Elischer 	ng_pptpgre_shutdown,
257add85a1dSArchie Cobbs 	ng_pptpgre_newhook,
258add85a1dSArchie Cobbs 	NULL,
259add85a1dSArchie Cobbs 	NULL,
260add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
261add85a1dSArchie Cobbs 	ng_pptpgre_disconnect,
262add85a1dSArchie Cobbs 	ng_pptpgre_cmdlist
263add85a1dSArchie Cobbs };
264add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
265add85a1dSArchie Cobbs 
266add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
267add85a1dSArchie Cobbs 
268add85a1dSArchie Cobbs /************************************************************************
269add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
270add85a1dSArchie Cobbs  ************************************************************************/
271add85a1dSArchie Cobbs 
272add85a1dSArchie Cobbs /*
273add85a1dSArchie Cobbs  * Node type constructor
274add85a1dSArchie Cobbs  */
275add85a1dSArchie Cobbs static int
276069154d5SJulian Elischer ng_pptpgre_constructor(node_p node)
277add85a1dSArchie Cobbs {
278add85a1dSArchie Cobbs 	priv_p priv;
279add85a1dSArchie Cobbs 
280add85a1dSArchie Cobbs 	/* Allocate private structure */
28199cdf4ccSDavid Malone 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
282add85a1dSArchie Cobbs 	if (priv == NULL)
283add85a1dSArchie Cobbs 		return (ENOMEM);
284add85a1dSArchie Cobbs 
28530400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
286add85a1dSArchie Cobbs 
287add85a1dSArchie Cobbs 	/* Initialize state */
2884a48abb2SArchie Cobbs 	callout_init(&priv->ackp.sackTimer, 0);
2894a48abb2SArchie Cobbs 	callout_init(&priv->ackp.rackTimer, 0);
290add85a1dSArchie Cobbs 
291add85a1dSArchie Cobbs 	/* Done */
292add85a1dSArchie Cobbs 	return (0);
293add85a1dSArchie Cobbs }
294add85a1dSArchie Cobbs 
295add85a1dSArchie Cobbs /*
296add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
297add85a1dSArchie Cobbs  */
298add85a1dSArchie Cobbs static int
299add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
300add85a1dSArchie Cobbs {
30130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
302add85a1dSArchie Cobbs 	hook_p *hookPtr;
303add85a1dSArchie Cobbs 
304add85a1dSArchie Cobbs 	/* Check hook name */
305add85a1dSArchie Cobbs 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0)
306add85a1dSArchie Cobbs 		hookPtr = &priv->upper;
307add85a1dSArchie Cobbs 	else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0)
308add85a1dSArchie Cobbs 		hookPtr = &priv->lower;
309add85a1dSArchie Cobbs 	else
310add85a1dSArchie Cobbs 		return (EINVAL);
311add85a1dSArchie Cobbs 
312add85a1dSArchie Cobbs 	/* See if already connected */
313add85a1dSArchie Cobbs 	if (*hookPtr != NULL)
314add85a1dSArchie Cobbs 		return (EISCONN);
315add85a1dSArchie Cobbs 
316add85a1dSArchie Cobbs 	/* OK */
317add85a1dSArchie Cobbs 	*hookPtr = hook;
318add85a1dSArchie Cobbs 	return (0);
319add85a1dSArchie Cobbs }
320add85a1dSArchie Cobbs 
321add85a1dSArchie Cobbs /*
322add85a1dSArchie Cobbs  * Receive a control message.
323add85a1dSArchie Cobbs  */
324add85a1dSArchie Cobbs static int
325069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
326add85a1dSArchie Cobbs {
32730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
328add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
329add85a1dSArchie Cobbs 	int error = 0;
330069154d5SJulian Elischer 	struct ng_mesg *msg;
331add85a1dSArchie Cobbs 
332069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
333add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
334add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
335add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
336add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
337add85a1dSArchie Cobbs 		    {
338add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
339add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
340add85a1dSArchie Cobbs 
341add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
342add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
343add85a1dSArchie Cobbs 				ERROUT(EINVAL);
344add85a1dSArchie Cobbs 			ng_pptpgre_reset(node);		/* reset on configure */
345add85a1dSArchie Cobbs 			priv->conf = *newConf;
346add85a1dSArchie Cobbs 			break;
347add85a1dSArchie Cobbs 		    }
348add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
349add85a1dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
350add85a1dSArchie Cobbs 			if (resp == NULL)
351add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
352add85a1dSArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
353add85a1dSArchie Cobbs 			break;
3549bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
3559bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
3569bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
3579bee7adfSArchie Cobbs 		    {
3589bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
3599bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3609bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
3619bee7adfSArchie Cobbs 				if (resp == NULL)
3629bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
3639bee7adfSArchie Cobbs 				bcopy(&priv->stats,
3649bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
3659bee7adfSArchie Cobbs 			}
3669bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
3679bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
3689bee7adfSArchie Cobbs 			break;
3699bee7adfSArchie Cobbs 		    }
370add85a1dSArchie Cobbs 		default:
371add85a1dSArchie Cobbs 			error = EINVAL;
372add85a1dSArchie Cobbs 			break;
373add85a1dSArchie Cobbs 		}
374add85a1dSArchie Cobbs 		break;
375add85a1dSArchie Cobbs 	default:
376add85a1dSArchie Cobbs 		error = EINVAL;
377add85a1dSArchie Cobbs 		break;
378add85a1dSArchie Cobbs 	}
379589f6ed8SJulian Elischer done:
380069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
381069154d5SJulian Elischer 	NG_FREE_MSG(msg);
382add85a1dSArchie Cobbs 	return (error);
383add85a1dSArchie Cobbs }
384add85a1dSArchie Cobbs 
385add85a1dSArchie Cobbs /*
386add85a1dSArchie Cobbs  * Receive incoming data on a hook.
387add85a1dSArchie Cobbs  */
388add85a1dSArchie Cobbs static int
389069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
390add85a1dSArchie Cobbs {
39130400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
39230400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
393add85a1dSArchie Cobbs 
394add85a1dSArchie Cobbs 	/* If not configured, reject */
395add85a1dSArchie Cobbs 	if (!priv->conf.enabled) {
396069154d5SJulian Elischer 		NG_FREE_ITEM(item);
397add85a1dSArchie Cobbs 		return (ENXIO);
398add85a1dSArchie Cobbs 	}
399add85a1dSArchie Cobbs 
400add85a1dSArchie Cobbs 	/* Treat as xmit or recv data */
401add85a1dSArchie Cobbs 	if (hook == priv->upper)
402069154d5SJulian Elischer 		return ng_pptpgre_xmit(node, item);
403add85a1dSArchie Cobbs 	if (hook == priv->lower)
404069154d5SJulian Elischer 		return ng_pptpgre_recv(node, item);
4056e551fb6SDavid E. O'Brien 	panic("%s: weird hook", __func__);
406add85a1dSArchie Cobbs }
407add85a1dSArchie Cobbs 
408add85a1dSArchie Cobbs /*
409add85a1dSArchie Cobbs  * Destroy node
410add85a1dSArchie Cobbs  */
411add85a1dSArchie Cobbs static int
412069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
413add85a1dSArchie Cobbs {
41430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
415add85a1dSArchie Cobbs 
4163cd7db22SArchie Cobbs 	/* Reset node */
417add85a1dSArchie Cobbs 	ng_pptpgre_reset(node);
418add85a1dSArchie Cobbs 
4194a48abb2SArchie Cobbs 	/* If no timers remain, free private info as well */
4204a48abb2SArchie Cobbs 	if (priv->timers == 0) {
421add85a1dSArchie Cobbs 		bzero(priv, sizeof(*priv));
422add85a1dSArchie Cobbs 		FREE(priv, M_NETGRAPH);
42330400f03SJulian Elischer 		NG_NODE_SET_PRIVATE(node, NULL);
4244a48abb2SArchie Cobbs 	}
4254a48abb2SArchie Cobbs 
4264a48abb2SArchie Cobbs 	/* Decrement ref count */
42730400f03SJulian Elischer 	NG_NODE_UNREF(node);
428add85a1dSArchie Cobbs 	return (0);
429add85a1dSArchie Cobbs }
430add85a1dSArchie Cobbs 
431add85a1dSArchie Cobbs /*
432add85a1dSArchie Cobbs  * Hook disconnection
433add85a1dSArchie Cobbs  */
434add85a1dSArchie Cobbs static int
435add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook)
436add85a1dSArchie Cobbs {
43730400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
43830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
439add85a1dSArchie Cobbs 
440add85a1dSArchie Cobbs 	/* Zero out hook pointer */
441add85a1dSArchie Cobbs 	if (hook == priv->upper)
442add85a1dSArchie Cobbs 		priv->upper = NULL;
443add85a1dSArchie Cobbs 	else if (hook == priv->lower)
444add85a1dSArchie Cobbs 		priv->lower = NULL;
445add85a1dSArchie Cobbs 	else
4466e551fb6SDavid E. O'Brien 		panic("%s: unknown hook", __func__);
447add85a1dSArchie Cobbs 
448add85a1dSArchie Cobbs 	/* Go away if no longer connected to anything */
44930400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(node) == 0)
45030400f03SJulian Elischer 	&& (NG_NODE_IS_VALID(node)))
451069154d5SJulian Elischer 		ng_rmnode_self(node);
452add85a1dSArchie Cobbs 	return (0);
453add85a1dSArchie Cobbs }
454add85a1dSArchie Cobbs 
455add85a1dSArchie Cobbs /*************************************************************************
456add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
457add85a1dSArchie Cobbs *************************************************************************/
458add85a1dSArchie Cobbs 
459add85a1dSArchie Cobbs /*
460add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
461add85a1dSArchie Cobbs  */
462add85a1dSArchie Cobbs static int
463069154d5SJulian Elischer ng_pptpgre_xmit(node_p node, item_p item)
464add85a1dSArchie Cobbs {
46530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
466add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
467add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
468add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
469add85a1dSArchie Cobbs 	int grelen, error;
470069154d5SJulian Elischer 	struct mbuf *m;
471add85a1dSArchie Cobbs 
472069154d5SJulian Elischer 	if (item) {
473069154d5SJulian Elischer 		NGI_GET_M(item, m);
474069154d5SJulian Elischer 	} else {
475069154d5SJulian Elischer 		m = NULL;
476069154d5SJulian Elischer 	}
4779bee7adfSArchie Cobbs 	/* Check if there's data */
4789bee7adfSArchie Cobbs 	if (m != NULL) {
4799bee7adfSArchie Cobbs 
480add85a1dSArchie Cobbs 		/* Is our transmit window full? */
4819bee7adfSArchie Cobbs 		if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck)
4829bee7adfSArchie Cobbs 		      >= a->xmitWin) {
4839bee7adfSArchie Cobbs 			priv->stats.xmitDrops++;
484069154d5SJulian Elischer 			NG_FREE_M(m);
485069154d5SJulian Elischer 			NG_FREE_ITEM(item);
486add85a1dSArchie Cobbs 			return (ENOBUFS);
487add85a1dSArchie Cobbs 		}
488add85a1dSArchie Cobbs 
489add85a1dSArchie Cobbs 		/* Sanity check frame length */
490add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
4919bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
492069154d5SJulian Elischer 			NG_FREE_M(m);
493069154d5SJulian Elischer 			NG_FREE_ITEM(item);
494add85a1dSArchie Cobbs 			return (EMSGSIZE);
495add85a1dSArchie Cobbs 		}
496069154d5SJulian Elischer 	} else {
4979bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
498069154d5SJulian Elischer 	}
499add85a1dSArchie Cobbs 
500add85a1dSArchie Cobbs 	/* Build GRE header */
501add85a1dSArchie Cobbs 	((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE);
502add85a1dSArchie Cobbs 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
503add85a1dSArchie Cobbs 	gre->cid = htons(priv->conf.peerCid);
504add85a1dSArchie Cobbs 
505add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
506add85a1dSArchie Cobbs 	if (m != NULL) {
507add85a1dSArchie Cobbs 		gre->hasSeq = 1;
508add85a1dSArchie Cobbs 		a->timeSent[priv->xmitSeq - priv->recvAck]
509add85a1dSArchie Cobbs 		    = ng_pptpgre_time(node);
510add85a1dSArchie Cobbs 		priv->xmitSeq++;
511add85a1dSArchie Cobbs 		gre->data[0] = htonl(priv->xmitSeq);
512add85a1dSArchie Cobbs 	}
513add85a1dSArchie Cobbs 
514add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
515678f9e33SArchie Cobbs 	if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) {
516add85a1dSArchie Cobbs 		gre->hasAck = 1;
517678f9e33SArchie Cobbs 		gre->data[gre->hasSeq] = htonl(priv->recvSeq);
518add85a1dSArchie Cobbs 		priv->xmitAck = priv->recvSeq;
5194a48abb2SArchie Cobbs 		ng_pptpgre_stop_send_ack_timer(node);
520da010626SArchie Cobbs 	}
521add85a1dSArchie Cobbs 
522add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
523add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
524add85a1dSArchie Cobbs 	if (m == NULL) {
525add85a1dSArchie Cobbs 		MGETHDR(m, M_DONTWAIT, MT_DATA);
526add85a1dSArchie Cobbs 		if (m == NULL) {
527678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
528069154d5SJulian Elischer 			if (item)
529069154d5SJulian Elischer 				NG_FREE_ITEM(item);
530add85a1dSArchie Cobbs 			return (ENOBUFS);
531add85a1dSArchie Cobbs 		}
532add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
533add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
534add85a1dSArchie Cobbs 	} else {
535add85a1dSArchie Cobbs 		M_PREPEND(m, grelen, M_NOWAIT);
536add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
537add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
538678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
539069154d5SJulian Elischer 			if (item)
540069154d5SJulian Elischer 				NG_FREE_ITEM(item);
541add85a1dSArchie Cobbs 			return (ENOBUFS);
542add85a1dSArchie Cobbs 		}
543add85a1dSArchie Cobbs 	}
544add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
545add85a1dSArchie Cobbs 
5469bee7adfSArchie Cobbs 	/* Update stats */
5479bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
5489bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
5499bee7adfSArchie Cobbs 
550add85a1dSArchie Cobbs 	/* Deliver packet */
551069154d5SJulian Elischer 	if (item) {
552069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
553069154d5SJulian Elischer 	} else {
554069154d5SJulian Elischer 		NG_SEND_DATA_ONLY(error, priv->lower, m);
555069154d5SJulian Elischer 	}
556069154d5SJulian Elischer 
557678f9e33SArchie Cobbs 
558da010626SArchie Cobbs 	/* Start receive ACK timer if data was sent and not already running */
559678f9e33SArchie Cobbs 	if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1)
560678f9e33SArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
561add85a1dSArchie Cobbs 	return (error);
562add85a1dSArchie Cobbs }
563add85a1dSArchie Cobbs 
564add85a1dSArchie Cobbs /*
565add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
566add85a1dSArchie Cobbs  */
567add85a1dSArchie Cobbs static int
568069154d5SJulian Elischer ng_pptpgre_recv(node_p node, item_p item)
569add85a1dSArchie Cobbs {
57030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
571add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
572816b834fSArchie Cobbs 	const struct greheader *gre;
573816b834fSArchie Cobbs 	const struct ip *ip;
574add85a1dSArchie Cobbs 	int error = 0;
575069154d5SJulian Elischer 	struct mbuf *m;
576add85a1dSArchie Cobbs 
577069154d5SJulian Elischer 	NGI_GET_M(item, m);
5789bee7adfSArchie Cobbs 	/* Update stats */
5799bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
5809bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
5819bee7adfSArchie Cobbs 
582add85a1dSArchie Cobbs 	/* Sanity check packet length */
583add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
5849bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
585add85a1dSArchie Cobbs bad:
586069154d5SJulian Elischer 		NG_FREE_M(m);
587069154d5SJulian Elischer 		NG_FREE_ITEM(item);
588add85a1dSArchie Cobbs 		return (EINVAL);
589add85a1dSArchie Cobbs 	}
590add85a1dSArchie Cobbs 
591add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
592add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
593add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
594678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
595069154d5SJulian Elischer 		NG_FREE_ITEM(item);
596add85a1dSArchie Cobbs 		return (ENOBUFS);
597add85a1dSArchie Cobbs 	}
598816b834fSArchie Cobbs 	ip = mtod(m, const struct ip *);
599add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
600add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
601add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
602678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
603069154d5SJulian Elischer 			NG_FREE_ITEM(item);
604add85a1dSArchie Cobbs 			return (ENOBUFS);
605add85a1dSArchie Cobbs 		}
606816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
607add85a1dSArchie Cobbs 	}
608816b834fSArchie Cobbs 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
609add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
6109bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
6119bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
612add85a1dSArchie Cobbs 		goto bad;
6139bee7adfSArchie Cobbs 	}
614add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
615add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
616678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
617069154d5SJulian Elischer 			NG_FREE_ITEM(item);
618add85a1dSArchie Cobbs 			return (ENOBUFS);
619add85a1dSArchie Cobbs 		}
620816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
621816b834fSArchie Cobbs 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
622add85a1dSArchie Cobbs 	}
623add85a1dSArchie Cobbs 
624add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
625add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
626add85a1dSArchie Cobbs 	    - (iphlen + grelen + (u_int16_t)ntohs(gre->length));
6279bee7adfSArchie Cobbs 	if (extralen < 0) {
6289bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
629add85a1dSArchie Cobbs 		goto bad;
6309bee7adfSArchie Cobbs 	}
631816b834fSArchie Cobbs 	if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK)
632816b834fSArchie Cobbs 	    != PPTP_INIT_VALUE) {
6339bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
634add85a1dSArchie Cobbs 		goto bad;
6359bee7adfSArchie Cobbs 	}
6369bee7adfSArchie Cobbs 	if (ntohs(gre->cid) != priv->conf.cid) {
6379bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
638add85a1dSArchie Cobbs 		goto bad;
6399bee7adfSArchie Cobbs 	}
640add85a1dSArchie Cobbs 
641add85a1dSArchie Cobbs 	/* Look for peer ack */
642add85a1dSArchie Cobbs 	if (gre->hasAck) {
643add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
644add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
645add85a1dSArchie Cobbs 		const int index = ack - priv->recvAck - 1;
64622dfb9bdSArchie Cobbs 		long sample;
647add85a1dSArchie Cobbs 		long diff;
648add85a1dSArchie Cobbs 
649add85a1dSArchie Cobbs 		/* Sanity check ack value */
6509bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) {
6519bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
6529bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
6539bee7adfSArchie Cobbs 		}
6549bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0)
6559bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
656add85a1dSArchie Cobbs 		priv->recvAck = ack;
657add85a1dSArchie Cobbs 
658add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
65922dfb9bdSArchie Cobbs 		sample = ng_pptpgre_time(node) - a->timeSent[index];
660add85a1dSArchie Cobbs 		diff = sample - a->rtt;
661add85a1dSArchie Cobbs 		a->rtt += PPTP_ACK_ALPHA(diff);
662add85a1dSArchie Cobbs 		if (diff < 0)
663add85a1dSArchie Cobbs 			diff = -diff;
664add85a1dSArchie Cobbs 		a->dev += PPTP_ACK_BETA(diff - a->dev);
665e962a823SArchie Cobbs 		a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
666add85a1dSArchie Cobbs 		if (a->ato > PPTP_MAX_TIMEOUT)
667add85a1dSArchie Cobbs 			a->ato = PPTP_MAX_TIMEOUT;
668e962a823SArchie Cobbs 		if (a->ato < PPTP_MIN_TIMEOUT)
669e962a823SArchie Cobbs 			a->ato = PPTP_MIN_TIMEOUT;
670e962a823SArchie Cobbs 
671e962a823SArchie Cobbs 		/* Shift packet transmit times in our transmit window */
672add85a1dSArchie Cobbs 		ovbcopy(a->timeSent + index + 1, a->timeSent,
673add85a1dSArchie Cobbs 		    sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1)));
674e962a823SArchie Cobbs 
675e962a823SArchie Cobbs 		/* If we sent an entire window, increase window size by one */
6769bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0
6779bee7adfSArchie Cobbs 		    && a->xmitWin < PPTP_XMIT_WIN) {
678add85a1dSArchie Cobbs 			a->xmitWin++;
679add85a1dSArchie Cobbs 			a->winAck = ack + a->xmitWin;
680add85a1dSArchie Cobbs 		}
681add85a1dSArchie Cobbs 
6829bee7adfSArchie Cobbs 		/* Stop/(re)start receive ACK timer as necessary */
6834a48abb2SArchie Cobbs 		ng_pptpgre_stop_recv_ack_timer(node);
684678f9e33SArchie Cobbs 		if (priv->recvAck != priv->xmitSeq)
685add85a1dSArchie Cobbs 			ng_pptpgre_start_recv_ack_timer(node);
686add85a1dSArchie Cobbs 	}
6879bee7adfSArchie Cobbs badAck:
688add85a1dSArchie Cobbs 
689add85a1dSArchie Cobbs 	/* See if frame contains any data */
690add85a1dSArchie Cobbs 	if (gre->hasSeq) {
691add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
692add85a1dSArchie Cobbs 		const u_int32_t seq = ntohl(gre->data[0]);
693add85a1dSArchie Cobbs 
694add85a1dSArchie Cobbs 		/* Sanity check sequence number */
6959bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) {
6969bee7adfSArchie Cobbs 			if (seq == priv->recvSeq)
6979bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
6989bee7adfSArchie Cobbs 			else
6999bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
7009bee7adfSArchie Cobbs 			goto bad;		/* out-of-order or dup */
7019bee7adfSArchie Cobbs 		}
702add85a1dSArchie Cobbs 		priv->recvSeq = seq;
703add85a1dSArchie Cobbs 
704add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
7053cd7db22SArchie Cobbs 		if (a->sackTimerPtr == NULL) {
706da010626SArchie Cobbs 			int maxWait;
707add85a1dSArchie Cobbs 
708da010626SArchie Cobbs 			/* Take 1/4 of the estimated round trip time */
709da010626SArchie Cobbs 			maxWait = (a->rtt >> 2);
710add85a1dSArchie Cobbs 
711678f9e33SArchie Cobbs 			/* If delayed ACK is disabled, send it now */
7124a48abb2SArchie Cobbs 			if (!priv->conf.enableDelayedAck)	/* ack now */
713069154d5SJulian Elischer 				ng_pptpgre_xmit(node, NULL);
7144a48abb2SArchie Cobbs 			else {					/* ack later */
7154a48abb2SArchie Cobbs 				if (maxWait < PPTP_MIN_ACK_DELAY)
7164a48abb2SArchie Cobbs 					maxWait = PPTP_MIN_ACK_DELAY;
7173cd7db22SArchie Cobbs 				if (maxWait > PPTP_MAX_ACK_DELAY)
7183cd7db22SArchie Cobbs 					maxWait = PPTP_MAX_ACK_DELAY;
7193cd7db22SArchie Cobbs 				ng_pptpgre_start_send_ack_timer(node, maxWait);
720add85a1dSArchie Cobbs 			}
721add85a1dSArchie Cobbs 		}
722add85a1dSArchie Cobbs 
723add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
724add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
725add85a1dSArchie Cobbs 		if (extralen > 0)
726add85a1dSArchie Cobbs 			m_adj(m, -extralen);
727add85a1dSArchie Cobbs 
728add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
729069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->upper, m);
7309bee7adfSArchie Cobbs 	} else {
7319bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
732069154d5SJulian Elischer 		NG_FREE_ITEM(item);
733069154d5SJulian Elischer 		NG_FREE_M(m);		/* no data to deliver */
7349bee7adfSArchie Cobbs 	}
735add85a1dSArchie Cobbs 	return (error);
736add85a1dSArchie Cobbs }
737add85a1dSArchie Cobbs 
738add85a1dSArchie Cobbs /*************************************************************************
739add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
740add85a1dSArchie Cobbs *************************************************************************/
741add85a1dSArchie Cobbs 
742add85a1dSArchie Cobbs /*
7439bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
744add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
745add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
746add85a1dSArchie Cobbs  * recv ack timer, if any.
747add85a1dSArchie Cobbs  */
748add85a1dSArchie Cobbs static void
749add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node)
750add85a1dSArchie Cobbs {
75130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
752add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
753678f9e33SArchie Cobbs 	int remain, ticks;
754add85a1dSArchie Cobbs 
755add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
756add85a1dSArchie Cobbs 	   and reset the timer to that time. */
7576e551fb6SDavid E. O'Brien 	KASSERT(a->rackTimerPtr == NULL, ("%s: rackTimer", __func__));
758add85a1dSArchie Cobbs 	remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node);
759add85a1dSArchie Cobbs 	if (remain < 0)
760add85a1dSArchie Cobbs 		remain = 0;
761678f9e33SArchie Cobbs #ifdef DEBUG_RAT
762678f9e33SArchie Cobbs 	a->timerLength = remain;
763678f9e33SArchie Cobbs 	a->timerStart = ng_pptpgre_time(node);
764678f9e33SArchie Cobbs #endif
7659bee7adfSArchie Cobbs 
7663cd7db22SArchie Cobbs 	/* Start new timer */
7673cd7db22SArchie Cobbs 	MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
768678f9e33SArchie Cobbs 	if (a->rackTimerPtr == NULL) {
769678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
7703cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
771678f9e33SArchie Cobbs 	}
7724a48abb2SArchie Cobbs 	*a->rackTimerPtr = node;	/* ensures the correct timeout event */
77330400f03SJulian Elischer 	NG_NODE_REF(node);
7744a48abb2SArchie Cobbs 	priv->timers++;
775678f9e33SArchie Cobbs 
7764a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
777678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
7784a48abb2SArchie Cobbs 	callout_reset(&a->rackTimer, ticks,
7794a48abb2SArchie Cobbs 	    ng_pptpgre_recv_ack_timeout, a->rackTimerPtr);
7804a48abb2SArchie Cobbs }
7814a48abb2SArchie Cobbs 
7824a48abb2SArchie Cobbs /*
7834a48abb2SArchie Cobbs  * Stop receive ack timer.
7844a48abb2SArchie Cobbs  */
7854a48abb2SArchie Cobbs static void
7864a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node_p node)
7874a48abb2SArchie Cobbs {
7884a48abb2SArchie Cobbs 	const priv_p priv = NG_NODE_PRIVATE(node);
7894a48abb2SArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
7904a48abb2SArchie Cobbs 
7914a48abb2SArchie Cobbs 	if (callout_stop(&a->rackTimer)) {
7924a48abb2SArchie Cobbs 		FREE(a->rackTimerPtr, M_NETGRAPH);
7934a48abb2SArchie Cobbs 		priv->timers--;
7944a48abb2SArchie Cobbs 		NG_NODE_UNREF(node);
7954a48abb2SArchie Cobbs 	}
7964a48abb2SArchie Cobbs 	a->rackTimerPtr = NULL;
797add85a1dSArchie Cobbs }
798add85a1dSArchie Cobbs 
799add85a1dSArchie Cobbs /*
800add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
801add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
802add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
803add85a1dSArchie Cobbs  */
804add85a1dSArchie Cobbs static void
805add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg)
806add85a1dSArchie Cobbs {
8074164c447SArchie Cobbs 	int s = splnet();
8083cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
80930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
810add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
811add85a1dSArchie Cobbs 
8123cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
8133cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
8146e551fb6SDavid E. O'Brien 	KASSERT(node->nd_refs > 0, ("%s: no nd_refs", __func__));
8154a48abb2SArchie Cobbs 	KASSERT(priv != NULL, ("%s: priv=NULL", __func__));
8164a48abb2SArchie Cobbs 	priv->timers--;
81730400f03SJulian Elischer 	if (NG_NODE_NOT_VALID(node)) {	/* shutdown race condition */
8184a48abb2SArchie Cobbs 		if (priv->timers == 0) {
8194a48abb2SArchie Cobbs 			FREE(priv, M_NETGRAPH);
8204a48abb2SArchie Cobbs 			NG_NODE_SET_PRIVATE(node, NULL);
8214a48abb2SArchie Cobbs 		}
82230400f03SJulian Elischer 		NG_NODE_UNREF(node);
8239bee7adfSArchie Cobbs 		splx(s);
8249bee7adfSArchie Cobbs 		return;
8259bee7adfSArchie Cobbs 	}
8263cd7db22SArchie Cobbs 	if (arg != a->rackTimerPtr) {	/* timer stopped race condition */
827422c7276SArchie Cobbs 		NG_NODE_UNREF(node);
8283cd7db22SArchie Cobbs 		splx(s);
8293cd7db22SArchie Cobbs 		return;
8303cd7db22SArchie Cobbs 	}
8313cd7db22SArchie Cobbs 	a->rackTimerPtr = NULL;
8329bee7adfSArchie Cobbs 
833add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
8349bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
835add85a1dSArchie Cobbs 	a->rtt = PPTP_ACK_DELTA(a->rtt);
836add85a1dSArchie Cobbs 	a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
837add85a1dSArchie Cobbs 	if (a->ato > PPTP_MAX_TIMEOUT)
838add85a1dSArchie Cobbs 		a->ato = PPTP_MAX_TIMEOUT;
839e962a823SArchie Cobbs 	if (a->ato < PPTP_MIN_TIMEOUT)
840e962a823SArchie Cobbs 		a->ato = PPTP_MIN_TIMEOUT;
841add85a1dSArchie Cobbs 
842678f9e33SArchie Cobbs #ifdef DEBUG_RAT
843678f9e33SArchie Cobbs     log(LOG_DEBUG,
844678f9e33SArchie Cobbs 	"RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n",
845678f9e33SArchie Cobbs 	(int)ng_pptpgre_time(node), priv->recvAck + 1,
846678f9e33SArchie Cobbs 	(int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato);
847678f9e33SArchie Cobbs #endif
848678f9e33SArchie Cobbs 
849e962a823SArchie Cobbs 	/* Reset ack and sliding window */
850e962a823SArchie Cobbs 	priv->recvAck = priv->xmitSeq;		/* pretend we got the ack */
851e962a823SArchie Cobbs 	a->xmitWin = (a->xmitWin + 1) / 2;	/* shrink transmit window */
852e962a823SArchie Cobbs 	a->winAck = priv->recvAck + a->xmitWin;	/* reset win expand time */
853422c7276SArchie Cobbs 	NG_NODE_UNREF(node);
8544164c447SArchie Cobbs 	splx(s);
855add85a1dSArchie Cobbs }
856add85a1dSArchie Cobbs 
857add85a1dSArchie Cobbs /*
8589bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
8599bee7adfSArchie Cobbs  * already running.
8609bee7adfSArchie Cobbs  */
8619bee7adfSArchie Cobbs static void
862da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout)
8639bee7adfSArchie Cobbs {
86430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
8659bee7adfSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
866678f9e33SArchie Cobbs 	int ticks;
8679bee7adfSArchie Cobbs 
8683cd7db22SArchie Cobbs 	/* Start new timer */
8696e551fb6SDavid E. O'Brien 	KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __func__));
8703cd7db22SArchie Cobbs 	MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
871678f9e33SArchie Cobbs 	if (a->sackTimerPtr == NULL) {
872678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
8733cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
874678f9e33SArchie Cobbs 	}
8754a48abb2SArchie Cobbs 	*a->sackTimerPtr = node;	/* ensures the correct timeout event */
87630400f03SJulian Elischer 	NG_NODE_REF(node);
8774a48abb2SArchie Cobbs 	priv->timers++;
878678f9e33SArchie Cobbs 
8794a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
880678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
8814a48abb2SArchie Cobbs 	callout_reset(&a->sackTimer, ticks,
8824a48abb2SArchie Cobbs 	    ng_pptpgre_send_ack_timeout, a->sackTimerPtr);
8834a48abb2SArchie Cobbs }
8844a48abb2SArchie Cobbs 
8854a48abb2SArchie Cobbs /*
8864a48abb2SArchie Cobbs  * Stop send ack timer.
8874a48abb2SArchie Cobbs  */
8884a48abb2SArchie Cobbs static void
8894a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node_p node)
8904a48abb2SArchie Cobbs {
8914a48abb2SArchie Cobbs 	const priv_p priv = NG_NODE_PRIVATE(node);
8924a48abb2SArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
8934a48abb2SArchie Cobbs 
8944a48abb2SArchie Cobbs 	if (callout_stop(&a->sackTimer)) {
8954a48abb2SArchie Cobbs 		FREE(a->sackTimerPtr, M_NETGRAPH);
8964a48abb2SArchie Cobbs 		priv->timers--;
8974a48abb2SArchie Cobbs 		NG_NODE_UNREF(node);
8984a48abb2SArchie Cobbs 	}
8994a48abb2SArchie Cobbs 	a->sackTimerPtr = NULL;
9009bee7adfSArchie Cobbs }
9019bee7adfSArchie Cobbs 
9029bee7adfSArchie Cobbs /*
903add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
904add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
905add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
906add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
907add85a1dSArchie Cobbs  */
908add85a1dSArchie Cobbs static void
909add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg)
910add85a1dSArchie Cobbs {
9114164c447SArchie Cobbs 	int s = splnet();
9123cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
91330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
914add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
915add85a1dSArchie Cobbs 
9163cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
9173cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
9186e551fb6SDavid E. O'Brien 	KASSERT(node->nd_refs > 0, ("%s: no nd_refs", __func__));
9194a48abb2SArchie Cobbs 	KASSERT(priv != NULL, ("%s: priv=NULL", __func__));
9204a48abb2SArchie Cobbs 	priv->timers--;
92130400f03SJulian Elischer 	if (NG_NODE_NOT_VALID(node)) {	/* shutdown race condition */
9224a48abb2SArchie Cobbs 		if (priv->timers == 0) {
9234a48abb2SArchie Cobbs 			FREE(priv, M_NETGRAPH);
9244a48abb2SArchie Cobbs 			NG_NODE_SET_PRIVATE(node, NULL);
9254a48abb2SArchie Cobbs 		}
92630400f03SJulian Elischer 		NG_NODE_UNREF(node);
9279bee7adfSArchie Cobbs 		splx(s);
9289bee7adfSArchie Cobbs 		return;
9299bee7adfSArchie Cobbs 	}
9303cd7db22SArchie Cobbs 	if (a->sackTimerPtr != arg) {	/* timer stopped race condition */
9315951069aSJulian Elischer 		NG_NODE_UNREF(node);
9323cd7db22SArchie Cobbs 		splx(s);
9333cd7db22SArchie Cobbs 		return;
9343cd7db22SArchie Cobbs 	}
9353cd7db22SArchie Cobbs 	a->sackTimerPtr = NULL;
9369bee7adfSArchie Cobbs 
9379bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
938069154d5SJulian Elischer   	ng_pptpgre_xmit(node, NULL);
9395951069aSJulian Elischer 	NG_NODE_UNREF(node);
9404164c447SArchie Cobbs 	splx(s);
941add85a1dSArchie Cobbs }
942add85a1dSArchie Cobbs 
943add85a1dSArchie Cobbs /*************************************************************************
944add85a1dSArchie Cobbs 		    MISC FUNCTIONS
945add85a1dSArchie Cobbs *************************************************************************/
946add85a1dSArchie Cobbs 
947add85a1dSArchie Cobbs /*
948add85a1dSArchie Cobbs  * Reset state
949add85a1dSArchie Cobbs  */
950add85a1dSArchie Cobbs static void
951add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node)
952add85a1dSArchie Cobbs {
95330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
954add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
955add85a1dSArchie Cobbs 
956add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
957add85a1dSArchie Cobbs 	a->ato = PPTP_MAX_TIMEOUT;
958add85a1dSArchie Cobbs 	a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
959add85a1dSArchie Cobbs 	if (a->rtt < PPTP_MIN_RTT)
960add85a1dSArchie Cobbs 		a->rtt = PPTP_MIN_RTT;
961add85a1dSArchie Cobbs 	a->dev = 0;
962add85a1dSArchie Cobbs 	a->xmitWin = (priv->conf.recvWin + 1) / 2;
963e962a823SArchie Cobbs 	if (a->xmitWin < 2)		/* often the first packet is lost */
964e962a823SArchie Cobbs 		a->xmitWin = 2;		/*   because the peer isn't ready */
965add85a1dSArchie Cobbs 	if (a->xmitWin > PPTP_XMIT_WIN)
966add85a1dSArchie Cobbs 		a->xmitWin = PPTP_XMIT_WIN;
967add85a1dSArchie Cobbs 	a->winAck = a->xmitWin;
968add85a1dSArchie Cobbs 
969add85a1dSArchie Cobbs 	/* Reset sequence numbers */
9703cd7db22SArchie Cobbs 	priv->recvSeq = ~0;
9713cd7db22SArchie Cobbs 	priv->recvAck = ~0;
9723cd7db22SArchie Cobbs 	priv->xmitSeq = ~0;
9733cd7db22SArchie Cobbs 	priv->xmitAck = ~0;
974add85a1dSArchie Cobbs 
975add85a1dSArchie Cobbs 	/* Reset start time */
9769bee7adfSArchie Cobbs 	getmicrouptime(&priv->startTime);
9779bee7adfSArchie Cobbs 
9789bee7adfSArchie Cobbs 	/* Reset stats */
9799bee7adfSArchie Cobbs 	bzero(&priv->stats, sizeof(priv->stats));
980add85a1dSArchie Cobbs 
9814a48abb2SArchie Cobbs 	/* Stop timers */
9824a48abb2SArchie Cobbs 	ng_pptpgre_stop_send_ack_timer(node);
9834a48abb2SArchie Cobbs 	ng_pptpgre_stop_recv_ack_timer(node);
984add85a1dSArchie Cobbs }
985add85a1dSArchie Cobbs 
986add85a1dSArchie Cobbs /*
987add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
988add85a1dSArchie Cobbs  */
989add85a1dSArchie Cobbs static pptptime_t
990add85a1dSArchie Cobbs ng_pptpgre_time(node_p node)
991add85a1dSArchie Cobbs {
99230400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
993add85a1dSArchie Cobbs 	struct timeval tv;
994678f9e33SArchie Cobbs 	pptptime_t t;
995add85a1dSArchie Cobbs 
996678f9e33SArchie Cobbs 	microuptime(&tv);
997add85a1dSArchie Cobbs 	if (tv.tv_sec < priv->startTime.tv_sec
998add85a1dSArchie Cobbs 	    || (tv.tv_sec == priv->startTime.tv_sec
999add85a1dSArchie Cobbs 	      && tv.tv_usec < priv->startTime.tv_usec))
1000add85a1dSArchie Cobbs 		return (0);
1001add85a1dSArchie Cobbs 	timevalsub(&tv, &priv->startTime);
1002678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1003678f9e33SArchie Cobbs 	t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1004678f9e33SArchie Cobbs 	return(t);
1005add85a1dSArchie Cobbs }
1006add85a1dSArchie Cobbs 
1007