xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision 2c2e2be7465fbb8dbc1a9f63ad9e3ba3a6ac8423)
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>
6538f2d636SAlexander Motin #include <sys/endian.h>
66add85a1dSArchie Cobbs #include <sys/errno.h>
67add85a1dSArchie Cobbs 
68add85a1dSArchie Cobbs #include <netinet/in.h>
69add85a1dSArchie Cobbs #include <netinet/in_systm.h>
70add85a1dSArchie Cobbs #include <netinet/ip.h>
71add85a1dSArchie Cobbs 
72add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
73add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
74add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
75add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
76add85a1dSArchie Cobbs 
77add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
78add85a1dSArchie Cobbs struct greheader {
79add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
80add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
81add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
82add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
83add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
84add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
85add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
86add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
87add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
88add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
89add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
90add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
91add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
92add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
93add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
94add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
95add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
96add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
97add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
98add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
99add85a1dSArchie Cobbs #else
100add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
101add85a1dSArchie Cobbs #endif
102add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
103add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
104add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
105add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
106add85a1dSArchie Cobbs };
107add85a1dSArchie Cobbs 
108add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
109add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
110add85a1dSArchie Cobbs 
111add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
112add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
113add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
114add85a1dSArchie Cobbs 
115add85a1dSArchie Cobbs /* Min and max packet length */
116add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
117add85a1dSArchie Cobbs 
118add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
119714f558bSAlexander Motin #define PPTP_TIME_SCALE		1024			/* milliseconds */
120678f9e33SArchie Cobbs typedef u_int64_t		pptptime_t;
121add85a1dSArchie Cobbs 
122add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
123e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
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 */
135f8e159d6SGleb Smirnoff #define PPTP_ACK_ALPHA(x)	(((x) + 4) >> 3)	/* alpha = 0.125 */
136f8e159d6SGleb Smirnoff #define PPTP_ACK_BETA(x)	(((x) + 2) >> 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 
142489290e9SAlexander Motin #define SESSHASHSIZE		0x0020
143489290e9SAlexander Motin #define SESSHASH(x)		(((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
144489290e9SAlexander Motin 
145add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
146489290e9SAlexander Motin struct ng_pptpgre_sess {
147489290e9SAlexander Motin 	node_p			node;		/* this node pointer */
148489290e9SAlexander Motin 	hook_p			hook;		/* hook to upper layers */
149489290e9SAlexander Motin 	struct ng_pptpgre_conf	conf;		/* configuration info */
150489290e9SAlexander Motin 	struct mtx		mtx;		/* session mutex */
151489290e9SAlexander Motin 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
152489290e9SAlexander Motin 	u_int32_t		xmitSeq;	/* last seq # we sent */
153489290e9SAlexander Motin 	u_int32_t		recvAck;	/* last seq # peer ack'd */
154489290e9SAlexander Motin 	u_int32_t		xmitAck;	/* last seq # we ack'd */
155add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
156add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
157add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
158add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
1594a48abb2SArchie Cobbs 	struct callout		sackTimer;	/* send ack timer */
1604a48abb2SArchie Cobbs 	struct callout		rackTimer;	/* recv ack timer */
1613cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
162add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
163489290e9SAlexander Motin 	LIST_ENTRY(ng_pptpgre_sess) sessions;
164add85a1dSArchie Cobbs };
165489290e9SAlexander Motin typedef struct ng_pptpgre_sess *hpriv_p;
166add85a1dSArchie Cobbs 
167add85a1dSArchie Cobbs /* Node private data */
168add85a1dSArchie Cobbs struct ng_pptpgre_private {
169add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
170add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
171489290e9SAlexander Motin 	struct ng_pptpgre_sess	uppersess;	/* default session for compat */
172489290e9SAlexander Motin 	LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
1739bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
174add85a1dSArchie Cobbs };
175add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
176add85a1dSArchie Cobbs 
177add85a1dSArchie Cobbs /* Netgraph node methods */
178add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
179add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
180069154d5SJulian Elischer static ng_shutdown_t	ng_pptpgre_shutdown;
181add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
182add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
183489290e9SAlexander Motin static ng_rcvdata_t	ng_pptpgre_rcvdata_lower;
184add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
185add85a1dSArchie Cobbs 
186add85a1dSArchie Cobbs /* Helper functions */
187489290e9SAlexander Motin static int	ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
188714f558bSAlexander Motin static void	ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
189489290e9SAlexander Motin static void	ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
190089323f3SGleb Smirnoff static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
191089323f3SGleb Smirnoff 		    void *arg1, int arg2);
192089323f3SGleb Smirnoff static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
193089323f3SGleb Smirnoff 		    void *arg1, int arg2);
194489290e9SAlexander Motin static hpriv_p	ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
195489290e9SAlexander Motin static void	ng_pptpgre_reset(hpriv_p hpriv);
196489290e9SAlexander Motin static pptptime_t ng_pptpgre_time(void);
197add85a1dSArchie Cobbs 
198add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
199f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
200f0184ff8SArchie Cobbs 	= NG_PPTPGRE_CONF_TYPE_INFO;
201add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
202add85a1dSArchie Cobbs 	&ng_parse_struct_type,
203f0184ff8SArchie Cobbs 	&ng_pptpgre_conf_type_fields,
204add85a1dSArchie Cobbs };
205add85a1dSArchie Cobbs 
2069bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
207f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
208f0184ff8SArchie Cobbs 	= NG_PPTPGRE_STATS_TYPE_INFO;
2099bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2109bee7adfSArchie Cobbs 	&ng_parse_struct_type,
211f0184ff8SArchie Cobbs 	&ng_pptpgre_stats_type_fields
2129bee7adfSArchie Cobbs };
2139bee7adfSArchie Cobbs 
214add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
215add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
216add85a1dSArchie Cobbs 	{
217add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
218add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
219add85a1dSArchie Cobbs 	  "setconfig",
220add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
221add85a1dSArchie Cobbs 	  NULL
222add85a1dSArchie Cobbs 	},
223add85a1dSArchie Cobbs 	{
224add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
225add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
226add85a1dSArchie Cobbs 	  "getconfig",
227489290e9SAlexander Motin 	  &ng_parse_hint16_type,
228add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
229add85a1dSArchie Cobbs 	},
2309bee7adfSArchie Cobbs 	{
2319bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2329bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2339bee7adfSArchie Cobbs 	  "getstats",
2349bee7adfSArchie Cobbs 	  NULL,
2359bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2369bee7adfSArchie Cobbs 	},
2379bee7adfSArchie Cobbs 	{
2389bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2399bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2409bee7adfSArchie Cobbs 	  "clrstats",
2419bee7adfSArchie Cobbs 	  NULL,
2429bee7adfSArchie Cobbs 	  NULL
2439bee7adfSArchie Cobbs 	},
2449bee7adfSArchie Cobbs 	{
2459bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2469bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2479bee7adfSArchie Cobbs 	  "getclrstats",
2489bee7adfSArchie Cobbs 	  NULL,
2499bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2509bee7adfSArchie Cobbs 	},
251add85a1dSArchie Cobbs 	{ 0 }
252add85a1dSArchie Cobbs };
253add85a1dSArchie Cobbs 
254add85a1dSArchie Cobbs /* Node type descriptor */
255add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
256f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
257f8aae777SJulian Elischer 	.name =		NG_PPTPGRE_NODE_TYPE,
258f8aae777SJulian Elischer 	.constructor =	ng_pptpgre_constructor,
259f8aae777SJulian Elischer 	.rcvmsg =	ng_pptpgre_rcvmsg,
260f8aae777SJulian Elischer 	.shutdown =	ng_pptpgre_shutdown,
261f8aae777SJulian Elischer 	.newhook =	ng_pptpgre_newhook,
262f8aae777SJulian Elischer 	.rcvdata =	ng_pptpgre_rcvdata,
263f8aae777SJulian Elischer 	.disconnect =	ng_pptpgre_disconnect,
264f8aae777SJulian Elischer 	.cmdlist =	ng_pptpgre_cmdlist,
265add85a1dSArchie Cobbs };
266add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
267add85a1dSArchie Cobbs 
268add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
269add85a1dSArchie Cobbs 
270add85a1dSArchie Cobbs /************************************************************************
271add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
272add85a1dSArchie Cobbs  ************************************************************************/
273add85a1dSArchie Cobbs 
274add85a1dSArchie Cobbs /*
275add85a1dSArchie Cobbs  * Node type constructor
276add85a1dSArchie Cobbs  */
277add85a1dSArchie Cobbs static int
278069154d5SJulian Elischer ng_pptpgre_constructor(node_p node)
279add85a1dSArchie Cobbs {
280add85a1dSArchie Cobbs 	priv_p priv;
281489290e9SAlexander Motin 	int i;
282add85a1dSArchie Cobbs 
283add85a1dSArchie Cobbs 	/* Allocate private structure */
284674d86bfSGleb Smirnoff 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
285add85a1dSArchie Cobbs 
28630400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
287add85a1dSArchie Cobbs 
288add85a1dSArchie Cobbs 	/* Initialize state */
289489290e9SAlexander Motin 	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
290489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.sackTimer);
291489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.rackTimer);
292489290e9SAlexander Motin 	priv->uppersess.node = node;
293489290e9SAlexander Motin 
294489290e9SAlexander Motin 	for (i = 0; i < SESSHASHSIZE; i++)
295489290e9SAlexander Motin 	    LIST_INIT(&priv->sesshash[i]);
296489290e9SAlexander Motin 
297489290e9SAlexander Motin 	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
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 {
30930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
310add85a1dSArchie Cobbs 
311add85a1dSArchie Cobbs 	/* Check hook name */
312489290e9SAlexander Motin 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
313489290e9SAlexander Motin 		priv->upper = hook;
314489290e9SAlexander Motin 		priv->uppersess.hook = hook;
315489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
316489290e9SAlexander Motin 	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
317489290e9SAlexander Motin 		priv->lower = hook;
318489290e9SAlexander Motin 		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
319489290e9SAlexander Motin 	} else {
320489290e9SAlexander Motin 		static const char hexdig[16] = "0123456789abcdef";
321489290e9SAlexander Motin 		const char *hex;
322489290e9SAlexander Motin 		hpriv_p hpriv;
323489290e9SAlexander Motin 		int i, j;
324489290e9SAlexander Motin 		uint16_t cid, hash;
325489290e9SAlexander Motin 
326489290e9SAlexander Motin 		/* Parse hook name to get session ID */
327489290e9SAlexander Motin 		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
328489290e9SAlexander Motin 		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
329489290e9SAlexander Motin 			return (EINVAL);
330489290e9SAlexander Motin 		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
331489290e9SAlexander Motin 		for (cid = i = 0; i < 4; i++) {
332489290e9SAlexander Motin 			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
333489290e9SAlexander Motin 			if (j == 16)
334489290e9SAlexander Motin 				return (EINVAL);
335489290e9SAlexander Motin 			cid = (cid << 4) | j;
336489290e9SAlexander Motin 		}
337489290e9SAlexander Motin 		if (hex[i] != '\0')
338add85a1dSArchie Cobbs 			return (EINVAL);
339add85a1dSArchie Cobbs 
340489290e9SAlexander Motin 		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
341489290e9SAlexander Motin 		if (hpriv == NULL)
342489290e9SAlexander Motin 			return (ENOMEM);
343add85a1dSArchie Cobbs 
344489290e9SAlexander Motin 		/* Initialize state */
345489290e9SAlexander Motin 		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
346489290e9SAlexander Motin 		ng_callout_init(&hpriv->sackTimer);
347489290e9SAlexander Motin 		ng_callout_init(&hpriv->rackTimer);
348489290e9SAlexander Motin 		hpriv->conf.cid = cid;
349489290e9SAlexander Motin 		hpriv->node = node;
350489290e9SAlexander Motin 		hpriv->hook = hook;
351489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, hpriv);
352489290e9SAlexander Motin 
353489290e9SAlexander Motin 		hash = SESSHASH(cid);
354489290e9SAlexander Motin 		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
355489290e9SAlexander Motin 	}
356489290e9SAlexander Motin 
357add85a1dSArchie Cobbs 	return (0);
358add85a1dSArchie Cobbs }
359add85a1dSArchie Cobbs 
360add85a1dSArchie Cobbs /*
361add85a1dSArchie Cobbs  * Receive a control message.
362add85a1dSArchie Cobbs  */
363add85a1dSArchie Cobbs static int
364069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
365add85a1dSArchie Cobbs {
36630400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
367add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
368add85a1dSArchie Cobbs 	int error = 0;
369069154d5SJulian Elischer 	struct ng_mesg *msg;
370add85a1dSArchie Cobbs 
371069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
372add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
373add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
374add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
375add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
376add85a1dSArchie Cobbs 		    {
377add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
378add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
379489290e9SAlexander Motin 			hpriv_p hpriv;
380489290e9SAlexander Motin 			uint16_t hash;
381add85a1dSArchie Cobbs 
382add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
383add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
384add85a1dSArchie Cobbs 				ERROUT(EINVAL);
385489290e9SAlexander Motin 			/* Try to find session by cid. */
386489290e9SAlexander Motin 			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
387489290e9SAlexander Motin 			/* If not present - use upper. */
388489290e9SAlexander Motin 			if (hpriv == NULL) {
389489290e9SAlexander Motin 				hpriv = &priv->uppersess;
390489290e9SAlexander Motin 				LIST_REMOVE(hpriv, sessions);
391489290e9SAlexander Motin 				hash = SESSHASH(newConf->cid);
392489290e9SAlexander Motin 				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
393489290e9SAlexander Motin 				    sessions);
394489290e9SAlexander Motin 			}
395489290e9SAlexander Motin 			ng_pptpgre_reset(hpriv);	/* reset on configure */
396489290e9SAlexander Motin 			hpriv->conf = *newConf;
397add85a1dSArchie Cobbs 			break;
398add85a1dSArchie Cobbs 		    }
399add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
400489290e9SAlexander Motin 		    {
401489290e9SAlexander Motin 			hpriv_p hpriv;
402489290e9SAlexander Motin 
403489290e9SAlexander Motin 			if (msg->header.arglen == 2) {
404489290e9SAlexander Motin 				/* Try to find session by cid. */
405489290e9SAlexander Motin 	    			hpriv = ng_pptpgre_find_session(priv,
406489290e9SAlexander Motin 				    *((uint16_t *)msg->data));
407489290e9SAlexander Motin 				if (hpriv == NULL)
408489290e9SAlexander Motin 					ERROUT(EINVAL);
409489290e9SAlexander Motin 			} else if (msg->header.arglen == 0) {
410489290e9SAlexander Motin 				/* Use upper. */
411489290e9SAlexander Motin 				hpriv = &priv->uppersess;
412489290e9SAlexander Motin 			} else
413489290e9SAlexander Motin 				ERROUT(EINVAL);
414489290e9SAlexander Motin 			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
415add85a1dSArchie Cobbs 			if (resp == NULL)
416add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
417489290e9SAlexander Motin 			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
418add85a1dSArchie Cobbs 			break;
419489290e9SAlexander Motin 		    }
4209bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
4219bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
4229bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
4239bee7adfSArchie Cobbs 		    {
4249bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
4259bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
4269bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
4279bee7adfSArchie Cobbs 				if (resp == NULL)
4289bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
4299bee7adfSArchie Cobbs 				bcopy(&priv->stats,
4309bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
4319bee7adfSArchie Cobbs 			}
4329bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
4339bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
4349bee7adfSArchie Cobbs 			break;
4359bee7adfSArchie Cobbs 		    }
436add85a1dSArchie Cobbs 		default:
437add85a1dSArchie Cobbs 			error = EINVAL;
438add85a1dSArchie Cobbs 			break;
439add85a1dSArchie Cobbs 		}
440add85a1dSArchie Cobbs 		break;
441add85a1dSArchie Cobbs 	default:
442add85a1dSArchie Cobbs 		error = EINVAL;
443add85a1dSArchie Cobbs 		break;
444add85a1dSArchie Cobbs 	}
445589f6ed8SJulian Elischer done:
446069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
447069154d5SJulian Elischer 	NG_FREE_MSG(msg);
448add85a1dSArchie Cobbs 	return (error);
449add85a1dSArchie Cobbs }
450add85a1dSArchie Cobbs 
451add85a1dSArchie Cobbs /*
452add85a1dSArchie Cobbs  * Receive incoming data on a hook.
453add85a1dSArchie Cobbs  */
454add85a1dSArchie Cobbs static int
455069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
456add85a1dSArchie Cobbs {
457489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
458f2ba84d7SGleb Smirnoff 	int rval;
459add85a1dSArchie Cobbs 
460add85a1dSArchie Cobbs 	/* If not configured, reject */
461489290e9SAlexander Motin 	if (!hpriv->conf.enabled) {
462069154d5SJulian Elischer 		NG_FREE_ITEM(item);
463add85a1dSArchie Cobbs 		return (ENXIO);
464add85a1dSArchie Cobbs 	}
465add85a1dSArchie Cobbs 
466489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
467f2ba84d7SGleb Smirnoff 
468489290e9SAlexander Motin 	rval = ng_pptpgre_xmit(hpriv, item);
469f2ba84d7SGleb Smirnoff 
470489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
471f2ba84d7SGleb Smirnoff 
472f2ba84d7SGleb Smirnoff 	return (rval);
473add85a1dSArchie Cobbs }
474add85a1dSArchie Cobbs 
475add85a1dSArchie Cobbs /*
476489290e9SAlexander Motin  * Hook disconnection
477489290e9SAlexander Motin  */
478489290e9SAlexander Motin static int
479489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook)
480489290e9SAlexander Motin {
481489290e9SAlexander Motin 	const node_p node = NG_HOOK_NODE(hook);
482489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
483489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
484489290e9SAlexander Motin 
485489290e9SAlexander Motin 	/* Zero out hook pointer */
486489290e9SAlexander Motin 	if (hook == priv->upper) {
487489290e9SAlexander Motin 		priv->upper = NULL;
488489290e9SAlexander Motin 		priv->uppersess.hook = NULL;
489489290e9SAlexander Motin 	} else if (hook == priv->lower) {
490489290e9SAlexander Motin 		priv->lower = NULL;
491489290e9SAlexander Motin 	} else {
492489290e9SAlexander Motin 		/* Reset node (stops timers) */
493489290e9SAlexander Motin 		ng_pptpgre_reset(hpriv);
494489290e9SAlexander Motin 
495489290e9SAlexander Motin 		LIST_REMOVE(hpriv, sessions);
496489290e9SAlexander Motin 		mtx_destroy(&hpriv->mtx);
497489290e9SAlexander Motin 		free(hpriv, M_NETGRAPH);
498489290e9SAlexander Motin 	}
499489290e9SAlexander Motin 
500489290e9SAlexander Motin 	/* Go away if no longer connected to anything */
501489290e9SAlexander Motin 	if ((NG_NODE_NUMHOOKS(node) == 0)
502489290e9SAlexander Motin 	&& (NG_NODE_IS_VALID(node)))
503489290e9SAlexander Motin 		ng_rmnode_self(node);
504489290e9SAlexander Motin 	return (0);
505489290e9SAlexander Motin }
506489290e9SAlexander Motin 
507489290e9SAlexander Motin /*
508add85a1dSArchie Cobbs  * Destroy node
509add85a1dSArchie Cobbs  */
510add85a1dSArchie Cobbs static int
511069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
512add85a1dSArchie Cobbs {
51330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
514add85a1dSArchie Cobbs 
515089323f3SGleb Smirnoff 	/* Reset node (stops timers) */
516489290e9SAlexander Motin 	ng_pptpgre_reset(&priv->uppersess);
517add85a1dSArchie Cobbs 
518489290e9SAlexander Motin 	LIST_REMOVE(&priv->uppersess, sessions);
519489290e9SAlexander Motin 	mtx_destroy(&priv->uppersess.mtx);
520f2ba84d7SGleb Smirnoff 
5211ede983cSDag-Erling Smørgrav 	free(priv, M_NETGRAPH);
5224a48abb2SArchie Cobbs 
5234a48abb2SArchie Cobbs 	/* Decrement ref count */
52430400f03SJulian Elischer 	NG_NODE_UNREF(node);
525add85a1dSArchie Cobbs 	return (0);
526add85a1dSArchie Cobbs }
527add85a1dSArchie Cobbs 
528add85a1dSArchie Cobbs /*************************************************************************
529add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
530add85a1dSArchie Cobbs *************************************************************************/
531add85a1dSArchie Cobbs 
532add85a1dSArchie Cobbs /*
533add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
534add85a1dSArchie Cobbs  */
535add85a1dSArchie Cobbs static int
536489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
537add85a1dSArchie Cobbs {
538489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
539add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
540add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
541add85a1dSArchie Cobbs 	int grelen, error;
542069154d5SJulian Elischer 	struct mbuf *m;
543add85a1dSArchie Cobbs 
544489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_OWNED);
54583beeed9SGleb Smirnoff 
546069154d5SJulian Elischer 	if (item) {
547069154d5SJulian Elischer 		NGI_GET_M(item, m);
548069154d5SJulian Elischer 	} else {
549069154d5SJulian Elischer 		m = NULL;
550069154d5SJulian Elischer 	}
5519bee7adfSArchie Cobbs 	/* Check if there's data */
5529bee7adfSArchie Cobbs 	if (m != NULL) {
5539bee7adfSArchie Cobbs 
554922ee196SArchie Cobbs 		/* Check if windowing is enabled */
555489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
556add85a1dSArchie Cobbs 			/* Is our transmit window full? */
557489290e9SAlexander Motin 			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
558489290e9SAlexander Motin 			    hpriv->recvAck) >= hpriv->xmitWin) {
5599bee7adfSArchie Cobbs 				priv->stats.xmitDrops++;
56083beeed9SGleb Smirnoff 				ERROUT(ENOBUFS);
561add85a1dSArchie Cobbs 			}
562922ee196SArchie Cobbs 		}
563add85a1dSArchie Cobbs 
564add85a1dSArchie Cobbs 		/* Sanity check frame length */
565*2c2e2be7SAlexander Motin 		if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
5669bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
56783beeed9SGleb Smirnoff 			ERROUT(EMSGSIZE);
568add85a1dSArchie Cobbs 		}
569069154d5SJulian Elischer 	} else {
5709bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
571069154d5SJulian Elischer 	}
572add85a1dSArchie Cobbs 
573add85a1dSArchie Cobbs 	/* Build GRE header */
57438f2d636SAlexander Motin 	be32enc(gre, PPTP_INIT_VALUE);
57538f2d636SAlexander Motin 	be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
57638f2d636SAlexander Motin 	be16enc(&gre->cid, hpriv->conf.peerCid);
577add85a1dSArchie Cobbs 
578add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
579add85a1dSArchie Cobbs 	if (m != NULL) {
580add85a1dSArchie Cobbs 		gre->hasSeq = 1;
581489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
582489290e9SAlexander Motin 			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
583489290e9SAlexander Motin 			    = ng_pptpgre_time();
584922ee196SArchie Cobbs 		}
585489290e9SAlexander Motin 		hpriv->xmitSeq++;
58638f2d636SAlexander Motin 		be32enc(&gre->data[0], hpriv->xmitSeq);
587add85a1dSArchie Cobbs 	}
588add85a1dSArchie Cobbs 
589add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
590489290e9SAlexander Motin 	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
591add85a1dSArchie Cobbs 		gre->hasAck = 1;
59238f2d636SAlexander Motin 		be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
593489290e9SAlexander Motin 		hpriv->xmitAck = hpriv->recvSeq;
594714f558bSAlexander Motin 		if (hpriv->conf.enableDelayedAck)
595714f558bSAlexander Motin 			ng_uncallout(&hpriv->sackTimer, hpriv->node);
596da010626SArchie Cobbs 	}
597add85a1dSArchie Cobbs 
598add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
599add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
600add85a1dSArchie Cobbs 	if (m == NULL) {
601a163d034SWarner Losh 		MGETHDR(m, M_DONTWAIT, MT_DATA);
602add85a1dSArchie Cobbs 		if (m == NULL) {
603678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
60483beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
605add85a1dSArchie Cobbs 		}
606add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
607add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
608add85a1dSArchie Cobbs 	} else {
609a163d034SWarner Losh 		M_PREPEND(m, grelen, M_DONTWAIT);
610add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
611add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
612678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
61383beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
614add85a1dSArchie Cobbs 		}
615add85a1dSArchie Cobbs 	}
616add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
617add85a1dSArchie Cobbs 
6189bee7adfSArchie Cobbs 	/* Update stats */
6199bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
6209bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
6219bee7adfSArchie Cobbs 
62283beeed9SGleb Smirnoff 	/*
62383beeed9SGleb Smirnoff 	 * XXX: we should reset timer only after an item has been sent
62483beeed9SGleb Smirnoff 	 * successfully.
62583beeed9SGleb Smirnoff 	 */
626489290e9SAlexander Motin 	if (hpriv->conf.enableWindowing &&
627489290e9SAlexander Motin 	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
628489290e9SAlexander Motin 		ng_pptpgre_start_recv_ack_timer(hpriv);
62983beeed9SGleb Smirnoff 
630489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
63183beeed9SGleb Smirnoff 
632add85a1dSArchie Cobbs 	/* Deliver packet */
633069154d5SJulian Elischer 	if (item) {
634069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
635069154d5SJulian Elischer 	} else {
636069154d5SJulian Elischer 		NG_SEND_DATA_ONLY(error, priv->lower, m);
637069154d5SJulian Elischer 	}
638069154d5SJulian Elischer 
63983beeed9SGleb Smirnoff 	return (error);
640678f9e33SArchie Cobbs 
64183beeed9SGleb Smirnoff done:
642489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
64383beeed9SGleb Smirnoff 	NG_FREE_M(m);
64483beeed9SGleb Smirnoff 	if (item)
64583beeed9SGleb Smirnoff 		NG_FREE_ITEM(item);
646add85a1dSArchie Cobbs 	return (error);
647add85a1dSArchie Cobbs }
648add85a1dSArchie Cobbs 
649add85a1dSArchie Cobbs /*
650add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
651add85a1dSArchie Cobbs  */
652add85a1dSArchie Cobbs static int
653489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
654add85a1dSArchie Cobbs {
655489290e9SAlexander Motin 	hpriv_p hpriv;
656489290e9SAlexander Motin 	node_p node = NG_HOOK_NODE(hook);
65730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
658add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
659816b834fSArchie Cobbs 	const struct greheader *gre;
660816b834fSArchie Cobbs 	const struct ip *ip;
661add85a1dSArchie Cobbs 	int error = 0;
662069154d5SJulian Elischer 	struct mbuf *m;
663add85a1dSArchie Cobbs 
664069154d5SJulian Elischer 	NGI_GET_M(item, m);
6659bee7adfSArchie Cobbs 	/* Update stats */
6669bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
6679bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
6689bee7adfSArchie Cobbs 
669add85a1dSArchie Cobbs 	/* Sanity check packet length */
670add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
6719bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
67283beeed9SGleb Smirnoff 		ERROUT(EINVAL);
673add85a1dSArchie Cobbs 	}
674add85a1dSArchie Cobbs 
675add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
676add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
677add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
678678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
67983beeed9SGleb Smirnoff 		ERROUT(ENOBUFS);
680add85a1dSArchie Cobbs 	}
681816b834fSArchie Cobbs 	ip = mtod(m, const struct ip *);
682add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
683add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
684add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
685678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
68683beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
687add85a1dSArchie Cobbs 		}
688816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
689add85a1dSArchie Cobbs 	}
690816b834fSArchie Cobbs 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
691add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
6929bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
6939bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
69483beeed9SGleb Smirnoff 		ERROUT(EINVAL);
6959bee7adfSArchie Cobbs 	}
696add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
697add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
698678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
69983beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
700add85a1dSArchie Cobbs 		}
701816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
702816b834fSArchie Cobbs 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
703add85a1dSArchie Cobbs 	}
704add85a1dSArchie Cobbs 
705add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
706add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
70738f2d636SAlexander Motin 	    - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
7089bee7adfSArchie Cobbs 	if (extralen < 0) {
7099bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
71083beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7119bee7adfSArchie Cobbs 	}
71238f2d636SAlexander Motin 	if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
7139bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
71483beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7159bee7adfSArchie Cobbs 	}
716489290e9SAlexander Motin 
71738f2d636SAlexander Motin 	hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
718489290e9SAlexander Motin 	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
7199bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
72083beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7219bee7adfSArchie Cobbs 	}
722489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
723add85a1dSArchie Cobbs 
724add85a1dSArchie Cobbs 	/* Look for peer ack */
725add85a1dSArchie Cobbs 	if (gre->hasAck) {
72638f2d636SAlexander Motin 		const u_int32_t	ack = be32dec(&gre->data[gre->hasSeq]);
727489290e9SAlexander Motin 		const int index = ack - hpriv->recvAck - 1;
72822dfb9bdSArchie Cobbs 		long sample;
729add85a1dSArchie Cobbs 		long diff;
730add85a1dSArchie Cobbs 
731add85a1dSArchie Cobbs 		/* Sanity check ack value */
732489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
7339bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
7349bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
7359bee7adfSArchie Cobbs 		}
736489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
7379bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
738489290e9SAlexander Motin 		hpriv->recvAck = ack;
739add85a1dSArchie Cobbs 
740add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
741489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
742489290e9SAlexander Motin 			sample = ng_pptpgre_time() - hpriv->timeSent[index];
743489290e9SAlexander Motin 			diff = sample - hpriv->rtt;
744489290e9SAlexander Motin 			hpriv->rtt += PPTP_ACK_ALPHA(diff);
745add85a1dSArchie Cobbs 			if (diff < 0)
746add85a1dSArchie Cobbs 				diff = -diff;
747489290e9SAlexander Motin 			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
748f8e159d6SGleb Smirnoff 			    /* +2 to compensate low precision of int math */
749489290e9SAlexander Motin 			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
750489290e9SAlexander Motin 			if (hpriv->ato > PPTP_MAX_TIMEOUT)
751489290e9SAlexander Motin 				hpriv->ato = PPTP_MAX_TIMEOUT;
752489290e9SAlexander Motin 			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
753489290e9SAlexander Motin 				hpriv->ato = PPTP_MIN_TIMEOUT;
754e962a823SArchie Cobbs 
755e962a823SArchie Cobbs 			/* Shift packet transmit times in our transmit window */
756489290e9SAlexander Motin 			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
757489290e9SAlexander Motin 			    sizeof(*hpriv->timeSent)
758922ee196SArchie Cobbs 			      * (PPTP_XMIT_WIN - (index + 1)));
759e962a823SArchie Cobbs 
760922ee196SArchie Cobbs 			/* If we sent an entire window, increase window size */
761489290e9SAlexander Motin 			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
762489290e9SAlexander Motin 			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
763489290e9SAlexander Motin 				hpriv->xmitWin++;
764489290e9SAlexander Motin 				hpriv->winAck = ack + hpriv->xmitWin;
765add85a1dSArchie Cobbs 			}
766add85a1dSArchie Cobbs 
7679bee7adfSArchie Cobbs 			/* Stop/(re)start receive ACK timer as necessary */
768714f558bSAlexander Motin 			ng_uncallout(&hpriv->rackTimer, hpriv->node);
769489290e9SAlexander Motin 			if (hpriv->recvAck != hpriv->xmitSeq)
770489290e9SAlexander Motin 				ng_pptpgre_start_recv_ack_timer(hpriv);
771add85a1dSArchie Cobbs 		}
772922ee196SArchie Cobbs 	}
7739bee7adfSArchie Cobbs badAck:
774add85a1dSArchie Cobbs 
775add85a1dSArchie Cobbs 	/* See if frame contains any data */
776add85a1dSArchie Cobbs 	if (gre->hasSeq) {
77738f2d636SAlexander Motin 		const u_int32_t seq = be32dec(&gre->data[0]);
778add85a1dSArchie Cobbs 
779add85a1dSArchie Cobbs 		/* Sanity check sequence number */
780489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) {
781489290e9SAlexander Motin 			if (seq == hpriv->recvSeq)
7829bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
7839bee7adfSArchie Cobbs 			else
7849bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
785489290e9SAlexander Motin 			mtx_unlock(&hpriv->mtx);
78683beeed9SGleb Smirnoff 			ERROUT(EINVAL);
7879bee7adfSArchie Cobbs 		}
788489290e9SAlexander Motin 		hpriv->recvSeq = seq;
789add85a1dSArchie Cobbs 
790add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
791489290e9SAlexander Motin 		if (!(callout_pending(&hpriv->sackTimer))) {
792678f9e33SArchie Cobbs 			/* If delayed ACK is disabled, send it now */
793489290e9SAlexander Motin 			if (!hpriv->conf.enableDelayedAck) {	/* ack now */
794489290e9SAlexander Motin 				ng_pptpgre_xmit(hpriv, NULL);
795489290e9SAlexander Motin 				/* ng_pptpgre_xmit() drops the mutex */
79683beeed9SGleb Smirnoff 			} else {				/* ack later */
797714f558bSAlexander Motin 				ng_pptpgre_start_send_ack_timer(hpriv);
798489290e9SAlexander Motin 				mtx_unlock(&hpriv->mtx);
799add85a1dSArchie Cobbs 			}
800489290e9SAlexander Motin 		} else
801489290e9SAlexander Motin 			mtx_unlock(&hpriv->mtx);
802add85a1dSArchie Cobbs 
803add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
804add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
805add85a1dSArchie Cobbs 		if (extralen > 0)
806add85a1dSArchie Cobbs 			m_adj(m, -extralen);
807add85a1dSArchie Cobbs 
808489290e9SAlexander Motin 		mtx_assert(&hpriv->mtx, MA_NOTOWNED);
809489290e9SAlexander Motin 
810add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
811489290e9SAlexander Motin 		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
8129bee7adfSArchie Cobbs 	} else {
8139bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
814489290e9SAlexander Motin 		mtx_unlock(&hpriv->mtx);
815069154d5SJulian Elischer 		NG_FREE_ITEM(item);
816069154d5SJulian Elischer 		NG_FREE_M(m);		/* no data to deliver */
8179bee7adfSArchie Cobbs 	}
81883beeed9SGleb Smirnoff 
81983beeed9SGleb Smirnoff 	return (error);
82083beeed9SGleb Smirnoff 
82183beeed9SGleb Smirnoff done:
82283beeed9SGleb Smirnoff 	NG_FREE_ITEM(item);
82383beeed9SGleb Smirnoff 	NG_FREE_M(m);
824add85a1dSArchie Cobbs 	return (error);
825add85a1dSArchie Cobbs }
826add85a1dSArchie Cobbs 
827add85a1dSArchie Cobbs /*************************************************************************
828add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
829add85a1dSArchie Cobbs *************************************************************************/
830add85a1dSArchie Cobbs 
831add85a1dSArchie Cobbs /*
8329bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
833add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
834add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
835add85a1dSArchie Cobbs  * recv ack timer, if any.
836add85a1dSArchie Cobbs  */
837add85a1dSArchie Cobbs static void
838489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
839add85a1dSArchie Cobbs {
840678f9e33SArchie Cobbs 	int remain, ticks;
841add85a1dSArchie Cobbs 
842add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
843add85a1dSArchie Cobbs 	   and reset the timer to that time. */
844489290e9SAlexander Motin 	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
845add85a1dSArchie Cobbs 	if (remain < 0)
846add85a1dSArchie Cobbs 		remain = 0;
8479bee7adfSArchie Cobbs 
8484a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
849678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
850489290e9SAlexander Motin 	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
851489290e9SAlexander Motin 	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
8524a48abb2SArchie Cobbs }
8534a48abb2SArchie Cobbs 
8544a48abb2SArchie Cobbs /*
855add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
856add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
857add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
858add85a1dSArchie Cobbs  */
859add85a1dSArchie Cobbs static void
860089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
861add85a1dSArchie Cobbs {
86230400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
863489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
8649bee7adfSArchie Cobbs 
865add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
8669bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
867489290e9SAlexander Motin 	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
868489290e9SAlexander Motin 	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
869489290e9SAlexander Motin 	if (hpriv->ato > PPTP_MAX_TIMEOUT)
870489290e9SAlexander Motin 		hpriv->ato = PPTP_MAX_TIMEOUT;
871489290e9SAlexander Motin 	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
872489290e9SAlexander Motin 		hpriv->ato = PPTP_MIN_TIMEOUT;
873678f9e33SArchie Cobbs 
874e962a823SArchie Cobbs 	/* Reset ack and sliding window */
875489290e9SAlexander Motin 	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
876489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
877489290e9SAlexander Motin 	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
878add85a1dSArchie Cobbs }
879add85a1dSArchie Cobbs 
880add85a1dSArchie Cobbs /*
8819bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
8829bee7adfSArchie Cobbs  * already running.
8839bee7adfSArchie Cobbs  */
8849bee7adfSArchie Cobbs static void
885714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
8869bee7adfSArchie Cobbs {
887714f558bSAlexander Motin 	int ackTimeout, ticks;
888714f558bSAlexander Motin 
889714f558bSAlexander Motin 	/* Take 1/4 of the estimated round trip time */
890714f558bSAlexander Motin 	ackTimeout = (hpriv->rtt >> 2);
891714f558bSAlexander Motin 	if (ackTimeout < PPTP_MIN_ACK_DELAY)
892714f558bSAlexander Motin 		ackTimeout = PPTP_MIN_ACK_DELAY;
893714f558bSAlexander Motin 	else if (ackTimeout > PPTP_MAX_ACK_DELAY)
894714f558bSAlexander Motin 		ackTimeout = PPTP_MAX_ACK_DELAY;
8959bee7adfSArchie Cobbs 
8964a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
897678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
898489290e9SAlexander Motin 	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
899489290e9SAlexander Motin 	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
9004a48abb2SArchie Cobbs }
9014a48abb2SArchie Cobbs 
9024a48abb2SArchie Cobbs /*
903add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
904add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
905add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
906add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
907add85a1dSArchie Cobbs  */
908add85a1dSArchie Cobbs static void
909089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
910add85a1dSArchie Cobbs {
911489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
91283beeed9SGleb Smirnoff 
913489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
9149bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
915489290e9SAlexander Motin   	ng_pptpgre_xmit(hpriv, NULL);
916489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
917add85a1dSArchie Cobbs }
918add85a1dSArchie Cobbs 
919add85a1dSArchie Cobbs /*************************************************************************
920add85a1dSArchie Cobbs 		    MISC FUNCTIONS
921add85a1dSArchie Cobbs *************************************************************************/
922add85a1dSArchie Cobbs 
923add85a1dSArchie Cobbs /*
924489290e9SAlexander Motin  * Find the hook with a given session ID.
925489290e9SAlexander Motin  */
926489290e9SAlexander Motin static hpriv_p
927489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
928489290e9SAlexander Motin {
929489290e9SAlexander Motin 	uint16_t	hash = SESSHASH(cid);
930489290e9SAlexander Motin 	hpriv_p	hpriv = NULL;
931489290e9SAlexander Motin 
932489290e9SAlexander Motin 	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
933489290e9SAlexander Motin 		if (hpriv->conf.cid == cid)
934489290e9SAlexander Motin 			break;
935489290e9SAlexander Motin 	}
936489290e9SAlexander Motin 
937489290e9SAlexander Motin 	return (hpriv);
938489290e9SAlexander Motin }
939489290e9SAlexander Motin 
940489290e9SAlexander Motin /*
941489290e9SAlexander Motin  * Reset state (must be called with lock held or from writer)
942add85a1dSArchie Cobbs  */
943add85a1dSArchie Cobbs static void
944489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv)
945add85a1dSArchie Cobbs {
946add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
947489290e9SAlexander Motin 	hpriv->ato = PPTP_MAX_TIMEOUT;
948714f558bSAlexander Motin 	hpriv->rtt = PPTP_TIME_SCALE / 10;
949714f558bSAlexander Motin 	if (hpriv->conf.peerPpd > 1)	/* ppd = 0 treat as = 1 */
950714f558bSAlexander Motin 		hpriv->rtt *= hpriv->conf.peerPpd;
951489290e9SAlexander Motin 	hpriv->dev = 0;
952489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
953489290e9SAlexander Motin 	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
954489290e9SAlexander Motin 		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
955489290e9SAlexander Motin 	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
956489290e9SAlexander Motin 		hpriv->xmitWin = PPTP_XMIT_WIN;
957489290e9SAlexander Motin 	hpriv->winAck = hpriv->xmitWin;
958add85a1dSArchie Cobbs 
959add85a1dSArchie Cobbs 	/* Reset sequence numbers */
960489290e9SAlexander Motin 	hpriv->recvSeq = ~0;
961489290e9SAlexander Motin 	hpriv->recvAck = ~0;
962489290e9SAlexander Motin 	hpriv->xmitSeq = ~0;
963489290e9SAlexander Motin 	hpriv->xmitAck = ~0;
964add85a1dSArchie Cobbs 
9654a48abb2SArchie Cobbs 	/* Stop timers */
966714f558bSAlexander Motin 	ng_uncallout(&hpriv->sackTimer, hpriv->node);
967714f558bSAlexander Motin 	ng_uncallout(&hpriv->rackTimer, hpriv->node);
968add85a1dSArchie Cobbs }
969add85a1dSArchie Cobbs 
970add85a1dSArchie Cobbs /*
971add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
972add85a1dSArchie Cobbs  */
973add85a1dSArchie Cobbs static pptptime_t
974489290e9SAlexander Motin ng_pptpgre_time(void)
975add85a1dSArchie Cobbs {
976add85a1dSArchie Cobbs 	struct timeval tv;
977678f9e33SArchie Cobbs 	pptptime_t t;
978add85a1dSArchie Cobbs 
979678f9e33SArchie Cobbs 	microuptime(&tv);
980678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
981714f558bSAlexander Motin 	t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
982678f9e33SArchie Cobbs 	return(t);
983add85a1dSArchie Cobbs }
984