xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision cc3bbd68c54fab5539ff1cfa5f7bfb454633239c)
1add85a1dSArchie Cobbs 
2add85a1dSArchie Cobbs /*
3add85a1dSArchie Cobbs  * ng_pptpgre.c
4add85a1dSArchie Cobbs  *
5add85a1dSArchie Cobbs  * Copyright (c) 1996-1999 Whistle Communications, Inc.
6add85a1dSArchie Cobbs  * All rights reserved.
7add85a1dSArchie Cobbs  *
8add85a1dSArchie Cobbs  * Subject to the following obligations and disclaimer of warranty, use and
9add85a1dSArchie Cobbs  * redistribution of this software, in source or object code forms, with or
10add85a1dSArchie Cobbs  * without modifications are expressly permitted by Whistle Communications;
11add85a1dSArchie Cobbs  * provided, however, that:
12add85a1dSArchie Cobbs  * 1. Any and all reproductions of the source or object code must include the
13add85a1dSArchie Cobbs  *    copyright notice above and the following disclaimer of warranties; and
14add85a1dSArchie Cobbs  * 2. No rights are granted, in any manner or form, to use Whistle
15add85a1dSArchie Cobbs  *    Communications, Inc. trademarks, including the mark "WHISTLE
16add85a1dSArchie Cobbs  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17add85a1dSArchie Cobbs  *    such appears in the above copyright notice or in the software.
18add85a1dSArchie Cobbs  *
19add85a1dSArchie Cobbs  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20add85a1dSArchie Cobbs  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21add85a1dSArchie Cobbs  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22add85a1dSArchie Cobbs  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23add85a1dSArchie Cobbs  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24add85a1dSArchie Cobbs  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25add85a1dSArchie Cobbs  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26add85a1dSArchie Cobbs  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27add85a1dSArchie Cobbs  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28add85a1dSArchie Cobbs  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29add85a1dSArchie Cobbs  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30add85a1dSArchie Cobbs  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31add85a1dSArchie Cobbs  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32add85a1dSArchie Cobbs  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33add85a1dSArchie Cobbs  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34add85a1dSArchie Cobbs  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35add85a1dSArchie Cobbs  * OF SUCH DAMAGE.
36add85a1dSArchie Cobbs  *
37cc3bbd68SJulian Elischer  * Author: Archie Cobbs <archie@freebsd.org>
38add85a1dSArchie Cobbs  *
39add85a1dSArchie Cobbs  * $FreeBSD$
40add85a1dSArchie Cobbs  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
41add85a1dSArchie Cobbs  */
42add85a1dSArchie Cobbs 
43add85a1dSArchie Cobbs /*
44add85a1dSArchie Cobbs  * PPTP/GRE netgraph node type.
45add85a1dSArchie Cobbs  *
46add85a1dSArchie Cobbs  * This node type does the GRE encapsulation as specified for the PPTP
47add85a1dSArchie Cobbs  * protocol (RFC 2637, section 4).  This includes sequencing and
48add85a1dSArchie Cobbs  * retransmission of frames, but not the actual packet delivery nor
49add85a1dSArchie Cobbs  * any of the TCP control stream protocol.
50add85a1dSArchie Cobbs  *
51add85a1dSArchie Cobbs  * The "upper" hook of this node is suitable for attaching to a "ppp"
52add85a1dSArchie Cobbs  * node link hook.  The "lower" hook of this node is suitable for attaching
53add85a1dSArchie Cobbs  * to a "ksocket" node on hook "inet/raw/gre".
54add85a1dSArchie Cobbs  */
55add85a1dSArchie Cobbs 
56add85a1dSArchie Cobbs #include <sys/param.h>
57add85a1dSArchie Cobbs #include <sys/systm.h>
58add85a1dSArchie Cobbs #include <sys/kernel.h>
59add85a1dSArchie Cobbs #include <sys/time.h>
60add85a1dSArchie Cobbs #include <sys/mbuf.h>
61add85a1dSArchie Cobbs #include <sys/malloc.h>
62add85a1dSArchie Cobbs #include <sys/errno.h>
63add85a1dSArchie Cobbs 
64add85a1dSArchie Cobbs #include <netinet/in.h>
65add85a1dSArchie Cobbs #include <netinet/in_systm.h>
66add85a1dSArchie Cobbs #include <netinet/ip.h>
67add85a1dSArchie Cobbs 
68add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
69add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
70add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
71add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
72add85a1dSArchie Cobbs 
73add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
74add85a1dSArchie Cobbs struct greheader {
75add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
76add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
77add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
78add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
79add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
80add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
81add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
82add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
83add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
84add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
85add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
86add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
87add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
88add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
89add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
90add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
91add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
92add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
93add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
94add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
95add85a1dSArchie Cobbs #else
96add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
97add85a1dSArchie Cobbs #endif
98add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
99add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
100add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
101add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
102add85a1dSArchie Cobbs };
103add85a1dSArchie Cobbs 
104add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
105add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
106add85a1dSArchie Cobbs 
107add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
108add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
109add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
110add85a1dSArchie Cobbs 
111add85a1dSArchie Cobbs /* Min and max packet length */
112add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
113add85a1dSArchie Cobbs 
114add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
115e962a823SArchie Cobbs #define PPTP_TIME_SCALE		1000			/* milliseconds */
116678f9e33SArchie Cobbs typedef u_int64_t		pptptime_t;
117add85a1dSArchie Cobbs 
118add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
119e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
120e962a823SArchie Cobbs #define PPTP_MIN_RTT		(PPTP_TIME_SCALE / 10)	/* 100 milliseconds */
121da010626SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
122add85a1dSArchie Cobbs #define PPTP_MAX_TIMEOUT	(10 * PPTP_TIME_SCALE)	/* 10 seconds */
123add85a1dSArchie Cobbs 
124da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet
125da010626SArchie Cobbs    we can piggy-back the ACK off of. These parameters determine the mimimum
126da010626SArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that.
127da010626SArchie Cobbs    These have no effect unless "enableDelayedAck" is turned on. */
128da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
129da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
130da010626SArchie Cobbs 
131e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
132add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x)	((x) >> 3)	/* alpha = 0.125 */
133add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x)	((x) >> 2)	/* beta = 0.25 */
134add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
135add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
136add85a1dSArchie Cobbs 
1379bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
1389bee7adfSArchie Cobbs 
139add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
140add85a1dSArchie Cobbs struct ng_pptpgre_ackp {
141add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
142add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
143add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
144add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
145add85a1dSArchie Cobbs 	struct callout_handle	sackTimer;	/* send ack timer */
146add85a1dSArchie Cobbs 	struct callout_handle	rackTimer;	/* recv ack timer */
1473cd7db22SArchie Cobbs 	node_p			*sackTimerPtr;	/* send ack timer pointer */
1483cd7db22SArchie Cobbs 	node_p			*rackTimerPtr;	/* recv ack timer pointer */
1493cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
150add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
151678f9e33SArchie Cobbs #ifdef DEBUG_RAT
152da010626SArchie Cobbs 	pptptime_t		timerStart;	/* when rackTimer started */
153da010626SArchie Cobbs 	pptptime_t		timerLength;	/* rackTimer duration */
154678f9e33SArchie Cobbs #endif
155add85a1dSArchie Cobbs };
156add85a1dSArchie Cobbs 
157add85a1dSArchie Cobbs /* Node private data */
158add85a1dSArchie Cobbs struct ng_pptpgre_private {
159add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
160add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
161add85a1dSArchie Cobbs 	struct ng_pptpgre_conf	conf;		/* configuration info */
162add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp	ackp;		/* packet transmit ack state */
163add85a1dSArchie Cobbs 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
164add85a1dSArchie Cobbs 	u_int32_t		xmitSeq;	/* last seq # we sent */
165add85a1dSArchie Cobbs 	u_int32_t		recvAck;	/* last seq # peer ack'd */
166add85a1dSArchie Cobbs 	u_int32_t		xmitAck;	/* last seq # we ack'd */
167add85a1dSArchie Cobbs 	struct timeval		startTime;	/* time node was created */
1689bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
169add85a1dSArchie Cobbs };
170add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
171add85a1dSArchie Cobbs 
172add85a1dSArchie Cobbs /* Netgraph node methods */
173add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
174add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
175add85a1dSArchie Cobbs static ng_shutdown_t	ng_pptpgre_rmnode;
176add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
177add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
178add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
179add85a1dSArchie Cobbs 
180add85a1dSArchie Cobbs /* Helper functions */
181add85a1dSArchie Cobbs static int	ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta);
182add85a1dSArchie Cobbs static int	ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta);
183da010626SArchie Cobbs static void	ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout);
184add85a1dSArchie Cobbs static void	ng_pptpgre_start_recv_ack_timer(node_p node);
185add85a1dSArchie Cobbs static void	ng_pptpgre_recv_ack_timeout(void *arg);
186add85a1dSArchie Cobbs static void	ng_pptpgre_send_ack_timeout(void *arg);
187add85a1dSArchie Cobbs static void	ng_pptpgre_reset(node_p node);
188add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node);
189add85a1dSArchie Cobbs 
190add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
191add85a1dSArchie Cobbs static const struct ng_parse_struct_info
192add85a1dSArchie Cobbs 	ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO;
193add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
194add85a1dSArchie Cobbs 	&ng_parse_struct_type,
195add85a1dSArchie Cobbs 	&ng_pptpgre_conf_type_info,
196add85a1dSArchie Cobbs };
197add85a1dSArchie Cobbs 
1989bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
1999bee7adfSArchie Cobbs static const struct ng_parse_struct_info
2009bee7adfSArchie Cobbs 	ng_pptpgre_stats_type_info = NG_PPTPGRE_STATS_TYPE_INFO;
2019bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2029bee7adfSArchie Cobbs 	&ng_parse_struct_type,
2039bee7adfSArchie Cobbs 	&ng_pptpgre_stats_type_info
2049bee7adfSArchie Cobbs };
2059bee7adfSArchie Cobbs 
206add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
207add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
208add85a1dSArchie Cobbs 	{
209add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
210add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
211add85a1dSArchie Cobbs 	  "setconfig",
212add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
213add85a1dSArchie Cobbs 	  NULL
214add85a1dSArchie Cobbs 	},
215add85a1dSArchie Cobbs 	{
216add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
217add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
218add85a1dSArchie Cobbs 	  "getconfig",
219add85a1dSArchie Cobbs 	  NULL,
220add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
221add85a1dSArchie Cobbs 	},
2229bee7adfSArchie Cobbs 	{
2239bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2249bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2259bee7adfSArchie Cobbs 	  "getstats",
2269bee7adfSArchie Cobbs 	  NULL,
2279bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2289bee7adfSArchie Cobbs 	},
2299bee7adfSArchie Cobbs 	{
2309bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2319bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2329bee7adfSArchie Cobbs 	  "clrstats",
2339bee7adfSArchie Cobbs 	  NULL,
2349bee7adfSArchie Cobbs 	  NULL
2359bee7adfSArchie Cobbs 	},
2369bee7adfSArchie Cobbs 	{
2379bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2389bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2399bee7adfSArchie Cobbs 	  "getclrstats",
2409bee7adfSArchie Cobbs 	  NULL,
2419bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2429bee7adfSArchie Cobbs 	},
243add85a1dSArchie Cobbs 	{ 0 }
244add85a1dSArchie Cobbs };
245add85a1dSArchie Cobbs 
246add85a1dSArchie Cobbs /* Node type descriptor */
247add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
248add85a1dSArchie Cobbs 	NG_VERSION,
249add85a1dSArchie Cobbs 	NG_PPTPGRE_NODE_TYPE,
250add85a1dSArchie Cobbs 	NULL,
251add85a1dSArchie Cobbs 	ng_pptpgre_constructor,
252add85a1dSArchie Cobbs 	ng_pptpgre_rcvmsg,
253add85a1dSArchie Cobbs 	ng_pptpgre_rmnode,
254add85a1dSArchie Cobbs 	ng_pptpgre_newhook,
255add85a1dSArchie Cobbs 	NULL,
256add85a1dSArchie Cobbs 	NULL,
257add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
258add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
259add85a1dSArchie Cobbs 	ng_pptpgre_disconnect,
260add85a1dSArchie Cobbs 	ng_pptpgre_cmdlist
261add85a1dSArchie Cobbs };
262add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
263add85a1dSArchie Cobbs 
264add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
265add85a1dSArchie Cobbs 
266add85a1dSArchie Cobbs /************************************************************************
267add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
268add85a1dSArchie Cobbs  ************************************************************************/
269add85a1dSArchie Cobbs 
270add85a1dSArchie Cobbs /*
271add85a1dSArchie Cobbs  * Node type constructor
272add85a1dSArchie Cobbs  */
273add85a1dSArchie Cobbs static int
274add85a1dSArchie Cobbs ng_pptpgre_constructor(node_p *nodep)
275add85a1dSArchie Cobbs {
276add85a1dSArchie Cobbs 	priv_p priv;
277add85a1dSArchie Cobbs 	int error;
278add85a1dSArchie Cobbs 
279add85a1dSArchie Cobbs 	/* Allocate private structure */
28065b9a0daSArchie Cobbs 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT);
281add85a1dSArchie Cobbs 	if (priv == NULL)
282add85a1dSArchie Cobbs 		return (ENOMEM);
283add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
284add85a1dSArchie Cobbs 
285add85a1dSArchie Cobbs 	/* Call generic node constructor */
286add85a1dSArchie Cobbs 	if ((error = ng_make_node_common(&ng_pptpgre_typestruct, nodep))) {
287add85a1dSArchie Cobbs 		FREE(priv, M_NETGRAPH);
288add85a1dSArchie Cobbs 		return (error);
289add85a1dSArchie Cobbs 	}
290add85a1dSArchie Cobbs 	(*nodep)->private = priv;
291add85a1dSArchie Cobbs 
292add85a1dSArchie Cobbs 	/* Initialize state */
293add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.sackTimer);
294add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.rackTimer);
295add85a1dSArchie Cobbs 
296add85a1dSArchie Cobbs 	/* Done */
297add85a1dSArchie Cobbs 	return (0);
298add85a1dSArchie Cobbs }
299add85a1dSArchie Cobbs 
300add85a1dSArchie Cobbs /*
301add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
302add85a1dSArchie Cobbs  */
303add85a1dSArchie Cobbs static int
304add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
305add85a1dSArchie Cobbs {
306add85a1dSArchie Cobbs 	const priv_p priv = node->private;
307add85a1dSArchie Cobbs 	hook_p *hookPtr;
308add85a1dSArchie Cobbs 
309add85a1dSArchie Cobbs 	/* Check hook name */
310add85a1dSArchie Cobbs 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0)
311add85a1dSArchie Cobbs 		hookPtr = &priv->upper;
312add85a1dSArchie Cobbs 	else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0)
313add85a1dSArchie Cobbs 		hookPtr = &priv->lower;
314add85a1dSArchie Cobbs 	else
315add85a1dSArchie Cobbs 		return (EINVAL);
316add85a1dSArchie Cobbs 
317add85a1dSArchie Cobbs 	/* See if already connected */
318add85a1dSArchie Cobbs 	if (*hookPtr != NULL)
319add85a1dSArchie Cobbs 		return (EISCONN);
320add85a1dSArchie Cobbs 
321add85a1dSArchie Cobbs 	/* OK */
322add85a1dSArchie Cobbs 	*hookPtr = hook;
323add85a1dSArchie Cobbs 	return (0);
324add85a1dSArchie Cobbs }
325add85a1dSArchie Cobbs 
326add85a1dSArchie Cobbs /*
327add85a1dSArchie Cobbs  * Receive a control message.
328add85a1dSArchie Cobbs  */
329add85a1dSArchie Cobbs static int
330add85a1dSArchie Cobbs ng_pptpgre_rcvmsg(node_p node, struct ng_mesg *msg,
331a4ec03cfSJulian Elischer 	      const char *raddr, struct ng_mesg **rptr, hook_p lasthook)
332add85a1dSArchie Cobbs {
333add85a1dSArchie Cobbs 	const priv_p priv = node->private;
334add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
335add85a1dSArchie Cobbs 	int error = 0;
336add85a1dSArchie Cobbs 
337add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
338add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
339add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
340add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
341add85a1dSArchie Cobbs 		    {
342add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
343add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
344add85a1dSArchie Cobbs 
345add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
346add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
347add85a1dSArchie Cobbs 				ERROUT(EINVAL);
348add85a1dSArchie Cobbs 			ng_pptpgre_reset(node);		/* reset on configure */
349add85a1dSArchie Cobbs 			priv->conf = *newConf;
350add85a1dSArchie Cobbs 			break;
351add85a1dSArchie Cobbs 		    }
352add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
353add85a1dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
354add85a1dSArchie Cobbs 			if (resp == NULL)
355add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
356add85a1dSArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
357add85a1dSArchie Cobbs 			break;
3589bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
3599bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
3609bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
3619bee7adfSArchie Cobbs 		    {
3629bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
3639bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3649bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
3659bee7adfSArchie Cobbs 				if (resp == NULL)
3669bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
3679bee7adfSArchie Cobbs 				bcopy(&priv->stats,
3689bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
3699bee7adfSArchie Cobbs 			}
3709bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
3719bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
3729bee7adfSArchie Cobbs 			break;
3739bee7adfSArchie Cobbs 		    }
374add85a1dSArchie Cobbs 		default:
375add85a1dSArchie Cobbs 			error = EINVAL;
376add85a1dSArchie Cobbs 			break;
377add85a1dSArchie Cobbs 		}
378add85a1dSArchie Cobbs 		break;
379add85a1dSArchie Cobbs 	default:
380add85a1dSArchie Cobbs 		error = EINVAL;
381add85a1dSArchie Cobbs 		break;
382add85a1dSArchie Cobbs 	}
383add85a1dSArchie Cobbs 	if (rptr)
384add85a1dSArchie Cobbs 		*rptr = resp;
385add85a1dSArchie Cobbs 	else if (resp)
386add85a1dSArchie Cobbs 		FREE(resp, M_NETGRAPH);
387add85a1dSArchie Cobbs 
388add85a1dSArchie Cobbs done:
389add85a1dSArchie Cobbs 	FREE(msg, M_NETGRAPH);
390add85a1dSArchie Cobbs 	return (error);
391add85a1dSArchie Cobbs }
392add85a1dSArchie Cobbs 
393add85a1dSArchie Cobbs /*
394add85a1dSArchie Cobbs  * Receive incoming data on a hook.
395add85a1dSArchie Cobbs  */
396add85a1dSArchie Cobbs static int
397a4ec03cfSJulian Elischer ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
398a4ec03cfSJulian Elischer 		struct mbuf **ret_m, meta_p *ret_meta)
399add85a1dSArchie Cobbs {
400add85a1dSArchie Cobbs 	const node_p node = hook->node;
401add85a1dSArchie Cobbs 	const priv_p priv = node->private;
402add85a1dSArchie Cobbs 
403add85a1dSArchie Cobbs 	/* If not configured, reject */
404add85a1dSArchie Cobbs 	if (!priv->conf.enabled) {
405add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
406add85a1dSArchie Cobbs 		return (ENXIO);
407add85a1dSArchie Cobbs 	}
408add85a1dSArchie Cobbs 
409add85a1dSArchie Cobbs 	/* Treat as xmit or recv data */
410add85a1dSArchie Cobbs 	if (hook == priv->upper)
411add85a1dSArchie Cobbs 		return ng_pptpgre_xmit(node, m, meta);
412add85a1dSArchie Cobbs 	if (hook == priv->lower)
413add85a1dSArchie Cobbs 		return ng_pptpgre_recv(node, m, meta);
414add85a1dSArchie Cobbs 	panic("%s: weird hook", __FUNCTION__);
415add85a1dSArchie Cobbs }
416add85a1dSArchie Cobbs 
417add85a1dSArchie Cobbs /*
418add85a1dSArchie Cobbs  * Destroy node
419add85a1dSArchie Cobbs  */
420add85a1dSArchie Cobbs static int
421add85a1dSArchie Cobbs ng_pptpgre_rmnode(node_p node)
422add85a1dSArchie Cobbs {
423add85a1dSArchie Cobbs 	const priv_p priv = node->private;
424add85a1dSArchie Cobbs 
4253cd7db22SArchie Cobbs 	/* Reset node */
426add85a1dSArchie Cobbs 	ng_pptpgre_reset(node);
427add85a1dSArchie Cobbs 
428add85a1dSArchie Cobbs 	/* Take down netgraph node */
429add85a1dSArchie Cobbs 	node->flags |= NG_INVALID;
430add85a1dSArchie Cobbs 	ng_cutlinks(node);
431add85a1dSArchie Cobbs 	ng_unname(node);
432add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
433add85a1dSArchie Cobbs 	FREE(priv, M_NETGRAPH);
434add85a1dSArchie Cobbs 	node->private = NULL;
435add85a1dSArchie Cobbs 	ng_unref(node);
436add85a1dSArchie Cobbs 	return (0);
437add85a1dSArchie Cobbs }
438add85a1dSArchie Cobbs 
439add85a1dSArchie Cobbs /*
440add85a1dSArchie Cobbs  * Hook disconnection
441add85a1dSArchie Cobbs  */
442add85a1dSArchie Cobbs static int
443add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook)
444add85a1dSArchie Cobbs {
445add85a1dSArchie Cobbs 	const node_p node = hook->node;
446add85a1dSArchie Cobbs 	const priv_p priv = node->private;
447add85a1dSArchie Cobbs 
448add85a1dSArchie Cobbs 	/* Zero out hook pointer */
449add85a1dSArchie Cobbs 	if (hook == priv->upper)
450add85a1dSArchie Cobbs 		priv->upper = NULL;
451add85a1dSArchie Cobbs 	else if (hook == priv->lower)
452add85a1dSArchie Cobbs 		priv->lower = NULL;
453add85a1dSArchie Cobbs 	else
454add85a1dSArchie Cobbs 		panic("%s: unknown hook", __FUNCTION__);
455add85a1dSArchie Cobbs 
456add85a1dSArchie Cobbs 	/* Go away if no longer connected to anything */
457add85a1dSArchie Cobbs 	if (node->numhooks == 0)
458add85a1dSArchie Cobbs 		ng_rmnode(node);
459add85a1dSArchie Cobbs 	return (0);
460add85a1dSArchie Cobbs }
461add85a1dSArchie Cobbs 
462add85a1dSArchie Cobbs /*************************************************************************
463add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
464add85a1dSArchie Cobbs *************************************************************************/
465add85a1dSArchie Cobbs 
466add85a1dSArchie Cobbs /*
467add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
468add85a1dSArchie Cobbs  */
469add85a1dSArchie Cobbs static int
470add85a1dSArchie Cobbs ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta)
471add85a1dSArchie Cobbs {
472add85a1dSArchie Cobbs 	const priv_p priv = node->private;
473add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
474add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
475add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
476add85a1dSArchie Cobbs 	int grelen, error;
477add85a1dSArchie Cobbs 
4789bee7adfSArchie Cobbs 	/* Check if there's data */
4799bee7adfSArchie Cobbs 	if (m != NULL) {
4809bee7adfSArchie Cobbs 
481add85a1dSArchie Cobbs 		/* Is our transmit window full? */
4829bee7adfSArchie Cobbs 		if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck)
4839bee7adfSArchie Cobbs 		      >= a->xmitWin) {
4849bee7adfSArchie Cobbs 			priv->stats.xmitDrops++;
485add85a1dSArchie Cobbs 			NG_FREE_DATA(m, meta);
486add85a1dSArchie Cobbs 			return (ENOBUFS);
487add85a1dSArchie Cobbs 		}
488add85a1dSArchie Cobbs 
489add85a1dSArchie Cobbs 		/* Sanity check frame length */
490add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
4919bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
492add85a1dSArchie Cobbs 			NG_FREE_DATA(m, meta);
493add85a1dSArchie Cobbs 			return (EMSGSIZE);
494add85a1dSArchie Cobbs 		}
4959bee7adfSArchie Cobbs 	} else
4969bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
497add85a1dSArchie Cobbs 
498add85a1dSArchie Cobbs 	/* Build GRE header */
499add85a1dSArchie Cobbs 	((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE);
500add85a1dSArchie Cobbs 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
501add85a1dSArchie Cobbs 	gre->cid = htons(priv->conf.peerCid);
502add85a1dSArchie Cobbs 
503add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
504add85a1dSArchie Cobbs 	if (m != NULL) {
505add85a1dSArchie Cobbs 		gre->hasSeq = 1;
506add85a1dSArchie Cobbs 		a->timeSent[priv->xmitSeq - priv->recvAck]
507add85a1dSArchie Cobbs 		    = ng_pptpgre_time(node);
508add85a1dSArchie Cobbs 		priv->xmitSeq++;
509add85a1dSArchie Cobbs 		gre->data[0] = htonl(priv->xmitSeq);
510add85a1dSArchie Cobbs 	}
511add85a1dSArchie Cobbs 
512add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
513678f9e33SArchie Cobbs 	if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) {
514add85a1dSArchie Cobbs 		gre->hasAck = 1;
515678f9e33SArchie Cobbs 		gre->data[gre->hasSeq] = htonl(priv->recvSeq);
516add85a1dSArchie Cobbs 		priv->xmitAck = priv->recvSeq;
517034d9dacSArchie Cobbs 		a->sackTimerPtr = NULL;		/* "stop" timer */
518da010626SArchie Cobbs 	}
519add85a1dSArchie Cobbs 
520add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
521add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
522add85a1dSArchie Cobbs 	if (m == NULL) {
523add85a1dSArchie Cobbs 		MGETHDR(m, M_DONTWAIT, MT_DATA);
524add85a1dSArchie Cobbs 		if (m == NULL) {
525678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
526add85a1dSArchie Cobbs 			NG_FREE_META(meta);
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 {
532add85a1dSArchie Cobbs 		M_PREPEND(m, grelen, M_NOWAIT);
533add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
534add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
535678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
536add85a1dSArchie Cobbs 			NG_FREE_META(meta);
537add85a1dSArchie Cobbs 			return (ENOBUFS);
538add85a1dSArchie Cobbs 		}
539add85a1dSArchie Cobbs 	}
540add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
541add85a1dSArchie Cobbs 
5429bee7adfSArchie Cobbs 	/* Update stats */
5439bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
5449bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
5459bee7adfSArchie Cobbs 
546add85a1dSArchie Cobbs 	/* Deliver packet */
547add85a1dSArchie Cobbs 	NG_SEND_DATA(error, priv->lower, m, meta);
548678f9e33SArchie Cobbs 
549da010626SArchie Cobbs 	/* Start receive ACK timer if data was sent and not already running */
550678f9e33SArchie Cobbs 	if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1)
551678f9e33SArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
552add85a1dSArchie Cobbs 	return (error);
553add85a1dSArchie Cobbs }
554add85a1dSArchie Cobbs 
555add85a1dSArchie Cobbs /*
556add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
557add85a1dSArchie Cobbs  */
558add85a1dSArchie Cobbs static int
559add85a1dSArchie Cobbs ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta)
560add85a1dSArchie Cobbs {
561add85a1dSArchie Cobbs 	const priv_p priv = node->private;
562add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
563add85a1dSArchie Cobbs 	struct greheader *gre;
564add85a1dSArchie Cobbs 	struct ip *ip;
565add85a1dSArchie Cobbs 	int error = 0;
566add85a1dSArchie Cobbs 
5679bee7adfSArchie Cobbs 	/* Update stats */
5689bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
5699bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
5709bee7adfSArchie Cobbs 
571add85a1dSArchie Cobbs 	/* Sanity check packet length */
572add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
5739bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
574add85a1dSArchie Cobbs bad:
575add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
576add85a1dSArchie Cobbs 		return (EINVAL);
577add85a1dSArchie Cobbs 	}
578add85a1dSArchie Cobbs 
579add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
580add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
581add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
582678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
583add85a1dSArchie Cobbs 		NG_FREE_META(meta);
584add85a1dSArchie Cobbs 		return (ENOBUFS);
585add85a1dSArchie Cobbs 	}
586add85a1dSArchie Cobbs 	ip = mtod(m, struct ip *);
587add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
588add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
589add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
590678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
591add85a1dSArchie Cobbs 			NG_FREE_META(meta);
592add85a1dSArchie Cobbs 			return (ENOBUFS);
593add85a1dSArchie Cobbs 		}
594add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
595add85a1dSArchie Cobbs 	}
596add85a1dSArchie Cobbs 	gre = (struct greheader *)((u_char *)ip + iphlen);
597add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
5989bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
5999bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
600add85a1dSArchie Cobbs 		goto bad;
6019bee7adfSArchie Cobbs 	}
602add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
603add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
604678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
605add85a1dSArchie Cobbs 			NG_FREE_META(meta);
606add85a1dSArchie Cobbs 			return (ENOBUFS);
607add85a1dSArchie Cobbs 		}
608add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
609add85a1dSArchie Cobbs 		gre = (struct greheader *)((u_char *)ip + iphlen);
610add85a1dSArchie Cobbs 	}
611add85a1dSArchie Cobbs 
612add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
613add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
614add85a1dSArchie Cobbs 	    - (iphlen + grelen + (u_int16_t)ntohs(gre->length));
6159bee7adfSArchie Cobbs 	if (extralen < 0) {
6169bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
617add85a1dSArchie Cobbs 		goto bad;
6189bee7adfSArchie Cobbs 	}
6199bee7adfSArchie Cobbs 	if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
6209bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
621add85a1dSArchie Cobbs 		goto bad;
6229bee7adfSArchie Cobbs 	}
6239bee7adfSArchie Cobbs 	if (ntohs(gre->cid) != priv->conf.cid) {
6249bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
625add85a1dSArchie Cobbs 		goto bad;
6269bee7adfSArchie Cobbs 	}
627add85a1dSArchie Cobbs 
628add85a1dSArchie Cobbs 	/* Look for peer ack */
629add85a1dSArchie Cobbs 	if (gre->hasAck) {
630add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
631add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
632add85a1dSArchie Cobbs 		const int index = ack - priv->recvAck - 1;
633add85a1dSArchie Cobbs 		const long sample = ng_pptpgre_time(node) - a->timeSent[index];
634add85a1dSArchie Cobbs 		long diff;
635add85a1dSArchie Cobbs 
636add85a1dSArchie Cobbs 		/* Sanity check ack value */
6379bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) {
6389bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
6399bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
6409bee7adfSArchie Cobbs 		}
6419bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0)
6429bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
643add85a1dSArchie Cobbs 		priv->recvAck = ack;
644add85a1dSArchie Cobbs 
645add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
646add85a1dSArchie Cobbs 		diff = sample - a->rtt;
647add85a1dSArchie Cobbs 		a->rtt += PPTP_ACK_ALPHA(diff);
648add85a1dSArchie Cobbs 		if (diff < 0)
649add85a1dSArchie Cobbs 			diff = -diff;
650add85a1dSArchie Cobbs 		a->dev += PPTP_ACK_BETA(diff - a->dev);
651e962a823SArchie Cobbs 		a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
652add85a1dSArchie Cobbs 		if (a->ato > PPTP_MAX_TIMEOUT)
653add85a1dSArchie Cobbs 			a->ato = PPTP_MAX_TIMEOUT;
654e962a823SArchie Cobbs 		if (a->ato < PPTP_MIN_TIMEOUT)
655e962a823SArchie Cobbs 			a->ato = PPTP_MIN_TIMEOUT;
656e962a823SArchie Cobbs 
657e962a823SArchie Cobbs 		/* Shift packet transmit times in our transmit window */
658add85a1dSArchie Cobbs 		ovbcopy(a->timeSent + index + 1, a->timeSent,
659add85a1dSArchie Cobbs 		    sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1)));
660e962a823SArchie Cobbs 
661e962a823SArchie Cobbs 		/* If we sent an entire window, increase window size by one */
6629bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0
6639bee7adfSArchie Cobbs 		    && a->xmitWin < PPTP_XMIT_WIN) {
664add85a1dSArchie Cobbs 			a->xmitWin++;
665add85a1dSArchie Cobbs 			a->winAck = ack + a->xmitWin;
666add85a1dSArchie Cobbs 		}
667add85a1dSArchie Cobbs 
6689bee7adfSArchie Cobbs 		/* Stop/(re)start receive ACK timer as necessary */
669678f9e33SArchie Cobbs 		a->rackTimerPtr = NULL;
670678f9e33SArchie Cobbs 		if (priv->recvAck != priv->xmitSeq)
671add85a1dSArchie Cobbs 			ng_pptpgre_start_recv_ack_timer(node);
672add85a1dSArchie Cobbs 	}
6739bee7adfSArchie Cobbs badAck:
674add85a1dSArchie Cobbs 
675add85a1dSArchie Cobbs 	/* See if frame contains any data */
676add85a1dSArchie Cobbs 	if (gre->hasSeq) {
677add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
678add85a1dSArchie Cobbs 		const u_int32_t seq = ntohl(gre->data[0]);
679add85a1dSArchie Cobbs 
680add85a1dSArchie Cobbs 		/* Sanity check sequence number */
6819bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) {
6829bee7adfSArchie Cobbs 			if (seq == priv->recvSeq)
6839bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
6849bee7adfSArchie Cobbs 			else
6859bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
6869bee7adfSArchie Cobbs 			goto bad;		/* out-of-order or dup */
6879bee7adfSArchie Cobbs 		}
688add85a1dSArchie Cobbs 		priv->recvSeq = seq;
689add85a1dSArchie Cobbs 
690add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
6913cd7db22SArchie Cobbs 		if (a->sackTimerPtr == NULL) {
692da010626SArchie Cobbs 			int maxWait;
693add85a1dSArchie Cobbs 
694da010626SArchie Cobbs 			/* Take 1/4 of the estimated round trip time */
695da010626SArchie Cobbs 			maxWait = (a->rtt >> 2);
696add85a1dSArchie Cobbs 
697678f9e33SArchie Cobbs 			/* If delayed ACK is disabled, send it now */
698da010626SArchie Cobbs 			if (!priv->conf.enableDelayedAck
699da010626SArchie Cobbs 			    || maxWait < PPTP_MIN_ACK_DELAY)
700add85a1dSArchie Cobbs 				ng_pptpgre_xmit(node, NULL, NULL);
701add85a1dSArchie Cobbs 			else {			/* send the ack later */
7023cd7db22SArchie Cobbs 				if (maxWait > PPTP_MAX_ACK_DELAY)
7033cd7db22SArchie Cobbs 					maxWait = PPTP_MAX_ACK_DELAY;
7043cd7db22SArchie Cobbs 				ng_pptpgre_start_send_ack_timer(node, maxWait);
705add85a1dSArchie Cobbs 			}
706add85a1dSArchie Cobbs 		}
707add85a1dSArchie Cobbs 
708add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
709add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
710add85a1dSArchie Cobbs 		if (extralen > 0)
711add85a1dSArchie Cobbs 			m_adj(m, -extralen);
712add85a1dSArchie Cobbs 
713add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
714add85a1dSArchie Cobbs 		NG_SEND_DATA(error, priv->upper, m, meta);
7159bee7adfSArchie Cobbs 	} else {
7169bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
717add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);		/* no data to deliver */
7189bee7adfSArchie Cobbs 	}
719add85a1dSArchie Cobbs 	return (error);
720add85a1dSArchie Cobbs }
721add85a1dSArchie Cobbs 
722add85a1dSArchie Cobbs /*************************************************************************
723add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
724add85a1dSArchie Cobbs *************************************************************************/
725add85a1dSArchie Cobbs 
726add85a1dSArchie Cobbs /*
7279bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
728add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
729add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
730add85a1dSArchie Cobbs  * recv ack timer, if any.
731add85a1dSArchie Cobbs  */
732add85a1dSArchie Cobbs static void
733add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node)
734add85a1dSArchie Cobbs {
735add85a1dSArchie Cobbs 	const priv_p priv = node->private;
736add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
737678f9e33SArchie Cobbs 	int remain, ticks;
738add85a1dSArchie Cobbs 
739add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
740add85a1dSArchie Cobbs 	   and reset the timer to that time. */
741da010626SArchie Cobbs 	KASSERT(a->rackTimerPtr == NULL, ("%s: rackTimer", __FUNCTION__));
742add85a1dSArchie Cobbs 	remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node);
743add85a1dSArchie Cobbs 	if (remain < 0)
744add85a1dSArchie Cobbs 		remain = 0;
745678f9e33SArchie Cobbs #ifdef DEBUG_RAT
746678f9e33SArchie Cobbs 	a->timerLength = remain;
747678f9e33SArchie Cobbs 	a->timerStart = ng_pptpgre_time(node);
748678f9e33SArchie Cobbs #endif
7499bee7adfSArchie Cobbs 
7503cd7db22SArchie Cobbs 	/* Start new timer */
7513cd7db22SArchie Cobbs 	MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
752678f9e33SArchie Cobbs 	if (a->rackTimerPtr == NULL) {
753678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
7543cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
755678f9e33SArchie Cobbs 	}
7563cd7db22SArchie Cobbs 	*a->rackTimerPtr = node;	/* insures the correct timeout event */
7579bee7adfSArchie Cobbs 	node->refs++;
758678f9e33SArchie Cobbs 
759678f9e33SArchie Cobbs 	/* Be conservative: timeout() can return up to 1 tick early */
760678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
7613cd7db22SArchie Cobbs 	a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout,
762678f9e33SArchie Cobbs 	    a->rackTimerPtr, ticks);
763add85a1dSArchie Cobbs }
764add85a1dSArchie Cobbs 
765add85a1dSArchie Cobbs /*
766add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
767add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
768add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
769add85a1dSArchie Cobbs  */
770add85a1dSArchie Cobbs static void
771add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg)
772add85a1dSArchie Cobbs {
7734164c447SArchie Cobbs 	int s = splnet();
7743cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
775add85a1dSArchie Cobbs 	const priv_p priv = node->private;
776add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
777add85a1dSArchie Cobbs 
7783cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
7793cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
7803cd7db22SArchie Cobbs 	KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__));
7813cd7db22SArchie Cobbs 	if ((node->flags & NG_INVALID) != 0) {	/* shutdown race condition */
7829bee7adfSArchie Cobbs 		ng_unref(node);
7839bee7adfSArchie Cobbs 		splx(s);
7849bee7adfSArchie Cobbs 		return;
7859bee7adfSArchie Cobbs 	}
7869bee7adfSArchie Cobbs 	ng_unref(node);
7873cd7db22SArchie Cobbs 	if (arg != a->rackTimerPtr) {	/* timer stopped race condition */
7883cd7db22SArchie Cobbs 		splx(s);
7893cd7db22SArchie Cobbs 		return;
7903cd7db22SArchie Cobbs 	}
7913cd7db22SArchie Cobbs 	a->rackTimerPtr = NULL;
7929bee7adfSArchie Cobbs 
793add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
7949bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
795add85a1dSArchie Cobbs 	a->rtt = PPTP_ACK_DELTA(a->rtt);
796add85a1dSArchie Cobbs 	a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
797add85a1dSArchie Cobbs 	if (a->ato > PPTP_MAX_TIMEOUT)
798add85a1dSArchie Cobbs 		a->ato = PPTP_MAX_TIMEOUT;
799e962a823SArchie Cobbs 	if (a->ato < PPTP_MIN_TIMEOUT)
800e962a823SArchie Cobbs 		a->ato = PPTP_MIN_TIMEOUT;
801add85a1dSArchie Cobbs 
802678f9e33SArchie Cobbs #ifdef DEBUG_RAT
803678f9e33SArchie Cobbs     log(LOG_DEBUG,
804678f9e33SArchie Cobbs 	"RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n",
805678f9e33SArchie Cobbs 	(int)ng_pptpgre_time(node), priv->recvAck + 1,
806678f9e33SArchie Cobbs 	(int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato);
807678f9e33SArchie Cobbs #endif
808678f9e33SArchie Cobbs 
809e962a823SArchie Cobbs 	/* Reset ack and sliding window */
810e962a823SArchie Cobbs 	priv->recvAck = priv->xmitSeq;		/* pretend we got the ack */
811e962a823SArchie Cobbs 	a->xmitWin = (a->xmitWin + 1) / 2;	/* shrink transmit window */
812e962a823SArchie Cobbs 	a->winAck = priv->recvAck + a->xmitWin;	/* reset win expand time */
8134164c447SArchie Cobbs 	splx(s);
814add85a1dSArchie Cobbs }
815add85a1dSArchie Cobbs 
816add85a1dSArchie Cobbs /*
8179bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
8189bee7adfSArchie Cobbs  * already running.
8199bee7adfSArchie Cobbs  */
8209bee7adfSArchie Cobbs static void
821da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout)
8229bee7adfSArchie Cobbs {
8239bee7adfSArchie Cobbs 	const priv_p priv = node->private;
8249bee7adfSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
825678f9e33SArchie Cobbs 	int ticks;
8269bee7adfSArchie Cobbs 
8273cd7db22SArchie Cobbs 	/* Start new timer */
8283cd7db22SArchie Cobbs 	KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __FUNCTION__));
8293cd7db22SArchie Cobbs 	MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
830678f9e33SArchie Cobbs 	if (a->sackTimerPtr == NULL) {
831678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
8323cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
833678f9e33SArchie Cobbs 	}
8343cd7db22SArchie Cobbs 	*a->sackTimerPtr = node;
8359bee7adfSArchie Cobbs 	node->refs++;
836678f9e33SArchie Cobbs 
837678f9e33SArchie Cobbs 	/* Be conservative: timeout() can return up to 1 tick early */
838678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
8393cd7db22SArchie Cobbs 	a->sackTimer = timeout(ng_pptpgre_send_ack_timeout,
840678f9e33SArchie Cobbs 	    a->sackTimerPtr, ticks);
8419bee7adfSArchie Cobbs }
8429bee7adfSArchie Cobbs 
8439bee7adfSArchie Cobbs /*
844add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
845add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
846add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
847add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
848add85a1dSArchie Cobbs  */
849add85a1dSArchie Cobbs static void
850add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg)
851add85a1dSArchie Cobbs {
8524164c447SArchie Cobbs 	int s = splnet();
8533cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
854add85a1dSArchie Cobbs 	const priv_p priv = node->private;
855add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
856add85a1dSArchie Cobbs 
8573cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
8583cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
8593cd7db22SArchie Cobbs 	KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__));
8603cd7db22SArchie Cobbs 	if ((node->flags & NG_INVALID) != 0) {	/* shutdown race condition */
8619bee7adfSArchie Cobbs 		ng_unref(node);
8629bee7adfSArchie Cobbs 		splx(s);
8639bee7adfSArchie Cobbs 		return;
8649bee7adfSArchie Cobbs 	}
8659bee7adfSArchie Cobbs 	ng_unref(node);
8663cd7db22SArchie Cobbs 	if (a->sackTimerPtr != arg) {	/* timer stopped race condition */
8673cd7db22SArchie Cobbs 		splx(s);
8683cd7db22SArchie Cobbs 		return;
8693cd7db22SArchie Cobbs 	}
8703cd7db22SArchie Cobbs 	a->sackTimerPtr = NULL;
8719bee7adfSArchie Cobbs 
8729bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
873add85a1dSArchie Cobbs   	ng_pptpgre_xmit(node, NULL, NULL);
8744164c447SArchie Cobbs 	splx(s);
875add85a1dSArchie Cobbs }
876add85a1dSArchie Cobbs 
877add85a1dSArchie Cobbs /*************************************************************************
878add85a1dSArchie Cobbs 		    MISC FUNCTIONS
879add85a1dSArchie Cobbs *************************************************************************/
880add85a1dSArchie Cobbs 
881add85a1dSArchie Cobbs /*
882add85a1dSArchie Cobbs  * Reset state
883add85a1dSArchie Cobbs  */
884add85a1dSArchie Cobbs static void
885add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node)
886add85a1dSArchie Cobbs {
887add85a1dSArchie Cobbs 	const priv_p priv = node->private;
888add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
889add85a1dSArchie Cobbs 
890add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
891add85a1dSArchie Cobbs 	a->ato = PPTP_MAX_TIMEOUT;
892add85a1dSArchie Cobbs 	a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
893add85a1dSArchie Cobbs 	if (a->rtt < PPTP_MIN_RTT)
894add85a1dSArchie Cobbs 		a->rtt = PPTP_MIN_RTT;
895add85a1dSArchie Cobbs 	a->dev = 0;
896add85a1dSArchie Cobbs 	a->xmitWin = (priv->conf.recvWin + 1) / 2;
897e962a823SArchie Cobbs 	if (a->xmitWin < 2)		/* often the first packet is lost */
898e962a823SArchie Cobbs 		a->xmitWin = 2;		/*   because the peer isn't ready */
899add85a1dSArchie Cobbs 	if (a->xmitWin > PPTP_XMIT_WIN)
900add85a1dSArchie Cobbs 		a->xmitWin = PPTP_XMIT_WIN;
901add85a1dSArchie Cobbs 	a->winAck = a->xmitWin;
902add85a1dSArchie Cobbs 
903add85a1dSArchie Cobbs 	/* Reset sequence numbers */
9043cd7db22SArchie Cobbs 	priv->recvSeq = ~0;
9053cd7db22SArchie Cobbs 	priv->recvAck = ~0;
9063cd7db22SArchie Cobbs 	priv->xmitSeq = ~0;
9073cd7db22SArchie Cobbs 	priv->xmitAck = ~0;
908add85a1dSArchie Cobbs 
909add85a1dSArchie Cobbs 	/* Reset start time */
9109bee7adfSArchie Cobbs 	getmicrouptime(&priv->startTime);
9119bee7adfSArchie Cobbs 
9129bee7adfSArchie Cobbs 	/* Reset stats */
9139bee7adfSArchie Cobbs 	bzero(&priv->stats, sizeof(priv->stats));
914add85a1dSArchie Cobbs 
915034d9dacSArchie Cobbs 	/* "Stop" timers */
9163cd7db22SArchie Cobbs 	a->sackTimerPtr = NULL;
9173cd7db22SArchie Cobbs 	a->rackTimerPtr = NULL;
918add85a1dSArchie Cobbs }
919add85a1dSArchie Cobbs 
920add85a1dSArchie Cobbs /*
921add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
922add85a1dSArchie Cobbs  */
923add85a1dSArchie Cobbs static pptptime_t
924add85a1dSArchie Cobbs ng_pptpgre_time(node_p node)
925add85a1dSArchie Cobbs {
926add85a1dSArchie Cobbs 	const priv_p priv = node->private;
927add85a1dSArchie Cobbs 	struct timeval tv;
928678f9e33SArchie Cobbs 	pptptime_t t;
929add85a1dSArchie Cobbs 
930678f9e33SArchie Cobbs 	microuptime(&tv);
931add85a1dSArchie Cobbs 	if (tv.tv_sec < priv->startTime.tv_sec
932add85a1dSArchie Cobbs 	    || (tv.tv_sec == priv->startTime.tv_sec
933add85a1dSArchie Cobbs 	      && tv.tv_usec < priv->startTime.tv_usec))
934add85a1dSArchie Cobbs 		return (0);
935add85a1dSArchie Cobbs 	timevalsub(&tv, &priv->startTime);
936678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
937678f9e33SArchie Cobbs 	t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
938678f9e33SArchie Cobbs 	return(t);
939add85a1dSArchie Cobbs }
940add85a1dSArchie Cobbs 
941