xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision 489290e9e92da96b9229f37598872a8d7183c63e)
1add85a1dSArchie Cobbs /*
2add85a1dSArchie Cobbs  * ng_pptpgre.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6add85a1dSArchie Cobbs  * Copyright (c) 1996-1999 Whistle Communications, Inc.
7add85a1dSArchie Cobbs  * All rights reserved.
8add85a1dSArchie Cobbs  *
9add85a1dSArchie Cobbs  * Subject to the following obligations and disclaimer of warranty, use and
10add85a1dSArchie Cobbs  * redistribution of this software, in source or object code forms, with or
11add85a1dSArchie Cobbs  * without modifications are expressly permitted by Whistle Communications;
12add85a1dSArchie Cobbs  * provided, however, that:
13add85a1dSArchie Cobbs  * 1. Any and all reproductions of the source or object code must include the
14add85a1dSArchie Cobbs  *    copyright notice above and the following disclaimer of warranties; and
15add85a1dSArchie Cobbs  * 2. No rights are granted, in any manner or form, to use Whistle
16add85a1dSArchie Cobbs  *    Communications, Inc. trademarks, including the mark "WHISTLE
17add85a1dSArchie Cobbs  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18add85a1dSArchie Cobbs  *    such appears in the above copyright notice or in the software.
19add85a1dSArchie Cobbs  *
20add85a1dSArchie Cobbs  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21add85a1dSArchie Cobbs  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22add85a1dSArchie Cobbs  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23add85a1dSArchie Cobbs  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24add85a1dSArchie Cobbs  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25add85a1dSArchie Cobbs  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26add85a1dSArchie Cobbs  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27add85a1dSArchie Cobbs  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28add85a1dSArchie Cobbs  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29add85a1dSArchie Cobbs  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30add85a1dSArchie Cobbs  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31add85a1dSArchie Cobbs  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32add85a1dSArchie Cobbs  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33add85a1dSArchie Cobbs  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34add85a1dSArchie Cobbs  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35add85a1dSArchie Cobbs  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36add85a1dSArchie Cobbs  * OF SUCH DAMAGE.
37add85a1dSArchie Cobbs  *
38cc3bbd68SJulian Elischer  * Author: Archie Cobbs <archie@freebsd.org>
39add85a1dSArchie Cobbs  *
40add85a1dSArchie Cobbs  * $FreeBSD$
41add85a1dSArchie Cobbs  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
42add85a1dSArchie Cobbs  */
43add85a1dSArchie Cobbs 
44add85a1dSArchie Cobbs /*
45add85a1dSArchie Cobbs  * PPTP/GRE netgraph node type.
46add85a1dSArchie Cobbs  *
47add85a1dSArchie Cobbs  * This node type does the GRE encapsulation as specified for the PPTP
48add85a1dSArchie Cobbs  * protocol (RFC 2637, section 4).  This includes sequencing and
49add85a1dSArchie Cobbs  * retransmission of frames, but not the actual packet delivery nor
50add85a1dSArchie Cobbs  * any of the TCP control stream protocol.
51add85a1dSArchie Cobbs  *
52add85a1dSArchie Cobbs  * The "upper" hook of this node is suitable for attaching to a "ppp"
53add85a1dSArchie Cobbs  * node link hook.  The "lower" hook of this node is suitable for attaching
54add85a1dSArchie Cobbs  * to a "ksocket" node on hook "inet/raw/gre".
55add85a1dSArchie Cobbs  */
56add85a1dSArchie Cobbs 
57add85a1dSArchie Cobbs #include <sys/param.h>
58add85a1dSArchie Cobbs #include <sys/systm.h>
59add85a1dSArchie Cobbs #include <sys/kernel.h>
60add85a1dSArchie Cobbs #include <sys/time.h>
61f2ba84d7SGleb Smirnoff #include <sys/lock.h>
62add85a1dSArchie Cobbs #include <sys/malloc.h>
63f2ba84d7SGleb Smirnoff #include <sys/mbuf.h>
64f2ba84d7SGleb Smirnoff #include <sys/mutex.h>
65add85a1dSArchie Cobbs #include <sys/errno.h>
66add85a1dSArchie Cobbs 
67add85a1dSArchie Cobbs #include <netinet/in.h>
68add85a1dSArchie Cobbs #include <netinet/in_systm.h>
69add85a1dSArchie Cobbs #include <netinet/ip.h>
70add85a1dSArchie Cobbs 
71add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
72add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
73add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
74add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
75add85a1dSArchie Cobbs 
76add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
77add85a1dSArchie Cobbs struct greheader {
78add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
79add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
80add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
81add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
82add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
83add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
84add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
85add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
86add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
87add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
88add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
89add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
90add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
91add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
92add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
93add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
94add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
95add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
96add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
97add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
98add85a1dSArchie Cobbs #else
99add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
100add85a1dSArchie Cobbs #endif
101add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
102add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
103add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
104add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
105add85a1dSArchie Cobbs };
106add85a1dSArchie Cobbs 
107add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
108add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
109add85a1dSArchie Cobbs 
110add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
111add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
112add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
113add85a1dSArchie Cobbs 
114add85a1dSArchie Cobbs /* Min and max packet length */
115add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
116add85a1dSArchie Cobbs 
117add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
118e962a823SArchie Cobbs #define PPTP_TIME_SCALE		1000			/* milliseconds */
119678f9e33SArchie Cobbs typedef u_int64_t		pptptime_t;
120add85a1dSArchie Cobbs 
121add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
122e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
123e962a823SArchie Cobbs #define PPTP_MIN_RTT		(PPTP_TIME_SCALE / 10)	/* 100 milliseconds */
1244a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
1250306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 seconds */
126add85a1dSArchie Cobbs 
127da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet
128da010626SArchie Cobbs    we can piggy-back the ACK off of. These parameters determine the mimimum
129da010626SArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that.
130da010626SArchie Cobbs    These have no effect unless "enableDelayedAck" is turned on. */
131da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
132da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
133da010626SArchie Cobbs 
134e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
135f8e159d6SGleb Smirnoff #define PPTP_ACK_ALPHA(x)	(((x) + 4) >> 3)	/* alpha = 0.125 */
136f8e159d6SGleb Smirnoff #define PPTP_ACK_BETA(x)	(((x) + 2) >> 2)	/* beta = 0.25 */
137add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
138add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
139add85a1dSArchie Cobbs 
1409bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
1419bee7adfSArchie Cobbs 
142489290e9SAlexander Motin #define SESSHASHSIZE		0x0020
143489290e9SAlexander Motin #define SESSHASH(x)		(((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
144489290e9SAlexander Motin 
145add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
146489290e9SAlexander Motin struct ng_pptpgre_sess {
147489290e9SAlexander Motin 	node_p			node;		/* this node pointer */
148489290e9SAlexander Motin 	hook_p			hook;		/* hook to upper layers */
149489290e9SAlexander Motin 	struct ng_pptpgre_conf	conf;		/* configuration info */
150489290e9SAlexander Motin 	struct mtx		mtx;		/* session mutex */
151489290e9SAlexander Motin 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
152489290e9SAlexander Motin 	u_int32_t		xmitSeq;	/* last seq # we sent */
153489290e9SAlexander Motin 	u_int32_t		recvAck;	/* last seq # peer ack'd */
154489290e9SAlexander Motin 	u_int32_t		xmitAck;	/* last seq # we ack'd */
155add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
156add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
157add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
158add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
1594a48abb2SArchie Cobbs 	struct callout		sackTimer;	/* send ack timer */
1604a48abb2SArchie Cobbs 	struct callout		rackTimer;	/* recv ack timer */
1613cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
162add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
163489290e9SAlexander Motin 	LIST_ENTRY(ng_pptpgre_sess) sessions;
164add85a1dSArchie Cobbs };
165489290e9SAlexander Motin typedef struct ng_pptpgre_sess *hpriv_p;
166add85a1dSArchie Cobbs 
167add85a1dSArchie Cobbs /* Node private data */
168add85a1dSArchie Cobbs struct ng_pptpgre_private {
169add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
170add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
171489290e9SAlexander Motin 	struct ng_pptpgre_sess	uppersess;	/* default session for compat */
172489290e9SAlexander Motin 	LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
1739bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
174add85a1dSArchie Cobbs };
175add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
176add85a1dSArchie Cobbs 
177add85a1dSArchie Cobbs /* Netgraph node methods */
178add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
179add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
180069154d5SJulian Elischer static ng_shutdown_t	ng_pptpgre_shutdown;
181add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
182add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
183489290e9SAlexander Motin static ng_rcvdata_t	ng_pptpgre_rcvdata_lower;
184add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
185add85a1dSArchie Cobbs 
186add85a1dSArchie Cobbs /* Helper functions */
187489290e9SAlexander Motin static int	ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
188489290e9SAlexander Motin static void	ng_pptpgre_start_send_ack_timer(hpriv_p hpriv, int ackTimeout);
189489290e9SAlexander Motin static void	ng_pptpgre_stop_send_ack_timer(hpriv_p hpriv);
190489290e9SAlexander Motin static void	ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
191489290e9SAlexander Motin static void	ng_pptpgre_stop_recv_ack_timer(hpriv_p hpriv);
192089323f3SGleb Smirnoff static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
193089323f3SGleb Smirnoff 		    void *arg1, int arg2);
194089323f3SGleb Smirnoff static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
195089323f3SGleb Smirnoff 		    void *arg1, int arg2);
196489290e9SAlexander Motin static hpriv_p	ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
197489290e9SAlexander Motin static void	ng_pptpgre_reset(hpriv_p hpriv);
198489290e9SAlexander Motin static pptptime_t ng_pptpgre_time(void);
199add85a1dSArchie Cobbs 
200add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
201f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
202f0184ff8SArchie Cobbs 	= NG_PPTPGRE_CONF_TYPE_INFO;
203add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
204add85a1dSArchie Cobbs 	&ng_parse_struct_type,
205f0184ff8SArchie Cobbs 	&ng_pptpgre_conf_type_fields,
206add85a1dSArchie Cobbs };
207add85a1dSArchie Cobbs 
2089bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
209f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
210f0184ff8SArchie Cobbs 	= NG_PPTPGRE_STATS_TYPE_INFO;
2119bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2129bee7adfSArchie Cobbs 	&ng_parse_struct_type,
213f0184ff8SArchie Cobbs 	&ng_pptpgre_stats_type_fields
2149bee7adfSArchie Cobbs };
2159bee7adfSArchie Cobbs 
216add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
217add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
218add85a1dSArchie Cobbs 	{
219add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
220add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
221add85a1dSArchie Cobbs 	  "setconfig",
222add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
223add85a1dSArchie Cobbs 	  NULL
224add85a1dSArchie Cobbs 	},
225add85a1dSArchie Cobbs 	{
226add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
227add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
228add85a1dSArchie Cobbs 	  "getconfig",
229489290e9SAlexander Motin 	  &ng_parse_hint16_type,
230add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
231add85a1dSArchie Cobbs 	},
2329bee7adfSArchie Cobbs 	{
2339bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2349bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2359bee7adfSArchie Cobbs 	  "getstats",
2369bee7adfSArchie Cobbs 	  NULL,
2379bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2389bee7adfSArchie Cobbs 	},
2399bee7adfSArchie Cobbs 	{
2409bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2419bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2429bee7adfSArchie Cobbs 	  "clrstats",
2439bee7adfSArchie Cobbs 	  NULL,
2449bee7adfSArchie Cobbs 	  NULL
2459bee7adfSArchie Cobbs 	},
2469bee7adfSArchie Cobbs 	{
2479bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2489bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2499bee7adfSArchie Cobbs 	  "getclrstats",
2509bee7adfSArchie Cobbs 	  NULL,
2519bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2529bee7adfSArchie Cobbs 	},
253add85a1dSArchie Cobbs 	{ 0 }
254add85a1dSArchie Cobbs };
255add85a1dSArchie Cobbs 
256add85a1dSArchie Cobbs /* Node type descriptor */
257add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
258f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
259f8aae777SJulian Elischer 	.name =		NG_PPTPGRE_NODE_TYPE,
260f8aae777SJulian Elischer 	.constructor =	ng_pptpgre_constructor,
261f8aae777SJulian Elischer 	.rcvmsg =	ng_pptpgre_rcvmsg,
262f8aae777SJulian Elischer 	.shutdown =	ng_pptpgre_shutdown,
263f8aae777SJulian Elischer 	.newhook =	ng_pptpgre_newhook,
264f8aae777SJulian Elischer 	.rcvdata =	ng_pptpgre_rcvdata,
265f8aae777SJulian Elischer 	.disconnect =	ng_pptpgre_disconnect,
266f8aae777SJulian Elischer 	.cmdlist =	ng_pptpgre_cmdlist,
267add85a1dSArchie Cobbs };
268add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
269add85a1dSArchie Cobbs 
270add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
271add85a1dSArchie Cobbs 
272add85a1dSArchie Cobbs /************************************************************************
273add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
274add85a1dSArchie Cobbs  ************************************************************************/
275add85a1dSArchie Cobbs 
276add85a1dSArchie Cobbs /*
277add85a1dSArchie Cobbs  * Node type constructor
278add85a1dSArchie Cobbs  */
279add85a1dSArchie Cobbs static int
280069154d5SJulian Elischer ng_pptpgre_constructor(node_p node)
281add85a1dSArchie Cobbs {
282add85a1dSArchie Cobbs 	priv_p priv;
283489290e9SAlexander Motin 	int i;
284add85a1dSArchie Cobbs 
285add85a1dSArchie Cobbs 	/* Allocate private structure */
28699cdf4ccSDavid Malone 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
287add85a1dSArchie Cobbs 	if (priv == NULL)
288add85a1dSArchie Cobbs 		return (ENOMEM);
289add85a1dSArchie Cobbs 
29030400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
291add85a1dSArchie Cobbs 
292add85a1dSArchie Cobbs 	/* Initialize state */
293489290e9SAlexander Motin 	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
294489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.sackTimer);
295489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.rackTimer);
296489290e9SAlexander Motin 	priv->uppersess.node = node;
297489290e9SAlexander Motin 
298489290e9SAlexander Motin 	for (i = 0; i < SESSHASHSIZE; i++)
299489290e9SAlexander Motin 	    LIST_INIT(&priv->sesshash[i]);
300489290e9SAlexander Motin 
301489290e9SAlexander Motin 	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
302add85a1dSArchie Cobbs 
303add85a1dSArchie Cobbs 	/* Done */
304add85a1dSArchie Cobbs 	return (0);
305add85a1dSArchie Cobbs }
306add85a1dSArchie Cobbs 
307add85a1dSArchie Cobbs /*
308add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
309add85a1dSArchie Cobbs  */
310add85a1dSArchie Cobbs static int
311add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
312add85a1dSArchie Cobbs {
31330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
314add85a1dSArchie Cobbs 
315add85a1dSArchie Cobbs 	/* Check hook name */
316489290e9SAlexander Motin 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
317489290e9SAlexander Motin 		priv->upper = hook;
318489290e9SAlexander Motin 		priv->uppersess.hook = hook;
319489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
320489290e9SAlexander Motin 	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
321489290e9SAlexander Motin 		priv->lower = hook;
322489290e9SAlexander Motin 		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
323489290e9SAlexander Motin 	} else {
324489290e9SAlexander Motin 		static const char hexdig[16] = "0123456789abcdef";
325489290e9SAlexander Motin 		const char *hex;
326489290e9SAlexander Motin 		hpriv_p hpriv;
327489290e9SAlexander Motin 		int i, j;
328489290e9SAlexander Motin 		uint16_t cid, hash;
329489290e9SAlexander Motin 
330489290e9SAlexander Motin 		/* Parse hook name to get session ID */
331489290e9SAlexander Motin 		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
332489290e9SAlexander Motin 		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
333489290e9SAlexander Motin 			return (EINVAL);
334489290e9SAlexander Motin 		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
335489290e9SAlexander Motin 		for (cid = i = 0; i < 4; i++) {
336489290e9SAlexander Motin 			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
337489290e9SAlexander Motin 			if (j == 16)
338489290e9SAlexander Motin 				return (EINVAL);
339489290e9SAlexander Motin 			cid = (cid << 4) | j;
340489290e9SAlexander Motin 		}
341489290e9SAlexander Motin 		if (hex[i] != '\0')
342add85a1dSArchie Cobbs 			return (EINVAL);
343add85a1dSArchie Cobbs 
344489290e9SAlexander Motin 		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
345489290e9SAlexander Motin 		if (hpriv == NULL)
346489290e9SAlexander Motin 			return (ENOMEM);
347add85a1dSArchie Cobbs 
348489290e9SAlexander Motin 		/* Initialize state */
349489290e9SAlexander Motin 		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
350489290e9SAlexander Motin 		ng_callout_init(&hpriv->sackTimer);
351489290e9SAlexander Motin 		ng_callout_init(&hpriv->rackTimer);
352489290e9SAlexander Motin 		hpriv->conf.cid = cid;
353489290e9SAlexander Motin 		hpriv->node = node;
354489290e9SAlexander Motin 		hpriv->hook = hook;
355489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, hpriv);
356489290e9SAlexander Motin 
357489290e9SAlexander Motin 		hash = SESSHASH(cid);
358489290e9SAlexander Motin 		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
359489290e9SAlexander Motin 	}
360489290e9SAlexander Motin 
361add85a1dSArchie Cobbs 	return (0);
362add85a1dSArchie Cobbs }
363add85a1dSArchie Cobbs 
364add85a1dSArchie Cobbs /*
365add85a1dSArchie Cobbs  * Receive a control message.
366add85a1dSArchie Cobbs  */
367add85a1dSArchie Cobbs static int
368069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
369add85a1dSArchie Cobbs {
37030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
371add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
372add85a1dSArchie Cobbs 	int error = 0;
373069154d5SJulian Elischer 	struct ng_mesg *msg;
374add85a1dSArchie Cobbs 
375069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
376add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
377add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
378add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
379add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
380add85a1dSArchie Cobbs 		    {
381add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
382add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
383489290e9SAlexander Motin 			hpriv_p hpriv;
384489290e9SAlexander Motin 			uint16_t hash;
385add85a1dSArchie Cobbs 
386add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
387add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
388add85a1dSArchie Cobbs 				ERROUT(EINVAL);
389489290e9SAlexander Motin 			/* Try to find session by cid. */
390489290e9SAlexander Motin 			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
391489290e9SAlexander Motin 			/* If not present - use upper. */
392489290e9SAlexander Motin 			if (hpriv == NULL) {
393489290e9SAlexander Motin 				hpriv = &priv->uppersess;
394489290e9SAlexander Motin 				LIST_REMOVE(hpriv, sessions);
395489290e9SAlexander Motin 				hash = SESSHASH(newConf->cid);
396489290e9SAlexander Motin 				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
397489290e9SAlexander Motin 				    sessions);
398489290e9SAlexander Motin 			}
399489290e9SAlexander Motin 			ng_pptpgre_reset(hpriv);	/* reset on configure */
400489290e9SAlexander Motin 			hpriv->conf = *newConf;
401add85a1dSArchie Cobbs 			break;
402add85a1dSArchie Cobbs 		    }
403add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
404489290e9SAlexander Motin 		    {
405489290e9SAlexander Motin 			hpriv_p hpriv;
406489290e9SAlexander Motin 
407489290e9SAlexander Motin 			if (msg->header.arglen == 2) {
408489290e9SAlexander Motin 				/* Try to find session by cid. */
409489290e9SAlexander Motin 	    			hpriv = ng_pptpgre_find_session(priv,
410489290e9SAlexander Motin 				    *((uint16_t *)msg->data));
411489290e9SAlexander Motin 				if (hpriv == NULL)
412489290e9SAlexander Motin 					ERROUT(EINVAL);
413489290e9SAlexander Motin 			} else if (msg->header.arglen == 0) {
414489290e9SAlexander Motin 				/* Use upper. */
415489290e9SAlexander Motin 				hpriv = &priv->uppersess;
416489290e9SAlexander Motin 			} else
417489290e9SAlexander Motin 				ERROUT(EINVAL);
418489290e9SAlexander Motin 			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
419add85a1dSArchie Cobbs 			if (resp == NULL)
420add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
421489290e9SAlexander Motin 			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
422add85a1dSArchie Cobbs 			break;
423489290e9SAlexander Motin 		    }
4249bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
4259bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
4269bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
4279bee7adfSArchie Cobbs 		    {
4289bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
4299bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
4309bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
4319bee7adfSArchie Cobbs 				if (resp == NULL)
4329bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
4339bee7adfSArchie Cobbs 				bcopy(&priv->stats,
4349bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
4359bee7adfSArchie Cobbs 			}
4369bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
4379bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
4389bee7adfSArchie Cobbs 			break;
4399bee7adfSArchie Cobbs 		    }
440add85a1dSArchie Cobbs 		default:
441add85a1dSArchie Cobbs 			error = EINVAL;
442add85a1dSArchie Cobbs 			break;
443add85a1dSArchie Cobbs 		}
444add85a1dSArchie Cobbs 		break;
445add85a1dSArchie Cobbs 	default:
446add85a1dSArchie Cobbs 		error = EINVAL;
447add85a1dSArchie Cobbs 		break;
448add85a1dSArchie Cobbs 	}
449589f6ed8SJulian Elischer done:
450069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
451069154d5SJulian Elischer 	NG_FREE_MSG(msg);
452add85a1dSArchie Cobbs 	return (error);
453add85a1dSArchie Cobbs }
454add85a1dSArchie Cobbs 
455add85a1dSArchie Cobbs /*
456add85a1dSArchie Cobbs  * Receive incoming data on a hook.
457add85a1dSArchie Cobbs  */
458add85a1dSArchie Cobbs static int
459069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
460add85a1dSArchie Cobbs {
461489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
462f2ba84d7SGleb Smirnoff 	int rval;
463add85a1dSArchie Cobbs 
464add85a1dSArchie Cobbs 	/* If not configured, reject */
465489290e9SAlexander Motin 	if (!hpriv->conf.enabled) {
466069154d5SJulian Elischer 		NG_FREE_ITEM(item);
467add85a1dSArchie Cobbs 		return (ENXIO);
468add85a1dSArchie Cobbs 	}
469add85a1dSArchie Cobbs 
470489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
471f2ba84d7SGleb Smirnoff 
472489290e9SAlexander Motin 	rval = ng_pptpgre_xmit(hpriv, item);
473f2ba84d7SGleb Smirnoff 
474489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
475f2ba84d7SGleb Smirnoff 
476f2ba84d7SGleb Smirnoff 	return (rval);
477add85a1dSArchie Cobbs }
478add85a1dSArchie Cobbs 
479add85a1dSArchie Cobbs /*
480489290e9SAlexander Motin  * Hook disconnection
481489290e9SAlexander Motin  */
482489290e9SAlexander Motin static int
483489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook)
484489290e9SAlexander Motin {
485489290e9SAlexander Motin 	const node_p node = NG_HOOK_NODE(hook);
486489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
487489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
488489290e9SAlexander Motin 
489489290e9SAlexander Motin 	/* Zero out hook pointer */
490489290e9SAlexander Motin 	if (hook == priv->upper) {
491489290e9SAlexander Motin 		priv->upper = NULL;
492489290e9SAlexander Motin 		priv->uppersess.hook = NULL;
493489290e9SAlexander Motin 	} else if (hook == priv->lower) {
494489290e9SAlexander Motin 		priv->lower = NULL;
495489290e9SAlexander Motin 	} else {
496489290e9SAlexander Motin 		/* Reset node (stops timers) */
497489290e9SAlexander Motin 		ng_pptpgre_reset(hpriv);
498489290e9SAlexander Motin 
499489290e9SAlexander Motin 		LIST_REMOVE(hpriv, sessions);
500489290e9SAlexander Motin 		mtx_destroy(&hpriv->mtx);
501489290e9SAlexander Motin 		free(hpriv, M_NETGRAPH);
502489290e9SAlexander Motin 	}
503489290e9SAlexander Motin 
504489290e9SAlexander Motin 	/* Go away if no longer connected to anything */
505489290e9SAlexander Motin 	if ((NG_NODE_NUMHOOKS(node) == 0)
506489290e9SAlexander Motin 	&& (NG_NODE_IS_VALID(node)))
507489290e9SAlexander Motin 		ng_rmnode_self(node);
508489290e9SAlexander Motin 	return (0);
509489290e9SAlexander Motin }
510489290e9SAlexander Motin 
511489290e9SAlexander Motin /*
512add85a1dSArchie Cobbs  * Destroy node
513add85a1dSArchie Cobbs  */
514add85a1dSArchie Cobbs static int
515069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
516add85a1dSArchie Cobbs {
51730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
518add85a1dSArchie Cobbs 
519089323f3SGleb Smirnoff 	/* Reset node (stops timers) */
520489290e9SAlexander Motin 	ng_pptpgre_reset(&priv->uppersess);
521add85a1dSArchie Cobbs 
522489290e9SAlexander Motin 	LIST_REMOVE(&priv->uppersess, sessions);
523489290e9SAlexander Motin 	mtx_destroy(&priv->uppersess.mtx);
524f2ba84d7SGleb Smirnoff 
525add85a1dSArchie Cobbs 	FREE(priv, M_NETGRAPH);
5264a48abb2SArchie Cobbs 
5274a48abb2SArchie Cobbs 	/* Decrement ref count */
52830400f03SJulian Elischer 	NG_NODE_UNREF(node);
529add85a1dSArchie Cobbs 	return (0);
530add85a1dSArchie Cobbs }
531add85a1dSArchie Cobbs 
532add85a1dSArchie Cobbs /*************************************************************************
533add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
534add85a1dSArchie Cobbs *************************************************************************/
535add85a1dSArchie Cobbs 
536add85a1dSArchie Cobbs /*
537add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
538add85a1dSArchie Cobbs  */
539add85a1dSArchie Cobbs static int
540489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
541add85a1dSArchie Cobbs {
542489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
543add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
544add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
545add85a1dSArchie Cobbs 	int grelen, error;
546069154d5SJulian Elischer 	struct mbuf *m;
547add85a1dSArchie Cobbs 
548489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_OWNED);
54983beeed9SGleb Smirnoff 
550069154d5SJulian Elischer 	if (item) {
551069154d5SJulian Elischer 		NGI_GET_M(item, m);
552069154d5SJulian Elischer 	} else {
553069154d5SJulian Elischer 		m = NULL;
554069154d5SJulian Elischer 	}
5559bee7adfSArchie Cobbs 	/* Check if there's data */
5569bee7adfSArchie Cobbs 	if (m != NULL) {
5579bee7adfSArchie Cobbs 
558922ee196SArchie Cobbs 		/* Check if windowing is enabled */
559489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
560add85a1dSArchie Cobbs 			/* Is our transmit window full? */
561489290e9SAlexander Motin 			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
562489290e9SAlexander Motin 			    hpriv->recvAck) >= hpriv->xmitWin) {
5639bee7adfSArchie Cobbs 				priv->stats.xmitDrops++;
56483beeed9SGleb Smirnoff 				ERROUT(ENOBUFS);
565add85a1dSArchie Cobbs 			}
566922ee196SArchie Cobbs 		}
567add85a1dSArchie Cobbs 
568add85a1dSArchie Cobbs 		/* Sanity check frame length */
569add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
5709bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
57183beeed9SGleb Smirnoff 			ERROUT(EMSGSIZE);
572add85a1dSArchie Cobbs 		}
573069154d5SJulian Elischer 	} else {
5749bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
575069154d5SJulian Elischer 	}
576add85a1dSArchie Cobbs 
577add85a1dSArchie Cobbs 	/* Build GRE header */
578add85a1dSArchie Cobbs 	((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE);
579add85a1dSArchie Cobbs 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
580489290e9SAlexander Motin 	gre->cid = htons(hpriv->conf.peerCid);
581add85a1dSArchie Cobbs 
582add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
583add85a1dSArchie Cobbs 	if (m != NULL) {
584add85a1dSArchie Cobbs 		gre->hasSeq = 1;
585489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
586489290e9SAlexander Motin 			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
587489290e9SAlexander Motin 			    = ng_pptpgre_time();
588922ee196SArchie Cobbs 		}
589489290e9SAlexander Motin 		hpriv->xmitSeq++;
590489290e9SAlexander Motin 		gre->data[0] = htonl(hpriv->xmitSeq);
591add85a1dSArchie Cobbs 	}
592add85a1dSArchie Cobbs 
593add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
594489290e9SAlexander Motin 	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
595add85a1dSArchie Cobbs 		gre->hasAck = 1;
596489290e9SAlexander Motin 		gre->data[gre->hasSeq] = htonl(hpriv->recvSeq);
597489290e9SAlexander Motin 		hpriv->xmitAck = hpriv->recvSeq;
598489290e9SAlexander Motin 		ng_pptpgre_stop_send_ack_timer(hpriv);
599da010626SArchie Cobbs 	}
600add85a1dSArchie Cobbs 
601add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
602add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
603add85a1dSArchie Cobbs 	if (m == NULL) {
604a163d034SWarner Losh 		MGETHDR(m, M_DONTWAIT, MT_DATA);
605add85a1dSArchie Cobbs 		if (m == NULL) {
606678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
60783beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
608add85a1dSArchie Cobbs 		}
609add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
610add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
611add85a1dSArchie Cobbs 	} else {
612a163d034SWarner Losh 		M_PREPEND(m, grelen, M_DONTWAIT);
613add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
614add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
615678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
61683beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
617add85a1dSArchie Cobbs 		}
618add85a1dSArchie Cobbs 	}
619add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
620add85a1dSArchie Cobbs 
6219bee7adfSArchie Cobbs 	/* Update stats */
6229bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
6239bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
6249bee7adfSArchie Cobbs 
62583beeed9SGleb Smirnoff 	/*
62683beeed9SGleb Smirnoff 	 * XXX: we should reset timer only after an item has been sent
62783beeed9SGleb Smirnoff 	 * successfully.
62883beeed9SGleb Smirnoff 	 */
629489290e9SAlexander Motin 	if (hpriv->conf.enableWindowing &&
630489290e9SAlexander Motin 	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
631489290e9SAlexander Motin 		ng_pptpgre_start_recv_ack_timer(hpriv);
63283beeed9SGleb Smirnoff 
633489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
63483beeed9SGleb Smirnoff 
635add85a1dSArchie Cobbs 	/* Deliver packet */
636069154d5SJulian Elischer 	if (item) {
637069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
638069154d5SJulian Elischer 	} else {
639069154d5SJulian Elischer 		NG_SEND_DATA_ONLY(error, priv->lower, m);
640069154d5SJulian Elischer 	}
641069154d5SJulian Elischer 
64283beeed9SGleb Smirnoff 	return (error);
643678f9e33SArchie Cobbs 
64483beeed9SGleb Smirnoff done:
645489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
64683beeed9SGleb Smirnoff 	NG_FREE_M(m);
64783beeed9SGleb Smirnoff 	if (item)
64883beeed9SGleb Smirnoff 		NG_FREE_ITEM(item);
649add85a1dSArchie Cobbs 	return (error);
650add85a1dSArchie Cobbs }
651add85a1dSArchie Cobbs 
652add85a1dSArchie Cobbs /*
653add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
654add85a1dSArchie Cobbs  */
655add85a1dSArchie Cobbs static int
656489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
657add85a1dSArchie Cobbs {
658489290e9SAlexander Motin 	hpriv_p hpriv;
659489290e9SAlexander Motin 	node_p node = NG_HOOK_NODE(hook);
66030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
661add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
662816b834fSArchie Cobbs 	const struct greheader *gre;
663816b834fSArchie Cobbs 	const struct ip *ip;
664add85a1dSArchie Cobbs 	int error = 0;
665069154d5SJulian Elischer 	struct mbuf *m;
666add85a1dSArchie Cobbs 
667069154d5SJulian Elischer 	NGI_GET_M(item, m);
6689bee7adfSArchie Cobbs 	/* Update stats */
6699bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
6709bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
6719bee7adfSArchie Cobbs 
672add85a1dSArchie Cobbs 	/* Sanity check packet length */
673add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
6749bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
67583beeed9SGleb Smirnoff 		ERROUT(EINVAL);
676add85a1dSArchie Cobbs 	}
677add85a1dSArchie Cobbs 
678add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
679add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
680add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
681678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
68283beeed9SGleb Smirnoff 		ERROUT(ENOBUFS);
683add85a1dSArchie Cobbs 	}
684816b834fSArchie Cobbs 	ip = mtod(m, const struct ip *);
685add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
686add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
687add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
688678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
68983beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
690add85a1dSArchie Cobbs 		}
691816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
692add85a1dSArchie Cobbs 	}
693816b834fSArchie Cobbs 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
694add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
6959bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
6969bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
69783beeed9SGleb Smirnoff 		ERROUT(EINVAL);
6989bee7adfSArchie Cobbs 	}
699add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
700add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
701678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
70283beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
703add85a1dSArchie Cobbs 		}
704816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
705816b834fSArchie Cobbs 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
706add85a1dSArchie Cobbs 	}
707add85a1dSArchie Cobbs 
708add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
709add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
710cc78c48aSArchie Cobbs 	    - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length));
7119bee7adfSArchie Cobbs 	if (extralen < 0) {
7129bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
71383beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7149bee7adfSArchie Cobbs 	}
715816b834fSArchie Cobbs 	if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK)
716816b834fSArchie Cobbs 	    != PPTP_INIT_VALUE) {
7179bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
71883beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7199bee7adfSArchie Cobbs 	}
720489290e9SAlexander Motin 
721489290e9SAlexander Motin 	hpriv = ng_pptpgre_find_session(priv, ntohs(gre->cid));
722489290e9SAlexander Motin 	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
7239bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
72483beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7259bee7adfSArchie Cobbs 	}
726489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
727add85a1dSArchie Cobbs 
728add85a1dSArchie Cobbs 	/* Look for peer ack */
729add85a1dSArchie Cobbs 	if (gre->hasAck) {
730add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
731489290e9SAlexander Motin 		const int index = ack - hpriv->recvAck - 1;
73222dfb9bdSArchie Cobbs 		long sample;
733add85a1dSArchie Cobbs 		long diff;
734add85a1dSArchie Cobbs 
735add85a1dSArchie Cobbs 		/* Sanity check ack value */
736489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
7379bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
7389bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
7399bee7adfSArchie Cobbs 		}
740489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
7419bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
742489290e9SAlexander Motin 		hpriv->recvAck = ack;
743add85a1dSArchie Cobbs 
744add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
745489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
746489290e9SAlexander Motin 			sample = ng_pptpgre_time() - hpriv->timeSent[index];
747489290e9SAlexander Motin 			diff = sample - hpriv->rtt;
748489290e9SAlexander Motin 			hpriv->rtt += PPTP_ACK_ALPHA(diff);
749add85a1dSArchie Cobbs 			if (diff < 0)
750add85a1dSArchie Cobbs 				diff = -diff;
751489290e9SAlexander Motin 			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
752f8e159d6SGleb Smirnoff 			    /* +2 to compensate low precision of int math */
753489290e9SAlexander Motin 			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
754489290e9SAlexander Motin 			if (hpriv->ato > PPTP_MAX_TIMEOUT)
755489290e9SAlexander Motin 				hpriv->ato = PPTP_MAX_TIMEOUT;
756489290e9SAlexander Motin 			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
757489290e9SAlexander Motin 				hpriv->ato = PPTP_MIN_TIMEOUT;
758e962a823SArchie Cobbs 
759e962a823SArchie Cobbs 			/* Shift packet transmit times in our transmit window */
760489290e9SAlexander Motin 			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
761489290e9SAlexander Motin 			    sizeof(*hpriv->timeSent)
762922ee196SArchie Cobbs 			      * (PPTP_XMIT_WIN - (index + 1)));
763e962a823SArchie Cobbs 
764922ee196SArchie Cobbs 			/* If we sent an entire window, increase window size */
765489290e9SAlexander Motin 			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
766489290e9SAlexander Motin 			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
767489290e9SAlexander Motin 				hpriv->xmitWin++;
768489290e9SAlexander Motin 				hpriv->winAck = ack + hpriv->xmitWin;
769add85a1dSArchie Cobbs 			}
770add85a1dSArchie Cobbs 
7719bee7adfSArchie Cobbs 			/* Stop/(re)start receive ACK timer as necessary */
772489290e9SAlexander Motin 			ng_pptpgre_stop_recv_ack_timer(hpriv);
773489290e9SAlexander Motin 			if (hpriv->recvAck != hpriv->xmitSeq)
774489290e9SAlexander Motin 				ng_pptpgre_start_recv_ack_timer(hpriv);
775add85a1dSArchie Cobbs 		}
776922ee196SArchie Cobbs 	}
7779bee7adfSArchie Cobbs badAck:
778add85a1dSArchie Cobbs 
779add85a1dSArchie Cobbs 	/* See if frame contains any data */
780add85a1dSArchie Cobbs 	if (gre->hasSeq) {
781add85a1dSArchie Cobbs 		const u_int32_t seq = ntohl(gre->data[0]);
782add85a1dSArchie Cobbs 
783add85a1dSArchie Cobbs 		/* Sanity check sequence number */
784489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) {
785489290e9SAlexander Motin 			if (seq == hpriv->recvSeq)
7869bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
7879bee7adfSArchie Cobbs 			else
7889bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
789489290e9SAlexander Motin 			mtx_unlock(&hpriv->mtx);
79083beeed9SGleb Smirnoff 			ERROUT(EINVAL);
7919bee7adfSArchie Cobbs 		}
792489290e9SAlexander Motin 		hpriv->recvSeq = seq;
793add85a1dSArchie Cobbs 
794add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
795489290e9SAlexander Motin 		if (!(callout_pending(&hpriv->sackTimer))) {
796678f9e33SArchie Cobbs 			/* If delayed ACK is disabled, send it now */
797489290e9SAlexander Motin 			if (!hpriv->conf.enableDelayedAck) {	/* ack now */
798489290e9SAlexander Motin 				ng_pptpgre_xmit(hpriv, NULL);
799489290e9SAlexander Motin 				/* ng_pptpgre_xmit() drops the mutex */
80083beeed9SGleb Smirnoff 			} else {				/* ack later */
801489290e9SAlexander Motin 				/* Take 1/4 of the estimated round trip time */
802489290e9SAlexander Motin 	    			int maxWait = (hpriv->rtt >> 2);
8034a48abb2SArchie Cobbs 				if (maxWait < PPTP_MIN_ACK_DELAY)
8044a48abb2SArchie Cobbs 					maxWait = PPTP_MIN_ACK_DELAY;
805489290e9SAlexander Motin 				else if (maxWait > PPTP_MAX_ACK_DELAY)
8063cd7db22SArchie Cobbs 					maxWait = PPTP_MAX_ACK_DELAY;
807489290e9SAlexander Motin 				ng_pptpgre_start_send_ack_timer(hpriv, maxWait);
808489290e9SAlexander Motin 				mtx_unlock(&hpriv->mtx);
809add85a1dSArchie Cobbs 			}
810489290e9SAlexander Motin 		} else
811489290e9SAlexander Motin 			mtx_unlock(&hpriv->mtx);
812add85a1dSArchie Cobbs 
813add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
814add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
815add85a1dSArchie Cobbs 		if (extralen > 0)
816add85a1dSArchie Cobbs 			m_adj(m, -extralen);
817add85a1dSArchie Cobbs 
818489290e9SAlexander Motin 		mtx_assert(&hpriv->mtx, MA_NOTOWNED);
819489290e9SAlexander Motin 
820add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
821489290e9SAlexander Motin 		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
8229bee7adfSArchie Cobbs 	} else {
8239bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
824489290e9SAlexander Motin 		mtx_unlock(&hpriv->mtx);
825069154d5SJulian Elischer 		NG_FREE_ITEM(item);
826069154d5SJulian Elischer 		NG_FREE_M(m);		/* no data to deliver */
8279bee7adfSArchie Cobbs 	}
82883beeed9SGleb Smirnoff 
82983beeed9SGleb Smirnoff 	return (error);
83083beeed9SGleb Smirnoff 
83183beeed9SGleb Smirnoff done:
83283beeed9SGleb Smirnoff 	NG_FREE_ITEM(item);
83383beeed9SGleb Smirnoff 	NG_FREE_M(m);
834add85a1dSArchie Cobbs 	return (error);
835add85a1dSArchie Cobbs }
836add85a1dSArchie Cobbs 
837add85a1dSArchie Cobbs /*************************************************************************
838add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
839add85a1dSArchie Cobbs *************************************************************************/
840add85a1dSArchie Cobbs 
841add85a1dSArchie Cobbs /*
8429bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
843add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
844add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
845add85a1dSArchie Cobbs  * recv ack timer, if any.
846add85a1dSArchie Cobbs  */
847add85a1dSArchie Cobbs static void
848489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
849add85a1dSArchie Cobbs {
850678f9e33SArchie Cobbs 	int remain, ticks;
851add85a1dSArchie Cobbs 
852add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
853add85a1dSArchie Cobbs 	   and reset the timer to that time. */
854489290e9SAlexander Motin 	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
855add85a1dSArchie Cobbs 	if (remain < 0)
856add85a1dSArchie Cobbs 		remain = 0;
8579bee7adfSArchie Cobbs 
8584a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
859678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
860489290e9SAlexander Motin 	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
861489290e9SAlexander Motin 	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
8624a48abb2SArchie Cobbs }
8634a48abb2SArchie Cobbs 
8644a48abb2SArchie Cobbs /*
8654a48abb2SArchie Cobbs  * Stop receive ack timer.
8664a48abb2SArchie Cobbs  */
8674a48abb2SArchie Cobbs static void
868489290e9SAlexander Motin ng_pptpgre_stop_recv_ack_timer(hpriv_p hpriv)
8694a48abb2SArchie Cobbs {
870489290e9SAlexander Motin 	ng_uncallout(&hpriv->rackTimer, hpriv->node);
871add85a1dSArchie Cobbs }
872add85a1dSArchie Cobbs 
873add85a1dSArchie Cobbs /*
874add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
875add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
876add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
877add85a1dSArchie Cobbs  */
878add85a1dSArchie Cobbs static void
879089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
880add85a1dSArchie Cobbs {
88130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
882489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
8839bee7adfSArchie Cobbs 
884add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
8859bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
886489290e9SAlexander Motin 	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
887489290e9SAlexander Motin 	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
888489290e9SAlexander Motin 	if (hpriv->ato > PPTP_MAX_TIMEOUT)
889489290e9SAlexander Motin 		hpriv->ato = PPTP_MAX_TIMEOUT;
890489290e9SAlexander Motin 	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
891489290e9SAlexander Motin 		hpriv->ato = PPTP_MIN_TIMEOUT;
892678f9e33SArchie Cobbs 
893e962a823SArchie Cobbs 	/* Reset ack and sliding window */
894489290e9SAlexander Motin 	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
895489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
896489290e9SAlexander Motin 	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
897add85a1dSArchie Cobbs }
898add85a1dSArchie Cobbs 
899add85a1dSArchie Cobbs /*
9009bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
9019bee7adfSArchie Cobbs  * already running.
9029bee7adfSArchie Cobbs  */
9039bee7adfSArchie Cobbs static void
904489290e9SAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv, int ackTimeout)
9059bee7adfSArchie Cobbs {
906678f9e33SArchie Cobbs 	int ticks;
9079bee7adfSArchie Cobbs 
9084a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
909678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
910489290e9SAlexander Motin 	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
911489290e9SAlexander Motin 	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
9124a48abb2SArchie Cobbs }
9134a48abb2SArchie Cobbs 
9144a48abb2SArchie Cobbs /*
9154a48abb2SArchie Cobbs  * Stop send ack timer.
9164a48abb2SArchie Cobbs  */
9174a48abb2SArchie Cobbs static void
918489290e9SAlexander Motin ng_pptpgre_stop_send_ack_timer(hpriv_p hpriv)
9194a48abb2SArchie Cobbs {
920489290e9SAlexander Motin 	ng_uncallout(&hpriv->sackTimer, hpriv->node);
9219bee7adfSArchie Cobbs }
9229bee7adfSArchie Cobbs 
9239bee7adfSArchie Cobbs /*
924add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
925add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
926add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
927add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
928add85a1dSArchie Cobbs  */
929add85a1dSArchie Cobbs static void
930089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
931add85a1dSArchie Cobbs {
932489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
93383beeed9SGleb Smirnoff 
934489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
9359bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
936489290e9SAlexander Motin   	ng_pptpgre_xmit(hpriv, NULL);
937489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
938add85a1dSArchie Cobbs }
939add85a1dSArchie Cobbs 
940add85a1dSArchie Cobbs /*************************************************************************
941add85a1dSArchie Cobbs 		    MISC FUNCTIONS
942add85a1dSArchie Cobbs *************************************************************************/
943add85a1dSArchie Cobbs 
944add85a1dSArchie Cobbs /*
945489290e9SAlexander Motin  * Find the hook with a given session ID.
946489290e9SAlexander Motin  */
947489290e9SAlexander Motin static hpriv_p
948489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
949489290e9SAlexander Motin {
950489290e9SAlexander Motin 	uint16_t	hash = SESSHASH(cid);
951489290e9SAlexander Motin 	hpriv_p	hpriv = NULL;
952489290e9SAlexander Motin 
953489290e9SAlexander Motin 	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
954489290e9SAlexander Motin 		if (hpriv->conf.cid == cid)
955489290e9SAlexander Motin 			break;
956489290e9SAlexander Motin 	}
957489290e9SAlexander Motin 
958489290e9SAlexander Motin 	return (hpriv);
959489290e9SAlexander Motin }
960489290e9SAlexander Motin 
961489290e9SAlexander Motin /*
962489290e9SAlexander Motin  * Reset state (must be called with lock held or from writer)
963add85a1dSArchie Cobbs  */
964add85a1dSArchie Cobbs static void
965489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv)
966add85a1dSArchie Cobbs {
967add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
968489290e9SAlexander Motin 	hpriv->ato = PPTP_MAX_TIMEOUT;
969489290e9SAlexander Motin 	hpriv->rtt = hpriv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
970489290e9SAlexander Motin 	if (hpriv->rtt < PPTP_MIN_RTT)
971489290e9SAlexander Motin 		hpriv->rtt = PPTP_MIN_RTT;
972489290e9SAlexander Motin 	hpriv->dev = 0;
973489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
974489290e9SAlexander Motin 	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
975489290e9SAlexander Motin 		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
976489290e9SAlexander Motin 	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
977489290e9SAlexander Motin 		hpriv->xmitWin = PPTP_XMIT_WIN;
978489290e9SAlexander Motin 	hpriv->winAck = hpriv->xmitWin;
979add85a1dSArchie Cobbs 
980add85a1dSArchie Cobbs 	/* Reset sequence numbers */
981489290e9SAlexander Motin 	hpriv->recvSeq = ~0;
982489290e9SAlexander Motin 	hpriv->recvAck = ~0;
983489290e9SAlexander Motin 	hpriv->xmitSeq = ~0;
984489290e9SAlexander Motin 	hpriv->xmitAck = ~0;
985add85a1dSArchie Cobbs 
9864a48abb2SArchie Cobbs 	/* Stop timers */
987489290e9SAlexander Motin 	ng_pptpgre_stop_send_ack_timer(hpriv);
988489290e9SAlexander Motin 	ng_pptpgre_stop_recv_ack_timer(hpriv);
989add85a1dSArchie Cobbs }
990add85a1dSArchie Cobbs 
991add85a1dSArchie Cobbs /*
992add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
993add85a1dSArchie Cobbs  */
994add85a1dSArchie Cobbs static pptptime_t
995489290e9SAlexander Motin ng_pptpgre_time(void)
996add85a1dSArchie Cobbs {
997add85a1dSArchie Cobbs 	struct timeval tv;
998678f9e33SArchie Cobbs 	pptptime_t t;
999add85a1dSArchie Cobbs 
1000678f9e33SArchie Cobbs 	microuptime(&tv);
1001678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1002678f9e33SArchie Cobbs 	t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1003678f9e33SArchie Cobbs 	return(t);
1004add85a1dSArchie Cobbs }
1005