xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision e962a823087f27141470dd856cdd84e1d28cc557)
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  *
37add85a1dSArchie Cobbs  * Author: Archie Cobbs <archie@whistle.com>
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 #include <sys/socket.h>
64add85a1dSArchie Cobbs #include <sys/syslog.h>
65add85a1dSArchie Cobbs #include <sys/ctype.h>
66add85a1dSArchie Cobbs 
67add85a1dSArchie Cobbs #include <netinet/in.h>
68add85a1dSArchie Cobbs #include <netinet/in_systm.h>
69add85a1dSArchie Cobbs #include <netinet/ip.h>
70add85a1dSArchie Cobbs 
71add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
72add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
73add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
74add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
75add85a1dSArchie Cobbs 
76add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
77add85a1dSArchie Cobbs struct greheader {
78add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
79add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
80add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
81add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
82add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
83add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
84add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
85add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
86add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
87add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
88add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
89add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
90add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
91add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
92add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
93add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
94add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
95add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
96add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
97add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
98add85a1dSArchie Cobbs #else
99add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
100add85a1dSArchie Cobbs #endif
101add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
102add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
103add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
104add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
105add85a1dSArchie Cobbs };
106add85a1dSArchie Cobbs 
107add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
108add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
109add85a1dSArchie Cobbs 
110add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
111add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
112add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
113add85a1dSArchie Cobbs 
114add85a1dSArchie Cobbs /* Min and max packet length */
115add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
116add85a1dSArchie Cobbs 
117add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
118e962a823SArchie Cobbs #define PPTP_TIME_SCALE		1000			/* milliseconds */
119add85a1dSArchie Cobbs typedef u_int32_t		pptptime_t;
120add85a1dSArchie Cobbs 
121add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
122e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
123e962a823SArchie Cobbs #define PPTP_MIN_RTT		(PPTP_TIME_SCALE / 10)	/* 100 milliseconds */
124e962a823SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 100)	/* 10 milliseconds */
125add85a1dSArchie Cobbs #define PPTP_MAX_TIMEOUT	(10 * PPTP_TIME_SCALE)	/* 10 seconds */
126add85a1dSArchie Cobbs 
127e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
128add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x)	((x) >> 3)	/* alpha = 0.125 */
129add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x)	((x) >> 2)	/* beta = 0.25 */
130add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
131add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
132add85a1dSArchie Cobbs 
1339bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
1349bee7adfSArchie Cobbs 
135add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
136add85a1dSArchie Cobbs struct ng_pptpgre_ackp {
137add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
138add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
139add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
140add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
141add85a1dSArchie Cobbs 	struct callout_handle	sackTimer;	/* send ack timer */
142add85a1dSArchie Cobbs 	struct callout_handle	rackTimer;	/* recv ack timer */
1433cd7db22SArchie Cobbs 	node_p			*sackTimerPtr;	/* send ack timer pointer */
1443cd7db22SArchie Cobbs 	node_p			*rackTimerPtr;	/* recv ack timer pointer */
1453cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
146add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
147add85a1dSArchie Cobbs };
148add85a1dSArchie Cobbs 
149add85a1dSArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet
150add85a1dSArchie Cobbs    we can piggy-back the ACK off of.  These parameters determine the mimimum
151add85a1dSArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that. */
152add85a1dSArchie Cobbs #define PPTP_MAX_ACK_DELAY	((int) (0.25 * PPTP_TIME_SCALE))
153add85a1dSArchie Cobbs 
154add85a1dSArchie Cobbs /* Node private data */
155add85a1dSArchie Cobbs struct ng_pptpgre_private {
156add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
157add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
158add85a1dSArchie Cobbs 	struct ng_pptpgre_conf	conf;		/* configuration info */
159add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp	ackp;		/* packet transmit ack state */
160add85a1dSArchie Cobbs 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
161add85a1dSArchie Cobbs 	u_int32_t		xmitSeq;	/* last seq # we sent */
162add85a1dSArchie Cobbs 	u_int32_t		recvAck;	/* last seq # peer ack'd */
163add85a1dSArchie Cobbs 	u_int32_t		xmitAck;	/* last seq # we ack'd */
164add85a1dSArchie Cobbs 	struct timeval		startTime;	/* time node was created */
1659bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
166add85a1dSArchie Cobbs };
167add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
168add85a1dSArchie Cobbs 
169add85a1dSArchie Cobbs /* Netgraph node methods */
170add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
171add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
172add85a1dSArchie Cobbs static ng_shutdown_t	ng_pptpgre_rmnode;
173add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
174add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
175add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
176add85a1dSArchie Cobbs 
177add85a1dSArchie Cobbs /* Helper functions */
178add85a1dSArchie Cobbs static int	ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta);
179add85a1dSArchie Cobbs static int	ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta);
1809bee7adfSArchie Cobbs static void	ng_pptpgre_start_send_ack_timer(node_p node, long ackTimeout);
181add85a1dSArchie Cobbs static void	ng_pptpgre_start_recv_ack_timer(node_p node);
182add85a1dSArchie Cobbs static void	ng_pptpgre_recv_ack_timeout(void *arg);
183add85a1dSArchie Cobbs static void	ng_pptpgre_send_ack_timeout(void *arg);
184add85a1dSArchie Cobbs static void	ng_pptpgre_reset(node_p node);
185add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node);
186add85a1dSArchie Cobbs 
187add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
188add85a1dSArchie Cobbs static const struct ng_parse_struct_info
189add85a1dSArchie Cobbs 	ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO;
190add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
191add85a1dSArchie Cobbs 	&ng_parse_struct_type,
192add85a1dSArchie Cobbs 	&ng_pptpgre_conf_type_info,
193add85a1dSArchie Cobbs };
194add85a1dSArchie Cobbs 
1959bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
1969bee7adfSArchie Cobbs static const struct ng_parse_struct_info
1979bee7adfSArchie Cobbs 	ng_pptpgre_stats_type_info = NG_PPTPGRE_STATS_TYPE_INFO;
1989bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
1999bee7adfSArchie Cobbs 	&ng_parse_struct_type,
2009bee7adfSArchie Cobbs 	&ng_pptpgre_stats_type_info
2019bee7adfSArchie Cobbs };
2029bee7adfSArchie Cobbs 
203add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
204add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
205add85a1dSArchie Cobbs 	{
206add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
207add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
208add85a1dSArchie Cobbs 	  "setconfig",
209add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
210add85a1dSArchie Cobbs 	  NULL
211add85a1dSArchie Cobbs 	},
212add85a1dSArchie Cobbs 	{
213add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
214add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
215add85a1dSArchie Cobbs 	  "getconfig",
216add85a1dSArchie Cobbs 	  NULL,
217add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
218add85a1dSArchie Cobbs 	},
2199bee7adfSArchie Cobbs 	{
2209bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2219bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2229bee7adfSArchie Cobbs 	  "getstats",
2239bee7adfSArchie Cobbs 	  NULL,
2249bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2259bee7adfSArchie Cobbs 	},
2269bee7adfSArchie Cobbs 	{
2279bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2289bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2299bee7adfSArchie Cobbs 	  "clrstats",
2309bee7adfSArchie Cobbs 	  NULL,
2319bee7adfSArchie Cobbs 	  NULL
2329bee7adfSArchie Cobbs 	},
2339bee7adfSArchie Cobbs 	{
2349bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2359bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2369bee7adfSArchie Cobbs 	  "getclrstats",
2379bee7adfSArchie Cobbs 	  NULL,
2389bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2399bee7adfSArchie Cobbs 	},
240add85a1dSArchie Cobbs 	{ 0 }
241add85a1dSArchie Cobbs };
242add85a1dSArchie Cobbs 
243add85a1dSArchie Cobbs /* Node type descriptor */
244add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
245add85a1dSArchie Cobbs 	NG_VERSION,
246add85a1dSArchie Cobbs 	NG_PPTPGRE_NODE_TYPE,
247add85a1dSArchie Cobbs 	NULL,
248add85a1dSArchie Cobbs 	ng_pptpgre_constructor,
249add85a1dSArchie Cobbs 	ng_pptpgre_rcvmsg,
250add85a1dSArchie Cobbs 	ng_pptpgre_rmnode,
251add85a1dSArchie Cobbs 	ng_pptpgre_newhook,
252add85a1dSArchie Cobbs 	NULL,
253add85a1dSArchie Cobbs 	NULL,
254add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
255add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
256add85a1dSArchie Cobbs 	ng_pptpgre_disconnect,
257add85a1dSArchie Cobbs 	ng_pptpgre_cmdlist
258add85a1dSArchie Cobbs };
259add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
260add85a1dSArchie Cobbs 
261add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
262add85a1dSArchie Cobbs 
263add85a1dSArchie Cobbs /************************************************************************
264add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
265add85a1dSArchie Cobbs  ************************************************************************/
266add85a1dSArchie Cobbs 
267add85a1dSArchie Cobbs /*
268add85a1dSArchie Cobbs  * Node type constructor
269add85a1dSArchie Cobbs  */
270add85a1dSArchie Cobbs static int
271add85a1dSArchie Cobbs ng_pptpgre_constructor(node_p *nodep)
272add85a1dSArchie Cobbs {
273add85a1dSArchie Cobbs 	priv_p priv;
274add85a1dSArchie Cobbs 	int error;
275add85a1dSArchie Cobbs 
276add85a1dSArchie Cobbs 	/* Allocate private structure */
277add85a1dSArchie Cobbs 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
278add85a1dSArchie Cobbs 	if (priv == NULL)
279add85a1dSArchie Cobbs 		return (ENOMEM);
280add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
281add85a1dSArchie Cobbs 
282add85a1dSArchie Cobbs 	/* Call generic node constructor */
283add85a1dSArchie Cobbs 	if ((error = ng_make_node_common(&ng_pptpgre_typestruct, nodep))) {
284add85a1dSArchie Cobbs 		FREE(priv, M_NETGRAPH);
285add85a1dSArchie Cobbs 		return (error);
286add85a1dSArchie Cobbs 	}
287add85a1dSArchie Cobbs 	(*nodep)->private = priv;
288add85a1dSArchie Cobbs 
289add85a1dSArchie Cobbs 	/* Initialize state */
290add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.sackTimer);
291add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.rackTimer);
292add85a1dSArchie Cobbs 
293add85a1dSArchie Cobbs 	/* Done */
294add85a1dSArchie Cobbs 	return (0);
295add85a1dSArchie Cobbs }
296add85a1dSArchie Cobbs 
297add85a1dSArchie Cobbs /*
298add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
299add85a1dSArchie Cobbs  */
300add85a1dSArchie Cobbs static int
301add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
302add85a1dSArchie Cobbs {
303add85a1dSArchie Cobbs 	const priv_p priv = node->private;
304add85a1dSArchie Cobbs 	hook_p *hookPtr;
305add85a1dSArchie Cobbs 
306add85a1dSArchie Cobbs 	/* Check hook name */
307add85a1dSArchie Cobbs 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0)
308add85a1dSArchie Cobbs 		hookPtr = &priv->upper;
309add85a1dSArchie Cobbs 	else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0)
310add85a1dSArchie Cobbs 		hookPtr = &priv->lower;
311add85a1dSArchie Cobbs 	else
312add85a1dSArchie Cobbs 		return (EINVAL);
313add85a1dSArchie Cobbs 
314add85a1dSArchie Cobbs 	/* See if already connected */
315add85a1dSArchie Cobbs 	if (*hookPtr != NULL)
316add85a1dSArchie Cobbs 		return (EISCONN);
317add85a1dSArchie Cobbs 
318add85a1dSArchie Cobbs 	/* OK */
319add85a1dSArchie Cobbs 	*hookPtr = hook;
320add85a1dSArchie Cobbs 	return (0);
321add85a1dSArchie Cobbs }
322add85a1dSArchie Cobbs 
323add85a1dSArchie Cobbs /*
324add85a1dSArchie Cobbs  * Receive a control message.
325add85a1dSArchie Cobbs  */
326add85a1dSArchie Cobbs static int
327add85a1dSArchie Cobbs ng_pptpgre_rcvmsg(node_p node, struct ng_mesg *msg,
328a4ec03cfSJulian Elischer 	      const char *raddr, struct ng_mesg **rptr, hook_p lasthook)
329add85a1dSArchie Cobbs {
330add85a1dSArchie Cobbs 	const priv_p priv = node->private;
331add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
332add85a1dSArchie Cobbs 	int error = 0;
333add85a1dSArchie Cobbs 
334add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
335add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
336add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
337add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
338add85a1dSArchie Cobbs 		    {
339add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
340add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
341add85a1dSArchie Cobbs 
342add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
343add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
344add85a1dSArchie Cobbs 				ERROUT(EINVAL);
345add85a1dSArchie Cobbs 			ng_pptpgre_reset(node);		/* reset on configure */
346add85a1dSArchie Cobbs 			priv->conf = *newConf;
347add85a1dSArchie Cobbs 			break;
348add85a1dSArchie Cobbs 		    }
349add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
350add85a1dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
351add85a1dSArchie Cobbs 			if (resp == NULL)
352add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
353add85a1dSArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
354add85a1dSArchie Cobbs 			break;
3559bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
3569bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
3579bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
3589bee7adfSArchie Cobbs 		    {
3599bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
3609bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3619bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
3629bee7adfSArchie Cobbs 				if (resp == NULL)
3639bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
3649bee7adfSArchie Cobbs 				bcopy(&priv->stats,
3659bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
3669bee7adfSArchie Cobbs 			}
3679bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
3689bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
3699bee7adfSArchie Cobbs 			break;
3709bee7adfSArchie Cobbs 		    }
371add85a1dSArchie Cobbs 		default:
372add85a1dSArchie Cobbs 			error = EINVAL;
373add85a1dSArchie Cobbs 			break;
374add85a1dSArchie Cobbs 		}
375add85a1dSArchie Cobbs 		break;
376add85a1dSArchie Cobbs 	default:
377add85a1dSArchie Cobbs 		error = EINVAL;
378add85a1dSArchie Cobbs 		break;
379add85a1dSArchie Cobbs 	}
380add85a1dSArchie Cobbs 	if (rptr)
381add85a1dSArchie Cobbs 		*rptr = resp;
382add85a1dSArchie Cobbs 	else if (resp)
383add85a1dSArchie Cobbs 		FREE(resp, M_NETGRAPH);
384add85a1dSArchie Cobbs 
385add85a1dSArchie Cobbs done:
386add85a1dSArchie Cobbs 	FREE(msg, M_NETGRAPH);
387add85a1dSArchie Cobbs 	return (error);
388add85a1dSArchie Cobbs }
389add85a1dSArchie Cobbs 
390add85a1dSArchie Cobbs /*
391add85a1dSArchie Cobbs  * Receive incoming data on a hook.
392add85a1dSArchie Cobbs  */
393add85a1dSArchie Cobbs static int
394a4ec03cfSJulian Elischer ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
395a4ec03cfSJulian Elischer 		struct mbuf **ret_m, meta_p *ret_meta)
396add85a1dSArchie Cobbs {
397add85a1dSArchie Cobbs 	const node_p node = hook->node;
398add85a1dSArchie Cobbs 	const priv_p priv = node->private;
399add85a1dSArchie Cobbs 
400add85a1dSArchie Cobbs 	/* If not configured, reject */
401add85a1dSArchie Cobbs 	if (!priv->conf.enabled) {
402add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
403add85a1dSArchie Cobbs 		return (ENXIO);
404add85a1dSArchie Cobbs 	}
405add85a1dSArchie Cobbs 
406add85a1dSArchie Cobbs 	/* Treat as xmit or recv data */
407add85a1dSArchie Cobbs 	if (hook == priv->upper)
408add85a1dSArchie Cobbs 		return ng_pptpgre_xmit(node, m, meta);
409add85a1dSArchie Cobbs 	if (hook == priv->lower)
410add85a1dSArchie Cobbs 		return ng_pptpgre_recv(node, m, meta);
411add85a1dSArchie Cobbs 	panic("%s: weird hook", __FUNCTION__);
412add85a1dSArchie Cobbs }
413add85a1dSArchie Cobbs 
414add85a1dSArchie Cobbs /*
415add85a1dSArchie Cobbs  * Destroy node
416add85a1dSArchie Cobbs  */
417add85a1dSArchie Cobbs static int
418add85a1dSArchie Cobbs ng_pptpgre_rmnode(node_p node)
419add85a1dSArchie Cobbs {
420add85a1dSArchie Cobbs 	const priv_p priv = node->private;
421add85a1dSArchie Cobbs 
4223cd7db22SArchie Cobbs 	/* Reset node */
423add85a1dSArchie Cobbs 	ng_pptpgre_reset(node);
424add85a1dSArchie Cobbs 
425add85a1dSArchie Cobbs 	/* Take down netgraph node */
426add85a1dSArchie Cobbs 	node->flags |= NG_INVALID;
427add85a1dSArchie Cobbs 	ng_cutlinks(node);
428add85a1dSArchie Cobbs 	ng_unname(node);
429add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
430add85a1dSArchie Cobbs 	FREE(priv, M_NETGRAPH);
431add85a1dSArchie Cobbs 	node->private = NULL;
432add85a1dSArchie Cobbs 	ng_unref(node);
433add85a1dSArchie Cobbs 	return (0);
434add85a1dSArchie Cobbs }
435add85a1dSArchie Cobbs 
436add85a1dSArchie Cobbs /*
437add85a1dSArchie Cobbs  * Hook disconnection
438add85a1dSArchie Cobbs  */
439add85a1dSArchie Cobbs static int
440add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook)
441add85a1dSArchie Cobbs {
442add85a1dSArchie Cobbs 	const node_p node = hook->node;
443add85a1dSArchie Cobbs 	const priv_p priv = node->private;
444add85a1dSArchie Cobbs 
445add85a1dSArchie Cobbs 	/* Zero out hook pointer */
446add85a1dSArchie Cobbs 	if (hook == priv->upper)
447add85a1dSArchie Cobbs 		priv->upper = NULL;
448add85a1dSArchie Cobbs 	else if (hook == priv->lower)
449add85a1dSArchie Cobbs 		priv->lower = NULL;
450add85a1dSArchie Cobbs 	else
451add85a1dSArchie Cobbs 		panic("%s: unknown hook", __FUNCTION__);
452add85a1dSArchie Cobbs 
453add85a1dSArchie Cobbs 	/* Go away if no longer connected to anything */
454add85a1dSArchie Cobbs 	if (node->numhooks == 0)
455add85a1dSArchie Cobbs 		ng_rmnode(node);
456add85a1dSArchie Cobbs 	return (0);
457add85a1dSArchie Cobbs }
458add85a1dSArchie Cobbs 
459add85a1dSArchie Cobbs /*************************************************************************
460add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
461add85a1dSArchie Cobbs *************************************************************************/
462add85a1dSArchie Cobbs 
463add85a1dSArchie Cobbs /*
464add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
465add85a1dSArchie Cobbs  */
466add85a1dSArchie Cobbs static int
467add85a1dSArchie Cobbs ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta)
468add85a1dSArchie Cobbs {
469add85a1dSArchie Cobbs 	const priv_p priv = node->private;
470add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
471add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
472add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
473add85a1dSArchie Cobbs 	int grelen, error;
474add85a1dSArchie Cobbs 
4759bee7adfSArchie Cobbs 	/* Check if there's data */
4769bee7adfSArchie Cobbs 	if (m != NULL) {
4779bee7adfSArchie Cobbs 
478add85a1dSArchie Cobbs 		/* Is our transmit window full? */
4799bee7adfSArchie Cobbs 		if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck)
4809bee7adfSArchie Cobbs 		      >= a->xmitWin) {
4819bee7adfSArchie Cobbs 			priv->stats.xmitDrops++;
482add85a1dSArchie Cobbs 			NG_FREE_DATA(m, meta);
483add85a1dSArchie Cobbs 			return (ENOBUFS);
484add85a1dSArchie Cobbs 		}
485add85a1dSArchie Cobbs 
486add85a1dSArchie Cobbs 		/* Sanity check frame length */
487add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
4889bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
489add85a1dSArchie Cobbs 			NG_FREE_DATA(m, meta);
490add85a1dSArchie Cobbs 			return (EMSGSIZE);
491add85a1dSArchie Cobbs 		}
4929bee7adfSArchie Cobbs 	} else
4939bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
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;
503add85a1dSArchie Cobbs 		a->timeSent[priv->xmitSeq - priv->recvAck]
504add85a1dSArchie Cobbs 		    = ng_pptpgre_time(node);
505add85a1dSArchie Cobbs 		priv->xmitSeq++;
506add85a1dSArchie Cobbs 		gre->data[0] = htonl(priv->xmitSeq);
507add85a1dSArchie Cobbs 		if (priv->xmitSeq == priv->recvAck + 1)
508add85a1dSArchie Cobbs 			ng_pptpgre_start_recv_ack_timer(node);
509add85a1dSArchie Cobbs 	}
510add85a1dSArchie Cobbs 
511add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
5129bee7adfSArchie Cobbs 	if (PPTP_SEQ_DIFF(priv->xmitAck, priv->recvSeq) < 0) {
513add85a1dSArchie Cobbs 		gre->hasAck = 1;
514add85a1dSArchie Cobbs 		priv->xmitAck = priv->recvSeq;
515add85a1dSArchie Cobbs 		gre->data[gre->hasSeq] = htonl(priv->xmitAck);
5163cd7db22SArchie Cobbs 		a->sackTimerPtr = NULL;
517add85a1dSArchie 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) {
522add85a1dSArchie Cobbs 		MGETHDR(m, M_DONTWAIT, MT_DATA);
523add85a1dSArchie Cobbs 		if (m == NULL) {
524add85a1dSArchie Cobbs 			NG_FREE_META(meta);
525add85a1dSArchie Cobbs 			return (ENOBUFS);
526add85a1dSArchie Cobbs 		}
527add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
528add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
529add85a1dSArchie Cobbs 	} else {
530add85a1dSArchie Cobbs 		M_PREPEND(m, grelen, M_NOWAIT);
531add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
532add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
533add85a1dSArchie Cobbs 			NG_FREE_META(meta);
534add85a1dSArchie Cobbs 			return (ENOBUFS);
535add85a1dSArchie Cobbs 		}
536add85a1dSArchie Cobbs 	}
537add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
538add85a1dSArchie Cobbs 
5399bee7adfSArchie Cobbs 	/* Update stats */
5409bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
5419bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
5429bee7adfSArchie Cobbs 
543add85a1dSArchie Cobbs 	/* Deliver packet */
544add85a1dSArchie Cobbs 	NG_SEND_DATA(error, priv->lower, m, meta);
545add85a1dSArchie Cobbs 	return (error);
546add85a1dSArchie Cobbs }
547add85a1dSArchie Cobbs 
548add85a1dSArchie Cobbs /*
549add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
550add85a1dSArchie Cobbs  */
551add85a1dSArchie Cobbs static int
552add85a1dSArchie Cobbs ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta)
553add85a1dSArchie Cobbs {
554add85a1dSArchie Cobbs 	const priv_p priv = node->private;
555add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
556add85a1dSArchie Cobbs 	struct greheader *gre;
557add85a1dSArchie Cobbs 	struct ip *ip;
558add85a1dSArchie Cobbs 	int error = 0;
559add85a1dSArchie Cobbs 
5609bee7adfSArchie Cobbs 	/* Update stats */
5619bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
5629bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
5639bee7adfSArchie Cobbs 
564add85a1dSArchie Cobbs 	/* Sanity check packet length */
565add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
5669bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
567add85a1dSArchie Cobbs bad:
568add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
569add85a1dSArchie Cobbs 		return (EINVAL);
570add85a1dSArchie Cobbs 	}
571add85a1dSArchie Cobbs 
572add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
573add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
574add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
575add85a1dSArchie Cobbs 		NG_FREE_META(meta);
576add85a1dSArchie Cobbs 		return (ENOBUFS);
577add85a1dSArchie Cobbs 	}
578add85a1dSArchie Cobbs 	ip = mtod(m, struct ip *);
579add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
580add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
581add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
582add85a1dSArchie Cobbs 			NG_FREE_META(meta);
583add85a1dSArchie Cobbs 			return (ENOBUFS);
584add85a1dSArchie Cobbs 		}
585add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
586add85a1dSArchie Cobbs 	}
587add85a1dSArchie Cobbs 	gre = (struct greheader *)((u_char *)ip + iphlen);
588add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
5899bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
5909bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
591add85a1dSArchie Cobbs 		goto bad;
5929bee7adfSArchie Cobbs 	}
593add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
594add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
595add85a1dSArchie Cobbs 			NG_FREE_META(meta);
596add85a1dSArchie Cobbs 			return (ENOBUFS);
597add85a1dSArchie Cobbs 		}
598add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
599add85a1dSArchie Cobbs 		gre = (struct greheader *)((u_char *)ip + iphlen);
600add85a1dSArchie Cobbs 	}
601add85a1dSArchie Cobbs 
602add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
603add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
604add85a1dSArchie Cobbs 	    - (iphlen + grelen + (u_int16_t)ntohs(gre->length));
6059bee7adfSArchie Cobbs 	if (extralen < 0) {
6069bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
607add85a1dSArchie Cobbs 		goto bad;
6089bee7adfSArchie Cobbs 	}
6099bee7adfSArchie Cobbs 	if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
6109bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
611add85a1dSArchie Cobbs 		goto bad;
6129bee7adfSArchie Cobbs 	}
6139bee7adfSArchie Cobbs 	if (ntohs(gre->cid) != priv->conf.cid) {
6149bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
615add85a1dSArchie Cobbs 		goto bad;
6169bee7adfSArchie Cobbs 	}
617add85a1dSArchie Cobbs 
618add85a1dSArchie Cobbs 	/* Look for peer ack */
619add85a1dSArchie Cobbs 	if (gre->hasAck) {
620add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
621add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
622add85a1dSArchie Cobbs 		const int index = ack - priv->recvAck - 1;
623add85a1dSArchie Cobbs 		const long sample = ng_pptpgre_time(node) - a->timeSent[index];
624add85a1dSArchie Cobbs 		long diff;
625add85a1dSArchie Cobbs 
626add85a1dSArchie Cobbs 		/* Sanity check ack value */
6279bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) {
6289bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
6299bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
6309bee7adfSArchie Cobbs 		}
6319bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0)
6329bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
633add85a1dSArchie Cobbs 		priv->recvAck = ack;
634add85a1dSArchie Cobbs 
635add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
636add85a1dSArchie Cobbs 		diff = sample - a->rtt;
637add85a1dSArchie Cobbs 		a->rtt += PPTP_ACK_ALPHA(diff);
638add85a1dSArchie Cobbs 		if (diff < 0)
639add85a1dSArchie Cobbs 			diff = -diff;
640add85a1dSArchie Cobbs 		a->dev += PPTP_ACK_BETA(diff - a->dev);
641e962a823SArchie Cobbs 		a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
642add85a1dSArchie Cobbs 		if (a->ato > PPTP_MAX_TIMEOUT)
643add85a1dSArchie Cobbs 			a->ato = PPTP_MAX_TIMEOUT;
644e962a823SArchie Cobbs 		if (a->ato < PPTP_MIN_TIMEOUT)
645e962a823SArchie Cobbs 			a->ato = PPTP_MIN_TIMEOUT;
646e962a823SArchie Cobbs 
647e962a823SArchie Cobbs 		/* Shift packet transmit times in our transmit window */
648add85a1dSArchie Cobbs 		ovbcopy(a->timeSent + index + 1, a->timeSent,
649add85a1dSArchie Cobbs 		    sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1)));
650e962a823SArchie Cobbs 
651e962a823SArchie Cobbs 		/* If we sent an entire window, increase window size by one */
6529bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0
6539bee7adfSArchie Cobbs 		    && a->xmitWin < PPTP_XMIT_WIN) {
654add85a1dSArchie Cobbs 			a->xmitWin++;
655add85a1dSArchie Cobbs 			a->winAck = ack + a->xmitWin;
656add85a1dSArchie Cobbs 		}
657add85a1dSArchie Cobbs 
6589bee7adfSArchie Cobbs 		/* Stop/(re)start receive ACK timer as necessary */
659add85a1dSArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
660add85a1dSArchie Cobbs 	}
6619bee7adfSArchie Cobbs badAck:
662add85a1dSArchie Cobbs 
663add85a1dSArchie Cobbs 	/* See if frame contains any data */
664add85a1dSArchie Cobbs 	if (gre->hasSeq) {
665add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
666add85a1dSArchie Cobbs 		const u_int32_t seq = ntohl(gre->data[0]);
667add85a1dSArchie Cobbs 
668add85a1dSArchie Cobbs 		/* Sanity check sequence number */
6699bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) {
6709bee7adfSArchie Cobbs 			if (seq == priv->recvSeq)
6719bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
6729bee7adfSArchie Cobbs 			else
6739bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
6749bee7adfSArchie Cobbs 			goto bad;		/* out-of-order or dup */
6759bee7adfSArchie Cobbs 		}
676add85a1dSArchie Cobbs 		priv->recvSeq = seq;
677add85a1dSArchie Cobbs 
678add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
6793cd7db22SArchie Cobbs 		if (a->sackTimerPtr == NULL) {
6803cd7db22SArchie Cobbs 			long maxWait;
681add85a1dSArchie Cobbs 
682add85a1dSArchie Cobbs 			/* Take half of the estimated round trip time */
6833cd7db22SArchie Cobbs 			maxWait = (a->rtt >> 1);
684add85a1dSArchie Cobbs 
685add85a1dSArchie Cobbs 			/* If too soon, just send one right now */
686add85a1dSArchie Cobbs 			if (!priv->conf.enableDelayedAck)
687add85a1dSArchie Cobbs 				ng_pptpgre_xmit(node, NULL, NULL);
688add85a1dSArchie Cobbs 			else {			/* send the ack later */
6893cd7db22SArchie Cobbs 				if (maxWait > PPTP_MAX_ACK_DELAY)
6903cd7db22SArchie Cobbs 					maxWait = PPTP_MAX_ACK_DELAY;
6913cd7db22SArchie Cobbs 				ng_pptpgre_start_send_ack_timer(node, maxWait);
692add85a1dSArchie Cobbs 			}
693add85a1dSArchie Cobbs 		}
694add85a1dSArchie Cobbs 
695add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
696add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
697add85a1dSArchie Cobbs 		if (extralen > 0)
698add85a1dSArchie Cobbs 			m_adj(m, -extralen);
699add85a1dSArchie Cobbs 
700add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
701add85a1dSArchie Cobbs 		NG_SEND_DATA(error, priv->upper, m, meta);
7029bee7adfSArchie Cobbs 	} else {
7039bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
704add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);		/* no data to deliver */
7059bee7adfSArchie Cobbs 	}
706add85a1dSArchie Cobbs 	return (error);
707add85a1dSArchie Cobbs }
708add85a1dSArchie Cobbs 
709add85a1dSArchie Cobbs /*************************************************************************
710add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
711add85a1dSArchie Cobbs *************************************************************************/
712add85a1dSArchie Cobbs 
713add85a1dSArchie Cobbs /*
7149bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
715add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
716add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
717add85a1dSArchie Cobbs  * recv ack timer, if any.
718add85a1dSArchie Cobbs  */
719add85a1dSArchie Cobbs static void
720add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node)
721add85a1dSArchie Cobbs {
722add85a1dSArchie Cobbs 	const priv_p priv = node->private;
723add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
724add85a1dSArchie Cobbs 	int remain;
725add85a1dSArchie Cobbs 
7263cd7db22SArchie Cobbs 	/* "Stop" current recv ack timer, if any */
7273cd7db22SArchie Cobbs 	a->rackTimerPtr = NULL;
7289bee7adfSArchie Cobbs 
7299bee7adfSArchie Cobbs 	/* Are we waiting for an acknowlegement? */
730add85a1dSArchie Cobbs 	if (priv->recvAck == priv->xmitSeq)
731add85a1dSArchie Cobbs 		return;
732add85a1dSArchie Cobbs 
733add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
734add85a1dSArchie Cobbs 	   and reset the timer to that time. */
735add85a1dSArchie Cobbs 	remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node);
736add85a1dSArchie Cobbs 	if (remain < 0)
737add85a1dSArchie Cobbs 		remain = 0;
7389bee7adfSArchie Cobbs 
7393cd7db22SArchie Cobbs 	/* Start new timer */
7403cd7db22SArchie Cobbs 	MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
7413cd7db22SArchie Cobbs 	if (a->rackTimerPtr == NULL)
7423cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
7433cd7db22SArchie Cobbs 	*a->rackTimerPtr = node;	/* insures the correct timeout event */
7449bee7adfSArchie Cobbs 	node->refs++;
7453cd7db22SArchie Cobbs 	a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout,
7463cd7db22SArchie Cobbs 	    a->rackTimerPtr, remain * hz / PPTP_TIME_SCALE);
747add85a1dSArchie Cobbs }
748add85a1dSArchie Cobbs 
749add85a1dSArchie Cobbs /*
750add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
751add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
752add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
753add85a1dSArchie Cobbs  */
754add85a1dSArchie Cobbs static void
755add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg)
756add85a1dSArchie Cobbs {
7574164c447SArchie Cobbs 	int s = splnet();
7583cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
759add85a1dSArchie Cobbs 	const priv_p priv = node->private;
760add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
761add85a1dSArchie Cobbs 
7623cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
7633cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
7643cd7db22SArchie Cobbs 	KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__));
7653cd7db22SArchie Cobbs 	if ((node->flags & NG_INVALID) != 0) {	/* shutdown race condition */
7669bee7adfSArchie Cobbs 		ng_unref(node);
7679bee7adfSArchie Cobbs 		splx(s);
7689bee7adfSArchie Cobbs 		return;
7699bee7adfSArchie Cobbs 	}
7709bee7adfSArchie Cobbs 	ng_unref(node);
7713cd7db22SArchie Cobbs 	if (arg != a->rackTimerPtr) {	/* timer stopped race condition */
7723cd7db22SArchie Cobbs 		splx(s);
7733cd7db22SArchie Cobbs 		return;
7743cd7db22SArchie Cobbs 	}
7753cd7db22SArchie Cobbs 	a->rackTimerPtr = NULL;
7769bee7adfSArchie Cobbs 
777add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
7789bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
779add85a1dSArchie Cobbs 	a->rtt = PPTP_ACK_DELTA(a->rtt);
780add85a1dSArchie Cobbs 	a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
781add85a1dSArchie Cobbs 	if (a->ato > PPTP_MAX_TIMEOUT)
782add85a1dSArchie Cobbs 		a->ato = PPTP_MAX_TIMEOUT;
783e962a823SArchie Cobbs 	if (a->ato < PPTP_MIN_TIMEOUT)
784e962a823SArchie Cobbs 		a->ato = PPTP_MIN_TIMEOUT;
785add85a1dSArchie Cobbs 
786e962a823SArchie Cobbs 	/* Reset ack and sliding window */
787e962a823SArchie Cobbs 	priv->recvAck = priv->xmitSeq;		/* pretend we got the ack */
788e962a823SArchie Cobbs 	a->xmitWin = (a->xmitWin + 1) / 2;	/* shrink transmit window */
789e962a823SArchie Cobbs 	a->winAck = priv->recvAck + a->xmitWin;	/* reset win expand time */
7904164c447SArchie Cobbs 	splx(s);
791add85a1dSArchie Cobbs }
792add85a1dSArchie Cobbs 
793add85a1dSArchie Cobbs /*
7949bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
7959bee7adfSArchie Cobbs  * already running.
7969bee7adfSArchie Cobbs  */
7979bee7adfSArchie Cobbs static void
7989bee7adfSArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, long ackTimeout)
7999bee7adfSArchie Cobbs {
8009bee7adfSArchie Cobbs 	const priv_p priv = node->private;
8019bee7adfSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
8029bee7adfSArchie Cobbs 
8033cd7db22SArchie Cobbs 	/* Start new timer */
8043cd7db22SArchie Cobbs 	KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __FUNCTION__));
8053cd7db22SArchie Cobbs 	MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
8063cd7db22SArchie Cobbs 	if (a->sackTimerPtr == NULL)
8073cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
8083cd7db22SArchie Cobbs 	*a->sackTimerPtr = node;
8099bee7adfSArchie Cobbs 	node->refs++;
8103cd7db22SArchie Cobbs 	a->sackTimer = timeout(ng_pptpgre_send_ack_timeout,
8113cd7db22SArchie Cobbs 	    a->sackTimerPtr, ackTimeout * hz / PPTP_TIME_SCALE);
8129bee7adfSArchie Cobbs }
8139bee7adfSArchie Cobbs 
8149bee7adfSArchie Cobbs /*
815add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
816add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
817add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
818add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
819add85a1dSArchie Cobbs  */
820add85a1dSArchie Cobbs static void
821add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg)
822add85a1dSArchie Cobbs {
8234164c447SArchie Cobbs 	int s = splnet();
8243cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
825add85a1dSArchie Cobbs 	const priv_p priv = node->private;
826add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
827add85a1dSArchie Cobbs 
8283cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
8293cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
8303cd7db22SArchie Cobbs 	KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__));
8313cd7db22SArchie Cobbs 	if ((node->flags & NG_INVALID) != 0) {	/* shutdown race condition */
8329bee7adfSArchie Cobbs 		ng_unref(node);
8339bee7adfSArchie Cobbs 		splx(s);
8349bee7adfSArchie Cobbs 		return;
8359bee7adfSArchie Cobbs 	}
8369bee7adfSArchie Cobbs 	ng_unref(node);
8373cd7db22SArchie Cobbs 	if (a->sackTimerPtr != arg) {	/* timer stopped race condition */
8383cd7db22SArchie Cobbs 		splx(s);
8393cd7db22SArchie Cobbs 		return;
8403cd7db22SArchie Cobbs 	}
8413cd7db22SArchie Cobbs 	a->sackTimerPtr = NULL;
8429bee7adfSArchie Cobbs 
8439bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
844add85a1dSArchie Cobbs   	ng_pptpgre_xmit(node, NULL, NULL);
8454164c447SArchie Cobbs 	splx(s);
846add85a1dSArchie Cobbs }
847add85a1dSArchie Cobbs 
848add85a1dSArchie Cobbs /*************************************************************************
849add85a1dSArchie Cobbs 		    MISC FUNCTIONS
850add85a1dSArchie Cobbs *************************************************************************/
851add85a1dSArchie Cobbs 
852add85a1dSArchie Cobbs /*
853add85a1dSArchie Cobbs  * Reset state
854add85a1dSArchie Cobbs  */
855add85a1dSArchie Cobbs static void
856add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node)
857add85a1dSArchie Cobbs {
858add85a1dSArchie Cobbs 	const priv_p priv = node->private;
859add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
860add85a1dSArchie Cobbs 
861add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
862add85a1dSArchie Cobbs 	a->ato = PPTP_MAX_TIMEOUT;
863add85a1dSArchie Cobbs 	a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
864add85a1dSArchie Cobbs 	if (a->rtt < PPTP_MIN_RTT)
865add85a1dSArchie Cobbs 		a->rtt = PPTP_MIN_RTT;
866add85a1dSArchie Cobbs 	a->dev = 0;
867add85a1dSArchie Cobbs 	a->xmitWin = (priv->conf.recvWin + 1) / 2;
868e962a823SArchie Cobbs 	if (a->xmitWin < 2)		/* often the first packet is lost */
869e962a823SArchie Cobbs 		a->xmitWin = 2;		/*   because the peer isn't ready */
870add85a1dSArchie Cobbs 	if (a->xmitWin > PPTP_XMIT_WIN)
871add85a1dSArchie Cobbs 		a->xmitWin = PPTP_XMIT_WIN;
872add85a1dSArchie Cobbs 	a->winAck = a->xmitWin;
873add85a1dSArchie Cobbs 
874add85a1dSArchie Cobbs 	/* Reset sequence numbers */
8753cd7db22SArchie Cobbs 	priv->recvSeq = ~0;
8763cd7db22SArchie Cobbs 	priv->recvAck = ~0;
8773cd7db22SArchie Cobbs 	priv->xmitSeq = ~0;
8783cd7db22SArchie Cobbs 	priv->xmitAck = ~0;
879add85a1dSArchie Cobbs 
880add85a1dSArchie Cobbs 	/* Reset start time */
8819bee7adfSArchie Cobbs 	getmicrouptime(&priv->startTime);
8829bee7adfSArchie Cobbs 
8839bee7adfSArchie Cobbs 	/* Reset stats */
8849bee7adfSArchie Cobbs 	bzero(&priv->stats, sizeof(priv->stats));
885add85a1dSArchie Cobbs 
8863cd7db22SArchie Cobbs 	/* "Stop" timers */
8873cd7db22SArchie Cobbs 	a->sackTimerPtr = NULL;
8883cd7db22SArchie Cobbs 	a->rackTimerPtr = NULL;
889add85a1dSArchie Cobbs }
890add85a1dSArchie Cobbs 
891add85a1dSArchie Cobbs /*
892add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
893add85a1dSArchie Cobbs  */
894add85a1dSArchie Cobbs static pptptime_t
895add85a1dSArchie Cobbs ng_pptpgre_time(node_p node)
896add85a1dSArchie Cobbs {
897add85a1dSArchie Cobbs 	const priv_p priv = node->private;
898add85a1dSArchie Cobbs 	struct timeval tv;
899add85a1dSArchie Cobbs 
9009bee7adfSArchie Cobbs 	getmicrouptime(&tv);
901add85a1dSArchie Cobbs 	if (tv.tv_sec < priv->startTime.tv_sec
902add85a1dSArchie Cobbs 	    || (tv.tv_sec == priv->startTime.tv_sec
903add85a1dSArchie Cobbs 	      && tv.tv_usec < priv->startTime.tv_usec))
904add85a1dSArchie Cobbs 		return (0);
905add85a1dSArchie Cobbs 	timevalsub(&tv, &priv->startTime);
906add85a1dSArchie Cobbs 	tv.tv_sec *= PPTP_TIME_SCALE;
907add85a1dSArchie Cobbs 	tv.tv_usec /= 1000000 / PPTP_TIME_SCALE;
908add85a1dSArchie Cobbs 	return(tv.tv_sec + tv.tv_usec);
909add85a1dSArchie Cobbs }
910add85a1dSArchie Cobbs 
911