xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision f2ba84d72ddf0f4c5ff41ac16496695049905963)
1add85a1dSArchie Cobbs /*
2add85a1dSArchie Cobbs  * ng_pptpgre.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6add85a1dSArchie Cobbs  * Copyright (c) 1996-1999 Whistle Communications, Inc.
7add85a1dSArchie Cobbs  * All rights reserved.
8add85a1dSArchie Cobbs  *
9add85a1dSArchie Cobbs  * Subject to the following obligations and disclaimer of warranty, use and
10add85a1dSArchie Cobbs  * redistribution of this software, in source or object code forms, with or
11add85a1dSArchie Cobbs  * without modifications are expressly permitted by Whistle Communications;
12add85a1dSArchie Cobbs  * provided, however, that:
13add85a1dSArchie Cobbs  * 1. Any and all reproductions of the source or object code must include the
14add85a1dSArchie Cobbs  *    copyright notice above and the following disclaimer of warranties; and
15add85a1dSArchie Cobbs  * 2. No rights are granted, in any manner or form, to use Whistle
16add85a1dSArchie Cobbs  *    Communications, Inc. trademarks, including the mark "WHISTLE
17add85a1dSArchie Cobbs  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18add85a1dSArchie Cobbs  *    such appears in the above copyright notice or in the software.
19add85a1dSArchie Cobbs  *
20add85a1dSArchie Cobbs  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21add85a1dSArchie Cobbs  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22add85a1dSArchie Cobbs  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23add85a1dSArchie Cobbs  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24add85a1dSArchie Cobbs  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25add85a1dSArchie Cobbs  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26add85a1dSArchie Cobbs  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27add85a1dSArchie Cobbs  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28add85a1dSArchie Cobbs  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29add85a1dSArchie Cobbs  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30add85a1dSArchie Cobbs  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31add85a1dSArchie Cobbs  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32add85a1dSArchie Cobbs  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33add85a1dSArchie Cobbs  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34add85a1dSArchie Cobbs  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35add85a1dSArchie Cobbs  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36add85a1dSArchie Cobbs  * OF SUCH DAMAGE.
37add85a1dSArchie Cobbs  *
38cc3bbd68SJulian Elischer  * Author: Archie Cobbs <archie@freebsd.org>
39add85a1dSArchie Cobbs  *
40add85a1dSArchie Cobbs  * $FreeBSD$
41add85a1dSArchie Cobbs  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
42add85a1dSArchie Cobbs  */
43add85a1dSArchie Cobbs 
44add85a1dSArchie Cobbs /*
45add85a1dSArchie Cobbs  * PPTP/GRE netgraph node type.
46add85a1dSArchie Cobbs  *
47add85a1dSArchie Cobbs  * This node type does the GRE encapsulation as specified for the PPTP
48add85a1dSArchie Cobbs  * protocol (RFC 2637, section 4).  This includes sequencing and
49add85a1dSArchie Cobbs  * retransmission of frames, but not the actual packet delivery nor
50add85a1dSArchie Cobbs  * any of the TCP control stream protocol.
51add85a1dSArchie Cobbs  *
52add85a1dSArchie Cobbs  * The "upper" hook of this node is suitable for attaching to a "ppp"
53add85a1dSArchie Cobbs  * node link hook.  The "lower" hook of this node is suitable for attaching
54add85a1dSArchie Cobbs  * to a "ksocket" node on hook "inet/raw/gre".
55add85a1dSArchie Cobbs  */
56add85a1dSArchie Cobbs 
57add85a1dSArchie Cobbs #include <sys/param.h>
58add85a1dSArchie Cobbs #include <sys/systm.h>
59add85a1dSArchie Cobbs #include <sys/kernel.h>
60add85a1dSArchie Cobbs #include <sys/time.h>
61f2ba84d7SGleb Smirnoff #include <sys/lock.h>
62add85a1dSArchie Cobbs #include <sys/malloc.h>
63f2ba84d7SGleb Smirnoff #include <sys/mbuf.h>
64f2ba84d7SGleb Smirnoff #include <sys/mutex.h>
65add85a1dSArchie Cobbs #include <sys/errno.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 */
1244a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
1250306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 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 */
1484a48abb2SArchie Cobbs 	struct callout		sackTimer;	/* send ack timer */
1494a48abb2SArchie Cobbs 	struct callout		rackTimer;	/* recv ack timer */
1503cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
151add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
152678f9e33SArchie Cobbs #ifdef DEBUG_RAT
153da010626SArchie Cobbs 	pptptime_t		timerStart;	/* when rackTimer started */
154da010626SArchie Cobbs 	pptptime_t		timerLength;	/* rackTimer duration */
155678f9e33SArchie Cobbs #endif
156add85a1dSArchie Cobbs };
157add85a1dSArchie Cobbs 
158add85a1dSArchie Cobbs /* Node private data */
159add85a1dSArchie Cobbs struct ng_pptpgre_private {
160add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
161add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
162add85a1dSArchie Cobbs 	struct ng_pptpgre_conf	conf;		/* configuration info */
163add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp	ackp;		/* packet transmit ack state */
164add85a1dSArchie Cobbs 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
165add85a1dSArchie Cobbs 	u_int32_t		xmitSeq;	/* last seq # we sent */
166add85a1dSArchie Cobbs 	u_int32_t		recvAck;	/* last seq # peer ack'd */
167add85a1dSArchie Cobbs 	u_int32_t		xmitAck;	/* last seq # we ack'd */
168add85a1dSArchie Cobbs 	struct timeval		startTime;	/* time node was created */
1699bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
170f2ba84d7SGleb Smirnoff 	struct mtx		mtx;		/* node mutex */
171add85a1dSArchie Cobbs };
172add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
173add85a1dSArchie Cobbs 
174add85a1dSArchie Cobbs /* Netgraph node methods */
175add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
176add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
177069154d5SJulian Elischer static ng_shutdown_t	ng_pptpgre_shutdown;
178add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
179add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
180add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
181add85a1dSArchie Cobbs 
182add85a1dSArchie Cobbs /* Helper functions */
183069154d5SJulian Elischer static int	ng_pptpgre_xmit(node_p node, item_p item);
184069154d5SJulian Elischer static int	ng_pptpgre_recv(node_p node, item_p item);
185da010626SArchie Cobbs static void	ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout);
1864a48abb2SArchie Cobbs static void	ng_pptpgre_stop_send_ack_timer(node_p node);
187add85a1dSArchie Cobbs static void	ng_pptpgre_start_recv_ack_timer(node_p node);
1884a48abb2SArchie Cobbs static void	ng_pptpgre_stop_recv_ack_timer(node_p node);
189089323f3SGleb Smirnoff static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
190089323f3SGleb Smirnoff 		    void *arg1, int arg2);
191089323f3SGleb Smirnoff static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
192089323f3SGleb Smirnoff 		    void *arg1, int arg2);
193add85a1dSArchie Cobbs static void	ng_pptpgre_reset(node_p node);
194add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node);
195add85a1dSArchie Cobbs 
196add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
197f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
198f0184ff8SArchie Cobbs 	= NG_PPTPGRE_CONF_TYPE_INFO;
199add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
200add85a1dSArchie Cobbs 	&ng_parse_struct_type,
201f0184ff8SArchie Cobbs 	&ng_pptpgre_conf_type_fields,
202add85a1dSArchie Cobbs };
203add85a1dSArchie Cobbs 
2049bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
205f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
206f0184ff8SArchie Cobbs 	= NG_PPTPGRE_STATS_TYPE_INFO;
2079bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2089bee7adfSArchie Cobbs 	&ng_parse_struct_type,
209f0184ff8SArchie Cobbs 	&ng_pptpgre_stats_type_fields
2109bee7adfSArchie Cobbs };
2119bee7adfSArchie Cobbs 
212add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
213add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
214add85a1dSArchie Cobbs 	{
215add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
216add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
217add85a1dSArchie Cobbs 	  "setconfig",
218add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
219add85a1dSArchie Cobbs 	  NULL
220add85a1dSArchie Cobbs 	},
221add85a1dSArchie Cobbs 	{
222add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
223add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
224add85a1dSArchie Cobbs 	  "getconfig",
225add85a1dSArchie Cobbs 	  NULL,
226add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
227add85a1dSArchie Cobbs 	},
2289bee7adfSArchie Cobbs 	{
2299bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2309bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2319bee7adfSArchie Cobbs 	  "getstats",
2329bee7adfSArchie Cobbs 	  NULL,
2339bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2349bee7adfSArchie Cobbs 	},
2359bee7adfSArchie Cobbs 	{
2369bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2379bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2389bee7adfSArchie Cobbs 	  "clrstats",
2399bee7adfSArchie Cobbs 	  NULL,
2409bee7adfSArchie Cobbs 	  NULL
2419bee7adfSArchie Cobbs 	},
2429bee7adfSArchie Cobbs 	{
2439bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2449bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2459bee7adfSArchie Cobbs 	  "getclrstats",
2469bee7adfSArchie Cobbs 	  NULL,
2479bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2489bee7adfSArchie Cobbs 	},
249add85a1dSArchie Cobbs 	{ 0 }
250add85a1dSArchie Cobbs };
251add85a1dSArchie Cobbs 
252add85a1dSArchie Cobbs /* Node type descriptor */
253add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
254f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
255f8aae777SJulian Elischer 	.name =		NG_PPTPGRE_NODE_TYPE,
256f8aae777SJulian Elischer 	.constructor =	ng_pptpgre_constructor,
257f8aae777SJulian Elischer 	.rcvmsg =	ng_pptpgre_rcvmsg,
258f8aae777SJulian Elischer 	.shutdown =	ng_pptpgre_shutdown,
259f8aae777SJulian Elischer 	.newhook =	ng_pptpgre_newhook,
260f8aae777SJulian Elischer 	.rcvdata =	ng_pptpgre_rcvdata,
261f8aae777SJulian Elischer 	.disconnect =	ng_pptpgre_disconnect,
262f8aae777SJulian Elischer 	.cmdlist =	ng_pptpgre_cmdlist,
263add85a1dSArchie Cobbs };
264add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
265add85a1dSArchie Cobbs 
266add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
267add85a1dSArchie Cobbs 
268add85a1dSArchie Cobbs /************************************************************************
269add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
270add85a1dSArchie Cobbs  ************************************************************************/
271add85a1dSArchie Cobbs 
272add85a1dSArchie Cobbs /*
273add85a1dSArchie Cobbs  * Node type constructor
274add85a1dSArchie Cobbs  */
275add85a1dSArchie Cobbs static int
276069154d5SJulian Elischer ng_pptpgre_constructor(node_p node)
277add85a1dSArchie Cobbs {
278add85a1dSArchie Cobbs 	priv_p priv;
279add85a1dSArchie Cobbs 
280add85a1dSArchie Cobbs 	/* Allocate private structure */
28199cdf4ccSDavid Malone 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
282add85a1dSArchie Cobbs 	if (priv == NULL)
283add85a1dSArchie Cobbs 		return (ENOMEM);
284add85a1dSArchie Cobbs 
28530400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
286add85a1dSArchie Cobbs 
287add85a1dSArchie Cobbs 	/* Initialize state */
288f2ba84d7SGleb Smirnoff 	mtx_init(&priv->mtx, "ng_pptp", NULL, MTX_DEF);
289f2ba84d7SGleb Smirnoff 	ng_callout_init_mtx(&priv->ackp.sackTimer, &priv->mtx);
290f2ba84d7SGleb Smirnoff 	ng_callout_init_mtx(&priv->ackp.rackTimer, &priv->mtx);
291add85a1dSArchie Cobbs 
292add85a1dSArchie Cobbs 	/* Done */
293add85a1dSArchie Cobbs 	return (0);
294add85a1dSArchie Cobbs }
295add85a1dSArchie Cobbs 
296add85a1dSArchie Cobbs /*
297add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
298add85a1dSArchie Cobbs  */
299add85a1dSArchie Cobbs static int
300add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
301add85a1dSArchie Cobbs {
30230400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
303add85a1dSArchie Cobbs 	hook_p *hookPtr;
304add85a1dSArchie Cobbs 
305add85a1dSArchie Cobbs 	/* Check hook name */
306add85a1dSArchie Cobbs 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0)
307add85a1dSArchie Cobbs 		hookPtr = &priv->upper;
308add85a1dSArchie Cobbs 	else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0)
309add85a1dSArchie Cobbs 		hookPtr = &priv->lower;
310add85a1dSArchie Cobbs 	else
311add85a1dSArchie Cobbs 		return (EINVAL);
312add85a1dSArchie Cobbs 
313add85a1dSArchie Cobbs 	/* See if already connected */
314add85a1dSArchie Cobbs 	if (*hookPtr != NULL)
315add85a1dSArchie Cobbs 		return (EISCONN);
316add85a1dSArchie Cobbs 
317add85a1dSArchie Cobbs 	/* OK */
318add85a1dSArchie Cobbs 	*hookPtr = hook;
319add85a1dSArchie Cobbs 	return (0);
320add85a1dSArchie Cobbs }
321add85a1dSArchie Cobbs 
322add85a1dSArchie Cobbs /*
323add85a1dSArchie Cobbs  * Receive a control message.
324add85a1dSArchie Cobbs  */
325add85a1dSArchie Cobbs static int
326069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
327add85a1dSArchie Cobbs {
32830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
329add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
330add85a1dSArchie Cobbs 	int error = 0;
331069154d5SJulian Elischer 	struct ng_mesg *msg;
332add85a1dSArchie Cobbs 
333069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
334add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
335add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
336add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
337add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
338add85a1dSArchie Cobbs 		    {
339add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
340add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
341add85a1dSArchie Cobbs 
342add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
343add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
344add85a1dSArchie Cobbs 				ERROUT(EINVAL);
345add85a1dSArchie Cobbs 			ng_pptpgre_reset(node);		/* reset on configure */
346add85a1dSArchie Cobbs 			priv->conf = *newConf;
347add85a1dSArchie Cobbs 			break;
348add85a1dSArchie Cobbs 		    }
349add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
350add85a1dSArchie Cobbs 			NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT);
351add85a1dSArchie Cobbs 			if (resp == NULL)
352add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
353add85a1dSArchie Cobbs 			bcopy(&priv->conf, resp->data, sizeof(priv->conf));
354add85a1dSArchie Cobbs 			break;
3559bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
3569bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
3579bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
3589bee7adfSArchie Cobbs 		    {
3599bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
3609bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
3619bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
3629bee7adfSArchie Cobbs 				if (resp == NULL)
3639bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
3649bee7adfSArchie Cobbs 				bcopy(&priv->stats,
3659bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
3669bee7adfSArchie Cobbs 			}
3679bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
3689bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
3699bee7adfSArchie Cobbs 			break;
3709bee7adfSArchie Cobbs 		    }
371add85a1dSArchie Cobbs 		default:
372add85a1dSArchie Cobbs 			error = EINVAL;
373add85a1dSArchie Cobbs 			break;
374add85a1dSArchie Cobbs 		}
375add85a1dSArchie Cobbs 		break;
376add85a1dSArchie Cobbs 	default:
377add85a1dSArchie Cobbs 		error = EINVAL;
378add85a1dSArchie Cobbs 		break;
379add85a1dSArchie Cobbs 	}
380589f6ed8SJulian Elischer done:
381069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
382069154d5SJulian Elischer 	NG_FREE_MSG(msg);
383add85a1dSArchie Cobbs 	return (error);
384add85a1dSArchie Cobbs }
385add85a1dSArchie Cobbs 
386add85a1dSArchie Cobbs /*
387add85a1dSArchie Cobbs  * Receive incoming data on a hook.
388add85a1dSArchie Cobbs  */
389add85a1dSArchie Cobbs static int
390069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
391add85a1dSArchie Cobbs {
39230400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
39330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
394f2ba84d7SGleb Smirnoff 	int rval;
395add85a1dSArchie Cobbs 
396add85a1dSArchie Cobbs 	/* If not configured, reject */
397add85a1dSArchie Cobbs 	if (!priv->conf.enabled) {
398069154d5SJulian Elischer 		NG_FREE_ITEM(item);
399add85a1dSArchie Cobbs 		return (ENXIO);
400add85a1dSArchie Cobbs 	}
401add85a1dSArchie Cobbs 
402f2ba84d7SGleb Smirnoff 	mtx_lock(&priv->mtx);
403f2ba84d7SGleb Smirnoff 
404add85a1dSArchie Cobbs 	/* Treat as xmit or recv data */
405add85a1dSArchie Cobbs 	if (hook == priv->upper)
406f2ba84d7SGleb Smirnoff 		rval = ng_pptpgre_xmit(node, item);
407f2ba84d7SGleb Smirnoff 	else if (hook == priv->lower)
408f2ba84d7SGleb Smirnoff 		rval = ng_pptpgre_recv(node, item);
409f2ba84d7SGleb Smirnoff 	else
4106e551fb6SDavid E. O'Brien 		panic("%s: weird hook", __func__);
411f2ba84d7SGleb Smirnoff 
412f2ba84d7SGleb Smirnoff 	mtx_unlock(&priv->mtx);
413f2ba84d7SGleb Smirnoff 
414f2ba84d7SGleb Smirnoff 	return (rval);
415add85a1dSArchie Cobbs }
416add85a1dSArchie Cobbs 
417add85a1dSArchie Cobbs /*
418add85a1dSArchie Cobbs  * Destroy node
419add85a1dSArchie Cobbs  */
420add85a1dSArchie Cobbs static int
421069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
422add85a1dSArchie Cobbs {
42330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
424add85a1dSArchie Cobbs 
425089323f3SGleb Smirnoff 	/* Reset node (stops timers) */
426add85a1dSArchie Cobbs 	ng_pptpgre_reset(node);
427add85a1dSArchie Cobbs 
428f2ba84d7SGleb Smirnoff 	mtx_destroy(&priv->mtx);
429f2ba84d7SGleb Smirnoff 
430add85a1dSArchie Cobbs 	FREE(priv, M_NETGRAPH);
4314a48abb2SArchie Cobbs 
4324a48abb2SArchie Cobbs 	/* Decrement ref count */
43330400f03SJulian Elischer 	NG_NODE_UNREF(node);
434add85a1dSArchie Cobbs 	return (0);
435add85a1dSArchie Cobbs }
436add85a1dSArchie Cobbs 
437add85a1dSArchie Cobbs /*
438add85a1dSArchie Cobbs  * Hook disconnection
439add85a1dSArchie Cobbs  */
440add85a1dSArchie Cobbs static int
441add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook)
442add85a1dSArchie Cobbs {
44330400f03SJulian Elischer 	const node_p node = NG_HOOK_NODE(hook);
44430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
445add85a1dSArchie Cobbs 
446add85a1dSArchie Cobbs 	/* Zero out hook pointer */
447add85a1dSArchie Cobbs 	if (hook == priv->upper)
448add85a1dSArchie Cobbs 		priv->upper = NULL;
449add85a1dSArchie Cobbs 	else if (hook == priv->lower)
450add85a1dSArchie Cobbs 		priv->lower = NULL;
451add85a1dSArchie Cobbs 	else
4526e551fb6SDavid E. O'Brien 		panic("%s: unknown hook", __func__);
453add85a1dSArchie Cobbs 
454add85a1dSArchie Cobbs 	/* Go away if no longer connected to anything */
45530400f03SJulian Elischer 	if ((NG_NODE_NUMHOOKS(node) == 0)
45630400f03SJulian Elischer 	&& (NG_NODE_IS_VALID(node)))
457069154d5SJulian Elischer 		ng_rmnode_self(node);
458add85a1dSArchie Cobbs 	return (0);
459add85a1dSArchie Cobbs }
460add85a1dSArchie Cobbs 
461add85a1dSArchie Cobbs /*************************************************************************
462add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
463add85a1dSArchie Cobbs *************************************************************************/
464add85a1dSArchie Cobbs 
465add85a1dSArchie Cobbs /*
466add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
467add85a1dSArchie Cobbs  */
468add85a1dSArchie Cobbs static int
469069154d5SJulian Elischer ng_pptpgre_xmit(node_p node, item_p item)
470add85a1dSArchie Cobbs {
47130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
472add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
473add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
474add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
475add85a1dSArchie Cobbs 	int grelen, error;
476069154d5SJulian Elischer 	struct mbuf *m;
477add85a1dSArchie Cobbs 
478069154d5SJulian Elischer 	if (item) {
479069154d5SJulian Elischer 		NGI_GET_M(item, m);
480069154d5SJulian Elischer 	} else {
481069154d5SJulian Elischer 		m = NULL;
482069154d5SJulian Elischer 	}
4839bee7adfSArchie Cobbs 	/* Check if there's data */
4849bee7adfSArchie Cobbs 	if (m != NULL) {
4859bee7adfSArchie Cobbs 
486922ee196SArchie Cobbs 		/* Check if windowing is enabled */
487922ee196SArchie Cobbs 		if (priv->conf.enableWindowing) {
488add85a1dSArchie Cobbs 			/* Is our transmit window full? */
489922ee196SArchie Cobbs 			if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq,
490922ee196SArchie Cobbs 			    priv->recvAck) >= a->xmitWin) {
4919bee7adfSArchie Cobbs 				priv->stats.xmitDrops++;
492069154d5SJulian Elischer 				NG_FREE_M(m);
493069154d5SJulian Elischer 				NG_FREE_ITEM(item);
494add85a1dSArchie Cobbs 				return (ENOBUFS);
495add85a1dSArchie Cobbs 			}
496922ee196SArchie Cobbs 		}
497add85a1dSArchie Cobbs 
498add85a1dSArchie Cobbs 		/* Sanity check frame length */
499add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
5009bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
501069154d5SJulian Elischer 			NG_FREE_M(m);
502069154d5SJulian Elischer 			NG_FREE_ITEM(item);
503add85a1dSArchie Cobbs 			return (EMSGSIZE);
504add85a1dSArchie Cobbs 		}
505069154d5SJulian Elischer 	} else {
5069bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
507069154d5SJulian Elischer 	}
508add85a1dSArchie Cobbs 
509add85a1dSArchie Cobbs 	/* Build GRE header */
510add85a1dSArchie Cobbs 	((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE);
511add85a1dSArchie Cobbs 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
512add85a1dSArchie Cobbs 	gre->cid = htons(priv->conf.peerCid);
513add85a1dSArchie Cobbs 
514add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
515add85a1dSArchie Cobbs 	if (m != NULL) {
516add85a1dSArchie Cobbs 		gre->hasSeq = 1;
517922ee196SArchie Cobbs 		if (priv->conf.enableWindowing) {
518add85a1dSArchie Cobbs 			a->timeSent[priv->xmitSeq - priv->recvAck]
519add85a1dSArchie Cobbs 			    = ng_pptpgre_time(node);
520922ee196SArchie Cobbs 		}
521add85a1dSArchie Cobbs 		priv->xmitSeq++;
522add85a1dSArchie Cobbs 		gre->data[0] = htonl(priv->xmitSeq);
523add85a1dSArchie Cobbs 	}
524add85a1dSArchie Cobbs 
525add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
526678f9e33SArchie Cobbs 	if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) {
527add85a1dSArchie Cobbs 		gre->hasAck = 1;
528678f9e33SArchie Cobbs 		gre->data[gre->hasSeq] = htonl(priv->recvSeq);
529add85a1dSArchie Cobbs 		priv->xmitAck = priv->recvSeq;
5304a48abb2SArchie Cobbs 		ng_pptpgre_stop_send_ack_timer(node);
531da010626SArchie Cobbs 	}
532add85a1dSArchie Cobbs 
533add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
534add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
535add85a1dSArchie Cobbs 	if (m == NULL) {
536a163d034SWarner Losh 		MGETHDR(m, M_DONTWAIT, MT_DATA);
537add85a1dSArchie Cobbs 		if (m == NULL) {
538678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
539069154d5SJulian Elischer 			if (item)
540069154d5SJulian Elischer 				NG_FREE_ITEM(item);
541add85a1dSArchie Cobbs 			return (ENOBUFS);
542add85a1dSArchie Cobbs 		}
543add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
544add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
545add85a1dSArchie Cobbs 	} else {
546a163d034SWarner Losh 		M_PREPEND(m, grelen, M_DONTWAIT);
547add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
548add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
549678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
550069154d5SJulian Elischer 			if (item)
551069154d5SJulian Elischer 				NG_FREE_ITEM(item);
552add85a1dSArchie Cobbs 			return (ENOBUFS);
553add85a1dSArchie Cobbs 		}
554add85a1dSArchie Cobbs 	}
555add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
556add85a1dSArchie Cobbs 
5579bee7adfSArchie Cobbs 	/* Update stats */
5589bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
5599bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
5609bee7adfSArchie Cobbs 
561add85a1dSArchie Cobbs 	/* Deliver packet */
562069154d5SJulian Elischer 	if (item) {
563069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
564069154d5SJulian Elischer 	} else {
565069154d5SJulian Elischer 		NG_SEND_DATA_ONLY(error, priv->lower, m);
566069154d5SJulian Elischer 	}
567069154d5SJulian Elischer 
568678f9e33SArchie Cobbs 
569da010626SArchie Cobbs 	/* Start receive ACK timer if data was sent and not already running */
570678f9e33SArchie Cobbs 	if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1)
571678f9e33SArchie Cobbs 		ng_pptpgre_start_recv_ack_timer(node);
572add85a1dSArchie Cobbs 	return (error);
573add85a1dSArchie Cobbs }
574add85a1dSArchie Cobbs 
575add85a1dSArchie Cobbs /*
576add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
577add85a1dSArchie Cobbs  */
578add85a1dSArchie Cobbs static int
579069154d5SJulian Elischer ng_pptpgre_recv(node_p node, item_p item)
580add85a1dSArchie Cobbs {
58130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
582add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
583816b834fSArchie Cobbs 	const struct greheader *gre;
584816b834fSArchie Cobbs 	const struct ip *ip;
585add85a1dSArchie Cobbs 	int error = 0;
586069154d5SJulian Elischer 	struct mbuf *m;
587add85a1dSArchie Cobbs 
588069154d5SJulian Elischer 	NGI_GET_M(item, m);
5899bee7adfSArchie Cobbs 	/* Update stats */
5909bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
5919bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
5929bee7adfSArchie Cobbs 
593add85a1dSArchie Cobbs 	/* Sanity check packet length */
594add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
5959bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
596add85a1dSArchie Cobbs bad:
597069154d5SJulian Elischer 		NG_FREE_M(m);
598069154d5SJulian Elischer 		NG_FREE_ITEM(item);
599add85a1dSArchie Cobbs 		return (EINVAL);
600add85a1dSArchie Cobbs 	}
601add85a1dSArchie Cobbs 
602add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
603add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
604add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
605678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
606069154d5SJulian Elischer 		NG_FREE_ITEM(item);
607add85a1dSArchie Cobbs 		return (ENOBUFS);
608add85a1dSArchie Cobbs 	}
609816b834fSArchie Cobbs 	ip = mtod(m, const struct ip *);
610add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
611add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
612add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
613678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
614069154d5SJulian Elischer 			NG_FREE_ITEM(item);
615add85a1dSArchie Cobbs 			return (ENOBUFS);
616add85a1dSArchie Cobbs 		}
617816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
618add85a1dSArchie Cobbs 	}
619816b834fSArchie Cobbs 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
620add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
6219bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
6229bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
623add85a1dSArchie Cobbs 		goto bad;
6249bee7adfSArchie Cobbs 	}
625add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
626add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
627678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
628069154d5SJulian Elischer 			NG_FREE_ITEM(item);
629add85a1dSArchie Cobbs 			return (ENOBUFS);
630add85a1dSArchie Cobbs 		}
631816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
632816b834fSArchie Cobbs 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
633add85a1dSArchie Cobbs 	}
634add85a1dSArchie Cobbs 
635add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
636add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
637cc78c48aSArchie Cobbs 	    - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length));
6389bee7adfSArchie Cobbs 	if (extralen < 0) {
6399bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
640add85a1dSArchie Cobbs 		goto bad;
6419bee7adfSArchie Cobbs 	}
642816b834fSArchie Cobbs 	if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK)
643816b834fSArchie Cobbs 	    != PPTP_INIT_VALUE) {
6449bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
645add85a1dSArchie Cobbs 		goto bad;
6469bee7adfSArchie Cobbs 	}
6479bee7adfSArchie Cobbs 	if (ntohs(gre->cid) != priv->conf.cid) {
6489bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
649add85a1dSArchie Cobbs 		goto bad;
6509bee7adfSArchie Cobbs 	}
651add85a1dSArchie Cobbs 
652add85a1dSArchie Cobbs 	/* Look for peer ack */
653add85a1dSArchie Cobbs 	if (gre->hasAck) {
654add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
655add85a1dSArchie Cobbs 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
656add85a1dSArchie Cobbs 		const int index = ack - priv->recvAck - 1;
65722dfb9bdSArchie Cobbs 		long sample;
658add85a1dSArchie Cobbs 		long diff;
659add85a1dSArchie Cobbs 
660add85a1dSArchie Cobbs 		/* Sanity check ack value */
6619bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) {
6629bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
6639bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
6649bee7adfSArchie Cobbs 		}
6659bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0)
6669bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
667add85a1dSArchie Cobbs 		priv->recvAck = ack;
668add85a1dSArchie Cobbs 
669add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
670922ee196SArchie Cobbs 		if (priv->conf.enableWindowing) {
67122dfb9bdSArchie Cobbs 			sample = ng_pptpgre_time(node) - a->timeSent[index];
672add85a1dSArchie Cobbs 			diff = sample - a->rtt;
673add85a1dSArchie Cobbs 			a->rtt += PPTP_ACK_ALPHA(diff);
674add85a1dSArchie Cobbs 			if (diff < 0)
675add85a1dSArchie Cobbs 				diff = -diff;
676add85a1dSArchie Cobbs 			a->dev += PPTP_ACK_BETA(diff - a->dev);
677e962a823SArchie Cobbs 			a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
678add85a1dSArchie Cobbs 			if (a->ato > PPTP_MAX_TIMEOUT)
679add85a1dSArchie Cobbs 				a->ato = PPTP_MAX_TIMEOUT;
680e962a823SArchie Cobbs 			if (a->ato < PPTP_MIN_TIMEOUT)
681e962a823SArchie Cobbs 				a->ato = PPTP_MIN_TIMEOUT;
682e962a823SArchie Cobbs 
683e962a823SArchie Cobbs 			/* Shift packet transmit times in our transmit window */
684f7854568SDag-Erling Smørgrav 			bcopy(a->timeSent + index + 1, a->timeSent,
685922ee196SArchie Cobbs 			    sizeof(*a->timeSent)
686922ee196SArchie Cobbs 			      * (PPTP_XMIT_WIN - (index + 1)));
687e962a823SArchie Cobbs 
688922ee196SArchie Cobbs 			/* If we sent an entire window, increase window size */
6899bee7adfSArchie Cobbs 			if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0
6909bee7adfSArchie Cobbs 			    && a->xmitWin < PPTP_XMIT_WIN) {
691add85a1dSArchie Cobbs 				a->xmitWin++;
692add85a1dSArchie Cobbs 				a->winAck = ack + a->xmitWin;
693add85a1dSArchie Cobbs 			}
694add85a1dSArchie Cobbs 
6959bee7adfSArchie Cobbs 			/* Stop/(re)start receive ACK timer as necessary */
6964a48abb2SArchie Cobbs 			ng_pptpgre_stop_recv_ack_timer(node);
697678f9e33SArchie Cobbs 			if (priv->recvAck != priv->xmitSeq)
698add85a1dSArchie Cobbs 				ng_pptpgre_start_recv_ack_timer(node);
699add85a1dSArchie Cobbs 		}
700922ee196SArchie Cobbs 	}
7019bee7adfSArchie Cobbs badAck:
702add85a1dSArchie Cobbs 
703add85a1dSArchie Cobbs 	/* See if frame contains any data */
704add85a1dSArchie Cobbs 	if (gre->hasSeq) {
705add85a1dSArchie Cobbs 		struct ng_pptpgre_ackp *const a = &priv->ackp;
706add85a1dSArchie Cobbs 		const u_int32_t seq = ntohl(gre->data[0]);
707add85a1dSArchie Cobbs 
708add85a1dSArchie Cobbs 		/* Sanity check sequence number */
7099bee7adfSArchie Cobbs 		if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) {
7109bee7adfSArchie Cobbs 			if (seq == priv->recvSeq)
7119bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
7129bee7adfSArchie Cobbs 			else
7139bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
7149bee7adfSArchie Cobbs 			goto bad;		/* out-of-order or dup */
7159bee7adfSArchie Cobbs 		}
716add85a1dSArchie Cobbs 		priv->recvSeq = seq;
717add85a1dSArchie Cobbs 
718add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
719dc2f4d7fSGleb Smirnoff 		if (!(callout_pending(&a->sackTimer))) {
720da010626SArchie Cobbs 			int maxWait;
721add85a1dSArchie Cobbs 
722da010626SArchie Cobbs 			/* Take 1/4 of the estimated round trip time */
723da010626SArchie Cobbs 			maxWait = (a->rtt >> 2);
724add85a1dSArchie Cobbs 
725678f9e33SArchie Cobbs 			/* If delayed ACK is disabled, send it now */
7264a48abb2SArchie Cobbs 			if (!priv->conf.enableDelayedAck)	/* ack now */
727069154d5SJulian Elischer 				ng_pptpgre_xmit(node, NULL);
7284a48abb2SArchie Cobbs 			else {					/* ack later */
7294a48abb2SArchie Cobbs 				if (maxWait < PPTP_MIN_ACK_DELAY)
7304a48abb2SArchie Cobbs 					maxWait = PPTP_MIN_ACK_DELAY;
7313cd7db22SArchie Cobbs 				if (maxWait > PPTP_MAX_ACK_DELAY)
7323cd7db22SArchie Cobbs 					maxWait = PPTP_MAX_ACK_DELAY;
7333cd7db22SArchie Cobbs 				ng_pptpgre_start_send_ack_timer(node, maxWait);
734add85a1dSArchie Cobbs 			}
735add85a1dSArchie Cobbs 		}
736add85a1dSArchie Cobbs 
737add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
738add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
739add85a1dSArchie Cobbs 		if (extralen > 0)
740add85a1dSArchie Cobbs 			m_adj(m, -extralen);
741add85a1dSArchie Cobbs 
742add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
743069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->upper, m);
7449bee7adfSArchie Cobbs 	} else {
7459bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
746069154d5SJulian Elischer 		NG_FREE_ITEM(item);
747069154d5SJulian Elischer 		NG_FREE_M(m);		/* no data to deliver */
7489bee7adfSArchie Cobbs 	}
749add85a1dSArchie Cobbs 	return (error);
750add85a1dSArchie Cobbs }
751add85a1dSArchie Cobbs 
752add85a1dSArchie Cobbs /*************************************************************************
753add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
754add85a1dSArchie Cobbs *************************************************************************/
755add85a1dSArchie Cobbs 
756add85a1dSArchie Cobbs /*
7579bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
758add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
759add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
760add85a1dSArchie Cobbs  * recv ack timer, if any.
761add85a1dSArchie Cobbs  */
762add85a1dSArchie Cobbs static void
763add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node)
764add85a1dSArchie Cobbs {
76530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
766add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
767678f9e33SArchie Cobbs 	int remain, ticks;
768add85a1dSArchie Cobbs 
769922ee196SArchie Cobbs 	if (!priv->conf.enableWindowing)
770922ee196SArchie Cobbs 		return;
771922ee196SArchie Cobbs 
772add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
773add85a1dSArchie Cobbs 	   and reset the timer to that time. */
774add85a1dSArchie Cobbs 	remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node);
775add85a1dSArchie Cobbs 	if (remain < 0)
776add85a1dSArchie Cobbs 		remain = 0;
777678f9e33SArchie Cobbs #ifdef DEBUG_RAT
778678f9e33SArchie Cobbs 	a->timerLength = remain;
779678f9e33SArchie Cobbs 	a->timerStart = ng_pptpgre_time(node);
780678f9e33SArchie Cobbs #endif
7819bee7adfSArchie Cobbs 
7824a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
783678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
784089323f3SGleb Smirnoff 	ng_callout(&a->rackTimer, node, NULL, ticks,
785089323f3SGleb Smirnoff 	    ng_pptpgre_recv_ack_timeout, NULL, 0);
7864a48abb2SArchie Cobbs }
7874a48abb2SArchie Cobbs 
7884a48abb2SArchie Cobbs /*
7894a48abb2SArchie Cobbs  * Stop receive ack timer.
7904a48abb2SArchie Cobbs  */
7914a48abb2SArchie Cobbs static void
7924a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node_p node)
7934a48abb2SArchie Cobbs {
7944a48abb2SArchie Cobbs 	const priv_p priv = NG_NODE_PRIVATE(node);
7954a48abb2SArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
7964a48abb2SArchie Cobbs 
797922ee196SArchie Cobbs 	if (!priv->conf.enableWindowing)
798922ee196SArchie Cobbs 		return;
799922ee196SArchie Cobbs 
800089323f3SGleb Smirnoff 	ng_uncallout(&a->rackTimer, node);
801add85a1dSArchie Cobbs }
802add85a1dSArchie Cobbs 
803add85a1dSArchie Cobbs /*
804add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
805add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
806add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
807add85a1dSArchie Cobbs  */
808add85a1dSArchie Cobbs static void
809089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
810add85a1dSArchie Cobbs {
81130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
812add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
813add85a1dSArchie Cobbs 
8149bee7adfSArchie Cobbs 
815add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
8169bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
817add85a1dSArchie Cobbs 	a->rtt = PPTP_ACK_DELTA(a->rtt);
818add85a1dSArchie Cobbs 	a->ato = a->rtt + PPTP_ACK_CHI(a->dev);
819add85a1dSArchie Cobbs 	if (a->ato > PPTP_MAX_TIMEOUT)
820add85a1dSArchie Cobbs 		a->ato = PPTP_MAX_TIMEOUT;
821e962a823SArchie Cobbs 	if (a->ato < PPTP_MIN_TIMEOUT)
822e962a823SArchie Cobbs 		a->ato = PPTP_MIN_TIMEOUT;
823add85a1dSArchie Cobbs 
824678f9e33SArchie Cobbs #ifdef DEBUG_RAT
825678f9e33SArchie Cobbs     log(LOG_DEBUG,
826678f9e33SArchie Cobbs 	"RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n",
827678f9e33SArchie Cobbs 	(int)ng_pptpgre_time(node), priv->recvAck + 1,
828678f9e33SArchie Cobbs 	(int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato);
829678f9e33SArchie Cobbs #endif
830678f9e33SArchie Cobbs 
831e962a823SArchie Cobbs 	/* Reset ack and sliding window */
832e962a823SArchie Cobbs 	priv->recvAck = priv->xmitSeq;		/* pretend we got the ack */
833e962a823SArchie Cobbs 	a->xmitWin = (a->xmitWin + 1) / 2;	/* shrink transmit window */
834e962a823SArchie Cobbs 	a->winAck = priv->recvAck + a->xmitWin;	/* reset win expand time */
835add85a1dSArchie Cobbs }
836add85a1dSArchie Cobbs 
837add85a1dSArchie Cobbs /*
8389bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
8399bee7adfSArchie Cobbs  * already running.
8409bee7adfSArchie Cobbs  */
8419bee7adfSArchie Cobbs static void
842da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout)
8439bee7adfSArchie Cobbs {
84430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
8459bee7adfSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
846678f9e33SArchie Cobbs 	int ticks;
8479bee7adfSArchie Cobbs 
8484a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
849678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
850089323f3SGleb Smirnoff 	ng_callout(&a->sackTimer, node, NULL, ticks,
851089323f3SGleb Smirnoff 	    ng_pptpgre_send_ack_timeout, NULL, 0);
8524a48abb2SArchie Cobbs }
8534a48abb2SArchie Cobbs 
8544a48abb2SArchie Cobbs /*
8554a48abb2SArchie Cobbs  * Stop send ack timer.
8564a48abb2SArchie Cobbs  */
8574a48abb2SArchie Cobbs static void
8584a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node_p node)
8594a48abb2SArchie Cobbs {
8604a48abb2SArchie Cobbs 	const priv_p priv = NG_NODE_PRIVATE(node);
8614a48abb2SArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
8624a48abb2SArchie Cobbs 
863089323f3SGleb Smirnoff 	ng_uncallout(&a->sackTimer, node);
8649bee7adfSArchie Cobbs }
8659bee7adfSArchie Cobbs 
8669bee7adfSArchie Cobbs /*
867add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
868add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
869add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
870add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
871add85a1dSArchie Cobbs  */
872add85a1dSArchie Cobbs static void
873089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
874add85a1dSArchie Cobbs {
8759bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
876069154d5SJulian Elischer   	ng_pptpgre_xmit(node, NULL);
877add85a1dSArchie Cobbs }
878add85a1dSArchie Cobbs 
879add85a1dSArchie Cobbs /*************************************************************************
880add85a1dSArchie Cobbs 		    MISC FUNCTIONS
881add85a1dSArchie Cobbs *************************************************************************/
882add85a1dSArchie Cobbs 
883add85a1dSArchie Cobbs /*
884add85a1dSArchie Cobbs  * Reset state
885add85a1dSArchie Cobbs  */
886add85a1dSArchie Cobbs static void
887add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node)
888add85a1dSArchie Cobbs {
88930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
890add85a1dSArchie Cobbs 	struct ng_pptpgre_ackp *const a = &priv->ackp;
891add85a1dSArchie Cobbs 
892f2ba84d7SGleb Smirnoff 	mtx_lock(&priv->mtx);
893f2ba84d7SGleb Smirnoff 
894add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
895add85a1dSArchie Cobbs 	a->ato = PPTP_MAX_TIMEOUT;
896add85a1dSArchie Cobbs 	a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10;  /* ppd in 10ths */
897add85a1dSArchie Cobbs 	if (a->rtt < PPTP_MIN_RTT)
898add85a1dSArchie Cobbs 		a->rtt = PPTP_MIN_RTT;
899add85a1dSArchie Cobbs 	a->dev = 0;
900add85a1dSArchie Cobbs 	a->xmitWin = (priv->conf.recvWin + 1) / 2;
901e962a823SArchie Cobbs 	if (a->xmitWin < 2)		/* often the first packet is lost */
902e962a823SArchie Cobbs 		a->xmitWin = 2;		/*   because the peer isn't ready */
903add85a1dSArchie Cobbs 	if (a->xmitWin > PPTP_XMIT_WIN)
904add85a1dSArchie Cobbs 		a->xmitWin = PPTP_XMIT_WIN;
905add85a1dSArchie Cobbs 	a->winAck = a->xmitWin;
906add85a1dSArchie Cobbs 
907add85a1dSArchie Cobbs 	/* Reset sequence numbers */
9083cd7db22SArchie Cobbs 	priv->recvSeq = ~0;
9093cd7db22SArchie Cobbs 	priv->recvAck = ~0;
9103cd7db22SArchie Cobbs 	priv->xmitSeq = ~0;
9113cd7db22SArchie Cobbs 	priv->xmitAck = ~0;
912add85a1dSArchie Cobbs 
913add85a1dSArchie Cobbs 	/* Reset start time */
9149bee7adfSArchie Cobbs 	getmicrouptime(&priv->startTime);
9159bee7adfSArchie Cobbs 
9169bee7adfSArchie Cobbs 	/* Reset stats */
9179bee7adfSArchie Cobbs 	bzero(&priv->stats, sizeof(priv->stats));
918add85a1dSArchie Cobbs 
9194a48abb2SArchie Cobbs 	/* Stop timers */
9204a48abb2SArchie Cobbs 	ng_pptpgre_stop_send_ack_timer(node);
9214a48abb2SArchie Cobbs 	ng_pptpgre_stop_recv_ack_timer(node);
922f2ba84d7SGleb Smirnoff 
923f2ba84d7SGleb Smirnoff 	mtx_unlock(&priv->mtx);
924add85a1dSArchie Cobbs }
925add85a1dSArchie Cobbs 
926add85a1dSArchie Cobbs /*
927add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
928add85a1dSArchie Cobbs  */
929add85a1dSArchie Cobbs static pptptime_t
930add85a1dSArchie Cobbs ng_pptpgre_time(node_p node)
931add85a1dSArchie Cobbs {
93230400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
933add85a1dSArchie Cobbs 	struct timeval tv;
934678f9e33SArchie Cobbs 	pptptime_t t;
935add85a1dSArchie Cobbs 
936678f9e33SArchie Cobbs 	microuptime(&tv);
937add85a1dSArchie Cobbs 	if (tv.tv_sec < priv->startTime.tv_sec
938add85a1dSArchie Cobbs 	    || (tv.tv_sec == priv->startTime.tv_sec
939add85a1dSArchie Cobbs 	      && tv.tv_usec < priv->startTime.tv_usec))
940add85a1dSArchie Cobbs 		return (0);
941add85a1dSArchie Cobbs 	timevalsub(&tv, &priv->startTime);
942678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
943678f9e33SArchie Cobbs 	t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
944678f9e33SArchie Cobbs 	return(t);
945add85a1dSArchie Cobbs }
946