xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision 38f2d636ca6858d32fd8eb158756a2ae8c7cdb65)
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 */
2841ede983cSDag-Erling Smørgrav 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
285add85a1dSArchie Cobbs 	if (priv == NULL)
286add85a1dSArchie Cobbs 		return (ENOMEM);
287add85a1dSArchie Cobbs 
28830400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
289add85a1dSArchie Cobbs 
290add85a1dSArchie Cobbs 	/* Initialize state */
291489290e9SAlexander Motin 	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
292489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.sackTimer);
293489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.rackTimer);
294489290e9SAlexander Motin 	priv->uppersess.node = node;
295489290e9SAlexander Motin 
296489290e9SAlexander Motin 	for (i = 0; i < SESSHASHSIZE; i++)
297489290e9SAlexander Motin 	    LIST_INIT(&priv->sesshash[i]);
298489290e9SAlexander Motin 
299489290e9SAlexander Motin 	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
300add85a1dSArchie Cobbs 
301add85a1dSArchie Cobbs 	/* Done */
302add85a1dSArchie Cobbs 	return (0);
303add85a1dSArchie Cobbs }
304add85a1dSArchie Cobbs 
305add85a1dSArchie Cobbs /*
306add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
307add85a1dSArchie Cobbs  */
308add85a1dSArchie Cobbs static int
309add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
310add85a1dSArchie Cobbs {
31130400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
312add85a1dSArchie Cobbs 
313add85a1dSArchie Cobbs 	/* Check hook name */
314489290e9SAlexander Motin 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
315489290e9SAlexander Motin 		priv->upper = hook;
316489290e9SAlexander Motin 		priv->uppersess.hook = hook;
317489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
318489290e9SAlexander Motin 	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
319489290e9SAlexander Motin 		priv->lower = hook;
320489290e9SAlexander Motin 		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
321489290e9SAlexander Motin 	} else {
322489290e9SAlexander Motin 		static const char hexdig[16] = "0123456789abcdef";
323489290e9SAlexander Motin 		const char *hex;
324489290e9SAlexander Motin 		hpriv_p hpriv;
325489290e9SAlexander Motin 		int i, j;
326489290e9SAlexander Motin 		uint16_t cid, hash;
327489290e9SAlexander Motin 
328489290e9SAlexander Motin 		/* Parse hook name to get session ID */
329489290e9SAlexander Motin 		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
330489290e9SAlexander Motin 		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
331489290e9SAlexander Motin 			return (EINVAL);
332489290e9SAlexander Motin 		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
333489290e9SAlexander Motin 		for (cid = i = 0; i < 4; i++) {
334489290e9SAlexander Motin 			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
335489290e9SAlexander Motin 			if (j == 16)
336489290e9SAlexander Motin 				return (EINVAL);
337489290e9SAlexander Motin 			cid = (cid << 4) | j;
338489290e9SAlexander Motin 		}
339489290e9SAlexander Motin 		if (hex[i] != '\0')
340add85a1dSArchie Cobbs 			return (EINVAL);
341add85a1dSArchie Cobbs 
342489290e9SAlexander Motin 		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
343489290e9SAlexander Motin 		if (hpriv == NULL)
344489290e9SAlexander Motin 			return (ENOMEM);
345add85a1dSArchie Cobbs 
346489290e9SAlexander Motin 		/* Initialize state */
347489290e9SAlexander Motin 		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
348489290e9SAlexander Motin 		ng_callout_init(&hpriv->sackTimer);
349489290e9SAlexander Motin 		ng_callout_init(&hpriv->rackTimer);
350489290e9SAlexander Motin 		hpriv->conf.cid = cid;
351489290e9SAlexander Motin 		hpriv->node = node;
352489290e9SAlexander Motin 		hpriv->hook = hook;
353489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, hpriv);
354489290e9SAlexander Motin 
355489290e9SAlexander Motin 		hash = SESSHASH(cid);
356489290e9SAlexander Motin 		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
357489290e9SAlexander Motin 	}
358489290e9SAlexander Motin 
359add85a1dSArchie Cobbs 	return (0);
360add85a1dSArchie Cobbs }
361add85a1dSArchie Cobbs 
362add85a1dSArchie Cobbs /*
363add85a1dSArchie Cobbs  * Receive a control message.
364add85a1dSArchie Cobbs  */
365add85a1dSArchie Cobbs static int
366069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
367add85a1dSArchie Cobbs {
36830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
369add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
370add85a1dSArchie Cobbs 	int error = 0;
371069154d5SJulian Elischer 	struct ng_mesg *msg;
372add85a1dSArchie Cobbs 
373069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
374add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
375add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
376add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
377add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
378add85a1dSArchie Cobbs 		    {
379add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
380add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
381489290e9SAlexander Motin 			hpriv_p hpriv;
382489290e9SAlexander Motin 			uint16_t hash;
383add85a1dSArchie Cobbs 
384add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
385add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
386add85a1dSArchie Cobbs 				ERROUT(EINVAL);
387489290e9SAlexander Motin 			/* Try to find session by cid. */
388489290e9SAlexander Motin 			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
389489290e9SAlexander Motin 			/* If not present - use upper. */
390489290e9SAlexander Motin 			if (hpriv == NULL) {
391489290e9SAlexander Motin 				hpriv = &priv->uppersess;
392489290e9SAlexander Motin 				LIST_REMOVE(hpriv, sessions);
393489290e9SAlexander Motin 				hash = SESSHASH(newConf->cid);
394489290e9SAlexander Motin 				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
395489290e9SAlexander Motin 				    sessions);
396489290e9SAlexander Motin 			}
397489290e9SAlexander Motin 			ng_pptpgre_reset(hpriv);	/* reset on configure */
398489290e9SAlexander Motin 			hpriv->conf = *newConf;
399add85a1dSArchie Cobbs 			break;
400add85a1dSArchie Cobbs 		    }
401add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
402489290e9SAlexander Motin 		    {
403489290e9SAlexander Motin 			hpriv_p hpriv;
404489290e9SAlexander Motin 
405489290e9SAlexander Motin 			if (msg->header.arglen == 2) {
406489290e9SAlexander Motin 				/* Try to find session by cid. */
407489290e9SAlexander Motin 	    			hpriv = ng_pptpgre_find_session(priv,
408489290e9SAlexander Motin 				    *((uint16_t *)msg->data));
409489290e9SAlexander Motin 				if (hpriv == NULL)
410489290e9SAlexander Motin 					ERROUT(EINVAL);
411489290e9SAlexander Motin 			} else if (msg->header.arglen == 0) {
412489290e9SAlexander Motin 				/* Use upper. */
413489290e9SAlexander Motin 				hpriv = &priv->uppersess;
414489290e9SAlexander Motin 			} else
415489290e9SAlexander Motin 				ERROUT(EINVAL);
416489290e9SAlexander Motin 			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
417add85a1dSArchie Cobbs 			if (resp == NULL)
418add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
419489290e9SAlexander Motin 			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
420add85a1dSArchie Cobbs 			break;
421489290e9SAlexander Motin 		    }
4229bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
4239bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
4249bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
4259bee7adfSArchie Cobbs 		    {
4269bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
4279bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
4289bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
4299bee7adfSArchie Cobbs 				if (resp == NULL)
4309bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
4319bee7adfSArchie Cobbs 				bcopy(&priv->stats,
4329bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
4339bee7adfSArchie Cobbs 			}
4349bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
4359bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
4369bee7adfSArchie Cobbs 			break;
4379bee7adfSArchie Cobbs 		    }
438add85a1dSArchie Cobbs 		default:
439add85a1dSArchie Cobbs 			error = EINVAL;
440add85a1dSArchie Cobbs 			break;
441add85a1dSArchie Cobbs 		}
442add85a1dSArchie Cobbs 		break;
443add85a1dSArchie Cobbs 	default:
444add85a1dSArchie Cobbs 		error = EINVAL;
445add85a1dSArchie Cobbs 		break;
446add85a1dSArchie Cobbs 	}
447589f6ed8SJulian Elischer done:
448069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
449069154d5SJulian Elischer 	NG_FREE_MSG(msg);
450add85a1dSArchie Cobbs 	return (error);
451add85a1dSArchie Cobbs }
452add85a1dSArchie Cobbs 
453add85a1dSArchie Cobbs /*
454add85a1dSArchie Cobbs  * Receive incoming data on a hook.
455add85a1dSArchie Cobbs  */
456add85a1dSArchie Cobbs static int
457069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
458add85a1dSArchie Cobbs {
459489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
460f2ba84d7SGleb Smirnoff 	int rval;
461add85a1dSArchie Cobbs 
462add85a1dSArchie Cobbs 	/* If not configured, reject */
463489290e9SAlexander Motin 	if (!hpriv->conf.enabled) {
464069154d5SJulian Elischer 		NG_FREE_ITEM(item);
465add85a1dSArchie Cobbs 		return (ENXIO);
466add85a1dSArchie Cobbs 	}
467add85a1dSArchie Cobbs 
468489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
469f2ba84d7SGleb Smirnoff 
470489290e9SAlexander Motin 	rval = ng_pptpgre_xmit(hpriv, item);
471f2ba84d7SGleb Smirnoff 
472489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
473f2ba84d7SGleb Smirnoff 
474f2ba84d7SGleb Smirnoff 	return (rval);
475add85a1dSArchie Cobbs }
476add85a1dSArchie Cobbs 
477add85a1dSArchie Cobbs /*
478489290e9SAlexander Motin  * Hook disconnection
479489290e9SAlexander Motin  */
480489290e9SAlexander Motin static int
481489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook)
482489290e9SAlexander Motin {
483489290e9SAlexander Motin 	const node_p node = NG_HOOK_NODE(hook);
484489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
485489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
486489290e9SAlexander Motin 
487489290e9SAlexander Motin 	/* Zero out hook pointer */
488489290e9SAlexander Motin 	if (hook == priv->upper) {
489489290e9SAlexander Motin 		priv->upper = NULL;
490489290e9SAlexander Motin 		priv->uppersess.hook = NULL;
491489290e9SAlexander Motin 	} else if (hook == priv->lower) {
492489290e9SAlexander Motin 		priv->lower = NULL;
493489290e9SAlexander Motin 	} else {
494489290e9SAlexander Motin 		/* Reset node (stops timers) */
495489290e9SAlexander Motin 		ng_pptpgre_reset(hpriv);
496489290e9SAlexander Motin 
497489290e9SAlexander Motin 		LIST_REMOVE(hpriv, sessions);
498489290e9SAlexander Motin 		mtx_destroy(&hpriv->mtx);
499489290e9SAlexander Motin 		free(hpriv, M_NETGRAPH);
500489290e9SAlexander Motin 	}
501489290e9SAlexander Motin 
502489290e9SAlexander Motin 	/* Go away if no longer connected to anything */
503489290e9SAlexander Motin 	if ((NG_NODE_NUMHOOKS(node) == 0)
504489290e9SAlexander Motin 	&& (NG_NODE_IS_VALID(node)))
505489290e9SAlexander Motin 		ng_rmnode_self(node);
506489290e9SAlexander Motin 	return (0);
507489290e9SAlexander Motin }
508489290e9SAlexander Motin 
509489290e9SAlexander Motin /*
510add85a1dSArchie Cobbs  * Destroy node
511add85a1dSArchie Cobbs  */
512add85a1dSArchie Cobbs static int
513069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
514add85a1dSArchie Cobbs {
51530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
516add85a1dSArchie Cobbs 
517089323f3SGleb Smirnoff 	/* Reset node (stops timers) */
518489290e9SAlexander Motin 	ng_pptpgre_reset(&priv->uppersess);
519add85a1dSArchie Cobbs 
520489290e9SAlexander Motin 	LIST_REMOVE(&priv->uppersess, sessions);
521489290e9SAlexander Motin 	mtx_destroy(&priv->uppersess.mtx);
522f2ba84d7SGleb Smirnoff 
5231ede983cSDag-Erling Smørgrav 	free(priv, M_NETGRAPH);
5244a48abb2SArchie Cobbs 
5254a48abb2SArchie Cobbs 	/* Decrement ref count */
52630400f03SJulian Elischer 	NG_NODE_UNREF(node);
527add85a1dSArchie Cobbs 	return (0);
528add85a1dSArchie Cobbs }
529add85a1dSArchie Cobbs 
530add85a1dSArchie Cobbs /*************************************************************************
531add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
532add85a1dSArchie Cobbs *************************************************************************/
533add85a1dSArchie Cobbs 
534add85a1dSArchie Cobbs /*
535add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
536add85a1dSArchie Cobbs  */
537add85a1dSArchie Cobbs static int
538489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
539add85a1dSArchie Cobbs {
540489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
541add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
542add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
543add85a1dSArchie Cobbs 	int grelen, error;
544069154d5SJulian Elischer 	struct mbuf *m;
545add85a1dSArchie Cobbs 
546489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_OWNED);
54783beeed9SGleb Smirnoff 
548069154d5SJulian Elischer 	if (item) {
549069154d5SJulian Elischer 		NGI_GET_M(item, m);
550069154d5SJulian Elischer 	} else {
551069154d5SJulian Elischer 		m = NULL;
552069154d5SJulian Elischer 	}
5539bee7adfSArchie Cobbs 	/* Check if there's data */
5549bee7adfSArchie Cobbs 	if (m != NULL) {
5559bee7adfSArchie Cobbs 
556922ee196SArchie Cobbs 		/* Check if windowing is enabled */
557489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
558add85a1dSArchie Cobbs 			/* Is our transmit window full? */
559489290e9SAlexander Motin 			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
560489290e9SAlexander Motin 			    hpriv->recvAck) >= hpriv->xmitWin) {
5619bee7adfSArchie Cobbs 				priv->stats.xmitDrops++;
56283beeed9SGleb Smirnoff 				ERROUT(ENOBUFS);
563add85a1dSArchie Cobbs 			}
564922ee196SArchie Cobbs 		}
565add85a1dSArchie Cobbs 
566add85a1dSArchie Cobbs 		/* Sanity check frame length */
567add85a1dSArchie Cobbs 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
5689bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
56983beeed9SGleb Smirnoff 			ERROUT(EMSGSIZE);
570add85a1dSArchie Cobbs 		}
571069154d5SJulian Elischer 	} else {
5729bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
573069154d5SJulian Elischer 	}
574add85a1dSArchie Cobbs 
575add85a1dSArchie Cobbs 	/* Build GRE header */
57638f2d636SAlexander Motin 	be32enc(gre, PPTP_INIT_VALUE);
57738f2d636SAlexander Motin 	be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
57838f2d636SAlexander Motin 	be16enc(&gre->cid, hpriv->conf.peerCid);
579add85a1dSArchie Cobbs 
580add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
581add85a1dSArchie Cobbs 	if (m != NULL) {
582add85a1dSArchie Cobbs 		gre->hasSeq = 1;
583489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
584489290e9SAlexander Motin 			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
585489290e9SAlexander Motin 			    = ng_pptpgre_time();
586922ee196SArchie Cobbs 		}
587489290e9SAlexander Motin 		hpriv->xmitSeq++;
58838f2d636SAlexander Motin 		be32enc(&gre->data[0], hpriv->xmitSeq);
589add85a1dSArchie Cobbs 	}
590add85a1dSArchie Cobbs 
591add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
592489290e9SAlexander Motin 	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
593add85a1dSArchie Cobbs 		gre->hasAck = 1;
59438f2d636SAlexander Motin 		be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
595489290e9SAlexander Motin 		hpriv->xmitAck = hpriv->recvSeq;
596714f558bSAlexander Motin 		if (hpriv->conf.enableDelayedAck)
597714f558bSAlexander Motin 			ng_uncallout(&hpriv->sackTimer, hpriv->node);
598da010626SArchie Cobbs 	}
599add85a1dSArchie Cobbs 
600add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
601add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
602add85a1dSArchie Cobbs 	if (m == NULL) {
603a163d034SWarner Losh 		MGETHDR(m, M_DONTWAIT, MT_DATA);
604add85a1dSArchie Cobbs 		if (m == NULL) {
605678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
60683beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
607add85a1dSArchie Cobbs 		}
608add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
609add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
610add85a1dSArchie Cobbs 	} else {
611a163d034SWarner Losh 		M_PREPEND(m, grelen, M_DONTWAIT);
612add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
613add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
614678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
61583beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
616add85a1dSArchie Cobbs 		}
617add85a1dSArchie Cobbs 	}
618add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
619add85a1dSArchie Cobbs 
6209bee7adfSArchie Cobbs 	/* Update stats */
6219bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
6229bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
6239bee7adfSArchie Cobbs 
62483beeed9SGleb Smirnoff 	/*
62583beeed9SGleb Smirnoff 	 * XXX: we should reset timer only after an item has been sent
62683beeed9SGleb Smirnoff 	 * successfully.
62783beeed9SGleb Smirnoff 	 */
628489290e9SAlexander Motin 	if (hpriv->conf.enableWindowing &&
629489290e9SAlexander Motin 	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
630489290e9SAlexander Motin 		ng_pptpgre_start_recv_ack_timer(hpriv);
63183beeed9SGleb Smirnoff 
632489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
63383beeed9SGleb Smirnoff 
634add85a1dSArchie Cobbs 	/* Deliver packet */
635069154d5SJulian Elischer 	if (item) {
636069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
637069154d5SJulian Elischer 	} else {
638069154d5SJulian Elischer 		NG_SEND_DATA_ONLY(error, priv->lower, m);
639069154d5SJulian Elischer 	}
640069154d5SJulian Elischer 
64183beeed9SGleb Smirnoff 	return (error);
642678f9e33SArchie Cobbs 
64383beeed9SGleb Smirnoff done:
644489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
64583beeed9SGleb Smirnoff 	NG_FREE_M(m);
64683beeed9SGleb Smirnoff 	if (item)
64783beeed9SGleb Smirnoff 		NG_FREE_ITEM(item);
648add85a1dSArchie Cobbs 	return (error);
649add85a1dSArchie Cobbs }
650add85a1dSArchie Cobbs 
651add85a1dSArchie Cobbs /*
652add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
653add85a1dSArchie Cobbs  */
654add85a1dSArchie Cobbs static int
655489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
656add85a1dSArchie Cobbs {
657489290e9SAlexander Motin 	hpriv_p hpriv;
658489290e9SAlexander Motin 	node_p node = NG_HOOK_NODE(hook);
65930400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
660add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
661816b834fSArchie Cobbs 	const struct greheader *gre;
662816b834fSArchie Cobbs 	const struct ip *ip;
663add85a1dSArchie Cobbs 	int error = 0;
664069154d5SJulian Elischer 	struct mbuf *m;
665add85a1dSArchie Cobbs 
666069154d5SJulian Elischer 	NGI_GET_M(item, m);
6679bee7adfSArchie Cobbs 	/* Update stats */
6689bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
6699bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
6709bee7adfSArchie Cobbs 
671add85a1dSArchie Cobbs 	/* Sanity check packet length */
672add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
6739bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
67483beeed9SGleb Smirnoff 		ERROUT(EINVAL);
675add85a1dSArchie Cobbs 	}
676add85a1dSArchie Cobbs 
677add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
678add85a1dSArchie Cobbs 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
679add85a1dSArchie Cobbs 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
680678f9e33SArchie Cobbs 		priv->stats.memoryFailures++;
68183beeed9SGleb Smirnoff 		ERROUT(ENOBUFS);
682add85a1dSArchie Cobbs 	}
683816b834fSArchie Cobbs 	ip = mtod(m, const struct ip *);
684add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
685add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
686add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
687678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
68883beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
689add85a1dSArchie Cobbs 		}
690816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
691add85a1dSArchie Cobbs 	}
692816b834fSArchie Cobbs 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
693add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
6949bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
6959bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
69683beeed9SGleb Smirnoff 		ERROUT(EINVAL);
6979bee7adfSArchie Cobbs 	}
698add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
699add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
700678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
70183beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
702add85a1dSArchie Cobbs 		}
703816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
704816b834fSArchie Cobbs 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
705add85a1dSArchie Cobbs 	}
706add85a1dSArchie Cobbs 
707add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
708add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
70938f2d636SAlexander Motin 	    - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
7109bee7adfSArchie Cobbs 	if (extralen < 0) {
7119bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
71283beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7139bee7adfSArchie Cobbs 	}
71438f2d636SAlexander Motin 	if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
7159bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
71683beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7179bee7adfSArchie Cobbs 	}
718489290e9SAlexander Motin 
71938f2d636SAlexander Motin 	hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
720489290e9SAlexander Motin 	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
7219bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
72283beeed9SGleb Smirnoff 		ERROUT(EINVAL);
7239bee7adfSArchie Cobbs 	}
724489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
725add85a1dSArchie Cobbs 
726add85a1dSArchie Cobbs 	/* Look for peer ack */
727add85a1dSArchie Cobbs 	if (gre->hasAck) {
72838f2d636SAlexander Motin 		const u_int32_t	ack = be32dec(&gre->data[gre->hasSeq]);
729489290e9SAlexander Motin 		const int index = ack - hpriv->recvAck - 1;
73022dfb9bdSArchie Cobbs 		long sample;
731add85a1dSArchie Cobbs 		long diff;
732add85a1dSArchie Cobbs 
733add85a1dSArchie Cobbs 		/* Sanity check ack value */
734489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
7359bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
7369bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
7379bee7adfSArchie Cobbs 		}
738489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
7399bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
740489290e9SAlexander Motin 		hpriv->recvAck = ack;
741add85a1dSArchie Cobbs 
742add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
743489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
744489290e9SAlexander Motin 			sample = ng_pptpgre_time() - hpriv->timeSent[index];
745489290e9SAlexander Motin 			diff = sample - hpriv->rtt;
746489290e9SAlexander Motin 			hpriv->rtt += PPTP_ACK_ALPHA(diff);
747add85a1dSArchie Cobbs 			if (diff < 0)
748add85a1dSArchie Cobbs 				diff = -diff;
749489290e9SAlexander Motin 			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
750f8e159d6SGleb Smirnoff 			    /* +2 to compensate low precision of int math */
751489290e9SAlexander Motin 			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
752489290e9SAlexander Motin 			if (hpriv->ato > PPTP_MAX_TIMEOUT)
753489290e9SAlexander Motin 				hpriv->ato = PPTP_MAX_TIMEOUT;
754489290e9SAlexander Motin 			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
755489290e9SAlexander Motin 				hpriv->ato = PPTP_MIN_TIMEOUT;
756e962a823SArchie Cobbs 
757e962a823SArchie Cobbs 			/* Shift packet transmit times in our transmit window */
758489290e9SAlexander Motin 			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
759489290e9SAlexander Motin 			    sizeof(*hpriv->timeSent)
760922ee196SArchie Cobbs 			      * (PPTP_XMIT_WIN - (index + 1)));
761e962a823SArchie Cobbs 
762922ee196SArchie Cobbs 			/* If we sent an entire window, increase window size */
763489290e9SAlexander Motin 			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
764489290e9SAlexander Motin 			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
765489290e9SAlexander Motin 				hpriv->xmitWin++;
766489290e9SAlexander Motin 				hpriv->winAck = ack + hpriv->xmitWin;
767add85a1dSArchie Cobbs 			}
768add85a1dSArchie Cobbs 
7699bee7adfSArchie Cobbs 			/* Stop/(re)start receive ACK timer as necessary */
770714f558bSAlexander Motin 			ng_uncallout(&hpriv->rackTimer, hpriv->node);
771489290e9SAlexander Motin 			if (hpriv->recvAck != hpriv->xmitSeq)
772489290e9SAlexander Motin 				ng_pptpgre_start_recv_ack_timer(hpriv);
773add85a1dSArchie Cobbs 		}
774922ee196SArchie Cobbs 	}
7759bee7adfSArchie Cobbs badAck:
776add85a1dSArchie Cobbs 
777add85a1dSArchie Cobbs 	/* See if frame contains any data */
778add85a1dSArchie Cobbs 	if (gre->hasSeq) {
77938f2d636SAlexander Motin 		const u_int32_t seq = be32dec(&gre->data[0]);
780add85a1dSArchie Cobbs 
781add85a1dSArchie Cobbs 		/* Sanity check sequence number */
782489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) {
783489290e9SAlexander Motin 			if (seq == hpriv->recvSeq)
7849bee7adfSArchie Cobbs 				priv->stats.recvDuplicates++;
7859bee7adfSArchie Cobbs 			else
7869bee7adfSArchie Cobbs 				priv->stats.recvOutOfOrder++;
787489290e9SAlexander Motin 			mtx_unlock(&hpriv->mtx);
78883beeed9SGleb Smirnoff 			ERROUT(EINVAL);
7899bee7adfSArchie Cobbs 		}
790489290e9SAlexander Motin 		hpriv->recvSeq = seq;
791add85a1dSArchie Cobbs 
792add85a1dSArchie Cobbs 		/* We need to acknowledge this packet; do it soon... */
793489290e9SAlexander Motin 		if (!(callout_pending(&hpriv->sackTimer))) {
794678f9e33SArchie Cobbs 			/* If delayed ACK is disabled, send it now */
795489290e9SAlexander Motin 			if (!hpriv->conf.enableDelayedAck) {	/* ack now */
796489290e9SAlexander Motin 				ng_pptpgre_xmit(hpriv, NULL);
797489290e9SAlexander Motin 				/* ng_pptpgre_xmit() drops the mutex */
79883beeed9SGleb Smirnoff 			} else {				/* ack later */
799714f558bSAlexander Motin 				ng_pptpgre_start_send_ack_timer(hpriv);
800489290e9SAlexander Motin 				mtx_unlock(&hpriv->mtx);
801add85a1dSArchie Cobbs 			}
802489290e9SAlexander Motin 		} else
803489290e9SAlexander Motin 			mtx_unlock(&hpriv->mtx);
804add85a1dSArchie Cobbs 
805add85a1dSArchie Cobbs 		/* Trim mbuf down to internal payload */
806add85a1dSArchie Cobbs 		m_adj(m, iphlen + grelen);
807add85a1dSArchie Cobbs 		if (extralen > 0)
808add85a1dSArchie Cobbs 			m_adj(m, -extralen);
809add85a1dSArchie Cobbs 
810489290e9SAlexander Motin 		mtx_assert(&hpriv->mtx, MA_NOTOWNED);
811489290e9SAlexander Motin 
812add85a1dSArchie Cobbs 		/* Deliver frame to upper layers */
813489290e9SAlexander Motin 		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
8149bee7adfSArchie Cobbs 	} else {
8159bee7adfSArchie Cobbs 		priv->stats.recvLoneAcks++;
816489290e9SAlexander Motin 		mtx_unlock(&hpriv->mtx);
817069154d5SJulian Elischer 		NG_FREE_ITEM(item);
818069154d5SJulian Elischer 		NG_FREE_M(m);		/* no data to deliver */
8199bee7adfSArchie Cobbs 	}
82083beeed9SGleb Smirnoff 
82183beeed9SGleb Smirnoff 	return (error);
82283beeed9SGleb Smirnoff 
82383beeed9SGleb Smirnoff done:
82483beeed9SGleb Smirnoff 	NG_FREE_ITEM(item);
82583beeed9SGleb Smirnoff 	NG_FREE_M(m);
826add85a1dSArchie Cobbs 	return (error);
827add85a1dSArchie Cobbs }
828add85a1dSArchie Cobbs 
829add85a1dSArchie Cobbs /*************************************************************************
830add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
831add85a1dSArchie Cobbs *************************************************************************/
832add85a1dSArchie Cobbs 
833add85a1dSArchie Cobbs /*
8349bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
835add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
836add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
837add85a1dSArchie Cobbs  * recv ack timer, if any.
838add85a1dSArchie Cobbs  */
839add85a1dSArchie Cobbs static void
840489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
841add85a1dSArchie Cobbs {
842678f9e33SArchie Cobbs 	int remain, ticks;
843add85a1dSArchie Cobbs 
844add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
845add85a1dSArchie Cobbs 	   and reset the timer to that time. */
846489290e9SAlexander Motin 	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
847add85a1dSArchie Cobbs 	if (remain < 0)
848add85a1dSArchie Cobbs 		remain = 0;
8499bee7adfSArchie Cobbs 
8504a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
851678f9e33SArchie Cobbs 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
852489290e9SAlexander Motin 	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
853489290e9SAlexander Motin 	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
8544a48abb2SArchie Cobbs }
8554a48abb2SArchie Cobbs 
8564a48abb2SArchie Cobbs /*
857add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
858add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
859add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
860add85a1dSArchie Cobbs  */
861add85a1dSArchie Cobbs static void
862089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
863add85a1dSArchie Cobbs {
86430400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
865489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
8669bee7adfSArchie Cobbs 
867add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
8689bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
869489290e9SAlexander Motin 	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
870489290e9SAlexander Motin 	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
871489290e9SAlexander Motin 	if (hpriv->ato > PPTP_MAX_TIMEOUT)
872489290e9SAlexander Motin 		hpriv->ato = PPTP_MAX_TIMEOUT;
873489290e9SAlexander Motin 	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
874489290e9SAlexander Motin 		hpriv->ato = PPTP_MIN_TIMEOUT;
875678f9e33SArchie Cobbs 
876e962a823SArchie Cobbs 	/* Reset ack and sliding window */
877489290e9SAlexander Motin 	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
878489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
879489290e9SAlexander Motin 	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
880add85a1dSArchie Cobbs }
881add85a1dSArchie Cobbs 
882add85a1dSArchie Cobbs /*
8839bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
8849bee7adfSArchie Cobbs  * already running.
8859bee7adfSArchie Cobbs  */
8869bee7adfSArchie Cobbs static void
887714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
8889bee7adfSArchie Cobbs {
889714f558bSAlexander Motin 	int ackTimeout, ticks;
890714f558bSAlexander Motin 
891714f558bSAlexander Motin 	/* Take 1/4 of the estimated round trip time */
892714f558bSAlexander Motin 	ackTimeout = (hpriv->rtt >> 2);
893714f558bSAlexander Motin 	if (ackTimeout < PPTP_MIN_ACK_DELAY)
894714f558bSAlexander Motin 		ackTimeout = PPTP_MIN_ACK_DELAY;
895714f558bSAlexander Motin 	else if (ackTimeout > PPTP_MAX_ACK_DELAY)
896714f558bSAlexander Motin 		ackTimeout = PPTP_MAX_ACK_DELAY;
8979bee7adfSArchie Cobbs 
8984a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
899678f9e33SArchie Cobbs 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
900489290e9SAlexander Motin 	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
901489290e9SAlexander Motin 	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
9024a48abb2SArchie Cobbs }
9034a48abb2SArchie Cobbs 
9044a48abb2SArchie Cobbs /*
905add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
906add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
907add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
908add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
909add85a1dSArchie Cobbs  */
910add85a1dSArchie Cobbs static void
911089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
912add85a1dSArchie Cobbs {
913489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
91483beeed9SGleb Smirnoff 
915489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
9169bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
917489290e9SAlexander Motin   	ng_pptpgre_xmit(hpriv, NULL);
918489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
919add85a1dSArchie Cobbs }
920add85a1dSArchie Cobbs 
921add85a1dSArchie Cobbs /*************************************************************************
922add85a1dSArchie Cobbs 		    MISC FUNCTIONS
923add85a1dSArchie Cobbs *************************************************************************/
924add85a1dSArchie Cobbs 
925add85a1dSArchie Cobbs /*
926489290e9SAlexander Motin  * Find the hook with a given session ID.
927489290e9SAlexander Motin  */
928489290e9SAlexander Motin static hpriv_p
929489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
930489290e9SAlexander Motin {
931489290e9SAlexander Motin 	uint16_t	hash = SESSHASH(cid);
932489290e9SAlexander Motin 	hpriv_p	hpriv = NULL;
933489290e9SAlexander Motin 
934489290e9SAlexander Motin 	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
935489290e9SAlexander Motin 		if (hpriv->conf.cid == cid)
936489290e9SAlexander Motin 			break;
937489290e9SAlexander Motin 	}
938489290e9SAlexander Motin 
939489290e9SAlexander Motin 	return (hpriv);
940489290e9SAlexander Motin }
941489290e9SAlexander Motin 
942489290e9SAlexander Motin /*
943489290e9SAlexander Motin  * Reset state (must be called with lock held or from writer)
944add85a1dSArchie Cobbs  */
945add85a1dSArchie Cobbs static void
946489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv)
947add85a1dSArchie Cobbs {
948add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
949489290e9SAlexander Motin 	hpriv->ato = PPTP_MAX_TIMEOUT;
950714f558bSAlexander Motin 	hpriv->rtt = PPTP_TIME_SCALE / 10;
951714f558bSAlexander Motin 	if (hpriv->conf.peerPpd > 1)	/* ppd = 0 treat as = 1 */
952714f558bSAlexander Motin 		hpriv->rtt *= hpriv->conf.peerPpd;
953489290e9SAlexander Motin 	hpriv->dev = 0;
954489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
955489290e9SAlexander Motin 	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
956489290e9SAlexander Motin 		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
957489290e9SAlexander Motin 	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
958489290e9SAlexander Motin 		hpriv->xmitWin = PPTP_XMIT_WIN;
959489290e9SAlexander Motin 	hpriv->winAck = hpriv->xmitWin;
960add85a1dSArchie Cobbs 
961add85a1dSArchie Cobbs 	/* Reset sequence numbers */
962489290e9SAlexander Motin 	hpriv->recvSeq = ~0;
963489290e9SAlexander Motin 	hpriv->recvAck = ~0;
964489290e9SAlexander Motin 	hpriv->xmitSeq = ~0;
965489290e9SAlexander Motin 	hpriv->xmitAck = ~0;
966add85a1dSArchie Cobbs 
9674a48abb2SArchie Cobbs 	/* Stop timers */
968714f558bSAlexander Motin 	ng_uncallout(&hpriv->sackTimer, hpriv->node);
969714f558bSAlexander Motin 	ng_uncallout(&hpriv->rackTimer, hpriv->node);
970add85a1dSArchie Cobbs }
971add85a1dSArchie Cobbs 
972add85a1dSArchie Cobbs /*
973add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
974add85a1dSArchie Cobbs  */
975add85a1dSArchie Cobbs static pptptime_t
976489290e9SAlexander Motin ng_pptpgre_time(void)
977add85a1dSArchie Cobbs {
978add85a1dSArchie Cobbs 	struct timeval tv;
979678f9e33SArchie Cobbs 	pptptime_t t;
980add85a1dSArchie Cobbs 
981678f9e33SArchie Cobbs 	microuptime(&tv);
982678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
983714f558bSAlexander Motin 	t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
984678f9e33SArchie Cobbs 	return(t);
985add85a1dSArchie Cobbs }
986