xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision add85a1d6e8136412581f01671b58e9e6738448c)
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/conf.h>
61add85a1dSArchie Cobbs #include <sys/mbuf.h>
62add85a1dSArchie Cobbs #include <sys/malloc.h>
63add85a1dSArchie Cobbs #include <sys/errno.h>
64add85a1dSArchie Cobbs #include <sys/socket.h>
65add85a1dSArchie Cobbs #include <sys/syslog.h>
66add85a1dSArchie Cobbs #include <sys/ctype.h>
67add85a1dSArchie Cobbs 
68add85a1dSArchie Cobbs #include <netinet/in.h>
69add85a1dSArchie Cobbs #include <netinet/in_systm.h>
70add85a1dSArchie Cobbs #include <netinet/ip.h>
71add85a1dSArchie Cobbs 
72add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
73add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
74add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
75add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
76add85a1dSArchie Cobbs 
77add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
78add85a1dSArchie Cobbs struct greheader {
79add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
80add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
81add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
82add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
83add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
84add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
85add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
86add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
87add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
88add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
89add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
90add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
91add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
92add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
93add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
94add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
95add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
96add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
97add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
98add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
99add85a1dSArchie Cobbs #else
100add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
101add85a1dSArchie Cobbs #endif
102add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
103add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
104add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
105add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
106add85a1dSArchie Cobbs };
107add85a1dSArchie Cobbs 
108add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
109add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
110add85a1dSArchie Cobbs 
111add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
112add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
113add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
114add85a1dSArchie Cobbs 
115add85a1dSArchie Cobbs /* Min and max packet length */
116add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
117add85a1dSArchie Cobbs 
118add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
119add85a1dSArchie Cobbs #define PPTP_TIME_SCALE		1000
120add85a1dSArchie Cobbs typedef u_int32_t		pptptime_t;
121add85a1dSArchie Cobbs 
122add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
123add85a1dSArchie Cobbs #define PPTP_XMIT_WIN		8			/* max xmit window */
124add85a1dSArchie Cobbs #define PPTP_MIN_RTT		(PPTP_TIME_SCALE / 10)	/* 1/10 second */
125add85a1dSArchie Cobbs #define PPTP_MAX_TIMEOUT	(10 * PPTP_TIME_SCALE)	/* 10 seconds */
126add85a1dSArchie Cobbs 
127add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x)	((x) >> 3)	/* alpha = 0.125 */
128add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x)	((x) >> 2)	/* beta = 0.25 */
129add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
130add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
131add85a1dSArchie Cobbs 
132add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
133add85a1dSArchie Cobbs struct ng_pptpgre_ackp {
134add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
135add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
136add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
137add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
138add85a1dSArchie Cobbs 	u_char			sackTimerRunning;/* send ack timer is running */
139add85a1dSArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
140add85a1dSArchie Cobbs 	struct callout_handle	sackTimer;	/* send ack timer */
141add85a1dSArchie Cobbs 	struct callout_handle	rackTimer;	/* recv ack timer */
142add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
143add85a1dSArchie Cobbs };
144add85a1dSArchie Cobbs 
145add85a1dSArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet
146add85a1dSArchie Cobbs    we can piggy-back the ACK off of.  These parameters determine the mimimum
147add85a1dSArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that. */
148add85a1dSArchie Cobbs #define PPTP_MAX_ACK_DELAY	((int) (0.25 * PPTP_TIME_SCALE))
149add85a1dSArchie Cobbs 
150add85a1dSArchie Cobbs /* Node private data */
151add85a1dSArchie Cobbs struct ng_pptpgre_private {
152add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
153add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
154add85a1dSArchie Cobbs 	struct ng_pptpgre_conf	conf;		/* configuration info */
155add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp	ackp;		/* packet transmit ack state */
156add85a1dSArchie Cobbs 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
157add85a1dSArchie Cobbs 	u_int32_t		xmitSeq;	/* last seq # we sent */
158add85a1dSArchie Cobbs 	u_int32_t		recvAck;	/* last seq # peer ack'd */
159add85a1dSArchie Cobbs 	u_int32_t		xmitAck;	/* last seq # we ack'd */
160add85a1dSArchie Cobbs 	struct timeval		startTime;	/* time node was created */
161add85a1dSArchie Cobbs };
162add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
163add85a1dSArchie Cobbs 
164add85a1dSArchie Cobbs /* Netgraph node methods */
165add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
166add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
167add85a1dSArchie Cobbs static ng_shutdown_t	ng_pptpgre_rmnode;
168add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
169add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
170add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
171add85a1dSArchie Cobbs 
172add85a1dSArchie Cobbs /* Helper functions */
173add85a1dSArchie Cobbs static int	ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta);
174add85a1dSArchie Cobbs static int	ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta);
175add85a1dSArchie Cobbs static void	ng_pptpgre_start_recv_ack_timer(node_p node);
176add85a1dSArchie Cobbs static void	ng_pptpgre_recv_ack_timeout(void *arg);
177add85a1dSArchie Cobbs static void	ng_pptpgre_send_ack_timeout(void *arg);
178add85a1dSArchie Cobbs static void	ng_pptpgre_reset(node_p node);
179add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node);
180add85a1dSArchie Cobbs 
181add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
182add85a1dSArchie Cobbs static const struct ng_parse_struct_info
183add85a1dSArchie Cobbs 	ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO;
184add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
185add85a1dSArchie Cobbs 	&ng_parse_struct_type,
186add85a1dSArchie Cobbs 	&ng_pptpgre_conf_type_info,
187add85a1dSArchie Cobbs };
188add85a1dSArchie Cobbs 
189add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
190add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
191add85a1dSArchie Cobbs 	{
192add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
193add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
194add85a1dSArchie Cobbs 	  "setconfig",
195add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
196add85a1dSArchie Cobbs 	  NULL
197add85a1dSArchie Cobbs 	},
198add85a1dSArchie Cobbs 	{
199add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
200add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
201add85a1dSArchie Cobbs 	  "getconfig",
202add85a1dSArchie Cobbs 	  NULL,
203add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
204add85a1dSArchie Cobbs 	},
205add85a1dSArchie Cobbs 	{ 0 }
206add85a1dSArchie Cobbs };
207add85a1dSArchie Cobbs 
208add85a1dSArchie Cobbs /* Node type descriptor */
209add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
210add85a1dSArchie Cobbs 	NG_VERSION,
211add85a1dSArchie Cobbs 	NG_PPTPGRE_NODE_TYPE,
212add85a1dSArchie Cobbs 	NULL,
213add85a1dSArchie Cobbs 	ng_pptpgre_constructor,
214add85a1dSArchie Cobbs 	ng_pptpgre_rcvmsg,
215add85a1dSArchie Cobbs 	ng_pptpgre_rmnode,
216add85a1dSArchie Cobbs 	ng_pptpgre_newhook,
217add85a1dSArchie Cobbs 	NULL,
218add85a1dSArchie Cobbs 	NULL,
219add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
220add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
221add85a1dSArchie Cobbs 	ng_pptpgre_disconnect,
222add85a1dSArchie Cobbs 	ng_pptpgre_cmdlist
223add85a1dSArchie Cobbs };
224add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
225add85a1dSArchie Cobbs 
226add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
227add85a1dSArchie Cobbs 
228add85a1dSArchie Cobbs /************************************************************************
229add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
230add85a1dSArchie Cobbs  ************************************************************************/
231add85a1dSArchie Cobbs 
232add85a1dSArchie Cobbs /*
233add85a1dSArchie Cobbs  * Node type constructor
234add85a1dSArchie Cobbs  */
235add85a1dSArchie Cobbs static int
236add85a1dSArchie Cobbs ng_pptpgre_constructor(node_p *nodep)
237add85a1dSArchie Cobbs {
238add85a1dSArchie Cobbs 	priv_p priv;
239add85a1dSArchie Cobbs 	int error;
240add85a1dSArchie Cobbs 
241add85a1dSArchie Cobbs 	/* Allocate private structure */
242add85a1dSArchie Cobbs 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
243add85a1dSArchie Cobbs 	if (priv == NULL)
244add85a1dSArchie Cobbs 		return (ENOMEM);
245add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
246add85a1dSArchie Cobbs 
247add85a1dSArchie Cobbs 	/* Call generic node constructor */
248add85a1dSArchie Cobbs 	if ((error = ng_make_node_common(&ng_pptpgre_typestruct, nodep))) {
249add85a1dSArchie Cobbs 		FREE(priv, M_NETGRAPH);
250add85a1dSArchie Cobbs 		return (error);
251add85a1dSArchie Cobbs 	}
252add85a1dSArchie Cobbs 	(*nodep)->private = priv;
253add85a1dSArchie Cobbs 
254add85a1dSArchie Cobbs 	/* Initialize state */
255add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.sackTimer);
256add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.rackTimer);
257add85a1dSArchie Cobbs 
258add85a1dSArchie Cobbs 	/* Done */
259add85a1dSArchie Cobbs 	return (0);
260add85a1dSArchie Cobbs }
261add85a1dSArchie Cobbs 
262add85a1dSArchie Cobbs /*
263add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
264add85a1dSArchie Cobbs  */
265add85a1dSArchie Cobbs static int
266add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
267add85a1dSArchie Cobbs {
268add85a1dSArchie Cobbs 	const priv_p priv = node->private;
269add85a1dSArchie Cobbs 	hook_p *hookPtr;
270add85a1dSArchie Cobbs 
271add85a1dSArchie Cobbs 	/* Check hook name */
272add85a1dSArchie Cobbs 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0)
273add85a1dSArchie Cobbs 		hookPtr = &priv->upper;
274add85a1dSArchie Cobbs 	else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0)
275add85a1dSArchie Cobbs 		hookPtr = &priv->lower;
276add85a1dSArchie Cobbs 	else
277add85a1dSArchie Cobbs 		return (EINVAL);
278add85a1dSArchie Cobbs 
279add85a1dSArchie Cobbs 	/* See if already connected */
280add85a1dSArchie Cobbs 	if (*hookPtr != NULL)
281add85a1dSArchie Cobbs 		return (EISCONN);
282add85a1dSArchie Cobbs 
283add85a1dSArchie Cobbs 	/* OK */
284add85a1dSArchie Cobbs 	*hookPtr = hook;
285add85a1dSArchie Cobbs 	return (0);
286add85a1dSArchie Cobbs }
287add85a1dSArchie Cobbs 
288add85a1dSArchie Cobbs /*
289add85a1dSArchie Cobbs  * Receive a control message.
290add85a1dSArchie Cobbs  */
291add85a1dSArchie Cobbs static int
292add85a1dSArchie Cobbs ng_pptpgre_rcvmsg(node_p node, struct ng_mesg *msg,
293add85a1dSArchie Cobbs 	      const char *raddr, struct ng_mesg **rptr)
294add85a1dSArchie Cobbs {
295add85a1dSArchie Cobbs 	const priv_p priv = node->private;
296add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
297add85a1dSArchie Cobbs 	int error = 0;
298add85a1dSArchie Cobbs 
299add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
300add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
301add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
302add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
303add85a1dSArchie Cobbs 		    {
304add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
305add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
306add85a1dSArchie Cobbs 
307add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
308add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
309add85a1dSArchie Cobbs 				ERROUT(EINVAL);
310add85a1dSArchie Cobbs 			ng_pptpgre_reset(node);		/* reset on configure */
311add85a1dSArchie Cobbs 			priv->conf = *newConf;
312add85a1dSArchie Cobbs 			break;
313add85a1dSArchie Cobbs 		    }
314add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
315add85a1dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
316add85a1dSArchie Cobbs 			if (resp == NULL)
317add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
318add85a1dSArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
319add85a1dSArchie Cobbs 			break;
320add85a1dSArchie Cobbs 		default:
321add85a1dSArchie Cobbs 			error = EINVAL;
322add85a1dSArchie Cobbs 			break;
323add85a1dSArchie Cobbs 		}
324add85a1dSArchie Cobbs 		break;
325add85a1dSArchie Cobbs 	default:
326add85a1dSArchie Cobbs 		error = EINVAL;
327add85a1dSArchie Cobbs 		break;
328add85a1dSArchie Cobbs 	}
329add85a1dSArchie Cobbs 	if (rptr)
330add85a1dSArchie Cobbs 		*rptr = resp;
331add85a1dSArchie Cobbs 	else if (resp)
332add85a1dSArchie Cobbs 		FREE(resp, M_NETGRAPH);
333add85a1dSArchie Cobbs 
334add85a1dSArchie Cobbs done:
335add85a1dSArchie Cobbs 	FREE(msg, M_NETGRAPH);
336add85a1dSArchie Cobbs 	return (error);
337add85a1dSArchie Cobbs }
338add85a1dSArchie Cobbs 
339add85a1dSArchie Cobbs /*
340add85a1dSArchie Cobbs  * Receive incoming data on a hook.
341add85a1dSArchie Cobbs  */
342add85a1dSArchie Cobbs static int
343add85a1dSArchie Cobbs ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
344add85a1dSArchie Cobbs {
345add85a1dSArchie Cobbs 	const node_p node = hook->node;
346add85a1dSArchie Cobbs 	const priv_p priv = node->private;
347add85a1dSArchie Cobbs 
348add85a1dSArchie Cobbs 	/* If not configured, reject */
349add85a1dSArchie Cobbs 	if (!priv->conf.enabled) {
350add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
351add85a1dSArchie Cobbs 		return (ENXIO);
352add85a1dSArchie Cobbs 	}
353add85a1dSArchie Cobbs 
354add85a1dSArchie Cobbs 	/* Treat as xmit or recv data */
355add85a1dSArchie Cobbs 	if (hook == priv->upper)
356add85a1dSArchie Cobbs 		return ng_pptpgre_xmit(node, m, meta);
357add85a1dSArchie Cobbs 	if (hook == priv->lower)
358add85a1dSArchie Cobbs 		return ng_pptpgre_recv(node, m, meta);
359add85a1dSArchie Cobbs 	panic("%s: weird hook", __FUNCTION__);
360add85a1dSArchie Cobbs }
361add85a1dSArchie Cobbs 
362add85a1dSArchie Cobbs /*
363add85a1dSArchie Cobbs  * Destroy node
364add85a1dSArchie Cobbs  */
365add85a1dSArchie Cobbs static int
366add85a1dSArchie Cobbs ng_pptpgre_rmnode(node_p node)
367add85a1dSArchie Cobbs {
368add85a1dSArchie Cobbs 	const priv_p priv = node->private;
369add85a1dSArchie Cobbs 
370add85a1dSArchie Cobbs 	/* Cancel timers */
371add85a1dSArchie Cobbs 	ng_pptpgre_reset(node);
372add85a1dSArchie Cobbs 
373add85a1dSArchie Cobbs 	/* Take down netgraph node */
374add85a1dSArchie Cobbs 	node->flags |= NG_INVALID;
375add85a1dSArchie Cobbs 	ng_cutlinks(node);
376add85a1dSArchie Cobbs 	ng_unname(node);
377add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
378add85a1dSArchie Cobbs 	FREE(priv, M_NETGRAPH);
379add85a1dSArchie Cobbs 	node->private = NULL;
380add85a1dSArchie Cobbs 	ng_unref(node);
381add85a1dSArchie Cobbs 	return (0);
382add85a1dSArchie Cobbs }
383add85a1dSArchie Cobbs 
384add85a1dSArchie Cobbs /*
385add85a1dSArchie Cobbs  * Hook disconnection
386add85a1dSArchie Cobbs  */
387add85a1dSArchie Cobbs static int
388add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook)
389add85a1dSArchie Cobbs {
390add85a1dSArchie Cobbs 	const node_p node = hook->node;
391add85a1dSArchie Cobbs 	const priv_p priv = node->private;
392add85a1dSArchie Cobbs 
393add85a1dSArchie Cobbs 	/* Zero out hook pointer */
394add85a1dSArchie Cobbs 	if (hook == priv->upper)
395add85a1dSArchie Cobbs 		priv->upper = NULL;
396add85a1dSArchie Cobbs 	else if (hook == priv->lower)
397add85a1dSArchie Cobbs 		priv->lower = NULL;
398add85a1dSArchie Cobbs 	else
399add85a1dSArchie Cobbs 		panic("%s: unknown hook", __FUNCTION__);
400add85a1dSArchie Cobbs 
401add85a1dSArchie Cobbs 	/* Go away if no longer connected to anything */
402add85a1dSArchie Cobbs 	if (node->numhooks == 0)
403add85a1dSArchie Cobbs 		ng_rmnode(node);
404add85a1dSArchie Cobbs 	return (0);
405add85a1dSArchie Cobbs }
406add85a1dSArchie Cobbs 
407add85a1dSArchie Cobbs /*************************************************************************
408add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
409add85a1dSArchie Cobbs *************************************************************************/
410add85a1dSArchie Cobbs 
411add85a1dSArchie Cobbs /*
412add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
413add85a1dSArchie Cobbs  */
414add85a1dSArchie Cobbs static int
415add85a1dSArchie Cobbs ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta)
416add85a1dSArchie Cobbs {
417add85a1dSArchie Cobbs 	const priv_p priv = node->private;
418add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
419add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
420add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
421add85a1dSArchie Cobbs 	int grelen, error;
422add85a1dSArchie Cobbs 
423add85a1dSArchie Cobbs 	/* Is our transmit window full? */
424add85a1dSArchie Cobbs 	if (m != NULL && priv->xmitSeq - priv->recvAck >= a->xmitWin) {
425add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
426add85a1dSArchie Cobbs 		return (ENOBUFS);
427add85a1dSArchie Cobbs 	}
428add85a1dSArchie Cobbs 
429add85a1dSArchie Cobbs 	/* Sanity check frame length */
430add85a1dSArchie Cobbs 	if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
431add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
432add85a1dSArchie Cobbs 		return (EMSGSIZE);
433add85a1dSArchie Cobbs 	}
434add85a1dSArchie Cobbs 
435add85a1dSArchie Cobbs 	/* Build GRE header */
436add85a1dSArchie Cobbs 	((u_int32_t *) gre)[0] = htonl(PPTP_INIT_VALUE);
437add85a1dSArchie Cobbs 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
438add85a1dSArchie Cobbs 	gre->cid = htons(priv->conf.peerCid);
439add85a1dSArchie Cobbs 
440add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
441add85a1dSArchie Cobbs 	if (m != NULL) {
442add85a1dSArchie Cobbs 		gre->hasSeq = 1;
443add85a1dSArchie Cobbs 		a->timeSent[priv->xmitSeq - priv->recvAck]
444add85a1dSArchie Cobbs 		    = ng_pptpgre_time(node);
445add85a1dSArchie Cobbs 		priv->xmitSeq++;
446add85a1dSArchie Cobbs 		gre->data[0] = htonl(priv->xmitSeq);
447add85a1dSArchie Cobbs 		if (priv->xmitSeq == priv->recvAck + 1)
448add85a1dSArchie Cobbs 			ng_pptpgre_start_recv_ack_timer(node);
449add85a1dSArchie Cobbs 	}
450add85a1dSArchie Cobbs 
451add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
452add85a1dSArchie Cobbs 	if (priv->xmitAck < priv->recvSeq) {
453add85a1dSArchie Cobbs 		gre->hasAck = 1;
454add85a1dSArchie Cobbs 		priv->xmitAck = priv->recvSeq;
455add85a1dSArchie Cobbs 		gre->data[gre->hasSeq] = htonl(priv->xmitAck);
456add85a1dSArchie Cobbs 		if (a->sackTimerRunning) {
457add85a1dSArchie Cobbs 			untimeout(ng_pptpgre_send_ack_timeout,
458add85a1dSArchie Cobbs 				node, a->sackTimer);
459add85a1dSArchie Cobbs 			a->sackTimerRunning = 0;
460add85a1dSArchie Cobbs 		}
461add85a1dSArchie Cobbs 	}
462add85a1dSArchie Cobbs 
463add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
464add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
465add85a1dSArchie Cobbs 	if (m == NULL) {
466add85a1dSArchie Cobbs 		MGETHDR(m, M_DONTWAIT, MT_DATA);
467add85a1dSArchie Cobbs 		if (m == NULL) {
468add85a1dSArchie Cobbs 			NG_FREE_META(meta);
469add85a1dSArchie Cobbs 			return (ENOBUFS);
470add85a1dSArchie Cobbs 		}
471add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
472add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
473add85a1dSArchie Cobbs 	} else {
474add85a1dSArchie Cobbs 		M_PREPEND(m, grelen, M_NOWAIT);
475add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
476add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
477add85a1dSArchie Cobbs 			NG_FREE_META(meta);
478add85a1dSArchie Cobbs 			return (ENOBUFS);
479add85a1dSArchie Cobbs 		}
480add85a1dSArchie Cobbs 	}
481add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
482add85a1dSArchie Cobbs 
483add85a1dSArchie Cobbs 	/* Deliver packet */
484add85a1dSArchie Cobbs 	NG_SEND_DATA(error, priv->lower, m, meta);
485add85a1dSArchie Cobbs 	return (error);
486add85a1dSArchie Cobbs }
487add85a1dSArchie Cobbs 
488add85a1dSArchie Cobbs /*
489add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
490add85a1dSArchie Cobbs  */
491add85a1dSArchie Cobbs static int
492add85a1dSArchie Cobbs ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta)
493add85a1dSArchie Cobbs {
494add85a1dSArchie Cobbs 	const priv_p priv = node->private;
495add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
496add85a1dSArchie Cobbs 	struct greheader *gre;
497add85a1dSArchie Cobbs 	struct ip *ip;
498add85a1dSArchie Cobbs 	int error = 0;
499add85a1dSArchie Cobbs 
500add85a1dSArchie Cobbs 	/* Sanity check packet length */
501add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
502add85a1dSArchie Cobbs bad:
503add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
504add85a1dSArchie Cobbs 		return (EINVAL);
505add85a1dSArchie Cobbs 	}
506add85a1dSArchie Cobbs 
507add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
508add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
509add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
510add85a1dSArchie Cobbs 		NG_FREE_META(meta);
511add85a1dSArchie Cobbs 		return (ENOBUFS);
512add85a1dSArchie Cobbs 	}
513add85a1dSArchie Cobbs 	ip = mtod(m, struct ip *);
514add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
515add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
516add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
517add85a1dSArchie Cobbs 			NG_FREE_META(meta);
518add85a1dSArchie Cobbs 			return (ENOBUFS);
519add85a1dSArchie Cobbs 		}
520add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
521add85a1dSArchie Cobbs 	}
522add85a1dSArchie Cobbs 	gre = (struct greheader *)((u_char *)ip + iphlen);
523add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
524add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen)
525add85a1dSArchie Cobbs 		goto bad;
526add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
527add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
528add85a1dSArchie Cobbs 			NG_FREE_META(meta);
529add85a1dSArchie Cobbs 			return (ENOBUFS);
530add85a1dSArchie Cobbs 		}
531add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
532add85a1dSArchie Cobbs 		gre = (struct greheader *)((u_char *)ip + iphlen);
533add85a1dSArchie Cobbs 	}
534add85a1dSArchie Cobbs 
535add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
536add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
537add85a1dSArchie Cobbs 	    - (iphlen + grelen + (u_int16_t)ntohs(gre->length));
538add85a1dSArchie Cobbs 	if (extralen < 0)
539add85a1dSArchie Cobbs 		goto bad;
540add85a1dSArchie Cobbs 	if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
541add85a1dSArchie Cobbs 		goto bad;
542add85a1dSArchie Cobbs 	if (ntohs(gre->cid) != priv->conf.cid)
543add85a1dSArchie Cobbs 		goto bad;
544add85a1dSArchie Cobbs 
545add85a1dSArchie Cobbs 	/* Look for peer ack */
546add85a1dSArchie Cobbs 	if (gre->hasAck) {
547add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
548add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
549add85a1dSArchie Cobbs 		const int index = ack - priv->recvAck - 1;
550add85a1dSArchie Cobbs 		const long sample = ng_pptpgre_time(node) - a->timeSent[index];
551add85a1dSArchie Cobbs 		long diff;
552add85a1dSArchie Cobbs 
553add85a1dSArchie Cobbs 		/* Sanity check ack value */
554add85a1dSArchie Cobbs 		if (ack <= priv->recvAck)	/* ack already timed out */
555add85a1dSArchie Cobbs 			goto bad;
556add85a1dSArchie Cobbs 		if (ack > priv->xmitSeq)	/* we never sent it! */
557add85a1dSArchie Cobbs 			goto bad;
558add85a1dSArchie Cobbs 		priv->recvAck = ack;
559add85a1dSArchie Cobbs 
560add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
561add85a1dSArchie Cobbs 		diff = sample - a->rtt;
562add85a1dSArchie Cobbs 		a->rtt += PPTP_ACK_ALPHA(diff);
563add85a1dSArchie Cobbs 		if (diff < 0)
564add85a1dSArchie Cobbs 			diff = -diff;
565add85a1dSArchie Cobbs 		a->dev += PPTP_ACK_BETA(diff - a->dev);
566add85a1dSArchie Cobbs 		a->ato = a->rtt + (u_int) (PPTP_ACK_CHI(a->dev));
567add85a1dSArchie Cobbs 		if (a->ato > PPTP_MAX_TIMEOUT)
568add85a1dSArchie Cobbs 			a->ato = PPTP_MAX_TIMEOUT;
569add85a1dSArchie Cobbs 		ovbcopy(a->timeSent + index + 1, a->timeSent,
570add85a1dSArchie Cobbs 		    sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1)));
571add85a1dSArchie Cobbs 		if (ack >= a->winAck && a->xmitWin < PPTP_XMIT_WIN) {
572add85a1dSArchie Cobbs 			a->xmitWin++;
573add85a1dSArchie Cobbs 			a->winAck = ack + a->xmitWin;
574add85a1dSArchie Cobbs 		}
575add85a1dSArchie Cobbs 
576add85a1dSArchie Cobbs 		/* (Re)start receive ACK timer as necessary */
577add85a1dSArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
578add85a1dSArchie Cobbs 	}
579add85a1dSArchie Cobbs 
580add85a1dSArchie Cobbs 	/* See if frame contains any data */
581add85a1dSArchie Cobbs 	if (gre->hasSeq) {
582add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
583add85a1dSArchie Cobbs 		const u_int32_t seq = ntohl(gre->data[0]);
584add85a1dSArchie Cobbs 
585add85a1dSArchie Cobbs 		/* Sanity check sequence number */
586add85a1dSArchie Cobbs 		if (seq <= priv->recvSeq)	/* out-of-order or dup */
587add85a1dSArchie Cobbs 			goto bad;
588add85a1dSArchie Cobbs 		priv->recvSeq = seq;
589add85a1dSArchie Cobbs 
590add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
591add85a1dSArchie Cobbs 		if (!a->sackTimerRunning) {
592add85a1dSArchie Cobbs 			long ackTimeout;
593add85a1dSArchie Cobbs 
594add85a1dSArchie Cobbs 			/* Take half of the estimated round trip time */
595add85a1dSArchie Cobbs 			ackTimeout = (a->rtt >> 1);
596add85a1dSArchie Cobbs 
597add85a1dSArchie Cobbs 			/* If too soon, just send one right now */
598add85a1dSArchie Cobbs 			if (!priv->conf.enableDelayedAck)
599add85a1dSArchie Cobbs 				ng_pptpgre_xmit(node, NULL, NULL);
600add85a1dSArchie Cobbs 			else {			/* send the ack later */
601add85a1dSArchie Cobbs 				if (ackTimeout > PPTP_MAX_ACK_DELAY)
602add85a1dSArchie Cobbs 					ackTimeout = PPTP_MAX_ACK_DELAY;
603add85a1dSArchie Cobbs 				a->sackTimer = timeout(
604add85a1dSArchie Cobbs 				    ng_pptpgre_send_ack_timeout, node,
605add85a1dSArchie Cobbs 				    ackTimeout * hz / PPTP_TIME_SCALE);
606add85a1dSArchie Cobbs 				a->sackTimerRunning = 1;
607add85a1dSArchie Cobbs 			}
608add85a1dSArchie Cobbs 		}
609add85a1dSArchie Cobbs 
610add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
611add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
612add85a1dSArchie Cobbs 		if (extralen > 0)
613add85a1dSArchie Cobbs 			m_adj(m, -extralen);
614add85a1dSArchie Cobbs 
615add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
616add85a1dSArchie Cobbs 		NG_SEND_DATA(error, priv->upper, m, meta);
617add85a1dSArchie Cobbs 	} else
618add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);		/* no data to deliver */
619add85a1dSArchie Cobbs 	return (error);
620add85a1dSArchie Cobbs }
621add85a1dSArchie Cobbs 
622add85a1dSArchie Cobbs /*************************************************************************
623add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
624add85a1dSArchie Cobbs *************************************************************************/
625add85a1dSArchie Cobbs 
626add85a1dSArchie Cobbs /*
627add85a1dSArchie Cobbs  * Set a timer for the peer's acknowledging our oldest unacknowledged
628add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
629add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
630add85a1dSArchie Cobbs  * recv ack timer, if any.
631add85a1dSArchie Cobbs  */
632add85a1dSArchie Cobbs static void
633add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node)
634add85a1dSArchie Cobbs {
635add85a1dSArchie Cobbs 	const priv_p priv = node->private;
636add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
637add85a1dSArchie Cobbs 	int remain;
638add85a1dSArchie Cobbs 
639add85a1dSArchie Cobbs 	/* Stop current recv ack timer, if any */
640add85a1dSArchie Cobbs 	untimeout(ng_pptpgre_recv_ack_timeout, node, a->rackTimer);
641add85a1dSArchie Cobbs 	if (priv->recvAck == priv->xmitSeq)
642add85a1dSArchie Cobbs 		return;
643add85a1dSArchie Cobbs 
644add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
645add85a1dSArchie Cobbs 	   and reset the timer to that time. */
646add85a1dSArchie Cobbs 	remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node);
647add85a1dSArchie Cobbs 	if (remain < 0)
648add85a1dSArchie Cobbs 		remain = 0;
649add85a1dSArchie Cobbs 	a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout,
650add85a1dSArchie Cobbs 	    node, remain * hz / PPTP_TIME_SCALE);
651add85a1dSArchie Cobbs }
652add85a1dSArchie Cobbs 
653add85a1dSArchie Cobbs /*
654add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
655add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
656add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
657add85a1dSArchie Cobbs  */
658add85a1dSArchie Cobbs static void
659add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg)
660add85a1dSArchie Cobbs {
661add85a1dSArchie Cobbs 	const node_p node = arg;
662add85a1dSArchie Cobbs 	const priv_p priv = node->private;
663add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
664add85a1dSArchie Cobbs 
665add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
666add85a1dSArchie Cobbs 	a->rtt = PPTP_ACK_DELTA(a->rtt);
667add85a1dSArchie Cobbs 	a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
668add85a1dSArchie Cobbs 	if (a->ato > PPTP_MAX_TIMEOUT)
669add85a1dSArchie Cobbs 		a->ato = PPTP_MAX_TIMEOUT;
670add85a1dSArchie Cobbs 	priv->recvAck++;			/* assume packet was lost */
671add85a1dSArchie Cobbs 	a->winAck = priv->recvAck + a->xmitWin;	/* reset win expand time */
672add85a1dSArchie Cobbs 	ovbcopy(a->timeSent + 1, a->timeSent,	/* shift xmit window times */
673add85a1dSArchie Cobbs 	    sizeof(*a->timeSent) * (PPTP_XMIT_WIN - 1));
674add85a1dSArchie Cobbs 	a->xmitWin = (a->xmitWin + 1) / 2;	/* shrink transmit window */
675add85a1dSArchie Cobbs 
676add85a1dSArchie Cobbs 	/* Restart timer if there are any more outstanding frames */
677add85a1dSArchie Cobbs 	if (priv->recvAck != priv->xmitSeq)
678add85a1dSArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
679add85a1dSArchie Cobbs }
680add85a1dSArchie Cobbs 
681add85a1dSArchie Cobbs /*
682add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
683add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
684add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
685add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
686add85a1dSArchie Cobbs  */
687add85a1dSArchie Cobbs static void
688add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg)
689add85a1dSArchie Cobbs {
690add85a1dSArchie Cobbs 	const node_p node = arg;
691add85a1dSArchie Cobbs 	const priv_p priv = node->private;
692add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
693add85a1dSArchie Cobbs 
694add85a1dSArchie Cobbs 	/* Send a frame with an ack but no payload */
695add85a1dSArchie Cobbs 	a->sackTimerRunning = 0;
696add85a1dSArchie Cobbs   	ng_pptpgre_xmit(node, NULL, NULL);
697add85a1dSArchie Cobbs }
698add85a1dSArchie Cobbs 
699add85a1dSArchie Cobbs /*************************************************************************
700add85a1dSArchie Cobbs 		    MISC FUNCTIONS
701add85a1dSArchie Cobbs *************************************************************************/
702add85a1dSArchie Cobbs 
703add85a1dSArchie Cobbs /*
704add85a1dSArchie Cobbs  * Reset state
705add85a1dSArchie Cobbs  */
706add85a1dSArchie Cobbs static void
707add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node)
708add85a1dSArchie Cobbs {
709add85a1dSArchie Cobbs 	const priv_p priv = node->private;
710add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
711add85a1dSArchie Cobbs 
712add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
713add85a1dSArchie Cobbs 	a->ato = PPTP_MAX_TIMEOUT;
714add85a1dSArchie Cobbs 	a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
715add85a1dSArchie Cobbs 	if (a->rtt < PPTP_MIN_RTT)
716add85a1dSArchie Cobbs 		a->rtt = PPTP_MIN_RTT;
717add85a1dSArchie Cobbs 	a->dev = 0;
718add85a1dSArchie Cobbs 	a->xmitWin = (priv->conf.recvWin + 1) / 2;
719add85a1dSArchie Cobbs 	if (a->xmitWin < 1)
720add85a1dSArchie Cobbs 		a->xmitWin = 1;
721add85a1dSArchie Cobbs 	if (a->xmitWin > PPTP_XMIT_WIN)
722add85a1dSArchie Cobbs 		a->xmitWin = PPTP_XMIT_WIN;
723add85a1dSArchie Cobbs 	a->winAck = a->xmitWin;
724add85a1dSArchie Cobbs 
725add85a1dSArchie Cobbs 	/* Reset sequence numbers */
726add85a1dSArchie Cobbs 	priv->recvSeq = 0;
727add85a1dSArchie Cobbs 	priv->recvAck = 0;
728add85a1dSArchie Cobbs 	priv->xmitSeq = 0;
729add85a1dSArchie Cobbs 	priv->xmitAck = 0;
730add85a1dSArchie Cobbs 
731add85a1dSArchie Cobbs 	/* Reset start time */
732add85a1dSArchie Cobbs 	getmicrotime(&priv->startTime);
733add85a1dSArchie Cobbs 
734add85a1dSArchie Cobbs 	/* Stop timers */
735add85a1dSArchie Cobbs 	untimeout(ng_pptpgre_send_ack_timeout, node, a->sackTimer);
736add85a1dSArchie Cobbs 	untimeout(ng_pptpgre_recv_ack_timeout, node, a->rackTimer);
737add85a1dSArchie Cobbs 	a->sackTimerRunning = 0;
738add85a1dSArchie Cobbs }
739add85a1dSArchie Cobbs 
740add85a1dSArchie Cobbs /*
741add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
742add85a1dSArchie Cobbs  */
743add85a1dSArchie Cobbs static pptptime_t
744add85a1dSArchie Cobbs ng_pptpgre_time(node_p node)
745add85a1dSArchie Cobbs {
746add85a1dSArchie Cobbs 	const priv_p priv = node->private;
747add85a1dSArchie Cobbs 	struct timeval tv;
748add85a1dSArchie Cobbs 
749add85a1dSArchie Cobbs 	getmicrotime(&tv);
750add85a1dSArchie Cobbs 	if (tv.tv_sec < priv->startTime.tv_sec
751add85a1dSArchie Cobbs 	    || (tv.tv_sec == priv->startTime.tv_sec
752add85a1dSArchie Cobbs 	      && tv.tv_usec < priv->startTime.tv_usec))
753add85a1dSArchie Cobbs 		return (0);
754add85a1dSArchie Cobbs 	timevalsub(&tv, &priv->startTime);
755add85a1dSArchie Cobbs 	tv.tv_sec *= PPTP_TIME_SCALE;
756add85a1dSArchie Cobbs 	tv.tv_usec /= 1000000 / PPTP_TIME_SCALE;
757add85a1dSArchie Cobbs 	return(tv.tv_sec + tv.tv_usec);
758add85a1dSArchie Cobbs }
759add85a1dSArchie Cobbs 
760