xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision da010626df7e62a562c36f5315b625913dd65bdc)
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 */
119678f9e33SArchie Cobbs typedef u_int64_t		pptptime_t;
120add85a1dSArchie Cobbs 
121add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
122e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
123e962a823SArchie Cobbs #define PPTP_MIN_RTT		(PPTP_TIME_SCALE / 10)	/* 100 milliseconds */
124da010626SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
125add85a1dSArchie Cobbs #define PPTP_MAX_TIMEOUT	(10 * PPTP_TIME_SCALE)	/* 10 seconds */
126add85a1dSArchie Cobbs 
127da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet
128da010626SArchie Cobbs    we can piggy-back the ACK off of. These parameters determine the mimimum
129da010626SArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that.
130da010626SArchie Cobbs    These have no effect unless "enableDelayedAck" is turned on. */
131da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
132da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
133da010626SArchie Cobbs 
134e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
135add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x)	((x) >> 3)	/* alpha = 0.125 */
136add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x)	((x) >> 2)	/* beta = 0.25 */
137add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
138add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
139add85a1dSArchie Cobbs 
1409bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
1419bee7adfSArchie Cobbs 
142add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
143add85a1dSArchie Cobbs struct ng_pptpgre_ackp {
144add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
145add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
146add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
147add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
148add85a1dSArchie Cobbs 	struct callout_handle	sackTimer;	/* send ack timer */
149add85a1dSArchie Cobbs 	struct callout_handle	rackTimer;	/* recv ack timer */
1503cd7db22SArchie Cobbs 	node_p			*sackTimerPtr;	/* send ack timer pointer */
1513cd7db22SArchie Cobbs 	node_p			*rackTimerPtr;	/* recv ack timer pointer */
1523cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
153add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
154678f9e33SArchie Cobbs #ifdef DEBUG_RAT
155da010626SArchie Cobbs 	pptptime_t		timerStart;	/* when rackTimer started */
156da010626SArchie Cobbs 	pptptime_t		timerLength;	/* rackTimer duration */
157678f9e33SArchie Cobbs #endif
158add85a1dSArchie Cobbs };
159add85a1dSArchie Cobbs 
160add85a1dSArchie Cobbs /* Node private data */
161add85a1dSArchie Cobbs struct ng_pptpgre_private {
162add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
163add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
164add85a1dSArchie Cobbs 	struct ng_pptpgre_conf	conf;		/* configuration info */
165add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp	ackp;		/* packet transmit ack state */
166add85a1dSArchie Cobbs 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
167add85a1dSArchie Cobbs 	u_int32_t		xmitSeq;	/* last seq # we sent */
168add85a1dSArchie Cobbs 	u_int32_t		recvAck;	/* last seq # peer ack'd */
169add85a1dSArchie Cobbs 	u_int32_t		xmitAck;	/* last seq # we ack'd */
170add85a1dSArchie Cobbs 	struct timeval		startTime;	/* time node was created */
1719bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
172add85a1dSArchie Cobbs };
173add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
174add85a1dSArchie Cobbs 
175add85a1dSArchie Cobbs /* Netgraph node methods */
176add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
177add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
178add85a1dSArchie Cobbs static ng_shutdown_t	ng_pptpgre_rmnode;
179add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
180add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
181add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
182add85a1dSArchie Cobbs 
183add85a1dSArchie Cobbs /* Helper functions */
184add85a1dSArchie Cobbs static int	ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta);
185add85a1dSArchie Cobbs static int	ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta);
186da010626SArchie Cobbs static void	ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout);
187add85a1dSArchie Cobbs static void	ng_pptpgre_start_recv_ack_timer(node_p node);
188add85a1dSArchie Cobbs static void	ng_pptpgre_recv_ack_timeout(void *arg);
189add85a1dSArchie Cobbs static void	ng_pptpgre_send_ack_timeout(void *arg);
190add85a1dSArchie Cobbs static void	ng_pptpgre_reset(node_p node);
191add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node);
192add85a1dSArchie Cobbs 
193add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
194add85a1dSArchie Cobbs static const struct ng_parse_struct_info
195add85a1dSArchie Cobbs 	ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO;
196add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
197add85a1dSArchie Cobbs 	&ng_parse_struct_type,
198add85a1dSArchie Cobbs 	&ng_pptpgre_conf_type_info,
199add85a1dSArchie Cobbs };
200add85a1dSArchie Cobbs 
2019bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
2029bee7adfSArchie Cobbs static const struct ng_parse_struct_info
2039bee7adfSArchie Cobbs 	ng_pptpgre_stats_type_info = NG_PPTPGRE_STATS_TYPE_INFO;
2049bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2059bee7adfSArchie Cobbs 	&ng_parse_struct_type,
2069bee7adfSArchie Cobbs 	&ng_pptpgre_stats_type_info
2079bee7adfSArchie Cobbs };
2089bee7adfSArchie Cobbs 
209add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
210add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
211add85a1dSArchie Cobbs 	{
212add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
213add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
214add85a1dSArchie Cobbs 	  "setconfig",
215add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
216add85a1dSArchie Cobbs 	  NULL
217add85a1dSArchie Cobbs 	},
218add85a1dSArchie Cobbs 	{
219add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
220add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
221add85a1dSArchie Cobbs 	  "getconfig",
222add85a1dSArchie Cobbs 	  NULL,
223add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
224add85a1dSArchie Cobbs 	},
2259bee7adfSArchie Cobbs 	{
2269bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2279bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2289bee7adfSArchie Cobbs 	  "getstats",
2299bee7adfSArchie Cobbs 	  NULL,
2309bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2319bee7adfSArchie Cobbs 	},
2329bee7adfSArchie Cobbs 	{
2339bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2349bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2359bee7adfSArchie Cobbs 	  "clrstats",
2369bee7adfSArchie Cobbs 	  NULL,
2379bee7adfSArchie Cobbs 	  NULL
2389bee7adfSArchie Cobbs 	},
2399bee7adfSArchie Cobbs 	{
2409bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2419bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2429bee7adfSArchie Cobbs 	  "getclrstats",
2439bee7adfSArchie Cobbs 	  NULL,
2449bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2459bee7adfSArchie Cobbs 	},
246add85a1dSArchie Cobbs 	{ 0 }
247add85a1dSArchie Cobbs };
248add85a1dSArchie Cobbs 
249add85a1dSArchie Cobbs /* Node type descriptor */
250add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
251add85a1dSArchie Cobbs 	NG_VERSION,
252add85a1dSArchie Cobbs 	NG_PPTPGRE_NODE_TYPE,
253add85a1dSArchie Cobbs 	NULL,
254add85a1dSArchie Cobbs 	ng_pptpgre_constructor,
255add85a1dSArchie Cobbs 	ng_pptpgre_rcvmsg,
256add85a1dSArchie Cobbs 	ng_pptpgre_rmnode,
257add85a1dSArchie Cobbs 	ng_pptpgre_newhook,
258add85a1dSArchie Cobbs 	NULL,
259add85a1dSArchie Cobbs 	NULL,
260add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
261add85a1dSArchie Cobbs 	ng_pptpgre_rcvdata,
262add85a1dSArchie Cobbs 	ng_pptpgre_disconnect,
263add85a1dSArchie Cobbs 	ng_pptpgre_cmdlist
264add85a1dSArchie Cobbs };
265add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
266add85a1dSArchie Cobbs 
267add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
268add85a1dSArchie Cobbs 
269add85a1dSArchie Cobbs /************************************************************************
270add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
271add85a1dSArchie Cobbs  ************************************************************************/
272add85a1dSArchie Cobbs 
273add85a1dSArchie Cobbs /*
274add85a1dSArchie Cobbs  * Node type constructor
275add85a1dSArchie Cobbs  */
276add85a1dSArchie Cobbs static int
277add85a1dSArchie Cobbs ng_pptpgre_constructor(node_p *nodep)
278add85a1dSArchie Cobbs {
279add85a1dSArchie Cobbs 	priv_p priv;
280add85a1dSArchie Cobbs 	int error;
281add85a1dSArchie Cobbs 
282add85a1dSArchie Cobbs 	/* Allocate private structure */
283add85a1dSArchie Cobbs 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK);
284add85a1dSArchie Cobbs 	if (priv == NULL)
285add85a1dSArchie Cobbs 		return (ENOMEM);
286add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
287add85a1dSArchie Cobbs 
288add85a1dSArchie Cobbs 	/* Call generic node constructor */
289add85a1dSArchie Cobbs 	if ((error = ng_make_node_common(&ng_pptpgre_typestruct, nodep))) {
290add85a1dSArchie Cobbs 		FREE(priv, M_NETGRAPH);
291add85a1dSArchie Cobbs 		return (error);
292add85a1dSArchie Cobbs 	}
293add85a1dSArchie Cobbs 	(*nodep)->private = priv;
294add85a1dSArchie Cobbs 
295add85a1dSArchie Cobbs 	/* Initialize state */
296add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.sackTimer);
297add85a1dSArchie Cobbs 	callout_handle_init(&priv->ackp.rackTimer);
298add85a1dSArchie Cobbs 
299add85a1dSArchie Cobbs 	/* Done */
300add85a1dSArchie Cobbs 	return (0);
301add85a1dSArchie Cobbs }
302add85a1dSArchie Cobbs 
303add85a1dSArchie Cobbs /*
304add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
305add85a1dSArchie Cobbs  */
306add85a1dSArchie Cobbs static int
307add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
308add85a1dSArchie Cobbs {
309add85a1dSArchie Cobbs 	const priv_p priv = node->private;
310add85a1dSArchie Cobbs 	hook_p *hookPtr;
311add85a1dSArchie Cobbs 
312add85a1dSArchie Cobbs 	/* Check hook name */
313add85a1dSArchie Cobbs 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0)
314add85a1dSArchie Cobbs 		hookPtr = &priv->upper;
315add85a1dSArchie Cobbs 	else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0)
316add85a1dSArchie Cobbs 		hookPtr = &priv->lower;
317add85a1dSArchie Cobbs 	else
318add85a1dSArchie Cobbs 		return (EINVAL);
319add85a1dSArchie Cobbs 
320add85a1dSArchie Cobbs 	/* See if already connected */
321add85a1dSArchie Cobbs 	if (*hookPtr != NULL)
322add85a1dSArchie Cobbs 		return (EISCONN);
323add85a1dSArchie Cobbs 
324add85a1dSArchie Cobbs 	/* OK */
325add85a1dSArchie Cobbs 	*hookPtr = hook;
326add85a1dSArchie Cobbs 	return (0);
327add85a1dSArchie Cobbs }
328add85a1dSArchie Cobbs 
329add85a1dSArchie Cobbs /*
330add85a1dSArchie Cobbs  * Receive a control message.
331add85a1dSArchie Cobbs  */
332add85a1dSArchie Cobbs static int
333add85a1dSArchie Cobbs ng_pptpgre_rcvmsg(node_p node, struct ng_mesg *msg,
334a4ec03cfSJulian Elischer 	      const char *raddr, struct ng_mesg **rptr, hook_p lasthook)
335add85a1dSArchie Cobbs {
336add85a1dSArchie Cobbs 	const priv_p priv = node->private;
337add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
338add85a1dSArchie Cobbs 	int error = 0;
339add85a1dSArchie Cobbs 
340add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
341add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
342add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
343add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
344add85a1dSArchie Cobbs 		    {
345add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
346add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
347add85a1dSArchie Cobbs 
348add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
349add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
350add85a1dSArchie Cobbs 				ERROUT(EINVAL);
351add85a1dSArchie Cobbs 			ng_pptpgre_reset(node);		/* reset on configure */
352add85a1dSArchie Cobbs 			priv->conf = *newConf;
353add85a1dSArchie Cobbs 			break;
354add85a1dSArchie Cobbs 		    }
355add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
356add85a1dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
357add85a1dSArchie Cobbs 			if (resp == NULL)
358add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
359add85a1dSArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
360add85a1dSArchie Cobbs 			break;
3619bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
3629bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
3639bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
3649bee7adfSArchie Cobbs 		    {
3659bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
3669bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3679bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
3689bee7adfSArchie Cobbs 				if (resp == NULL)
3699bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
3709bee7adfSArchie Cobbs 				bcopy(&priv->stats,
3719bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
3729bee7adfSArchie Cobbs 			}
3739bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
3749bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
3759bee7adfSArchie Cobbs 			break;
3769bee7adfSArchie Cobbs 		    }
377add85a1dSArchie Cobbs 		default:
378add85a1dSArchie Cobbs 			error = EINVAL;
379add85a1dSArchie Cobbs 			break;
380add85a1dSArchie Cobbs 		}
381add85a1dSArchie Cobbs 		break;
382add85a1dSArchie Cobbs 	default:
383add85a1dSArchie Cobbs 		error = EINVAL;
384add85a1dSArchie Cobbs 		break;
385add85a1dSArchie Cobbs 	}
386add85a1dSArchie Cobbs 	if (rptr)
387add85a1dSArchie Cobbs 		*rptr = resp;
388add85a1dSArchie Cobbs 	else if (resp)
389add85a1dSArchie Cobbs 		FREE(resp, M_NETGRAPH);
390add85a1dSArchie Cobbs 
391add85a1dSArchie Cobbs done:
392add85a1dSArchie Cobbs 	FREE(msg, M_NETGRAPH);
393add85a1dSArchie Cobbs 	return (error);
394add85a1dSArchie Cobbs }
395add85a1dSArchie Cobbs 
396add85a1dSArchie Cobbs /*
397add85a1dSArchie Cobbs  * Receive incoming data on a hook.
398add85a1dSArchie Cobbs  */
399add85a1dSArchie Cobbs static int
400a4ec03cfSJulian Elischer ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta,
401a4ec03cfSJulian Elischer 		struct mbuf **ret_m, meta_p *ret_meta)
402add85a1dSArchie Cobbs {
403add85a1dSArchie Cobbs 	const node_p node = hook->node;
404add85a1dSArchie Cobbs 	const priv_p priv = node->private;
405add85a1dSArchie Cobbs 
406add85a1dSArchie Cobbs 	/* If not configured, reject */
407add85a1dSArchie Cobbs 	if (!priv->conf.enabled) {
408add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
409add85a1dSArchie Cobbs 		return (ENXIO);
410add85a1dSArchie Cobbs 	}
411add85a1dSArchie Cobbs 
412add85a1dSArchie Cobbs 	/* Treat as xmit or recv data */
413add85a1dSArchie Cobbs 	if (hook == priv->upper)
414add85a1dSArchie Cobbs 		return ng_pptpgre_xmit(node, m, meta);
415add85a1dSArchie Cobbs 	if (hook == priv->lower)
416add85a1dSArchie Cobbs 		return ng_pptpgre_recv(node, m, meta);
417add85a1dSArchie Cobbs 	panic("%s: weird hook", __FUNCTION__);
418add85a1dSArchie Cobbs }
419add85a1dSArchie Cobbs 
420add85a1dSArchie Cobbs /*
421add85a1dSArchie Cobbs  * Destroy node
422add85a1dSArchie Cobbs  */
423add85a1dSArchie Cobbs static int
424add85a1dSArchie Cobbs ng_pptpgre_rmnode(node_p node)
425add85a1dSArchie Cobbs {
426add85a1dSArchie Cobbs 	const priv_p priv = node->private;
427add85a1dSArchie Cobbs 
4283cd7db22SArchie Cobbs 	/* Reset node */
429add85a1dSArchie Cobbs 	ng_pptpgre_reset(node);
430add85a1dSArchie Cobbs 
431add85a1dSArchie Cobbs 	/* Take down netgraph node */
432add85a1dSArchie Cobbs 	node->flags |= NG_INVALID;
433add85a1dSArchie Cobbs 	ng_cutlinks(node);
434add85a1dSArchie Cobbs 	ng_unname(node);
435add85a1dSArchie Cobbs 	bzero(priv, sizeof(*priv));
436add85a1dSArchie Cobbs 	FREE(priv, M_NETGRAPH);
437add85a1dSArchie Cobbs 	node->private = NULL;
438add85a1dSArchie Cobbs 	ng_unref(node);
439add85a1dSArchie Cobbs 	return (0);
440add85a1dSArchie Cobbs }
441add85a1dSArchie Cobbs 
442add85a1dSArchie Cobbs /*
443add85a1dSArchie Cobbs  * Hook disconnection
444add85a1dSArchie Cobbs  */
445add85a1dSArchie Cobbs static int
446add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook)
447add85a1dSArchie Cobbs {
448add85a1dSArchie Cobbs 	const node_p node = hook->node;
449add85a1dSArchie Cobbs 	const priv_p priv = node->private;
450add85a1dSArchie Cobbs 
451add85a1dSArchie Cobbs 	/* Zero out hook pointer */
452add85a1dSArchie Cobbs 	if (hook == priv->upper)
453add85a1dSArchie Cobbs 		priv->upper = NULL;
454add85a1dSArchie Cobbs 	else if (hook == priv->lower)
455add85a1dSArchie Cobbs 		priv->lower = NULL;
456add85a1dSArchie Cobbs 	else
457add85a1dSArchie Cobbs 		panic("%s: unknown hook", __FUNCTION__);
458add85a1dSArchie Cobbs 
459add85a1dSArchie Cobbs 	/* Go away if no longer connected to anything */
460add85a1dSArchie Cobbs 	if (node->numhooks == 0)
461add85a1dSArchie Cobbs 		ng_rmnode(node);
462add85a1dSArchie Cobbs 	return (0);
463add85a1dSArchie Cobbs }
464add85a1dSArchie Cobbs 
465add85a1dSArchie Cobbs /*************************************************************************
466add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
467add85a1dSArchie Cobbs *************************************************************************/
468add85a1dSArchie Cobbs 
469add85a1dSArchie Cobbs /*
470add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
471add85a1dSArchie Cobbs  */
472add85a1dSArchie Cobbs static int
473add85a1dSArchie Cobbs ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta)
474add85a1dSArchie Cobbs {
475add85a1dSArchie Cobbs 	const priv_p priv = node->private;
476add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
477add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
478add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
479add85a1dSArchie Cobbs 	int grelen, error;
480add85a1dSArchie Cobbs 
4819bee7adfSArchie Cobbs 	/* Check if there's data */
4829bee7adfSArchie Cobbs 	if (m != NULL) {
4839bee7adfSArchie Cobbs 
484add85a1dSArchie Cobbs 		/* Is our transmit window full? */
4859bee7adfSArchie Cobbs 		if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck)
4869bee7adfSArchie Cobbs 		      >= a->xmitWin) {
4879bee7adfSArchie Cobbs 			priv->stats.xmitDrops++;
488add85a1dSArchie Cobbs 			NG_FREE_DATA(m, meta);
489add85a1dSArchie Cobbs 			return (ENOBUFS);
490add85a1dSArchie Cobbs 		}
491add85a1dSArchie Cobbs 
492add85a1dSArchie Cobbs 		/* Sanity check frame length */
493add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
4949bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
495add85a1dSArchie Cobbs 			NG_FREE_DATA(m, meta);
496add85a1dSArchie Cobbs 			return (EMSGSIZE);
497add85a1dSArchie Cobbs 		}
4989bee7adfSArchie Cobbs 	} else
4999bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
500add85a1dSArchie Cobbs 
501add85a1dSArchie Cobbs 	/* Build GRE header */
502add85a1dSArchie Cobbs 	((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE);
503add85a1dSArchie Cobbs 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
504add85a1dSArchie Cobbs 	gre->cid = htons(priv->conf.peerCid);
505add85a1dSArchie Cobbs 
506add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
507add85a1dSArchie Cobbs 	if (m != NULL) {
508add85a1dSArchie Cobbs 		gre->hasSeq = 1;
509add85a1dSArchie Cobbs 		a->timeSent[priv->xmitSeq - priv->recvAck]
510add85a1dSArchie Cobbs 		    = ng_pptpgre_time(node);
511add85a1dSArchie Cobbs 		priv->xmitSeq++;
512add85a1dSArchie Cobbs 		gre->data[0] = htonl(priv->xmitSeq);
513add85a1dSArchie Cobbs 	}
514add85a1dSArchie Cobbs 
515add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
516678f9e33SArchie Cobbs 	if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) {
517add85a1dSArchie Cobbs 		gre->hasAck = 1;
518678f9e33SArchie Cobbs 		gre->data[gre->hasSeq] = htonl(priv->recvSeq);
519add85a1dSArchie Cobbs 		priv->xmitAck = priv->recvSeq;
520da010626SArchie Cobbs 		if (a->sackTimerPtr != NULL) {
521da010626SArchie Cobbs 			untimeout(ng_pptpgre_send_ack_timeout,
522da010626SArchie Cobbs 			    a->sackTimerPtr, a->sackTimer);
5233cd7db22SArchie Cobbs 			a->sackTimerPtr = NULL;
524add85a1dSArchie Cobbs 		}
525da010626SArchie Cobbs 	}
526add85a1dSArchie Cobbs 
527add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
528add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
529add85a1dSArchie Cobbs 	if (m == NULL) {
530add85a1dSArchie Cobbs 		MGETHDR(m, M_DONTWAIT, MT_DATA);
531add85a1dSArchie Cobbs 		if (m == NULL) {
532678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
533add85a1dSArchie Cobbs 			NG_FREE_META(meta);
534add85a1dSArchie Cobbs 			return (ENOBUFS);
535add85a1dSArchie Cobbs 		}
536add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
537add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
538add85a1dSArchie Cobbs 	} else {
539add85a1dSArchie Cobbs 		M_PREPEND(m, grelen, M_NOWAIT);
540add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
541add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
542678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
543add85a1dSArchie Cobbs 			NG_FREE_META(meta);
544add85a1dSArchie Cobbs 			return (ENOBUFS);
545add85a1dSArchie Cobbs 		}
546add85a1dSArchie Cobbs 	}
547add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
548add85a1dSArchie Cobbs 
5499bee7adfSArchie Cobbs 	/* Update stats */
5509bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
5519bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
5529bee7adfSArchie Cobbs 
553add85a1dSArchie Cobbs 	/* Deliver packet */
554add85a1dSArchie Cobbs 	NG_SEND_DATA(error, priv->lower, m, meta);
555678f9e33SArchie Cobbs 
556da010626SArchie Cobbs 	/* Start receive ACK timer if data was sent and not already running */
557678f9e33SArchie Cobbs 	if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1)
558678f9e33SArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
559add85a1dSArchie Cobbs 	return (error);
560add85a1dSArchie Cobbs }
561add85a1dSArchie Cobbs 
562add85a1dSArchie Cobbs /*
563add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
564add85a1dSArchie Cobbs  */
565add85a1dSArchie Cobbs static int
566add85a1dSArchie Cobbs ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta)
567add85a1dSArchie Cobbs {
568add85a1dSArchie Cobbs 	const priv_p priv = node->private;
569add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
570add85a1dSArchie Cobbs 	struct greheader *gre;
571add85a1dSArchie Cobbs 	struct ip *ip;
572add85a1dSArchie Cobbs 	int error = 0;
573add85a1dSArchie Cobbs 
5749bee7adfSArchie Cobbs 	/* Update stats */
5759bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
5769bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
5779bee7adfSArchie Cobbs 
578add85a1dSArchie Cobbs 	/* Sanity check packet length */
579add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
5809bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
581add85a1dSArchie Cobbs bad:
582add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);
583add85a1dSArchie Cobbs 		return (EINVAL);
584add85a1dSArchie Cobbs 	}
585add85a1dSArchie Cobbs 
586add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
587add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
588add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
589678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
590add85a1dSArchie Cobbs 		NG_FREE_META(meta);
591add85a1dSArchie Cobbs 		return (ENOBUFS);
592add85a1dSArchie Cobbs 	}
593add85a1dSArchie Cobbs 	ip = mtod(m, struct ip *);
594add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
595add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
596add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
597678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
598add85a1dSArchie Cobbs 			NG_FREE_META(meta);
599add85a1dSArchie Cobbs 			return (ENOBUFS);
600add85a1dSArchie Cobbs 		}
601add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
602add85a1dSArchie Cobbs 	}
603add85a1dSArchie Cobbs 	gre = (struct greheader *)((u_char *)ip + iphlen);
604add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
6059bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
6069bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
607add85a1dSArchie Cobbs 		goto bad;
6089bee7adfSArchie Cobbs 	}
609add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
610add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
611678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
612add85a1dSArchie Cobbs 			NG_FREE_META(meta);
613add85a1dSArchie Cobbs 			return (ENOBUFS);
614add85a1dSArchie Cobbs 		}
615add85a1dSArchie Cobbs 		ip = mtod(m, struct ip *);
616add85a1dSArchie Cobbs 		gre = (struct greheader *)((u_char *)ip + iphlen);
617add85a1dSArchie Cobbs 	}
618add85a1dSArchie Cobbs 
619add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
620add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
621add85a1dSArchie Cobbs 	    - (iphlen + grelen + (u_int16_t)ntohs(gre->length));
6229bee7adfSArchie Cobbs 	if (extralen < 0) {
6239bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
624add85a1dSArchie Cobbs 		goto bad;
6259bee7adfSArchie Cobbs 	}
6269bee7adfSArchie Cobbs 	if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
6279bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
628add85a1dSArchie Cobbs 		goto bad;
6299bee7adfSArchie Cobbs 	}
6309bee7adfSArchie Cobbs 	if (ntohs(gre->cid) != priv->conf.cid) {
6319bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
632add85a1dSArchie Cobbs 		goto bad;
6339bee7adfSArchie Cobbs 	}
634add85a1dSArchie Cobbs 
635add85a1dSArchie Cobbs 	/* Look for peer ack */
636add85a1dSArchie Cobbs 	if (gre->hasAck) {
637add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
638add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
639add85a1dSArchie Cobbs 		const int index = ack - priv->recvAck - 1;
640add85a1dSArchie Cobbs 		const long sample = ng_pptpgre_time(node) - a->timeSent[index];
641add85a1dSArchie Cobbs 		long diff;
642add85a1dSArchie Cobbs 
643add85a1dSArchie Cobbs 		/* Sanity check ack value */
6449bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) {
6459bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
6469bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
6479bee7adfSArchie Cobbs 		}
6489bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0)
6499bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
650add85a1dSArchie Cobbs 		priv->recvAck = ack;
651add85a1dSArchie Cobbs 
652add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
653add85a1dSArchie Cobbs 		diff = sample - a->rtt;
654add85a1dSArchie Cobbs 		a->rtt += PPTP_ACK_ALPHA(diff);
655add85a1dSArchie Cobbs 		if (diff < 0)
656add85a1dSArchie Cobbs 			diff = -diff;
657add85a1dSArchie Cobbs 		a->dev += PPTP_ACK_BETA(diff - a->dev);
658e962a823SArchie Cobbs 		a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
659add85a1dSArchie Cobbs 		if (a->ato > PPTP_MAX_TIMEOUT)
660add85a1dSArchie Cobbs 			a->ato = PPTP_MAX_TIMEOUT;
661e962a823SArchie Cobbs 		if (a->ato < PPTP_MIN_TIMEOUT)
662e962a823SArchie Cobbs 			a->ato = PPTP_MIN_TIMEOUT;
663e962a823SArchie Cobbs 
664e962a823SArchie Cobbs 		/* Shift packet transmit times in our transmit window */
665add85a1dSArchie Cobbs 		ovbcopy(a->timeSent + index + 1, a->timeSent,
666add85a1dSArchie Cobbs 		    sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1)));
667e962a823SArchie Cobbs 
668e962a823SArchie Cobbs 		/* If we sent an entire window, increase window size by one */
6699bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0
6709bee7adfSArchie Cobbs 		    && a->xmitWin < PPTP_XMIT_WIN) {
671add85a1dSArchie Cobbs 			a->xmitWin++;
672add85a1dSArchie Cobbs 			a->winAck = ack + a->xmitWin;
673add85a1dSArchie Cobbs 		}
674add85a1dSArchie Cobbs 
6759bee7adfSArchie Cobbs 		/* Stop/(re)start receive ACK timer as necessary */
676da010626SArchie Cobbs 		if (a->rackTimerPtr != NULL) {
677da010626SArchie Cobbs 			untimeout(ng_pptpgre_recv_ack_timeout,
678da010626SArchie Cobbs 			    a->rackTimerPtr, a->rackTimer);
679678f9e33SArchie Cobbs 			a->rackTimerPtr = NULL;
680da010626SArchie Cobbs 		}
681678f9e33SArchie Cobbs 		if (priv->recvAck != priv->xmitSeq)
682add85a1dSArchie Cobbs 			ng_pptpgre_start_recv_ack_timer(node);
683add85a1dSArchie Cobbs 	}
6849bee7adfSArchie Cobbs badAck:
685add85a1dSArchie Cobbs 
686add85a1dSArchie Cobbs 	/* See if frame contains any data */
687add85a1dSArchie Cobbs 	if (gre->hasSeq) {
688add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
689add85a1dSArchie Cobbs 		const u_int32_t seq = ntohl(gre->data[0]);
690add85a1dSArchie Cobbs 
691add85a1dSArchie Cobbs 		/* Sanity check sequence number */
6929bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) {
6939bee7adfSArchie Cobbs 			if (seq == priv->recvSeq)
6949bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
6959bee7adfSArchie Cobbs 			else
6969bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
6979bee7adfSArchie Cobbs 			goto bad;		/* out-of-order or dup */
6989bee7adfSArchie Cobbs 		}
699add85a1dSArchie Cobbs 		priv->recvSeq = seq;
700add85a1dSArchie Cobbs 
701add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
7023cd7db22SArchie Cobbs 		if (a->sackTimerPtr == NULL) {
703da010626SArchie Cobbs 			int maxWait;
704add85a1dSArchie Cobbs 
705da010626SArchie Cobbs 			/* Take 1/4 of the estimated round trip time */
706da010626SArchie Cobbs 			maxWait = (a->rtt >> 2);
707add85a1dSArchie Cobbs 
708678f9e33SArchie Cobbs 			/* If delayed ACK is disabled, send it now */
709da010626SArchie Cobbs 			if (!priv->conf.enableDelayedAck
710da010626SArchie Cobbs 			    || maxWait < PPTP_MIN_ACK_DELAY)
711add85a1dSArchie Cobbs 				ng_pptpgre_xmit(node, NULL, NULL);
712add85a1dSArchie Cobbs 			else {			/* send the ack later */
7133cd7db22SArchie Cobbs 				if (maxWait > PPTP_MAX_ACK_DELAY)
7143cd7db22SArchie Cobbs 					maxWait = PPTP_MAX_ACK_DELAY;
7153cd7db22SArchie Cobbs 				ng_pptpgre_start_send_ack_timer(node, maxWait);
716add85a1dSArchie Cobbs 			}
717add85a1dSArchie Cobbs 		}
718add85a1dSArchie Cobbs 
719add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
720add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
721add85a1dSArchie Cobbs 		if (extralen > 0)
722add85a1dSArchie Cobbs 			m_adj(m, -extralen);
723add85a1dSArchie Cobbs 
724add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
725add85a1dSArchie Cobbs 		NG_SEND_DATA(error, priv->upper, m, meta);
7269bee7adfSArchie Cobbs 	} else {
7279bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
728add85a1dSArchie Cobbs 		NG_FREE_DATA(m, meta);		/* no data to deliver */
7299bee7adfSArchie Cobbs 	}
730add85a1dSArchie Cobbs 	return (error);
731add85a1dSArchie Cobbs }
732add85a1dSArchie Cobbs 
733add85a1dSArchie Cobbs /*************************************************************************
734add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
735add85a1dSArchie Cobbs *************************************************************************/
736add85a1dSArchie Cobbs 
737add85a1dSArchie Cobbs /*
7389bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
739add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
740add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
741add85a1dSArchie Cobbs  * recv ack timer, if any.
742add85a1dSArchie Cobbs  */
743add85a1dSArchie Cobbs static void
744add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node)
745add85a1dSArchie Cobbs {
746add85a1dSArchie Cobbs 	const priv_p priv = node->private;
747add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
748678f9e33SArchie Cobbs 	int remain, ticks;
749add85a1dSArchie Cobbs 
750add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
751add85a1dSArchie Cobbs 	   and reset the timer to that time. */
752da010626SArchie Cobbs 	KASSERT(a->rackTimerPtr == NULL, ("%s: rackTimer", __FUNCTION__));
753add85a1dSArchie Cobbs 	remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node);
754add85a1dSArchie Cobbs 	if (remain < 0)
755add85a1dSArchie Cobbs 		remain = 0;
756678f9e33SArchie Cobbs #ifdef DEBUG_RAT
757678f9e33SArchie Cobbs 	a->timerLength = remain;
758678f9e33SArchie Cobbs 	a->timerStart = ng_pptpgre_time(node);
759678f9e33SArchie Cobbs #endif
7609bee7adfSArchie Cobbs 
7613cd7db22SArchie Cobbs 	/* Start new timer */
7623cd7db22SArchie Cobbs 	MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
763678f9e33SArchie Cobbs 	if (a->rackTimerPtr == NULL) {
764678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
7653cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
766678f9e33SArchie Cobbs 	}
7673cd7db22SArchie Cobbs 	*a->rackTimerPtr = node;	/* insures the correct timeout event */
7689bee7adfSArchie Cobbs 	node->refs++;
769678f9e33SArchie Cobbs 
770678f9e33SArchie Cobbs 	/* Be conservative: timeout() can return up to 1 tick early */
771678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
7723cd7db22SArchie Cobbs 	a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout,
773678f9e33SArchie Cobbs 	    a->rackTimerPtr, ticks);
774add85a1dSArchie Cobbs }
775add85a1dSArchie Cobbs 
776add85a1dSArchie Cobbs /*
777add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
778add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
779add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
780add85a1dSArchie Cobbs  */
781add85a1dSArchie Cobbs static void
782add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg)
783add85a1dSArchie Cobbs {
7844164c447SArchie Cobbs 	int s = splnet();
7853cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
786add85a1dSArchie Cobbs 	const priv_p priv = node->private;
787add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
788add85a1dSArchie Cobbs 
7893cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
7903cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
7913cd7db22SArchie Cobbs 	KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__));
7923cd7db22SArchie Cobbs 	if ((node->flags & NG_INVALID) != 0) {	/* shutdown race condition */
7939bee7adfSArchie Cobbs 		ng_unref(node);
7949bee7adfSArchie Cobbs 		splx(s);
7959bee7adfSArchie Cobbs 		return;
7969bee7adfSArchie Cobbs 	}
7979bee7adfSArchie Cobbs 	ng_unref(node);
7983cd7db22SArchie Cobbs 	if (arg != a->rackTimerPtr) {	/* timer stopped race condition */
7993cd7db22SArchie Cobbs 		splx(s);
8003cd7db22SArchie Cobbs 		return;
8013cd7db22SArchie Cobbs 	}
8023cd7db22SArchie Cobbs 	a->rackTimerPtr = NULL;
8039bee7adfSArchie Cobbs 
804add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
8059bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
806add85a1dSArchie Cobbs 	a->rtt = PPTP_ACK_DELTA(a->rtt);
807add85a1dSArchie Cobbs 	a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
808add85a1dSArchie Cobbs 	if (a->ato > PPTP_MAX_TIMEOUT)
809add85a1dSArchie Cobbs 		a->ato = PPTP_MAX_TIMEOUT;
810e962a823SArchie Cobbs 	if (a->ato < PPTP_MIN_TIMEOUT)
811e962a823SArchie Cobbs 		a->ato = PPTP_MIN_TIMEOUT;
812add85a1dSArchie Cobbs 
813678f9e33SArchie Cobbs #ifdef DEBUG_RAT
814678f9e33SArchie Cobbs     log(LOG_DEBUG,
815678f9e33SArchie Cobbs 	"RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n",
816678f9e33SArchie Cobbs 	(int)ng_pptpgre_time(node), priv->recvAck + 1,
817678f9e33SArchie Cobbs 	(int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato);
818678f9e33SArchie Cobbs #endif
819678f9e33SArchie Cobbs 
820e962a823SArchie Cobbs 	/* Reset ack and sliding window */
821e962a823SArchie Cobbs 	priv->recvAck = priv->xmitSeq;		/* pretend we got the ack */
822e962a823SArchie Cobbs 	a->xmitWin = (a->xmitWin + 1) / 2;	/* shrink transmit window */
823e962a823SArchie Cobbs 	a->winAck = priv->recvAck + a->xmitWin;	/* reset win expand time */
8244164c447SArchie Cobbs 	splx(s);
825add85a1dSArchie Cobbs }
826add85a1dSArchie Cobbs 
827add85a1dSArchie Cobbs /*
8289bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
8299bee7adfSArchie Cobbs  * already running.
8309bee7adfSArchie Cobbs  */
8319bee7adfSArchie Cobbs static void
832da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout)
8339bee7adfSArchie Cobbs {
8349bee7adfSArchie Cobbs 	const priv_p priv = node->private;
8359bee7adfSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
836678f9e33SArchie Cobbs 	int ticks;
8379bee7adfSArchie Cobbs 
8383cd7db22SArchie Cobbs 	/* Start new timer */
8393cd7db22SArchie Cobbs 	KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __FUNCTION__));
8403cd7db22SArchie Cobbs 	MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT);
841678f9e33SArchie Cobbs 	if (a->sackTimerPtr == NULL) {
842678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
8433cd7db22SArchie Cobbs 		return;			/* XXX potential hang here */
844678f9e33SArchie Cobbs 	}
8453cd7db22SArchie Cobbs 	*a->sackTimerPtr = node;
8469bee7adfSArchie Cobbs 	node->refs++;
847678f9e33SArchie Cobbs 
848678f9e33SArchie Cobbs 	/* Be conservative: timeout() can return up to 1 tick early */
849678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
8503cd7db22SArchie Cobbs 	a->sackTimer = timeout(ng_pptpgre_send_ack_timeout,
851678f9e33SArchie Cobbs 	    a->sackTimerPtr, ticks);
8529bee7adfSArchie Cobbs }
8539bee7adfSArchie Cobbs 
8549bee7adfSArchie Cobbs /*
855add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
856add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
857add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
858add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
859add85a1dSArchie Cobbs  */
860add85a1dSArchie Cobbs static void
861add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg)
862add85a1dSArchie Cobbs {
8634164c447SArchie Cobbs 	int s = splnet();
8643cd7db22SArchie Cobbs 	const node_p node = *((node_p *)arg);
865add85a1dSArchie Cobbs 	const priv_p priv = node->private;
866add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
867add85a1dSArchie Cobbs 
8683cd7db22SArchie Cobbs 	/* This complicated stuff is needed to avoid race conditions */
8693cd7db22SArchie Cobbs 	FREE(arg, M_NETGRAPH);
8703cd7db22SArchie Cobbs 	KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__));
8713cd7db22SArchie Cobbs 	if ((node->flags & NG_INVALID) != 0) {	/* shutdown race condition */
8729bee7adfSArchie Cobbs 		ng_unref(node);
8739bee7adfSArchie Cobbs 		splx(s);
8749bee7adfSArchie Cobbs 		return;
8759bee7adfSArchie Cobbs 	}
8769bee7adfSArchie Cobbs 	ng_unref(node);
8773cd7db22SArchie Cobbs 	if (a->sackTimerPtr != arg) {	/* timer stopped race condition */
8783cd7db22SArchie Cobbs 		splx(s);
8793cd7db22SArchie Cobbs 		return;
8803cd7db22SArchie Cobbs 	}
8813cd7db22SArchie Cobbs 	a->sackTimerPtr = NULL;
8829bee7adfSArchie Cobbs 
8839bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
884add85a1dSArchie Cobbs   	ng_pptpgre_xmit(node, NULL, NULL);
8854164c447SArchie Cobbs 	splx(s);
886add85a1dSArchie Cobbs }
887add85a1dSArchie Cobbs 
888add85a1dSArchie Cobbs /*************************************************************************
889add85a1dSArchie Cobbs 		    MISC FUNCTIONS
890add85a1dSArchie Cobbs *************************************************************************/
891add85a1dSArchie Cobbs 
892add85a1dSArchie Cobbs /*
893add85a1dSArchie Cobbs  * Reset state
894add85a1dSArchie Cobbs  */
895add85a1dSArchie Cobbs static void
896add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node)
897add85a1dSArchie Cobbs {
898add85a1dSArchie Cobbs 	const priv_p priv = node->private;
899add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
900add85a1dSArchie Cobbs 
901add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
902add85a1dSArchie Cobbs 	a->ato = PPTP_MAX_TIMEOUT;
903add85a1dSArchie Cobbs 	a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
904add85a1dSArchie Cobbs 	if (a->rtt < PPTP_MIN_RTT)
905add85a1dSArchie Cobbs 		a->rtt = PPTP_MIN_RTT;
906add85a1dSArchie Cobbs 	a->dev = 0;
907add85a1dSArchie Cobbs 	a->xmitWin = (priv->conf.recvWin + 1) / 2;
908e962a823SArchie Cobbs 	if (a->xmitWin < 2)		/* often the first packet is lost */
909e962a823SArchie Cobbs 		a->xmitWin = 2;		/*   because the peer isn't ready */
910add85a1dSArchie Cobbs 	if (a->xmitWin > PPTP_XMIT_WIN)
911add85a1dSArchie Cobbs 		a->xmitWin = PPTP_XMIT_WIN;
912add85a1dSArchie Cobbs 	a->winAck = a->xmitWin;
913add85a1dSArchie Cobbs 
914add85a1dSArchie Cobbs 	/* Reset sequence numbers */
9153cd7db22SArchie Cobbs 	priv->recvSeq = ~0;
9163cd7db22SArchie Cobbs 	priv->recvAck = ~0;
9173cd7db22SArchie Cobbs 	priv->xmitSeq = ~0;
9183cd7db22SArchie Cobbs 	priv->xmitAck = ~0;
919add85a1dSArchie Cobbs 
920add85a1dSArchie Cobbs 	/* Reset start time */
9219bee7adfSArchie Cobbs 	getmicrouptime(&priv->startTime);
9229bee7adfSArchie Cobbs 
9239bee7adfSArchie Cobbs 	/* Reset stats */
9249bee7adfSArchie Cobbs 	bzero(&priv->stats, sizeof(priv->stats));
925add85a1dSArchie Cobbs 
926da010626SArchie Cobbs 	/* Stop timers */
927da010626SArchie Cobbs 	if (a->sackTimerPtr != NULL) {
928da010626SArchie Cobbs 		untimeout(ng_pptpgre_send_ack_timeout,
929da010626SArchie Cobbs 		    a->sackTimerPtr, a->sackTimer);
9303cd7db22SArchie Cobbs 		a->sackTimerPtr = NULL;
931da010626SArchie Cobbs 	}
932da010626SArchie Cobbs 	if (a->rackTimerPtr != NULL) {
933da010626SArchie Cobbs 		untimeout(ng_pptpgre_recv_ack_timeout,
934da010626SArchie Cobbs 		    a->rackTimerPtr, a->rackTimer);
9353cd7db22SArchie Cobbs 		a->rackTimerPtr = NULL;
936add85a1dSArchie Cobbs 	}
937da010626SArchie Cobbs }
938add85a1dSArchie Cobbs 
939add85a1dSArchie Cobbs /*
940add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
941add85a1dSArchie Cobbs  */
942add85a1dSArchie Cobbs static pptptime_t
943add85a1dSArchie Cobbs ng_pptpgre_time(node_p node)
944add85a1dSArchie Cobbs {
945add85a1dSArchie Cobbs 	const priv_p priv = node->private;
946add85a1dSArchie Cobbs 	struct timeval tv;
947678f9e33SArchie Cobbs 	pptptime_t t;
948add85a1dSArchie Cobbs 
949678f9e33SArchie Cobbs 	microuptime(&tv);
950add85a1dSArchie Cobbs 	if (tv.tv_sec < priv->startTime.tv_sec
951add85a1dSArchie Cobbs 	    || (tv.tv_sec == priv->startTime.tv_sec
952add85a1dSArchie Cobbs 	      && tv.tv_usec < priv->startTime.tv_usec))
953add85a1dSArchie Cobbs 		return (0);
954add85a1dSArchie Cobbs 	timevalsub(&tv, &priv->startTime);
955678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
956678f9e33SArchie Cobbs 	t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
957678f9e33SArchie Cobbs 	return(t);
958add85a1dSArchie Cobbs }
959add85a1dSArchie Cobbs 
960