xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision c398230b64aea809cb7c5cea8db580af7097920c)
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>
61add85a1dSArchie Cobbs #include <sys/mbuf.h>
62add85a1dSArchie Cobbs #include <sys/malloc.h>
63add85a1dSArchie Cobbs #include <sys/errno.h>
64add85a1dSArchie Cobbs 
65add85a1dSArchie Cobbs #include <netinet/in.h>
66add85a1dSArchie Cobbs #include <netinet/in_systm.h>
67add85a1dSArchie Cobbs #include <netinet/ip.h>
68add85a1dSArchie Cobbs 
69add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
70add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
71add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
72add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
73add85a1dSArchie Cobbs 
74add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
75add85a1dSArchie Cobbs struct greheader {
76add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
77add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
78add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
79add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
80add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
81add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
82add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
83add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
84add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
85add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
86add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
87add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
88add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
89add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
90add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
91add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
92add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
93add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
94add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
95add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
96add85a1dSArchie Cobbs #else
97add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
98add85a1dSArchie Cobbs #endif
99add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
100add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
101add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
102add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
103add85a1dSArchie Cobbs };
104add85a1dSArchie Cobbs 
105add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
106add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
107add85a1dSArchie Cobbs 
108add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
109add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
110add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
111add85a1dSArchie Cobbs 
112add85a1dSArchie Cobbs /* Min and max packet length */
113add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
114add85a1dSArchie Cobbs 
115add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
116e962a823SArchie Cobbs #define PPTP_TIME_SCALE		1000			/* milliseconds */
117678f9e33SArchie Cobbs typedef u_int64_t		pptptime_t;
118add85a1dSArchie Cobbs 
119add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
120e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
121e962a823SArchie Cobbs #define PPTP_MIN_RTT		(PPTP_TIME_SCALE / 10)	/* 100 milliseconds */
1224a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
1230306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 seconds */
124add85a1dSArchie Cobbs 
125da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet
126da010626SArchie Cobbs    we can piggy-back the ACK off of. These parameters determine the mimimum
127da010626SArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that.
128da010626SArchie Cobbs    These have no effect unless "enableDelayedAck" is turned on. */
129da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
130da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
131da010626SArchie Cobbs 
132e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
133add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x)	((x) >> 3)	/* alpha = 0.125 */
134add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x)	((x) >> 2)	/* beta = 0.25 */
135add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
136add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
137add85a1dSArchie Cobbs 
1389bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
1399bee7adfSArchie Cobbs 
140add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
141add85a1dSArchie Cobbs struct ng_pptpgre_ackp {
142add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
143add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
144add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
145add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
1464a48abb2SArchie Cobbs 	struct callout		sackTimer;	/* send ack timer */
1474a48abb2SArchie Cobbs 	struct callout		rackTimer;	/* recv ack timer */
1483cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
149add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
150678f9e33SArchie Cobbs #ifdef DEBUG_RAT
151da010626SArchie Cobbs 	pptptime_t		timerStart;	/* when rackTimer started */
152da010626SArchie Cobbs 	pptptime_t		timerLength;	/* rackTimer duration */
153678f9e33SArchie Cobbs #endif
154add85a1dSArchie Cobbs };
155add85a1dSArchie Cobbs 
156add85a1dSArchie Cobbs /* Node private data */
157add85a1dSArchie Cobbs struct ng_pptpgre_private {
158add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
159add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
160add85a1dSArchie Cobbs 	struct ng_pptpgre_conf	conf;		/* configuration info */
161add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp	ackp;		/* packet transmit ack state */
162add85a1dSArchie Cobbs 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
163add85a1dSArchie Cobbs 	u_int32_t		xmitSeq;	/* last seq # we sent */
164add85a1dSArchie Cobbs 	u_int32_t		recvAck;	/* last seq # peer ack'd */
165add85a1dSArchie Cobbs 	u_int32_t		xmitAck;	/* last seq # we ack'd */
166add85a1dSArchie Cobbs 	struct timeval		startTime;	/* time node was created */
1679bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
168add85a1dSArchie Cobbs };
169add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
170add85a1dSArchie Cobbs 
171add85a1dSArchie Cobbs /* Netgraph node methods */
172add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
173add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
174069154d5SJulian Elischer static ng_shutdown_t	ng_pptpgre_shutdown;
175add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
176add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
177add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
178add85a1dSArchie Cobbs 
179add85a1dSArchie Cobbs /* Helper functions */
180069154d5SJulian Elischer static int	ng_pptpgre_xmit(node_p node, item_p item);
181069154d5SJulian Elischer static int	ng_pptpgre_recv(node_p node, item_p item);
182da010626SArchie Cobbs static void	ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout);
1834a48abb2SArchie Cobbs static void	ng_pptpgre_stop_send_ack_timer(node_p node);
184add85a1dSArchie Cobbs static void	ng_pptpgre_start_recv_ack_timer(node_p node);
1854a48abb2SArchie Cobbs static void	ng_pptpgre_stop_recv_ack_timer(node_p node);
186089323f3SGleb Smirnoff static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
187089323f3SGleb Smirnoff 		    void *arg1, int arg2);
188089323f3SGleb Smirnoff static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
189089323f3SGleb Smirnoff 		    void *arg1, int arg2);
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 = {
251f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
252f8aae777SJulian Elischer 	.name =		NG_PPTPGRE_NODE_TYPE,
253f8aae777SJulian Elischer 	.constructor =	ng_pptpgre_constructor,
254f8aae777SJulian Elischer 	.rcvmsg =	ng_pptpgre_rcvmsg,
255f8aae777SJulian Elischer 	.shutdown =	ng_pptpgre_shutdown,
256f8aae777SJulian Elischer 	.newhook =	ng_pptpgre_newhook,
257f8aae777SJulian Elischer 	.rcvdata =	ng_pptpgre_rcvdata,
258f8aae777SJulian Elischer 	.disconnect =	ng_pptpgre_disconnect,
259f8aae777SJulian Elischer 	.cmdlist =	ng_pptpgre_cmdlist,
260add85a1dSArchie Cobbs };
261add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
262add85a1dSArchie Cobbs 
263add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
264add85a1dSArchie Cobbs 
265add85a1dSArchie Cobbs /************************************************************************
266add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
267add85a1dSArchie Cobbs  ************************************************************************/
268add85a1dSArchie Cobbs 
269add85a1dSArchie Cobbs /*
270add85a1dSArchie Cobbs  * Node type constructor
271add85a1dSArchie Cobbs  */
272add85a1dSArchie Cobbs static int
273069154d5SJulian Elischer ng_pptpgre_constructor(node_p node)
274add85a1dSArchie Cobbs {
275add85a1dSArchie Cobbs 	priv_p priv;
276add85a1dSArchie Cobbs 
277add85a1dSArchie Cobbs 	/* Allocate private structure */
27899cdf4ccSDavid Malone 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
279add85a1dSArchie Cobbs 	if (priv == NULL)
280add85a1dSArchie Cobbs 		return (ENOMEM);
281add85a1dSArchie Cobbs 
28230400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
283add85a1dSArchie Cobbs 
284add85a1dSArchie Cobbs 	/* Initialize state */
285089323f3SGleb Smirnoff 	ng_callout_init(&priv->ackp.sackTimer);
286089323f3SGleb Smirnoff 	ng_callout_init(&priv->ackp.rackTimer);
287add85a1dSArchie Cobbs 
288add85a1dSArchie Cobbs 	/* Done */
289add85a1dSArchie Cobbs 	return (0);
290add85a1dSArchie Cobbs }
291add85a1dSArchie Cobbs 
292add85a1dSArchie Cobbs /*
293add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
294add85a1dSArchie Cobbs  */
295add85a1dSArchie Cobbs static int
296add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
297add85a1dSArchie Cobbs {
29830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
299add85a1dSArchie Cobbs 	hook_p *hookPtr;
300add85a1dSArchie Cobbs 
301add85a1dSArchie Cobbs 	/* Check hook name */
302add85a1dSArchie Cobbs 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0)
303add85a1dSArchie Cobbs 		hookPtr = &priv->upper;
304add85a1dSArchie Cobbs 	else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0)
305add85a1dSArchie Cobbs 		hookPtr = &priv->lower;
306add85a1dSArchie Cobbs 	else
307add85a1dSArchie Cobbs 		return (EINVAL);
308add85a1dSArchie Cobbs 
309add85a1dSArchie Cobbs 	/* See if already connected */
310add85a1dSArchie Cobbs 	if (*hookPtr != NULL)
311add85a1dSArchie Cobbs 		return (EISCONN);
312add85a1dSArchie Cobbs 
313add85a1dSArchie Cobbs 	/* OK */
314add85a1dSArchie Cobbs 	*hookPtr = hook;
315add85a1dSArchie Cobbs 	return (0);
316add85a1dSArchie Cobbs }
317add85a1dSArchie Cobbs 
318add85a1dSArchie Cobbs /*
319add85a1dSArchie Cobbs  * Receive a control message.
320add85a1dSArchie Cobbs  */
321add85a1dSArchie Cobbs static int
322069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
323add85a1dSArchie Cobbs {
32430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
325add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
326add85a1dSArchie Cobbs 	int error = 0;
327069154d5SJulian Elischer 	struct ng_mesg *msg;
328add85a1dSArchie Cobbs 
329069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
330add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
331add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
332add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
333add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
334add85a1dSArchie Cobbs 		    {
335add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
336add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
337add85a1dSArchie Cobbs 
338add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
339add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
340add85a1dSArchie Cobbs 				ERROUT(EINVAL);
341add85a1dSArchie Cobbs 			ng_pptpgre_reset(node);		/* reset on configure */
342add85a1dSArchie Cobbs 			priv->conf = *newConf;
343add85a1dSArchie Cobbs 			break;
344add85a1dSArchie Cobbs 		    }
345add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
346add85a1dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
347add85a1dSArchie Cobbs 			if (resp == NULL)
348add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
349add85a1dSArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
350add85a1dSArchie Cobbs 			break;
3519bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
3529bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
3539bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
3549bee7adfSArchie Cobbs 		    {
3559bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
3569bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3579bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
3589bee7adfSArchie Cobbs 				if (resp == NULL)
3599bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
3609bee7adfSArchie Cobbs 				bcopy(&priv->stats,
3619bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
3629bee7adfSArchie Cobbs 			}
3639bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
3649bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
3659bee7adfSArchie Cobbs 			break;
3669bee7adfSArchie Cobbs 		    }
367add85a1dSArchie Cobbs 		default:
368add85a1dSArchie Cobbs 			error = EINVAL;
369add85a1dSArchie Cobbs 			break;
370add85a1dSArchie Cobbs 		}
371add85a1dSArchie Cobbs 		break;
372add85a1dSArchie Cobbs 	default:
373add85a1dSArchie Cobbs 		error = EINVAL;
374add85a1dSArchie Cobbs 		break;
375add85a1dSArchie Cobbs 	}
376589f6ed8SJulian Elischer done:
377069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
378069154d5SJulian Elischer 	NG_FREE_MSG(msg);
379add85a1dSArchie Cobbs 	return (error);
380add85a1dSArchie Cobbs }
381add85a1dSArchie Cobbs 
382add85a1dSArchie Cobbs /*
383add85a1dSArchie Cobbs  * Receive incoming data on a hook.
384add85a1dSArchie Cobbs  */
385add85a1dSArchie Cobbs static int
386069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
387add85a1dSArchie Cobbs {
38830400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
38930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
390add85a1dSArchie Cobbs 
391add85a1dSArchie Cobbs 	/* If not configured, reject */
392add85a1dSArchie Cobbs 	if (!priv->conf.enabled) {
393069154d5SJulian Elischer 		NG_FREE_ITEM(item);
394add85a1dSArchie Cobbs 		return (ENXIO);
395add85a1dSArchie Cobbs 	}
396add85a1dSArchie Cobbs 
397add85a1dSArchie Cobbs 	/* Treat as xmit or recv data */
398add85a1dSArchie Cobbs 	if (hook == priv->upper)
399069154d5SJulian Elischer 		return ng_pptpgre_xmit(node, item);
400add85a1dSArchie Cobbs 	if (hook == priv->lower)
401069154d5SJulian Elischer 		return ng_pptpgre_recv(node, item);
4026e551fb6SDavid E. O'Brien 	panic("%s: weird hook", __func__);
403add85a1dSArchie Cobbs }
404add85a1dSArchie Cobbs 
405add85a1dSArchie Cobbs /*
406add85a1dSArchie Cobbs  * Destroy node
407add85a1dSArchie Cobbs  */
408add85a1dSArchie Cobbs static int
409069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
410add85a1dSArchie Cobbs {
41130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
412add85a1dSArchie Cobbs 
413089323f3SGleb Smirnoff 	/* Reset node (stops timers) */
414add85a1dSArchie Cobbs 	ng_pptpgre_reset(node);
415add85a1dSArchie Cobbs 
416add85a1dSArchie Cobbs 	FREE(priv, M_NETGRAPH);
4174a48abb2SArchie Cobbs 
4184a48abb2SArchie Cobbs 	/* Decrement ref count */
41930400f03SJulian Elischer 	NG_NODE_UNREF(node);
420add85a1dSArchie Cobbs 	return (0);
421add85a1dSArchie Cobbs }
422add85a1dSArchie Cobbs 
423add85a1dSArchie Cobbs /*
424add85a1dSArchie Cobbs  * Hook disconnection
425add85a1dSArchie Cobbs  */
426add85a1dSArchie Cobbs static int
427add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook)
428add85a1dSArchie Cobbs {
42930400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
43030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
431add85a1dSArchie Cobbs 
432add85a1dSArchie Cobbs 	/* Zero out hook pointer */
433add85a1dSArchie Cobbs 	if (hook == priv->upper)
434add85a1dSArchie Cobbs 		priv->upper = NULL;
435add85a1dSArchie Cobbs 	else if (hook == priv->lower)
436add85a1dSArchie Cobbs 		priv->lower = NULL;
437add85a1dSArchie Cobbs 	else
4386e551fb6SDavid E. O'Brien 		panic("%s: unknown hook", __func__);
439add85a1dSArchie Cobbs 
440add85a1dSArchie Cobbs 	/* Go away if no longer connected to anything */
44130400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(node) == 0)
44230400f03SJulian Elischer 	&& (NG_NODE_IS_VALID(node)))
443069154d5SJulian Elischer 		ng_rmnode_self(node);
444add85a1dSArchie Cobbs 	return (0);
445add85a1dSArchie Cobbs }
446add85a1dSArchie Cobbs 
447add85a1dSArchie Cobbs /*************************************************************************
448add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
449add85a1dSArchie Cobbs *************************************************************************/
450add85a1dSArchie Cobbs 
451add85a1dSArchie Cobbs /*
452add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
453add85a1dSArchie Cobbs  */
454add85a1dSArchie Cobbs static int
455069154d5SJulian Elischer ng_pptpgre_xmit(node_p node, item_p item)
456add85a1dSArchie Cobbs {
45730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
458add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
459add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
460add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
461add85a1dSArchie Cobbs 	int grelen, error;
462069154d5SJulian Elischer 	struct mbuf *m;
463add85a1dSArchie Cobbs 
464069154d5SJulian Elischer 	if (item) {
465069154d5SJulian Elischer 		NGI_GET_M(item, m);
466069154d5SJulian Elischer 	} else {
467069154d5SJulian Elischer 		m = NULL;
468069154d5SJulian Elischer 	}
4699bee7adfSArchie Cobbs 	/* Check if there's data */
4709bee7adfSArchie Cobbs 	if (m != NULL) {
4719bee7adfSArchie Cobbs 
472922ee196SArchie Cobbs 		/* Check if windowing is enabled */
473922ee196SArchie Cobbs 		if (priv->conf.enableWindowing) {
474add85a1dSArchie Cobbs 			/* Is our transmit window full? */
475922ee196SArchie Cobbs 			if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq,
476922ee196SArchie Cobbs 			    priv->recvAck) >= a->xmitWin) {
4779bee7adfSArchie Cobbs 				priv->stats.xmitDrops++;
478069154d5SJulian Elischer 				NG_FREE_M(m);
479069154d5SJulian Elischer 				NG_FREE_ITEM(item);
480add85a1dSArchie Cobbs 				return (ENOBUFS);
481add85a1dSArchie Cobbs 			}
482922ee196SArchie Cobbs 		}
483add85a1dSArchie Cobbs 
484add85a1dSArchie Cobbs 		/* Sanity check frame length */
485add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
4869bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
487069154d5SJulian Elischer 			NG_FREE_M(m);
488069154d5SJulian Elischer 			NG_FREE_ITEM(item);
489add85a1dSArchie Cobbs 			return (EMSGSIZE);
490add85a1dSArchie Cobbs 		}
491069154d5SJulian Elischer 	} else {
4929bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
493069154d5SJulian Elischer 	}
494add85a1dSArchie Cobbs 
495add85a1dSArchie Cobbs 	/* Build GRE header */
496add85a1dSArchie Cobbs 	((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE);
497add85a1dSArchie Cobbs 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
498add85a1dSArchie Cobbs 	gre->cid = htons(priv->conf.peerCid);
499add85a1dSArchie Cobbs 
500add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
501add85a1dSArchie Cobbs 	if (m != NULL) {
502add85a1dSArchie Cobbs 		gre->hasSeq = 1;
503922ee196SArchie Cobbs 		if (priv->conf.enableWindowing) {
504add85a1dSArchie Cobbs 			a->timeSent[priv->xmitSeq - priv->recvAck]
505add85a1dSArchie Cobbs 			    = ng_pptpgre_time(node);
506922ee196SArchie Cobbs 		}
507add85a1dSArchie Cobbs 		priv->xmitSeq++;
508add85a1dSArchie Cobbs 		gre->data[0] = htonl(priv->xmitSeq);
509add85a1dSArchie Cobbs 	}
510add85a1dSArchie Cobbs 
511add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
512678f9e33SArchie Cobbs 	if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) {
513add85a1dSArchie Cobbs 		gre->hasAck = 1;
514678f9e33SArchie Cobbs 		gre->data[gre->hasSeq] = htonl(priv->recvSeq);
515add85a1dSArchie Cobbs 		priv->xmitAck = priv->recvSeq;
5164a48abb2SArchie Cobbs 		ng_pptpgre_stop_send_ack_timer(node);
517da010626SArchie Cobbs 	}
518add85a1dSArchie Cobbs 
519add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
520add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
521add85a1dSArchie Cobbs 	if (m == NULL) {
522a163d034SWarner Losh 		MGETHDR(m, M_DONTWAIT, MT_DATA);
523add85a1dSArchie Cobbs 		if (m == NULL) {
524678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
525069154d5SJulian Elischer 			if (item)
526069154d5SJulian Elischer 				NG_FREE_ITEM(item);
527add85a1dSArchie Cobbs 			return (ENOBUFS);
528add85a1dSArchie Cobbs 		}
529add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
530add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
531add85a1dSArchie Cobbs 	} else {
532a163d034SWarner Losh 		M_PREPEND(m, grelen, M_DONTWAIT);
533add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
534add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
535678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
536069154d5SJulian Elischer 			if (item)
537069154d5SJulian Elischer 				NG_FREE_ITEM(item);
538add85a1dSArchie Cobbs 			return (ENOBUFS);
539add85a1dSArchie Cobbs 		}
540add85a1dSArchie Cobbs 	}
541add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
542add85a1dSArchie Cobbs 
5439bee7adfSArchie Cobbs 	/* Update stats */
5449bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
5459bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
5469bee7adfSArchie Cobbs 
547add85a1dSArchie Cobbs 	/* Deliver packet */
548069154d5SJulian Elischer 	if (item) {
549069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
550069154d5SJulian Elischer 	} else {
551069154d5SJulian Elischer 		NG_SEND_DATA_ONLY(error, priv->lower, m);
552069154d5SJulian Elischer 	}
553069154d5SJulian Elischer 
554678f9e33SArchie Cobbs 
555da010626SArchie Cobbs 	/* Start receive ACK timer if data was sent and not already running */
556678f9e33SArchie Cobbs 	if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1)
557678f9e33SArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
558add85a1dSArchie Cobbs 	return (error);
559add85a1dSArchie Cobbs }
560add85a1dSArchie Cobbs 
561add85a1dSArchie Cobbs /*
562add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
563add85a1dSArchie Cobbs  */
564add85a1dSArchie Cobbs static int
565069154d5SJulian Elischer ng_pptpgre_recv(node_p node, item_p item)
566add85a1dSArchie Cobbs {
56730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
568add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
569816b834fSArchie Cobbs 	const struct greheader *gre;
570816b834fSArchie Cobbs 	const struct ip *ip;
571add85a1dSArchie Cobbs 	int error = 0;
572069154d5SJulian Elischer 	struct mbuf *m;
573add85a1dSArchie Cobbs 
574069154d5SJulian Elischer 	NGI_GET_M(item, m);
5759bee7adfSArchie Cobbs 	/* Update stats */
5769bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
5779bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
5789bee7adfSArchie Cobbs 
579add85a1dSArchie Cobbs 	/* Sanity check packet length */
580add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
5819bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
582add85a1dSArchie Cobbs bad:
583069154d5SJulian Elischer 		NG_FREE_M(m);
584069154d5SJulian Elischer 		NG_FREE_ITEM(item);
585add85a1dSArchie Cobbs 		return (EINVAL);
586add85a1dSArchie Cobbs 	}
587add85a1dSArchie Cobbs 
588add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
589add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
590add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
591678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
592069154d5SJulian Elischer 		NG_FREE_ITEM(item);
593add85a1dSArchie Cobbs 		return (ENOBUFS);
594add85a1dSArchie Cobbs 	}
595816b834fSArchie Cobbs 	ip = mtod(m, const struct ip *);
596add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
597add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
598add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
599678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
600069154d5SJulian Elischer 			NG_FREE_ITEM(item);
601add85a1dSArchie Cobbs 			return (ENOBUFS);
602add85a1dSArchie Cobbs 		}
603816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
604add85a1dSArchie Cobbs 	}
605816b834fSArchie Cobbs 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
606add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
6079bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
6089bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
609add85a1dSArchie Cobbs 		goto bad;
6109bee7adfSArchie Cobbs 	}
611add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
612add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
613678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
614069154d5SJulian Elischer 			NG_FREE_ITEM(item);
615add85a1dSArchie Cobbs 			return (ENOBUFS);
616add85a1dSArchie Cobbs 		}
617816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
618816b834fSArchie Cobbs 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
619add85a1dSArchie Cobbs 	}
620add85a1dSArchie Cobbs 
621add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
622add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
623cc78c48aSArchie Cobbs 	    - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length));
6249bee7adfSArchie Cobbs 	if (extralen < 0) {
6259bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
626add85a1dSArchie Cobbs 		goto bad;
6279bee7adfSArchie Cobbs 	}
628816b834fSArchie Cobbs 	if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK)
629816b834fSArchie Cobbs 	    != PPTP_INIT_VALUE) {
6309bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
631add85a1dSArchie Cobbs 		goto bad;
6329bee7adfSArchie Cobbs 	}
6339bee7adfSArchie Cobbs 	if (ntohs(gre->cid) != priv->conf.cid) {
6349bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
635add85a1dSArchie Cobbs 		goto bad;
6369bee7adfSArchie Cobbs 	}
637add85a1dSArchie Cobbs 
638add85a1dSArchie Cobbs 	/* Look for peer ack */
639add85a1dSArchie Cobbs 	if (gre->hasAck) {
640add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
641add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
642add85a1dSArchie Cobbs 		const int index = ack - priv->recvAck - 1;
64322dfb9bdSArchie Cobbs 		long sample;
644add85a1dSArchie Cobbs 		long diff;
645add85a1dSArchie Cobbs 
646add85a1dSArchie Cobbs 		/* Sanity check ack value */
6479bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) {
6489bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
6499bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
6509bee7adfSArchie Cobbs 		}
6519bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0)
6529bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
653add85a1dSArchie Cobbs 		priv->recvAck = ack;
654add85a1dSArchie Cobbs 
655add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
656922ee196SArchie Cobbs 		if (priv->conf.enableWindowing) {
65722dfb9bdSArchie Cobbs 			sample = ng_pptpgre_time(node) - a->timeSent[index];
658add85a1dSArchie Cobbs 			diff = sample - a->rtt;
659add85a1dSArchie Cobbs 			a->rtt += PPTP_ACK_ALPHA(diff);
660add85a1dSArchie Cobbs 			if (diff < 0)
661add85a1dSArchie Cobbs 				diff = -diff;
662add85a1dSArchie Cobbs 			a->dev += PPTP_ACK_BETA(diff - a->dev);
663e962a823SArchie Cobbs 			a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
664add85a1dSArchie Cobbs 			if (a->ato > PPTP_MAX_TIMEOUT)
665add85a1dSArchie Cobbs 				a->ato = PPTP_MAX_TIMEOUT;
666e962a823SArchie Cobbs 			if (a->ato < PPTP_MIN_TIMEOUT)
667e962a823SArchie Cobbs 				a->ato = PPTP_MIN_TIMEOUT;
668e962a823SArchie Cobbs 
669e962a823SArchie Cobbs 			/* Shift packet transmit times in our transmit window */
670f7854568SDag-Erling Smørgrav 			bcopy(a->timeSent + index + 1, a->timeSent,
671922ee196SArchie Cobbs 			    sizeof(*a->timeSent)
672922ee196SArchie Cobbs 			      * (PPTP_XMIT_WIN - (index + 1)));
673e962a823SArchie Cobbs 
674922ee196SArchie Cobbs 			/* If we sent an entire window, increase window size */
6759bee7adfSArchie Cobbs 			if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0
6769bee7adfSArchie Cobbs 			    && a->xmitWin < PPTP_XMIT_WIN) {
677add85a1dSArchie Cobbs 				a->xmitWin++;
678add85a1dSArchie Cobbs 				a->winAck = ack + a->xmitWin;
679add85a1dSArchie Cobbs 			}
680add85a1dSArchie Cobbs 
6819bee7adfSArchie Cobbs 			/* Stop/(re)start receive ACK timer as necessary */
6824a48abb2SArchie Cobbs 			ng_pptpgre_stop_recv_ack_timer(node);
683678f9e33SArchie Cobbs 			if (priv->recvAck != priv->xmitSeq)
684add85a1dSArchie Cobbs 				ng_pptpgre_start_recv_ack_timer(node);
685add85a1dSArchie Cobbs 		}
686922ee196SArchie 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... */
705089323f3SGleb Smirnoff 		if (!(a->sackTimer.c_flags & CALLOUT_PENDING)) {
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 
755922ee196SArchie Cobbs 	if (!priv->conf.enableWindowing)
756922ee196SArchie Cobbs 		return;
757922ee196SArchie Cobbs 
758add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
759add85a1dSArchie Cobbs 	   and reset the timer to that time. */
760add85a1dSArchie Cobbs 	remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node);
761add85a1dSArchie Cobbs 	if (remain < 0)
762add85a1dSArchie Cobbs 		remain = 0;
763678f9e33SArchie Cobbs #ifdef DEBUG_RAT
764678f9e33SArchie Cobbs 	a->timerLength = remain;
765678f9e33SArchie Cobbs 	a->timerStart = ng_pptpgre_time(node);
766678f9e33SArchie Cobbs #endif
7679bee7adfSArchie Cobbs 
7684a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
769678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
770089323f3SGleb Smirnoff 	ng_callout(&a->rackTimer, node, NULL, ticks,
771089323f3SGleb Smirnoff 	    ng_pptpgre_recv_ack_timeout, NULL, 0);
7724a48abb2SArchie Cobbs }
7734a48abb2SArchie Cobbs 
7744a48abb2SArchie Cobbs /*
7754a48abb2SArchie Cobbs  * Stop receive ack timer.
7764a48abb2SArchie Cobbs  */
7774a48abb2SArchie Cobbs static void
7784a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node_p node)
7794a48abb2SArchie Cobbs {
7804a48abb2SArchie Cobbs 	const priv_p priv = NG_NODE_PRIVATE(node);
7814a48abb2SArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
7824a48abb2SArchie Cobbs 
783922ee196SArchie Cobbs 	if (!priv->conf.enableWindowing)
784922ee196SArchie Cobbs 		return;
785922ee196SArchie Cobbs 
786089323f3SGleb Smirnoff 	ng_uncallout(&a->rackTimer, node);
787add85a1dSArchie Cobbs }
788add85a1dSArchie Cobbs 
789add85a1dSArchie Cobbs /*
790add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
791add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
792add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
793add85a1dSArchie Cobbs  */
794add85a1dSArchie Cobbs static void
795089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
796add85a1dSArchie Cobbs {
79730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
798add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
799add85a1dSArchie Cobbs 
8009bee7adfSArchie Cobbs 
801add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
8029bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
803add85a1dSArchie Cobbs 	a->rtt = PPTP_ACK_DELTA(a->rtt);
804add85a1dSArchie Cobbs 	a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
805add85a1dSArchie Cobbs 	if (a->ato > PPTP_MAX_TIMEOUT)
806add85a1dSArchie Cobbs 		a->ato = PPTP_MAX_TIMEOUT;
807e962a823SArchie Cobbs 	if (a->ato < PPTP_MIN_TIMEOUT)
808e962a823SArchie Cobbs 		a->ato = PPTP_MIN_TIMEOUT;
809add85a1dSArchie Cobbs 
810678f9e33SArchie Cobbs #ifdef DEBUG_RAT
811678f9e33SArchie Cobbs     log(LOG_DEBUG,
812678f9e33SArchie Cobbs 	"RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n",
813678f9e33SArchie Cobbs 	(int)ng_pptpgre_time(node), priv->recvAck + 1,
814678f9e33SArchie Cobbs 	(int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato);
815678f9e33SArchie Cobbs #endif
816678f9e33SArchie Cobbs 
817e962a823SArchie Cobbs 	/* Reset ack and sliding window */
818e962a823SArchie Cobbs 	priv->recvAck = priv->xmitSeq;		/* pretend we got the ack */
819e962a823SArchie Cobbs 	a->xmitWin = (a->xmitWin + 1) / 2;	/* shrink transmit window */
820e962a823SArchie Cobbs 	a->winAck = priv->recvAck + a->xmitWin;	/* reset win expand time */
821add85a1dSArchie Cobbs }
822add85a1dSArchie Cobbs 
823add85a1dSArchie Cobbs /*
8249bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
8259bee7adfSArchie Cobbs  * already running.
8269bee7adfSArchie Cobbs  */
8279bee7adfSArchie Cobbs static void
828da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout)
8299bee7adfSArchie Cobbs {
83030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
8319bee7adfSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
832678f9e33SArchie Cobbs 	int ticks;
8339bee7adfSArchie Cobbs 
8344a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
835678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
836089323f3SGleb Smirnoff 	ng_callout(&a->sackTimer, node, NULL, ticks,
837089323f3SGleb Smirnoff 	    ng_pptpgre_send_ack_timeout, NULL, 0);
8384a48abb2SArchie Cobbs }
8394a48abb2SArchie Cobbs 
8404a48abb2SArchie Cobbs /*
8414a48abb2SArchie Cobbs  * Stop send ack timer.
8424a48abb2SArchie Cobbs  */
8434a48abb2SArchie Cobbs static void
8444a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node_p node)
8454a48abb2SArchie Cobbs {
8464a48abb2SArchie Cobbs 	const priv_p priv = NG_NODE_PRIVATE(node);
8474a48abb2SArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
8484a48abb2SArchie Cobbs 
849089323f3SGleb Smirnoff 	ng_uncallout(&a->sackTimer, node);
8509bee7adfSArchie Cobbs }
8519bee7adfSArchie Cobbs 
8529bee7adfSArchie Cobbs /*
853add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
854add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
855add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
856add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
857add85a1dSArchie Cobbs  */
858add85a1dSArchie Cobbs static void
859089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
860add85a1dSArchie Cobbs {
8619bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
862069154d5SJulian Elischer   	ng_pptpgre_xmit(node, NULL);
863add85a1dSArchie Cobbs }
864add85a1dSArchie Cobbs 
865add85a1dSArchie Cobbs /*************************************************************************
866add85a1dSArchie Cobbs 		    MISC FUNCTIONS
867add85a1dSArchie Cobbs *************************************************************************/
868add85a1dSArchie Cobbs 
869add85a1dSArchie Cobbs /*
870add85a1dSArchie Cobbs  * Reset state
871add85a1dSArchie Cobbs  */
872add85a1dSArchie Cobbs static void
873add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node)
874add85a1dSArchie Cobbs {
87530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
876add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
877add85a1dSArchie Cobbs 
878add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
879add85a1dSArchie Cobbs 	a->ato = PPTP_MAX_TIMEOUT;
880add85a1dSArchie Cobbs 	a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
881add85a1dSArchie Cobbs 	if (a->rtt < PPTP_MIN_RTT)
882add85a1dSArchie Cobbs 		a->rtt = PPTP_MIN_RTT;
883add85a1dSArchie Cobbs 	a->dev = 0;
884add85a1dSArchie Cobbs 	a->xmitWin = (priv->conf.recvWin + 1) / 2;
885e962a823SArchie Cobbs 	if (a->xmitWin < 2)		/* often the first packet is lost */
886e962a823SArchie Cobbs 		a->xmitWin = 2;		/*   because the peer isn't ready */
887add85a1dSArchie Cobbs 	if (a->xmitWin > PPTP_XMIT_WIN)
888add85a1dSArchie Cobbs 		a->xmitWin = PPTP_XMIT_WIN;
889add85a1dSArchie Cobbs 	a->winAck = a->xmitWin;
890add85a1dSArchie Cobbs 
891add85a1dSArchie Cobbs 	/* Reset sequence numbers */
8923cd7db22SArchie Cobbs 	priv->recvSeq = ~0;
8933cd7db22SArchie Cobbs 	priv->recvAck = ~0;
8943cd7db22SArchie Cobbs 	priv->xmitSeq = ~0;
8953cd7db22SArchie Cobbs 	priv->xmitAck = ~0;
896add85a1dSArchie Cobbs 
897add85a1dSArchie Cobbs 	/* Reset start time */
8989bee7adfSArchie Cobbs 	getmicrouptime(&priv->startTime);
8999bee7adfSArchie Cobbs 
9009bee7adfSArchie Cobbs 	/* Reset stats */
9019bee7adfSArchie Cobbs 	bzero(&priv->stats, sizeof(priv->stats));
902add85a1dSArchie Cobbs 
9034a48abb2SArchie Cobbs 	/* Stop timers */
9044a48abb2SArchie Cobbs 	ng_pptpgre_stop_send_ack_timer(node);
9054a48abb2SArchie Cobbs 	ng_pptpgre_stop_recv_ack_timer(node);
906add85a1dSArchie Cobbs }
907add85a1dSArchie Cobbs 
908add85a1dSArchie Cobbs /*
909add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
910add85a1dSArchie Cobbs  */
911add85a1dSArchie Cobbs static pptptime_t
912add85a1dSArchie Cobbs ng_pptpgre_time(node_p node)
913add85a1dSArchie Cobbs {
91430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
915add85a1dSArchie Cobbs 	struct timeval tv;
916678f9e33SArchie Cobbs 	pptptime_t t;
917add85a1dSArchie Cobbs 
918678f9e33SArchie Cobbs 	microuptime(&tv);
919add85a1dSArchie Cobbs 	if (tv.tv_sec < priv->startTime.tv_sec
920add85a1dSArchie Cobbs 	    || (tv.tv_sec == priv->startTime.tv_sec
921add85a1dSArchie Cobbs 	      && tv.tv_usec < priv->startTime.tv_usec))
922add85a1dSArchie Cobbs 		return (0);
923add85a1dSArchie Cobbs 	timevalsub(&tv, &priv->startTime);
924678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
925678f9e33SArchie Cobbs 	t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
926678f9e33SArchie Cobbs 	return(t);
927add85a1dSArchie Cobbs }
928