xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision 9b8db66402e94919ab47fc2d69863968e49e305a)
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  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
40add85a1dSArchie Cobbs  */
41add85a1dSArchie Cobbs 
42add85a1dSArchie Cobbs /*
43add85a1dSArchie Cobbs  * PPTP/GRE netgraph node type.
44add85a1dSArchie Cobbs  *
45add85a1dSArchie Cobbs  * This node type does the GRE encapsulation as specified for the PPTP
46add85a1dSArchie Cobbs  * protocol (RFC 2637, section 4).  This includes sequencing and
47add85a1dSArchie Cobbs  * retransmission of frames, but not the actual packet delivery nor
48add85a1dSArchie Cobbs  * any of the TCP control stream protocol.
49add85a1dSArchie Cobbs  *
50add85a1dSArchie Cobbs  * The "upper" hook of this node is suitable for attaching to a "ppp"
51add85a1dSArchie Cobbs  * node link hook.  The "lower" hook of this node is suitable for attaching
52add85a1dSArchie Cobbs  * to a "ksocket" node on hook "inet/raw/gre".
53add85a1dSArchie Cobbs  */
54add85a1dSArchie Cobbs 
55add85a1dSArchie Cobbs #include <sys/param.h>
56add85a1dSArchie Cobbs #include <sys/systm.h>
57add85a1dSArchie Cobbs #include <sys/kernel.h>
58add85a1dSArchie Cobbs #include <sys/time.h>
59f2ba84d7SGleb Smirnoff #include <sys/lock.h>
60add85a1dSArchie Cobbs #include <sys/malloc.h>
61f2ba84d7SGleb Smirnoff #include <sys/mbuf.h>
62f2ba84d7SGleb Smirnoff #include <sys/mutex.h>
6338f2d636SAlexander Motin #include <sys/endian.h>
64add85a1dSArchie Cobbs #include <sys/errno.h>
65a594f945SEugene Grosbein #include <sys/sysctl.h>
66add85a1dSArchie Cobbs 
67add85a1dSArchie Cobbs #include <netinet/in.h>
68add85a1dSArchie Cobbs #include <netinet/in_systm.h>
69add85a1dSArchie Cobbs #include <netinet/ip.h>
70add85a1dSArchie Cobbs 
71add85a1dSArchie Cobbs #include <netgraph/ng_message.h>
72add85a1dSArchie Cobbs #include <netgraph/netgraph.h>
73add85a1dSArchie Cobbs #include <netgraph/ng_parse.h>
74add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h>
75add85a1dSArchie Cobbs 
76*9b8db664SDmitry Lukhtionov #ifdef NG_SEPARATE_MALLOC
77*9b8db664SDmitry Lukhtionov static MALLOC_DEFINE(M_NETGRAPH_PPTP, "netgraph_pptp", "netgraph pptpgre node");
78*9b8db664SDmitry Lukhtionov #else
79*9b8db664SDmitry Lukhtionov #define M_NETGRAPH_PPTP M_NETGRAPH
80*9b8db664SDmitry Lukhtionov #endif
81*9b8db664SDmitry Lukhtionov 
82add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
83add85a1dSArchie Cobbs struct greheader {
84add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
85add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
86add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
87add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
88add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
89add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
90add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
91add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
92add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
93add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
94add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
95add85a1dSArchie Cobbs 	u_char		hasSum:1;		/* checksum present */
96add85a1dSArchie Cobbs 	u_char		hasRoute:1;		/* routing present */
97add85a1dSArchie Cobbs 	u_char		hasKey:1;		/* key present */
98add85a1dSArchie Cobbs 	u_char		hasSeq:1;		/* sequence number present */
99add85a1dSArchie Cobbs 	u_char		ssr:1;			/* strict source route */
100add85a1dSArchie Cobbs 	u_char		recursion:3;		/* recursion control */
101add85a1dSArchie Cobbs 	u_char		hasAck:1;		/* acknowlege number present */
102add85a1dSArchie Cobbs 	u_char		flags:4;		/* flags */
103add85a1dSArchie Cobbs 	u_char		vers:3;			/* version */
104add85a1dSArchie Cobbs #else
105add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
106add85a1dSArchie Cobbs #endif
107add85a1dSArchie Cobbs 	u_int16_t	proto;			/* protocol (ethertype) */
108add85a1dSArchie Cobbs 	u_int16_t	length;			/* payload length */
109add85a1dSArchie Cobbs 	u_int16_t	cid;			/* call id */
110add85a1dSArchie Cobbs 	u_int32_t	data[0];		/* opt. seq, ack, then data */
111add85a1dSArchie Cobbs };
112add85a1dSArchie Cobbs 
113add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
114add85a1dSArchie Cobbs #define PPTP_GRE_PROTO		0x880b
115add85a1dSArchie Cobbs 
116add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
117add85a1dSArchie Cobbs #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
118add85a1dSArchie Cobbs #define PPTP_INIT_MASK		0xef7fffff
119add85a1dSArchie Cobbs 
120add85a1dSArchie Cobbs /* Min and max packet length */
121add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
122add85a1dSArchie Cobbs 
123add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
124714f558bSAlexander Motin #define PPTP_TIME_SCALE		1024			/* milliseconds */
125678f9e33SArchie Cobbs typedef u_int64_t		pptptime_t;
126add85a1dSArchie Cobbs 
127add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
128e962a823SArchie Cobbs #define PPTP_XMIT_WIN		16			/* max xmit window */
1294a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
1300306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 seconds */
131add85a1dSArchie Cobbs 
132a594f945SEugene Grosbein #define PPTP_REORDER_TIMEOUT	1
133a594f945SEugene Grosbein 
134053359b7SPedro F. Giffuni /* When we receive a packet, we wait to see if there's an outgoing packet
13540a57b00SGordon Bergling    we can piggy-back the ACK off of. These parameters determine the minimum
136da010626SArchie Cobbs    and maxmimum length of time we're willing to wait in order to do that.
137da010626SArchie Cobbs    These have no effect unless "enableDelayedAck" is turned on. */
138da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
139da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
140da010626SArchie Cobbs 
141e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
142f8e159d6SGleb Smirnoff #define PPTP_ACK_ALPHA(x)	(((x) + 4) >> 3)	/* alpha = 0.125 */
143f8e159d6SGleb Smirnoff #define PPTP_ACK_BETA(x)	(((x) + 2) >> 2)	/* beta = 0.25 */
144add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
145add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
146add85a1dSArchie Cobbs 
1479bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
1489bee7adfSArchie Cobbs 
149489290e9SAlexander Motin #define SESSHASHSIZE		0x0020
150489290e9SAlexander Motin #define SESSHASH(x)		(((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
151489290e9SAlexander Motin 
1527029da5cSPawel Biernacki SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1537029da5cSPawel Biernacki     "PPTPGRE");
154a594f945SEugene Grosbein 
155a594f945SEugene Grosbein /*
156a594f945SEugene Grosbein  * Reorder queue maximum length. Zero disables reorder.
157a594f945SEugene Grosbein  *
158a594f945SEugene Grosbein  * The node may keep reorder_max queue entries per session
159a594f945SEugene Grosbein  * if reorder is enabled, plus allocate one more for short time.
160a594f945SEugene Grosbein  *
161a594f945SEugene Grosbein  * Be conservative in memory consumption by default.
162a594f945SEugene Grosbein  * Lots of sessions with large queues can overflow M_NETGRAPH zone.
163a594f945SEugene Grosbein  */
164a594f945SEugene Grosbein static int reorder_max = 1; /* reorder up to two swapped packets in a row */
165a594f945SEugene Grosbein SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
166a594f945SEugene Grosbein 	&reorder_max, 0, "Reorder queue maximum length");
167a594f945SEugene Grosbein 
168a594f945SEugene Grosbein static int reorder_timeout = PPTP_REORDER_TIMEOUT;
169a594f945SEugene Grosbein SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
170a594f945SEugene Grosbein 	&reorder_timeout, 0, "Reorder timeout is milliseconds");
171a594f945SEugene Grosbein 
172a594f945SEugene Grosbein /* Packet reorder FIFO queue */
173a594f945SEugene Grosbein struct ng_pptpgre_roq {
174a594f945SEugene Grosbein 	SLIST_ENTRY(ng_pptpgre_roq)  next;	/* next entry of the queue */
175a594f945SEugene Grosbein 	item_p			item;		/* netgraph item */
176a594f945SEugene Grosbein 	u_int32_t		seq;		/* packet sequence number */
177a594f945SEugene Grosbein };
178a594f945SEugene Grosbein SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
179a594f945SEugene Grosbein typedef struct ng_pptpgre_roq_head roqh;
180a594f945SEugene Grosbein 
181add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
182489290e9SAlexander Motin struct ng_pptpgre_sess {
183489290e9SAlexander Motin 	node_p			node;		/* this node pointer */
184489290e9SAlexander Motin 	hook_p			hook;		/* hook to upper layers */
185489290e9SAlexander Motin 	struct ng_pptpgre_conf	conf;		/* configuration info */
186489290e9SAlexander Motin 	struct mtx		mtx;		/* session mutex */
187489290e9SAlexander Motin 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
188489290e9SAlexander Motin 	u_int32_t		xmitSeq;	/* last seq # we sent */
189489290e9SAlexander Motin 	u_int32_t		recvAck;	/* last seq # peer ack'd */
190489290e9SAlexander Motin 	u_int32_t		xmitAck;	/* last seq # we ack'd */
191add85a1dSArchie Cobbs 	int32_t			ato;		/* adaptive time-out value */
192add85a1dSArchie Cobbs 	int32_t			rtt;		/* round trip time estimate */
193add85a1dSArchie Cobbs 	int32_t			dev;		/* deviation estimate */
194add85a1dSArchie Cobbs 	u_int16_t		xmitWin;	/* size of xmit window */
1954a48abb2SArchie Cobbs 	struct callout		sackTimer;	/* send ack timer */
1964a48abb2SArchie Cobbs 	struct callout		rackTimer;	/* recv ack timer */
1973cd7db22SArchie Cobbs 	u_int32_t		winAck;		/* seq when xmitWin will grow */
198add85a1dSArchie Cobbs 	pptptime_t		timeSent[PPTP_XMIT_WIN];
199489290e9SAlexander Motin 	LIST_ENTRY(ng_pptpgre_sess) sessions;
200a594f945SEugene Grosbein 	roqh			roq;		/* reorder queue head */
201a594f945SEugene Grosbein 	u_int8_t		roq_len;	/* reorder queue length */
202a594f945SEugene Grosbein 	struct callout		reorderTimer;	/* reorder timeout handler */
203add85a1dSArchie Cobbs };
204489290e9SAlexander Motin typedef struct ng_pptpgre_sess *hpriv_p;
205add85a1dSArchie Cobbs 
206add85a1dSArchie Cobbs /* Node private data */
207add85a1dSArchie Cobbs struct ng_pptpgre_private {
208add85a1dSArchie Cobbs 	hook_p			upper;		/* hook to upper layers */
209add85a1dSArchie Cobbs 	hook_p			lower;		/* hook to lower layers */
210489290e9SAlexander Motin 	struct ng_pptpgre_sess	uppersess;	/* default session for compat */
211489290e9SAlexander Motin 	LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
2129bee7adfSArchie Cobbs 	struct ng_pptpgre_stats	stats;		/* node statistics */
213add85a1dSArchie Cobbs };
214add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
215add85a1dSArchie Cobbs 
216add85a1dSArchie Cobbs /* Netgraph node methods */
217add85a1dSArchie Cobbs static ng_constructor_t	ng_pptpgre_constructor;
218add85a1dSArchie Cobbs static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
219069154d5SJulian Elischer static ng_shutdown_t	ng_pptpgre_shutdown;
220add85a1dSArchie Cobbs static ng_newhook_t	ng_pptpgre_newhook;
221add85a1dSArchie Cobbs static ng_rcvdata_t	ng_pptpgre_rcvdata;
222489290e9SAlexander Motin static ng_rcvdata_t	ng_pptpgre_rcvdata_lower;
223add85a1dSArchie Cobbs static ng_disconnect_t	ng_pptpgre_disconnect;
224add85a1dSArchie Cobbs 
225add85a1dSArchie Cobbs /* Helper functions */
226489290e9SAlexander Motin static int	ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
227714f558bSAlexander Motin static void	ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
228489290e9SAlexander Motin static void	ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
229a594f945SEugene Grosbein static void	ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
230089323f3SGleb Smirnoff static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
231089323f3SGleb Smirnoff 		    void *arg1, int arg2);
232089323f3SGleb Smirnoff static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
233089323f3SGleb Smirnoff 		    void *arg1, int arg2);
234a594f945SEugene Grosbein static void	ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
235a594f945SEugene Grosbein 		    void *arg1, int arg2);
236489290e9SAlexander Motin static hpriv_p	ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
237489290e9SAlexander Motin static void	ng_pptpgre_reset(hpriv_p hpriv);
238489290e9SAlexander Motin static pptptime_t ng_pptpgre_time(void);
239a594f945SEugene Grosbein static void	ng_pptpgre_ack(const hpriv_p hpriv);
240a594f945SEugene Grosbein static int	ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
241a594f945SEugene Grosbein 		    const struct ng_pptpgre_roq *st);
242add85a1dSArchie Cobbs 
243add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
244f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
245f0184ff8SArchie Cobbs 	= NG_PPTPGRE_CONF_TYPE_INFO;
246add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
247add85a1dSArchie Cobbs 	&ng_parse_struct_type,
248f0184ff8SArchie Cobbs 	&ng_pptpgre_conf_type_fields,
249add85a1dSArchie Cobbs };
250add85a1dSArchie Cobbs 
2519bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
252f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
253f0184ff8SArchie Cobbs 	= NG_PPTPGRE_STATS_TYPE_INFO;
2549bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2559bee7adfSArchie Cobbs 	&ng_parse_struct_type,
256f0184ff8SArchie Cobbs 	&ng_pptpgre_stats_type_fields
2579bee7adfSArchie Cobbs };
2589bee7adfSArchie Cobbs 
259add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
260add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
261add85a1dSArchie Cobbs 	{
262add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
263add85a1dSArchie Cobbs 	  NGM_PPTPGRE_SET_CONFIG,
264add85a1dSArchie Cobbs 	  "setconfig",
265add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type,
266add85a1dSArchie Cobbs 	  NULL
267add85a1dSArchie Cobbs 	},
268add85a1dSArchie Cobbs 	{
269add85a1dSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
270add85a1dSArchie Cobbs 	  NGM_PPTPGRE_GET_CONFIG,
271add85a1dSArchie Cobbs 	  "getconfig",
272489290e9SAlexander Motin 	  &ng_parse_hint16_type,
273add85a1dSArchie Cobbs 	  &ng_pptpgre_conf_type
274add85a1dSArchie Cobbs 	},
2759bee7adfSArchie Cobbs 	{
2769bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2779bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GET_STATS,
2789bee7adfSArchie Cobbs 	  "getstats",
2799bee7adfSArchie Cobbs 	  NULL,
2809bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2819bee7adfSArchie Cobbs 	},
2829bee7adfSArchie Cobbs 	{
2839bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2849bee7adfSArchie Cobbs 	  NGM_PPTPGRE_CLR_STATS,
2859bee7adfSArchie Cobbs 	  "clrstats",
2869bee7adfSArchie Cobbs 	  NULL,
2879bee7adfSArchie Cobbs 	  NULL
2889bee7adfSArchie Cobbs 	},
2899bee7adfSArchie Cobbs 	{
2909bee7adfSArchie Cobbs 	  NGM_PPTPGRE_COOKIE,
2919bee7adfSArchie Cobbs 	  NGM_PPTPGRE_GETCLR_STATS,
2929bee7adfSArchie Cobbs 	  "getclrstats",
2939bee7adfSArchie Cobbs 	  NULL,
2949bee7adfSArchie Cobbs 	  &ng_pptp_stats_type
2959bee7adfSArchie Cobbs 	},
296add85a1dSArchie Cobbs 	{ 0 }
297add85a1dSArchie Cobbs };
298add85a1dSArchie Cobbs 
299add85a1dSArchie Cobbs /* Node type descriptor */
300add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
301f8aae777SJulian Elischer 	.version =	NG_ABI_VERSION,
302f8aae777SJulian Elischer 	.name =		NG_PPTPGRE_NODE_TYPE,
303f8aae777SJulian Elischer 	.constructor =	ng_pptpgre_constructor,
304f8aae777SJulian Elischer 	.rcvmsg =	ng_pptpgre_rcvmsg,
305f8aae777SJulian Elischer 	.shutdown =	ng_pptpgre_shutdown,
306f8aae777SJulian Elischer 	.newhook =	ng_pptpgre_newhook,
307f8aae777SJulian Elischer 	.rcvdata =	ng_pptpgre_rcvdata,
308f8aae777SJulian Elischer 	.disconnect =	ng_pptpgre_disconnect,
309f8aae777SJulian Elischer 	.cmdlist =	ng_pptpgre_cmdlist,
310add85a1dSArchie Cobbs };
311add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
312add85a1dSArchie Cobbs 
313add85a1dSArchie Cobbs #define ERROUT(x)	do { error = (x); goto done; } while (0)
314add85a1dSArchie Cobbs 
315add85a1dSArchie Cobbs /************************************************************************
316add85a1dSArchie Cobbs 			NETGRAPH NODE STUFF
317add85a1dSArchie Cobbs  ************************************************************************/
318add85a1dSArchie Cobbs 
319add85a1dSArchie Cobbs /*
320add85a1dSArchie Cobbs  * Node type constructor
321add85a1dSArchie Cobbs  */
322add85a1dSArchie Cobbs static int
ng_pptpgre_constructor(node_p node)323069154d5SJulian Elischer ng_pptpgre_constructor(node_p node)
324add85a1dSArchie Cobbs {
325add85a1dSArchie Cobbs 	priv_p priv;
326489290e9SAlexander Motin 	int i;
327add85a1dSArchie Cobbs 
328add85a1dSArchie Cobbs 	/* Allocate private structure */
329*9b8db664SDmitry Lukhtionov 	priv = malloc(sizeof(*priv), M_NETGRAPH_PPTP, M_WAITOK | M_ZERO);
330add85a1dSArchie Cobbs 
33130400f03SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
332add85a1dSArchie Cobbs 
333add85a1dSArchie Cobbs 	/* Initialize state */
334489290e9SAlexander Motin 	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
335489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.sackTimer);
336489290e9SAlexander Motin 	ng_callout_init(&priv->uppersess.rackTimer);
337489290e9SAlexander Motin 	priv->uppersess.node = node;
338489290e9SAlexander Motin 
339a594f945SEugene Grosbein 	SLIST_INIT(&priv->uppersess.roq);
340a594f945SEugene Grosbein 	priv->uppersess.roq_len = 0;
341a594f945SEugene Grosbein 	ng_callout_init(&priv->uppersess.reorderTimer);
342a594f945SEugene Grosbein 
343489290e9SAlexander Motin 	for (i = 0; i < SESSHASHSIZE; i++)
344489290e9SAlexander Motin 	    LIST_INIT(&priv->sesshash[i]);
345489290e9SAlexander Motin 
346489290e9SAlexander Motin 	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
347add85a1dSArchie Cobbs 
348add85a1dSArchie Cobbs 	/* Done */
349add85a1dSArchie Cobbs 	return (0);
350add85a1dSArchie Cobbs }
351add85a1dSArchie Cobbs 
352add85a1dSArchie Cobbs /*
353add85a1dSArchie Cobbs  * Give our OK for a hook to be added.
354add85a1dSArchie Cobbs  */
355add85a1dSArchie Cobbs static int
ng_pptpgre_newhook(node_p node,hook_p hook,const char * name)356add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
357add85a1dSArchie Cobbs {
35830400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
359add85a1dSArchie Cobbs 
360add85a1dSArchie Cobbs 	/* Check hook name */
361489290e9SAlexander Motin 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
362489290e9SAlexander Motin 		priv->upper = hook;
363489290e9SAlexander Motin 		priv->uppersess.hook = hook;
364489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
365489290e9SAlexander Motin 	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
366489290e9SAlexander Motin 		priv->lower = hook;
367489290e9SAlexander Motin 		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
368489290e9SAlexander Motin 	} else {
369489290e9SAlexander Motin 		static const char hexdig[16] = "0123456789abcdef";
370489290e9SAlexander Motin 		const char *hex;
371489290e9SAlexander Motin 		hpriv_p hpriv;
372489290e9SAlexander Motin 		int i, j;
373489290e9SAlexander Motin 		uint16_t cid, hash;
374489290e9SAlexander Motin 
375489290e9SAlexander Motin 		/* Parse hook name to get session ID */
376489290e9SAlexander Motin 		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
377489290e9SAlexander Motin 		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
378489290e9SAlexander Motin 			return (EINVAL);
379489290e9SAlexander Motin 		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
380489290e9SAlexander Motin 		for (cid = i = 0; i < 4; i++) {
381489290e9SAlexander Motin 			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
382489290e9SAlexander Motin 			if (j == 16)
383489290e9SAlexander Motin 				return (EINVAL);
384489290e9SAlexander Motin 			cid = (cid << 4) | j;
385489290e9SAlexander Motin 		}
386489290e9SAlexander Motin 		if (hex[i] != '\0')
387add85a1dSArchie Cobbs 			return (EINVAL);
388add85a1dSArchie Cobbs 
389*9b8db664SDmitry Lukhtionov 		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
390489290e9SAlexander Motin 		if (hpriv == NULL)
391489290e9SAlexander Motin 			return (ENOMEM);
392add85a1dSArchie Cobbs 
393489290e9SAlexander Motin 		/* Initialize state */
394489290e9SAlexander Motin 		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
395489290e9SAlexander Motin 		ng_callout_init(&hpriv->sackTimer);
396489290e9SAlexander Motin 		ng_callout_init(&hpriv->rackTimer);
397489290e9SAlexander Motin 		hpriv->conf.cid = cid;
398489290e9SAlexander Motin 		hpriv->node = node;
399489290e9SAlexander Motin 		hpriv->hook = hook;
400a594f945SEugene Grosbein 
401a594f945SEugene Grosbein 		SLIST_INIT(&hpriv->roq);
402a594f945SEugene Grosbein 		hpriv->roq_len = 0;
403a594f945SEugene Grosbein 		ng_callout_init(&hpriv->reorderTimer);
404a594f945SEugene Grosbein 
405489290e9SAlexander Motin 		NG_HOOK_SET_PRIVATE(hook, hpriv);
406489290e9SAlexander Motin 
407489290e9SAlexander Motin 		hash = SESSHASH(cid);
408489290e9SAlexander Motin 		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
409489290e9SAlexander Motin 	}
410489290e9SAlexander Motin 
411add85a1dSArchie Cobbs 	return (0);
412add85a1dSArchie Cobbs }
413add85a1dSArchie Cobbs 
414add85a1dSArchie Cobbs /*
415add85a1dSArchie Cobbs  * Receive a control message.
416add85a1dSArchie Cobbs  */
417add85a1dSArchie Cobbs static int
ng_pptpgre_rcvmsg(node_p node,item_p item,hook_p lasthook)418069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
419add85a1dSArchie Cobbs {
42030400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
421add85a1dSArchie Cobbs 	struct ng_mesg *resp = NULL;
422add85a1dSArchie Cobbs 	int error = 0;
423069154d5SJulian Elischer 	struct ng_mesg *msg;
424add85a1dSArchie Cobbs 
425069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
426add85a1dSArchie Cobbs 	switch (msg->header.typecookie) {
427add85a1dSArchie Cobbs 	case NGM_PPTPGRE_COOKIE:
428add85a1dSArchie Cobbs 		switch (msg->header.cmd) {
429add85a1dSArchie Cobbs 		case NGM_PPTPGRE_SET_CONFIG:
430add85a1dSArchie Cobbs 		    {
431add85a1dSArchie Cobbs 			struct ng_pptpgre_conf *const newConf =
432add85a1dSArchie Cobbs 				(struct ng_pptpgre_conf *) msg->data;
433489290e9SAlexander Motin 			hpriv_p hpriv;
434489290e9SAlexander Motin 			uint16_t hash;
435add85a1dSArchie Cobbs 
436add85a1dSArchie Cobbs 			/* Check for invalid or illegal config */
437add85a1dSArchie Cobbs 			if (msg->header.arglen != sizeof(*newConf))
438add85a1dSArchie Cobbs 				ERROUT(EINVAL);
439489290e9SAlexander Motin 			/* Try to find session by cid. */
440489290e9SAlexander Motin 			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
441489290e9SAlexander Motin 			/* If not present - use upper. */
442489290e9SAlexander Motin 			if (hpriv == NULL) {
443489290e9SAlexander Motin 				hpriv = &priv->uppersess;
444489290e9SAlexander Motin 				LIST_REMOVE(hpriv, sessions);
445489290e9SAlexander Motin 				hash = SESSHASH(newConf->cid);
446489290e9SAlexander Motin 				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
447489290e9SAlexander Motin 				    sessions);
448489290e9SAlexander Motin 			}
449489290e9SAlexander Motin 			ng_pptpgre_reset(hpriv);	/* reset on configure */
450489290e9SAlexander Motin 			hpriv->conf = *newConf;
451add85a1dSArchie Cobbs 			break;
452add85a1dSArchie Cobbs 		    }
453add85a1dSArchie Cobbs 		case NGM_PPTPGRE_GET_CONFIG:
454489290e9SAlexander Motin 		    {
455489290e9SAlexander Motin 			hpriv_p hpriv;
456489290e9SAlexander Motin 
457489290e9SAlexander Motin 			if (msg->header.arglen == 2) {
458489290e9SAlexander Motin 				/* Try to find session by cid. */
459489290e9SAlexander Motin 	    			hpriv = ng_pptpgre_find_session(priv,
460489290e9SAlexander Motin 				    *((uint16_t *)msg->data));
461489290e9SAlexander Motin 				if (hpriv == NULL)
462489290e9SAlexander Motin 					ERROUT(EINVAL);
463489290e9SAlexander Motin 			} else if (msg->header.arglen == 0) {
464489290e9SAlexander Motin 				/* Use upper. */
465489290e9SAlexander Motin 				hpriv = &priv->uppersess;
466489290e9SAlexander Motin 			} else
467489290e9SAlexander Motin 				ERROUT(EINVAL);
468489290e9SAlexander Motin 			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
469add85a1dSArchie Cobbs 			if (resp == NULL)
470add85a1dSArchie Cobbs 				ERROUT(ENOMEM);
471489290e9SAlexander Motin 			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
472add85a1dSArchie Cobbs 			break;
473489290e9SAlexander Motin 		    }
4749bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GET_STATS:
4759bee7adfSArchie Cobbs 		case NGM_PPTPGRE_CLR_STATS:
4769bee7adfSArchie Cobbs 		case NGM_PPTPGRE_GETCLR_STATS:
4779bee7adfSArchie Cobbs 		    {
4789bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
4799bee7adfSArchie Cobbs 				NG_MKRESPONSE(resp, msg,
4809bee7adfSArchie Cobbs 				    sizeof(priv->stats), M_NOWAIT);
4819bee7adfSArchie Cobbs 				if (resp == NULL)
4829bee7adfSArchie Cobbs 					ERROUT(ENOMEM);
4839bee7adfSArchie Cobbs 				bcopy(&priv->stats,
4849bee7adfSArchie Cobbs 				    resp->data, sizeof(priv->stats));
4859bee7adfSArchie Cobbs 			}
4869bee7adfSArchie Cobbs 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
4879bee7adfSArchie Cobbs 				bzero(&priv->stats, sizeof(priv->stats));
4889bee7adfSArchie Cobbs 			break;
4899bee7adfSArchie Cobbs 		    }
490add85a1dSArchie Cobbs 		default:
491add85a1dSArchie Cobbs 			error = EINVAL;
492add85a1dSArchie Cobbs 			break;
493add85a1dSArchie Cobbs 		}
494add85a1dSArchie Cobbs 		break;
495add85a1dSArchie Cobbs 	default:
496add85a1dSArchie Cobbs 		error = EINVAL;
497add85a1dSArchie Cobbs 		break;
498add85a1dSArchie Cobbs 	}
499589f6ed8SJulian Elischer done:
500069154d5SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
501069154d5SJulian Elischer 	NG_FREE_MSG(msg);
502add85a1dSArchie Cobbs 	return (error);
503add85a1dSArchie Cobbs }
504add85a1dSArchie Cobbs 
505add85a1dSArchie Cobbs /*
506add85a1dSArchie Cobbs  * Receive incoming data on a hook.
507add85a1dSArchie Cobbs  */
508add85a1dSArchie Cobbs static int
ng_pptpgre_rcvdata(hook_p hook,item_p item)509069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
510add85a1dSArchie Cobbs {
511489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
512f2ba84d7SGleb Smirnoff 	int rval;
513add85a1dSArchie Cobbs 
514add85a1dSArchie Cobbs 	/* If not configured, reject */
515489290e9SAlexander Motin 	if (!hpriv->conf.enabled) {
516069154d5SJulian Elischer 		NG_FREE_ITEM(item);
517add85a1dSArchie Cobbs 		return (ENXIO);
518add85a1dSArchie Cobbs 	}
519add85a1dSArchie Cobbs 
520489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
521f2ba84d7SGleb Smirnoff 
522489290e9SAlexander Motin 	rval = ng_pptpgre_xmit(hpriv, item);
523f2ba84d7SGleb Smirnoff 
524489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
525f2ba84d7SGleb Smirnoff 
526f2ba84d7SGleb Smirnoff 	return (rval);
527add85a1dSArchie Cobbs }
528add85a1dSArchie Cobbs 
529add85a1dSArchie Cobbs /*
530489290e9SAlexander Motin  * Hook disconnection
531489290e9SAlexander Motin  */
532489290e9SAlexander Motin static int
ng_pptpgre_disconnect(hook_p hook)533489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook)
534489290e9SAlexander Motin {
535489290e9SAlexander Motin 	const node_p node = NG_HOOK_NODE(hook);
536489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(node);
537489290e9SAlexander Motin 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
538489290e9SAlexander Motin 
539489290e9SAlexander Motin 	/* Zero out hook pointer */
540489290e9SAlexander Motin 	if (hook == priv->upper) {
541489290e9SAlexander Motin 		priv->upper = NULL;
542489290e9SAlexander Motin 		priv->uppersess.hook = NULL;
543489290e9SAlexander Motin 	} else if (hook == priv->lower) {
544489290e9SAlexander Motin 		priv->lower = NULL;
545489290e9SAlexander Motin 	} else {
546489290e9SAlexander Motin 		/* Reset node (stops timers) */
547489290e9SAlexander Motin 		ng_pptpgre_reset(hpriv);
548489290e9SAlexander Motin 
549489290e9SAlexander Motin 		LIST_REMOVE(hpriv, sessions);
550489290e9SAlexander Motin 		mtx_destroy(&hpriv->mtx);
551*9b8db664SDmitry Lukhtionov 		free(hpriv, M_NETGRAPH_PPTP);
552489290e9SAlexander Motin 	}
553489290e9SAlexander Motin 
554489290e9SAlexander Motin 	/* Go away if no longer connected to anything */
555489290e9SAlexander Motin 	if ((NG_NODE_NUMHOOKS(node) == 0)
556489290e9SAlexander Motin 	&& (NG_NODE_IS_VALID(node)))
557489290e9SAlexander Motin 		ng_rmnode_self(node);
558489290e9SAlexander Motin 	return (0);
559489290e9SAlexander Motin }
560489290e9SAlexander Motin 
561489290e9SAlexander Motin /*
562add85a1dSArchie Cobbs  * Destroy node
563add85a1dSArchie Cobbs  */
564add85a1dSArchie Cobbs static int
ng_pptpgre_shutdown(node_p node)565069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
566add85a1dSArchie Cobbs {
56730400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
568add85a1dSArchie Cobbs 
569089323f3SGleb Smirnoff 	/* Reset node (stops timers) */
570489290e9SAlexander Motin 	ng_pptpgre_reset(&priv->uppersess);
571add85a1dSArchie Cobbs 
572489290e9SAlexander Motin 	LIST_REMOVE(&priv->uppersess, sessions);
573489290e9SAlexander Motin 	mtx_destroy(&priv->uppersess.mtx);
574f2ba84d7SGleb Smirnoff 
575*9b8db664SDmitry Lukhtionov 	free(priv, M_NETGRAPH_PPTP);
5764a48abb2SArchie Cobbs 
5774a48abb2SArchie Cobbs 	/* Decrement ref count */
57830400f03SJulian Elischer 	NG_NODE_UNREF(node);
579add85a1dSArchie Cobbs 	return (0);
580add85a1dSArchie Cobbs }
581add85a1dSArchie Cobbs 
582add85a1dSArchie Cobbs /*************************************************************************
583add85a1dSArchie Cobbs 		    TRANSMIT AND RECEIVE FUNCTIONS
584add85a1dSArchie Cobbs *************************************************************************/
585add85a1dSArchie Cobbs 
586add85a1dSArchie Cobbs /*
587add85a1dSArchie Cobbs  * Transmit an outgoing frame, or just an ack if m is NULL.
588add85a1dSArchie Cobbs  */
589add85a1dSArchie Cobbs static int
ng_pptpgre_xmit(hpriv_p hpriv,item_p item)590489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
591add85a1dSArchie Cobbs {
592489290e9SAlexander Motin 	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
593add85a1dSArchie Cobbs 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
594add85a1dSArchie Cobbs 	struct greheader *const gre = (struct greheader *)buf;
595add85a1dSArchie Cobbs 	int grelen, error;
596069154d5SJulian Elischer 	struct mbuf *m;
597add85a1dSArchie Cobbs 
598489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_OWNED);
59983beeed9SGleb Smirnoff 
600069154d5SJulian Elischer 	if (item) {
601069154d5SJulian Elischer 		NGI_GET_M(item, m);
602069154d5SJulian Elischer 	} else {
603069154d5SJulian Elischer 		m = NULL;
604069154d5SJulian Elischer 	}
6059bee7adfSArchie Cobbs 	/* Check if there's data */
6069bee7adfSArchie Cobbs 	if (m != NULL) {
607922ee196SArchie Cobbs 		/* Check if windowing is enabled */
608489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
609add85a1dSArchie Cobbs 			/* Is our transmit window full? */
610489290e9SAlexander Motin 			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
611489290e9SAlexander Motin 			    hpriv->recvAck) >= hpriv->xmitWin) {
6129bee7adfSArchie Cobbs 				priv->stats.xmitDrops++;
61383beeed9SGleb Smirnoff 				ERROUT(ENOBUFS);
614add85a1dSArchie Cobbs 			}
615922ee196SArchie Cobbs 		}
616add85a1dSArchie Cobbs 
617add85a1dSArchie Cobbs 		/* Sanity check frame length */
6182c2e2be7SAlexander Motin 		if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
6199bee7adfSArchie Cobbs 			priv->stats.xmitTooBig++;
62083beeed9SGleb Smirnoff 			ERROUT(EMSGSIZE);
621add85a1dSArchie Cobbs 		}
622069154d5SJulian Elischer 	} else {
6239bee7adfSArchie Cobbs 		priv->stats.xmitLoneAcks++;
624069154d5SJulian Elischer 	}
625add85a1dSArchie Cobbs 
626add85a1dSArchie Cobbs 	/* Build GRE header */
62738f2d636SAlexander Motin 	be32enc(gre, PPTP_INIT_VALUE);
62838f2d636SAlexander Motin 	be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
62938f2d636SAlexander Motin 	be16enc(&gre->cid, hpriv->conf.peerCid);
630add85a1dSArchie Cobbs 
631add85a1dSArchie Cobbs 	/* Include sequence number if packet contains any data */
632add85a1dSArchie Cobbs 	if (m != NULL) {
633add85a1dSArchie Cobbs 		gre->hasSeq = 1;
634489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
635489290e9SAlexander Motin 			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
636489290e9SAlexander Motin 			    = ng_pptpgre_time();
637922ee196SArchie Cobbs 		}
638489290e9SAlexander Motin 		hpriv->xmitSeq++;
63938f2d636SAlexander Motin 		be32enc(&gre->data[0], hpriv->xmitSeq);
640add85a1dSArchie Cobbs 	}
641add85a1dSArchie Cobbs 
642add85a1dSArchie Cobbs 	/* Include acknowledgement (and stop send ack timer) if needed */
643489290e9SAlexander Motin 	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
644add85a1dSArchie Cobbs 		gre->hasAck = 1;
64538f2d636SAlexander Motin 		be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
646489290e9SAlexander Motin 		hpriv->xmitAck = hpriv->recvSeq;
647714f558bSAlexander Motin 		if (hpriv->conf.enableDelayedAck)
648714f558bSAlexander Motin 			ng_uncallout(&hpriv->sackTimer, hpriv->node);
649da010626SArchie Cobbs 	}
650add85a1dSArchie Cobbs 
651add85a1dSArchie Cobbs 	/* Prepend GRE header to outgoing frame */
652add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
653add85a1dSArchie Cobbs 	if (m == NULL) {
654eb1b1807SGleb Smirnoff 		MGETHDR(m, M_NOWAIT, MT_DATA);
655add85a1dSArchie Cobbs 		if (m == NULL) {
656678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
65783beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
658add85a1dSArchie Cobbs 		}
659add85a1dSArchie Cobbs 		m->m_len = m->m_pkthdr.len = grelen;
660add85a1dSArchie Cobbs 		m->m_pkthdr.rcvif = NULL;
661add85a1dSArchie Cobbs 	} else {
662eb1b1807SGleb Smirnoff 		M_PREPEND(m, grelen, M_NOWAIT);
663add85a1dSArchie Cobbs 		if (m == NULL || (m->m_len < grelen
664add85a1dSArchie Cobbs 		    && (m = m_pullup(m, grelen)) == NULL)) {
665678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
66683beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
667add85a1dSArchie Cobbs 		}
668add85a1dSArchie Cobbs 	}
669add85a1dSArchie Cobbs 	bcopy(gre, mtod(m, u_char *), grelen);
670add85a1dSArchie Cobbs 
6719bee7adfSArchie Cobbs 	/* Update stats */
6729bee7adfSArchie Cobbs 	priv->stats.xmitPackets++;
6739bee7adfSArchie Cobbs 	priv->stats.xmitOctets += m->m_pkthdr.len;
6749bee7adfSArchie Cobbs 
67583beeed9SGleb Smirnoff 	/*
67683beeed9SGleb Smirnoff 	 * XXX: we should reset timer only after an item has been sent
67783beeed9SGleb Smirnoff 	 * successfully.
67883beeed9SGleb Smirnoff 	 */
679489290e9SAlexander Motin 	if (hpriv->conf.enableWindowing &&
680489290e9SAlexander Motin 	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
681489290e9SAlexander Motin 		ng_pptpgre_start_recv_ack_timer(hpriv);
68283beeed9SGleb Smirnoff 
683489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
68483beeed9SGleb Smirnoff 
685add85a1dSArchie Cobbs 	/* Deliver packet */
686069154d5SJulian Elischer 	if (item) {
687069154d5SJulian Elischer 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
688069154d5SJulian Elischer 	} else {
689069154d5SJulian Elischer 		NG_SEND_DATA_ONLY(error, priv->lower, m);
690069154d5SJulian Elischer 	}
691069154d5SJulian Elischer 
69283beeed9SGleb Smirnoff 	return (error);
693678f9e33SArchie Cobbs 
69483beeed9SGleb Smirnoff done:
695489290e9SAlexander Motin 	mtx_unlock(&hpriv->mtx);
69683beeed9SGleb Smirnoff 	NG_FREE_M(m);
69783beeed9SGleb Smirnoff 	if (item)
69883beeed9SGleb Smirnoff 		NG_FREE_ITEM(item);
699add85a1dSArchie Cobbs 	return (error);
700add85a1dSArchie Cobbs }
701add85a1dSArchie Cobbs 
702a594f945SEugene Grosbein static void
ng_pptpgre_ack(const hpriv_p hpriv)703a594f945SEugene Grosbein ng_pptpgre_ack(const hpriv_p hpriv)
704a594f945SEugene Grosbein {
705a594f945SEugene Grosbein 	mtx_assert(&hpriv->mtx, MA_OWNED);
706a594f945SEugene Grosbein 	if (!(callout_pending(&hpriv->sackTimer))) {
707a594f945SEugene Grosbein 		/* If delayed ACK is disabled, send it now */
708a594f945SEugene Grosbein 		if (!hpriv->conf.enableDelayedAck) {	/* ack now */
709a594f945SEugene Grosbein 			ng_pptpgre_xmit(hpriv, NULL);
710a594f945SEugene Grosbein 			/* ng_pptpgre_xmit() drops the mutex */
711a594f945SEugene Grosbein 			return;
712a594f945SEugene Grosbein 		}
713a594f945SEugene Grosbein 		/* ack later */
714a594f945SEugene Grosbein 		ng_pptpgre_start_send_ack_timer(hpriv);
715a594f945SEugene Grosbein 		mtx_unlock(&hpriv->mtx);
716a594f945SEugene Grosbein 		return;
717a594f945SEugene Grosbein 	}
718a594f945SEugene Grosbein 	mtx_unlock(&hpriv->mtx);
719a594f945SEugene Grosbein }
720a594f945SEugene Grosbein 
721a594f945SEugene Grosbein /*
722a594f945SEugene Grosbein  * Delivers packets from the queue "q" to upper layers. Frees delivered
723a594f945SEugene Grosbein  * entries with the exception of one equal to "st" that is allocated
724a594f945SEugene Grosbein  * on caller's stack and not on the heap.
725a594f945SEugene Grosbein  */
726a594f945SEugene Grosbein static int
ng_pptpgre_sendq(const hpriv_p hpriv,roqh * q,const struct ng_pptpgre_roq * st)727a594f945SEugene Grosbein ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
728a594f945SEugene Grosbein {
729a594f945SEugene Grosbein 	struct ng_pptpgre_roq *np;
730a594f945SEugene Grosbein 	struct mbuf *m;
731a594f945SEugene Grosbein 	int error = 0;
732a594f945SEugene Grosbein 
733a594f945SEugene Grosbein 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
734a594f945SEugene Grosbein 	while (!SLIST_EMPTY(q)) {
735a594f945SEugene Grosbein 		np = SLIST_FIRST(q);
736a594f945SEugene Grosbein 		SLIST_REMOVE_HEAD(q, next);
737a594f945SEugene Grosbein 		NGI_GET_M(np->item, m);
738a594f945SEugene Grosbein 		NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
739a594f945SEugene Grosbein 		if (np != st)
740*9b8db664SDmitry Lukhtionov 			free(np, M_NETGRAPH_PPTP);
741a594f945SEugene Grosbein 	}
742a594f945SEugene Grosbein 	return (error);
743a594f945SEugene Grosbein }
744a594f945SEugene Grosbein 
745add85a1dSArchie Cobbs /*
746add85a1dSArchie Cobbs  * Handle an incoming packet.  The packet includes the IP header.
747add85a1dSArchie Cobbs  */
748add85a1dSArchie Cobbs static int
ng_pptpgre_rcvdata_lower(hook_p hook,item_p item)749489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
750add85a1dSArchie Cobbs {
751489290e9SAlexander Motin 	hpriv_p hpriv;
752489290e9SAlexander Motin 	node_p node = NG_HOOK_NODE(hook);
75330400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
754add85a1dSArchie Cobbs 	int iphlen, grelen, extralen;
755816b834fSArchie Cobbs 	const struct greheader *gre;
756816b834fSArchie Cobbs 	const struct ip *ip;
757add85a1dSArchie Cobbs 	int error = 0;
758069154d5SJulian Elischer 	struct mbuf *m;
759add85a1dSArchie Cobbs 
760a594f945SEugene Grosbein 	roqh sendq = SLIST_HEAD_INITIALIZER(sendq);  /* send queue on stack */
761a594f945SEugene Grosbein 	struct ng_pptpgre_roq *last = NULL;	/* last packet in the sendq */
762a594f945SEugene Grosbein 	struct ng_pptpgre_roq *np, *prev;
763a594f945SEugene Grosbein 	struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
764a594f945SEugene Grosbein 	long diff;
765a594f945SEugene Grosbein 	u_int32_t seq;
766a594f945SEugene Grosbein 
767a594f945SEugene Grosbein 	m = NGI_M(item);
7689bee7adfSArchie Cobbs 	/* Update stats */
7699bee7adfSArchie Cobbs 	priv->stats.recvPackets++;
7709bee7adfSArchie Cobbs 	priv->stats.recvOctets += m->m_pkthdr.len;
7719bee7adfSArchie Cobbs 
772add85a1dSArchie Cobbs 	/* Sanity check packet length */
773add85a1dSArchie Cobbs 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
7749bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
77583beeed9SGleb Smirnoff 		ERROUT(EINVAL);
776add85a1dSArchie Cobbs 	}
777add85a1dSArchie Cobbs 
778add85a1dSArchie Cobbs 	/* Safely pull up the complete IP+GRE headers */
779a594f945SEugene Grosbein 	if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
780a594f945SEugene Grosbein 		if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
781678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
782a594f945SEugene Grosbein 			_NGI_M(item) = NULL;
78383beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
784add85a1dSArchie Cobbs 		}
785a594f945SEugene Grosbein 		_NGI_M(item) = m;
786a594f945SEugene Grosbein 	}
787816b834fSArchie Cobbs 	ip = mtod(m, const struct ip *);
788add85a1dSArchie Cobbs 	iphlen = ip->ip_hl << 2;
789add85a1dSArchie Cobbs 	if (m->m_len < iphlen + sizeof(*gre)) {
790add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
791678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
792a594f945SEugene Grosbein 			_NGI_M(item) = NULL;
79383beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
794add85a1dSArchie Cobbs 		}
795a594f945SEugene Grosbein 		_NGI_M(item) = m;
796816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
797add85a1dSArchie Cobbs 	}
798816b834fSArchie Cobbs 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
799add85a1dSArchie Cobbs 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
8009bee7adfSArchie Cobbs 	if (m->m_pkthdr.len < iphlen + grelen) {
8019bee7adfSArchie Cobbs 		priv->stats.recvRunts++;
80283beeed9SGleb Smirnoff 		ERROUT(EINVAL);
8039bee7adfSArchie Cobbs 	}
804add85a1dSArchie Cobbs 	if (m->m_len < iphlen + grelen) {
805add85a1dSArchie Cobbs 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
806678f9e33SArchie Cobbs 			priv->stats.memoryFailures++;
807a594f945SEugene Grosbein 			_NGI_M(item) = NULL;
80883beeed9SGleb Smirnoff 			ERROUT(ENOBUFS);
809add85a1dSArchie Cobbs 		}
810a594f945SEugene Grosbein 		_NGI_M(item) = m;
811816b834fSArchie Cobbs 		ip = mtod(m, const struct ip *);
812816b834fSArchie Cobbs 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
813add85a1dSArchie Cobbs 	}
814add85a1dSArchie Cobbs 
815add85a1dSArchie Cobbs 	/* Sanity check packet length and GRE header bits */
816add85a1dSArchie Cobbs 	extralen = m->m_pkthdr.len
81738f2d636SAlexander Motin 	    - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
8189bee7adfSArchie Cobbs 	if (extralen < 0) {
8199bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
82083beeed9SGleb Smirnoff 		ERROUT(EINVAL);
8219bee7adfSArchie Cobbs 	}
82238f2d636SAlexander Motin 	if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
8239bee7adfSArchie Cobbs 		priv->stats.recvBadGRE++;
82483beeed9SGleb Smirnoff 		ERROUT(EINVAL);
8259bee7adfSArchie Cobbs 	}
826489290e9SAlexander Motin 
82738f2d636SAlexander Motin 	hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
828489290e9SAlexander Motin 	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
8299bee7adfSArchie Cobbs 		priv->stats.recvBadCID++;
83083beeed9SGleb Smirnoff 		ERROUT(EINVAL);
8319bee7adfSArchie Cobbs 	}
832489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
833add85a1dSArchie Cobbs 
834add85a1dSArchie Cobbs 	/* Look for peer ack */
835add85a1dSArchie Cobbs 	if (gre->hasAck) {
83638f2d636SAlexander Motin 		const u_int32_t	ack = be32dec(&gre->data[gre->hasSeq]);
837489290e9SAlexander Motin 		const int index = ack - hpriv->recvAck - 1;
83822dfb9bdSArchie Cobbs 		long sample;
839add85a1dSArchie Cobbs 
840add85a1dSArchie Cobbs 		/* Sanity check ack value */
841489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
8429bee7adfSArchie Cobbs 			priv->stats.recvBadAcks++;
8439bee7adfSArchie Cobbs 			goto badAck;		/* we never sent it! */
8449bee7adfSArchie Cobbs 		}
845489290e9SAlexander Motin 		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
8469bee7adfSArchie Cobbs 			goto badAck;		/* ack already timed out */
847489290e9SAlexander Motin 		hpriv->recvAck = ack;
848add85a1dSArchie Cobbs 
849add85a1dSArchie Cobbs 		/* Update adaptive timeout stuff */
850489290e9SAlexander Motin 		if (hpriv->conf.enableWindowing) {
851489290e9SAlexander Motin 			sample = ng_pptpgre_time() - hpriv->timeSent[index];
852489290e9SAlexander Motin 			diff = sample - hpriv->rtt;
853489290e9SAlexander Motin 			hpriv->rtt += PPTP_ACK_ALPHA(diff);
854add85a1dSArchie Cobbs 			if (diff < 0)
855add85a1dSArchie Cobbs 				diff = -diff;
856489290e9SAlexander Motin 			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
857f8e159d6SGleb Smirnoff 			    /* +2 to compensate low precision of int math */
858489290e9SAlexander Motin 			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
859489290e9SAlexander Motin 			if (hpriv->ato > PPTP_MAX_TIMEOUT)
860489290e9SAlexander Motin 				hpriv->ato = PPTP_MAX_TIMEOUT;
861489290e9SAlexander Motin 			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
862489290e9SAlexander Motin 				hpriv->ato = PPTP_MIN_TIMEOUT;
863e962a823SArchie Cobbs 
864e962a823SArchie Cobbs 			/* Shift packet transmit times in our transmit window */
865489290e9SAlexander Motin 			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
866489290e9SAlexander Motin 			    sizeof(*hpriv->timeSent)
867922ee196SArchie Cobbs 			      * (PPTP_XMIT_WIN - (index + 1)));
868e962a823SArchie Cobbs 
869922ee196SArchie Cobbs 			/* If we sent an entire window, increase window size */
870489290e9SAlexander Motin 			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
871489290e9SAlexander Motin 			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
872489290e9SAlexander Motin 				hpriv->xmitWin++;
873489290e9SAlexander Motin 				hpriv->winAck = ack + hpriv->xmitWin;
874add85a1dSArchie Cobbs 			}
875add85a1dSArchie Cobbs 
8769bee7adfSArchie Cobbs 			/* Stop/(re)start receive ACK timer as necessary */
877714f558bSAlexander Motin 			ng_uncallout(&hpriv->rackTimer, hpriv->node);
878489290e9SAlexander Motin 			if (hpriv->recvAck != hpriv->xmitSeq)
879489290e9SAlexander Motin 				ng_pptpgre_start_recv_ack_timer(hpriv);
880add85a1dSArchie Cobbs 		}
881922ee196SArchie Cobbs 	}
8829bee7adfSArchie Cobbs badAck:
883add85a1dSArchie Cobbs 
884add85a1dSArchie Cobbs 	/* See if frame contains any data */
885a594f945SEugene Grosbein 	if (!gre->hasSeq) {		/* no data to deliver */
886a594f945SEugene Grosbein 		priv->stats.recvLoneAcks++;
887a594f945SEugene Grosbein 		mtx_unlock(&hpriv->mtx);
888a594f945SEugene Grosbein 		ERROUT(0);
889a594f945SEugene Grosbein 	}
890add85a1dSArchie Cobbs 
891a594f945SEugene Grosbein 	seq = be32dec(&gre->data[0]);
892a594f945SEugene Grosbein 
893a594f945SEugene Grosbein 	diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
894a594f945SEugene Grosbein 	if (diff <= 0) {			/* late or duplicate packet */
895a594f945SEugene Grosbein 		if (diff < 0 && reorder_max == 0)	/* reorder disabled */
896a594f945SEugene Grosbein 			priv->stats.recvOutOfOrder++;	/* late */
8979bee7adfSArchie Cobbs 		else
898a594f945SEugene Grosbein 			priv->stats.recvDuplicates++;	/* duplicate */
899489290e9SAlexander Motin 		mtx_unlock(&hpriv->mtx);
90083beeed9SGleb Smirnoff 		ERROUT(EINVAL);
9019bee7adfSArchie Cobbs 	}
902add85a1dSArchie Cobbs 
903add85a1dSArchie Cobbs 	/* Trim mbuf down to internal payload */
904add85a1dSArchie Cobbs 	m_adj(m, iphlen + grelen);
905add85a1dSArchie Cobbs 	if (extralen > 0)
906add85a1dSArchie Cobbs 		m_adj(m, -extralen);
907add85a1dSArchie Cobbs 
908a594f945SEugene Grosbein #define INIT_SENDQ(t) do {				\
909a594f945SEugene Grosbein 		t.item = item;				\
910a594f945SEugene Grosbein 		t.seq = seq;				\
911a594f945SEugene Grosbein 		SLIST_INSERT_HEAD(&sendq, &t, next);	\
912a594f945SEugene Grosbein 		last = &t;				\
913a594f945SEugene Grosbein 		hpriv->recvSeq = seq;			\
914a594f945SEugene Grosbein 		goto deliver;				\
915a594f945SEugene Grosbein 	} while(0)
916489290e9SAlexander Motin 
917a594f945SEugene Grosbein 	if (diff == 1)
918a594f945SEugene Grosbein 		/* the packet came in order, place it at the start of sendq */
919a594f945SEugene Grosbein 		INIT_SENDQ(temp);
920a594f945SEugene Grosbein 
921a594f945SEugene Grosbein 	/* The packet came too early, try to enqueue it.
922a594f945SEugene Grosbein 	 *
923a594f945SEugene Grosbein 	 * Check for duplicate in the queue. After this loop, "prev" will be
924a594f945SEugene Grosbein 	 * NULL if the packet should become new head of the queue,
925a594f945SEugene Grosbein 	 * or else it should be inserted after the "prev".
926a594f945SEugene Grosbein 	 */
927a594f945SEugene Grosbein 	prev = SLIST_FIRST(&hpriv->roq);
928a594f945SEugene Grosbein 	SLIST_FOREACH(np, &hpriv->roq, next) {
929a594f945SEugene Grosbein 		diff = PPTP_SEQ_DIFF(np->seq, seq);
930a594f945SEugene Grosbein 		if (diff == 0) { /* do not add duplicate, drop it */
931a594f945SEugene Grosbein 			priv->stats.recvDuplicates++;
932489290e9SAlexander Motin 			mtx_unlock(&hpriv->mtx);
933a594f945SEugene Grosbein 			ERROUT(EINVAL);
934a594f945SEugene Grosbein 		}
935a594f945SEugene Grosbein 		if (diff > 0) {		/* we found newer packet */
936a594f945SEugene Grosbein 			if (np == prev)	/* that is the head of the queue */
937a594f945SEugene Grosbein 			    prev = NULL; /* put current packet to the head */
938a594f945SEugene Grosbein 			break;
939a594f945SEugene Grosbein 		}
940a594f945SEugene Grosbein 		prev = np;
9419bee7adfSArchie Cobbs 	}
94283beeed9SGleb Smirnoff 
943a594f945SEugene Grosbein 	priv->stats.recvOutOfOrder++;	/* duplicate not found */
944a594f945SEugene Grosbein 	if (hpriv->roq_len < reorder_max)
945a594f945SEugene Grosbein 		goto enqueue;	/* reorder enabled and there is a room */
946a594f945SEugene Grosbein 
947a594f945SEugene Grosbein 	/*
948a594f945SEugene Grosbein 	 * There is no room in the queue or reorder disabled.
949a594f945SEugene Grosbein 	 *
950a594f945SEugene Grosbein 	 * It the latter case, we may still have non-empty reorder queue
951a594f945SEugene Grosbein 	 * if reorder was disabled in process of reordering.
952a594f945SEugene Grosbein 	 * Then we correctly deliver the queue without growing it.
953a594f945SEugene Grosbein 	 *
954a594f945SEugene Grosbein 	 * In both cases, no malloc()'s until the queue is shortened.
955a594f945SEugene Grosbein 	 */
956a594f945SEugene Grosbein 	priv->stats.recvReorderOverflow++;
957a594f945SEugene Grosbein 	if (prev == NULL) {	  /* new packet goes before the head      */
958a594f945SEugene Grosbein 		INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
959a594f945SEugene Grosbein 	}
960a594f945SEugene Grosbein #undef INIT_SENDQ
961a594f945SEugene Grosbein 
962a594f945SEugene Grosbein 	/*
963a594f945SEugene Grosbein 	 * Current packet goes after the head of reorder queue.
964a594f945SEugene Grosbein 	 * Move the head to sendq to make room for current packet.
965a594f945SEugene Grosbein 	 */
966a594f945SEugene Grosbein 	np = SLIST_FIRST(&hpriv->roq);
967a594f945SEugene Grosbein 	if (prev == np)
968a594f945SEugene Grosbein 		prev = NULL;
969a594f945SEugene Grosbein 	SLIST_REMOVE_HEAD(&hpriv->roq, next);
970a594f945SEugene Grosbein 	hpriv->roq_len--;	/* we are allowed to use malloc() now */
971a594f945SEugene Grosbein 	SLIST_INSERT_HEAD(&sendq, np, next);
972a594f945SEugene Grosbein 	last = np;
973a594f945SEugene Grosbein 	hpriv->recvSeq = np->seq;
974a594f945SEugene Grosbein 
975a594f945SEugene Grosbein enqueue:
976*9b8db664SDmitry Lukhtionov 	np = malloc(sizeof(*np), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
977a594f945SEugene Grosbein 	if (np == NULL) {
978a594f945SEugene Grosbein 		priv->stats.memoryFailures++;
979a594f945SEugene Grosbein 		/*
980a594f945SEugene Grosbein 		 * Emergency: we cannot save new data.
981a594f945SEugene Grosbein 		 * Flush the queue delivering all queued packets preceeding
982a594f945SEugene Grosbein 		 * current one despite of gaps.
983a594f945SEugene Grosbein 		 */
984a594f945SEugene Grosbein 		while (!SLIST_EMPTY(&hpriv->roq)) {
985a594f945SEugene Grosbein 			np = SLIST_FIRST(&hpriv->roq);
986a594f945SEugene Grosbein 			if (np->seq > seq)
987a594f945SEugene Grosbein 				break;
988a594f945SEugene Grosbein 			SLIST_REMOVE_HEAD(&hpriv->roq, next);
989a594f945SEugene Grosbein 			hpriv->roq_len--;
990a594f945SEugene Grosbein 			if (last == NULL)
991a594f945SEugene Grosbein 				SLIST_INSERT_HEAD(&sendq, np, next);
992a594f945SEugene Grosbein 			else
993a594f945SEugene Grosbein 				SLIST_INSERT_AFTER(last, np, next);
994a594f945SEugene Grosbein 			last = np;
995a594f945SEugene Grosbein 		}
996a594f945SEugene Grosbein 
997a594f945SEugene Grosbein 		/*
998a594f945SEugene Grosbein 		 * Pretend we got all packets till the current one
999a594f945SEugene Grosbein 		 * and acknowledge it.
1000a594f945SEugene Grosbein 		 */
1001a594f945SEugene Grosbein 		hpriv->recvSeq = seq;
1002a594f945SEugene Grosbein 		ng_pptpgre_ack(hpriv);	/* drops lock */
1003a594f945SEugene Grosbein 		ng_pptpgre_sendq(hpriv, &sendq, &temp);
1004a594f945SEugene Grosbein 		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1005a594f945SEugene Grosbein 		ERROUT(ENOMEM);
1006a594f945SEugene Grosbein 	}
1007a594f945SEugene Grosbein 
1008a594f945SEugene Grosbein 	/* Add current (early) packet to the reorder queue. */
1009a594f945SEugene Grosbein 	np->item = item;
1010a594f945SEugene Grosbein 	np->seq = seq;
1011a594f945SEugene Grosbein 	if (prev == NULL)
1012a594f945SEugene Grosbein 		SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1013a594f945SEugene Grosbein 	else
1014a594f945SEugene Grosbein 		SLIST_INSERT_AFTER(prev, np, next);
1015a594f945SEugene Grosbein 	hpriv->roq_len++;
1016a594f945SEugene Grosbein 
1017a594f945SEugene Grosbein deliver:
1018a594f945SEugene Grosbein 	/* Look if we have some packets in sequence after sendq. */
1019a594f945SEugene Grosbein 	while (!SLIST_EMPTY(&hpriv->roq)) {
1020a594f945SEugene Grosbein 		np = SLIST_FIRST(&hpriv->roq);
1021a594f945SEugene Grosbein 		if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1022a594f945SEugene Grosbein 			break; /* the gap in the sequence */
1023a594f945SEugene Grosbein 
1024a594f945SEugene Grosbein 		/* "np" is in sequence, move it to the sendq. */
1025a594f945SEugene Grosbein 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1026a594f945SEugene Grosbein 		hpriv->roq_len--;
1027a594f945SEugene Grosbein 		hpriv->recvSeq = np->seq;
1028a594f945SEugene Grosbein 
1029a594f945SEugene Grosbein 		if (last == NULL)
1030a594f945SEugene Grosbein 			SLIST_INSERT_HEAD(&sendq, np, next);
1031a594f945SEugene Grosbein 		else
1032a594f945SEugene Grosbein 			SLIST_INSERT_AFTER(last, np, next);
1033a594f945SEugene Grosbein 		last = np;
1034a594f945SEugene Grosbein 	}
1035a594f945SEugene Grosbein 
1036a594f945SEugene Grosbein 	if (SLIST_EMPTY(&hpriv->roq)) {
1037a594f945SEugene Grosbein 		if (callout_pending(&hpriv->reorderTimer))
1038a594f945SEugene Grosbein 			ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1039a594f945SEugene Grosbein 	} else {
1040a594f945SEugene Grosbein 		if (!callout_pending(&hpriv->reorderTimer))
1041a594f945SEugene Grosbein 			ng_pptpgre_start_reorder_timer(hpriv);
1042a594f945SEugene Grosbein 	}
1043a594f945SEugene Grosbein 
1044a594f945SEugene Grosbein 	if (SLIST_EMPTY(&sendq)) {
1045a594f945SEugene Grosbein 		/* Current packet has been queued, nothing to free/deliver. */
1046a594f945SEugene Grosbein 		mtx_unlock(&hpriv->mtx);
1047a594f945SEugene Grosbein 		return (error);
1048a594f945SEugene Grosbein 	}
1049a594f945SEugene Grosbein 
1050a594f945SEugene Grosbein 	/* We need to acknowledge last packet; do it soon... */
1051a594f945SEugene Grosbein 	ng_pptpgre_ack(hpriv);		/* drops lock */
1052a594f945SEugene Grosbein 	ng_pptpgre_sendq(hpriv, &sendq, &temp);
105383beeed9SGleb Smirnoff 	return (error);
105483beeed9SGleb Smirnoff 
105583beeed9SGleb Smirnoff done:
105683beeed9SGleb Smirnoff 	NG_FREE_ITEM(item);
1057add85a1dSArchie Cobbs 	return (error);
1058add85a1dSArchie Cobbs }
1059add85a1dSArchie Cobbs 
1060add85a1dSArchie Cobbs /*************************************************************************
1061add85a1dSArchie Cobbs 		    TIMER RELATED FUNCTIONS
1062add85a1dSArchie Cobbs *************************************************************************/
1063add85a1dSArchie Cobbs 
1064add85a1dSArchie Cobbs /*
10659bee7adfSArchie Cobbs  * Start a timer for the peer's acknowledging our oldest unacknowledged
1066add85a1dSArchie Cobbs  * sequence number.  If we get an ack for this sequence number before
1067add85a1dSArchie Cobbs  * the timer goes off, we cancel the timer.  Resets currently running
1068add85a1dSArchie Cobbs  * recv ack timer, if any.
1069add85a1dSArchie Cobbs  */
1070add85a1dSArchie Cobbs static void
ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)1071489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1072add85a1dSArchie Cobbs {
1073678f9e33SArchie Cobbs 	int remain, ticks;
1074add85a1dSArchie Cobbs 
1075add85a1dSArchie Cobbs 	/* Compute how long until oldest unack'd packet times out,
1076add85a1dSArchie Cobbs 	   and reset the timer to that time. */
1077489290e9SAlexander Motin 	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1078add85a1dSArchie Cobbs 	if (remain < 0)
1079add85a1dSArchie Cobbs 		remain = 0;
10809bee7adfSArchie Cobbs 
10814a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
108255e0987aSPedro F. Giffuni 	ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1083489290e9SAlexander Motin 	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1084489290e9SAlexander Motin 	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
10854a48abb2SArchie Cobbs }
10864a48abb2SArchie Cobbs 
10874a48abb2SArchie Cobbs /*
1088add85a1dSArchie Cobbs  * The peer has failed to acknowledge the oldest unacknowledged sequence
1089add85a1dSArchie Cobbs  * number within the time allotted.  Update our adaptive timeout parameters
1090add85a1dSArchie Cobbs  * and reset/restart the recv ack timer.
1091add85a1dSArchie Cobbs  */
1092add85a1dSArchie Cobbs static void
ng_pptpgre_recv_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1093089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1094add85a1dSArchie Cobbs {
109530400f03SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
1096489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
10979bee7adfSArchie Cobbs 
1098add85a1dSArchie Cobbs 	/* Update adaptive timeout stuff */
10999bee7adfSArchie Cobbs 	priv->stats.recvAckTimeouts++;
1100489290e9SAlexander Motin 	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1101489290e9SAlexander Motin 	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1102489290e9SAlexander Motin 	if (hpriv->ato > PPTP_MAX_TIMEOUT)
1103489290e9SAlexander Motin 		hpriv->ato = PPTP_MAX_TIMEOUT;
1104489290e9SAlexander Motin 	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1105489290e9SAlexander Motin 		hpriv->ato = PPTP_MIN_TIMEOUT;
1106678f9e33SArchie Cobbs 
1107e962a823SArchie Cobbs 	/* Reset ack and sliding window */
1108489290e9SAlexander Motin 	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
1109489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
1110489290e9SAlexander Motin 	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
1111add85a1dSArchie Cobbs }
1112add85a1dSArchie Cobbs 
1113add85a1dSArchie Cobbs /*
11149bee7adfSArchie Cobbs  * Start the send ack timer. This assumes the timer is not
11159bee7adfSArchie Cobbs  * already running.
11169bee7adfSArchie Cobbs  */
11179bee7adfSArchie Cobbs static void
ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)1118714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
11199bee7adfSArchie Cobbs {
1120714f558bSAlexander Motin 	int ackTimeout, ticks;
1121714f558bSAlexander Motin 
1122714f558bSAlexander Motin 	/* Take 1/4 of the estimated round trip time */
1123714f558bSAlexander Motin 	ackTimeout = (hpriv->rtt >> 2);
1124714f558bSAlexander Motin 	if (ackTimeout < PPTP_MIN_ACK_DELAY)
1125714f558bSAlexander Motin 		ackTimeout = PPTP_MIN_ACK_DELAY;
1126714f558bSAlexander Motin 	else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1127714f558bSAlexander Motin 		ackTimeout = PPTP_MAX_ACK_DELAY;
11289bee7adfSArchie Cobbs 
11294a48abb2SArchie Cobbs 	/* Be conservative: timeout can happen up to 1 tick early */
113055e0987aSPedro F. Giffuni 	ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1131489290e9SAlexander Motin 	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1132489290e9SAlexander Motin 	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
11334a48abb2SArchie Cobbs }
11344a48abb2SArchie Cobbs 
11354a48abb2SArchie Cobbs /*
1136add85a1dSArchie Cobbs  * We've waited as long as we're willing to wait before sending an
1137add85a1dSArchie Cobbs  * acknowledgement to the peer for received frames. We had hoped to
1138add85a1dSArchie Cobbs  * be able to piggy back our acknowledgement on an outgoing data frame,
1139add85a1dSArchie Cobbs  * but apparently there haven't been any since. So send the ack now.
1140add85a1dSArchie Cobbs  */
1141add85a1dSArchie Cobbs static void
ng_pptpgre_send_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1142089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1143add85a1dSArchie Cobbs {
1144489290e9SAlexander Motin 	const hpriv_p hpriv = arg1;
114583beeed9SGleb Smirnoff 
1146489290e9SAlexander Motin 	mtx_lock(&hpriv->mtx);
11479bee7adfSArchie Cobbs 	/* Send a frame with an ack but no payload */
1148489290e9SAlexander Motin   	ng_pptpgre_xmit(hpriv, NULL);
1149489290e9SAlexander Motin 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1150add85a1dSArchie Cobbs }
1151add85a1dSArchie Cobbs 
1152a594f945SEugene Grosbein /*
1153a594f945SEugene Grosbein  * Start a timer for the reorder queue. This assumes the timer is not
1154a594f945SEugene Grosbein  * already running.
1155a594f945SEugene Grosbein  */
1156a594f945SEugene Grosbein static void
ng_pptpgre_start_reorder_timer(hpriv_p hpriv)1157a594f945SEugene Grosbein ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1158a594f945SEugene Grosbein {
1159a594f945SEugene Grosbein 	int ticks;
1160a594f945SEugene Grosbein 
1161a594f945SEugene Grosbein 	/* Be conservative: timeout can happen up to 1 tick early */
1162a594f945SEugene Grosbein 	ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1163a594f945SEugene Grosbein 	ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1164a594f945SEugene Grosbein 		ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1165a594f945SEugene Grosbein }
1166a594f945SEugene Grosbein 
1167a594f945SEugene Grosbein /*
1168a594f945SEugene Grosbein  * The oldest packet spent too much time in the reorder queue.
1169a594f945SEugene Grosbein  * Deliver it and next packets in sequence, if any.
1170a594f945SEugene Grosbein  */
1171a594f945SEugene Grosbein static void
ng_pptpgre_reorder_timeout(node_p node,hook_p hook,void * arg1,int arg2)1172a594f945SEugene Grosbein ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1173a594f945SEugene Grosbein {
1174a594f945SEugene Grosbein 	const priv_p priv = NG_NODE_PRIVATE(node);
1175a594f945SEugene Grosbein 	const hpriv_p hpriv = arg1;
1176a594f945SEugene Grosbein 	roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1177a594f945SEugene Grosbein 	struct ng_pptpgre_roq *np, *last = NULL;
1178a594f945SEugene Grosbein 
1179a594f945SEugene Grosbein 	priv->stats.recvReorderTimeouts++;
1180a594f945SEugene Grosbein 	mtx_lock(&hpriv->mtx);
1181a594f945SEugene Grosbein 	if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1182a594f945SEugene Grosbein 		mtx_unlock(&hpriv->mtx);
1183a594f945SEugene Grosbein 		return;
1184a594f945SEugene Grosbein 	}
1185a594f945SEugene Grosbein 
1186a594f945SEugene Grosbein 	last = np = SLIST_FIRST(&hpriv->roq);
1187a594f945SEugene Grosbein 	hpriv->roq_len--;
1188a594f945SEugene Grosbein 	SLIST_REMOVE_HEAD(&hpriv->roq, next);
1189a594f945SEugene Grosbein 	SLIST_INSERT_HEAD(&sendq, np, next);
1190a594f945SEugene Grosbein 
1191a594f945SEugene Grosbein 	/* Look if we have more packets in sequence */
1192a594f945SEugene Grosbein 	while (!SLIST_EMPTY(&hpriv->roq)) {
1193a594f945SEugene Grosbein 		np = SLIST_FIRST(&hpriv->roq);
1194a594f945SEugene Grosbein 		if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1195a594f945SEugene Grosbein 			break; /* the gap in the sequence */
1196a594f945SEugene Grosbein 
1197a594f945SEugene Grosbein 		/* Next packet is in sequence, move it to the sendq. */
1198a594f945SEugene Grosbein 		hpriv->roq_len--;
1199a594f945SEugene Grosbein 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1200a594f945SEugene Grosbein 		SLIST_INSERT_AFTER(last, np, next);
1201a594f945SEugene Grosbein 		last = np;
1202a594f945SEugene Grosbein 	}
1203a594f945SEugene Grosbein 
1204a594f945SEugene Grosbein 	hpriv->recvSeq = last->seq;
1205a594f945SEugene Grosbein 	if (!SLIST_EMPTY(&hpriv->roq))
1206a594f945SEugene Grosbein 		ng_pptpgre_start_reorder_timer(hpriv);
1207a594f945SEugene Grosbein 
1208a594f945SEugene Grosbein 	/* We need to acknowledge last packet; do it soon... */
1209a594f945SEugene Grosbein 	ng_pptpgre_ack(hpriv);		/* drops lock */
1210a594f945SEugene Grosbein 	ng_pptpgre_sendq(hpriv, &sendq, NULL);
1211a594f945SEugene Grosbein 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1212a594f945SEugene Grosbein }
1213a594f945SEugene Grosbein 
1214add85a1dSArchie Cobbs /*************************************************************************
1215add85a1dSArchie Cobbs 		    MISC FUNCTIONS
1216add85a1dSArchie Cobbs *************************************************************************/
1217add85a1dSArchie Cobbs 
1218add85a1dSArchie Cobbs /*
1219489290e9SAlexander Motin  * Find the hook with a given session ID.
1220489290e9SAlexander Motin  */
1221489290e9SAlexander Motin static hpriv_p
ng_pptpgre_find_session(priv_p privp,u_int16_t cid)1222489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1223489290e9SAlexander Motin {
1224489290e9SAlexander Motin 	uint16_t	hash = SESSHASH(cid);
1225489290e9SAlexander Motin 	hpriv_p	hpriv = NULL;
1226489290e9SAlexander Motin 
1227489290e9SAlexander Motin 	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1228489290e9SAlexander Motin 		if (hpriv->conf.cid == cid)
1229489290e9SAlexander Motin 			break;
1230489290e9SAlexander Motin 	}
1231489290e9SAlexander Motin 
1232489290e9SAlexander Motin 	return (hpriv);
1233489290e9SAlexander Motin }
1234489290e9SAlexander Motin 
1235489290e9SAlexander Motin /*
1236489290e9SAlexander Motin  * Reset state (must be called with lock held or from writer)
1237add85a1dSArchie Cobbs  */
1238add85a1dSArchie Cobbs static void
ng_pptpgre_reset(hpriv_p hpriv)1239489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv)
1240add85a1dSArchie Cobbs {
1241a594f945SEugene Grosbein 	struct ng_pptpgre_roq *np;
1242a594f945SEugene Grosbein 
1243add85a1dSArchie Cobbs 	/* Reset adaptive timeout state */
1244489290e9SAlexander Motin 	hpriv->ato = PPTP_MAX_TIMEOUT;
1245714f558bSAlexander Motin 	hpriv->rtt = PPTP_TIME_SCALE / 10;
1246714f558bSAlexander Motin 	if (hpriv->conf.peerPpd > 1)	/* ppd = 0 treat as = 1 */
1247714f558bSAlexander Motin 		hpriv->rtt *= hpriv->conf.peerPpd;
1248489290e9SAlexander Motin 	hpriv->dev = 0;
1249489290e9SAlexander Motin 	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1250489290e9SAlexander Motin 	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
1251489290e9SAlexander Motin 		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
1252489290e9SAlexander Motin 	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1253489290e9SAlexander Motin 		hpriv->xmitWin = PPTP_XMIT_WIN;
1254489290e9SAlexander Motin 	hpriv->winAck = hpriv->xmitWin;
1255add85a1dSArchie Cobbs 
1256add85a1dSArchie Cobbs 	/* Reset sequence numbers */
1257489290e9SAlexander Motin 	hpriv->recvSeq = ~0;
1258489290e9SAlexander Motin 	hpriv->recvAck = ~0;
1259489290e9SAlexander Motin 	hpriv->xmitSeq = ~0;
1260489290e9SAlexander Motin 	hpriv->xmitAck = ~0;
1261add85a1dSArchie Cobbs 
12624a48abb2SArchie Cobbs 	/* Stop timers */
1263714f558bSAlexander Motin 	ng_uncallout(&hpriv->sackTimer, hpriv->node);
1264714f558bSAlexander Motin 	ng_uncallout(&hpriv->rackTimer, hpriv->node);
1265a594f945SEugene Grosbein 	ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1266a594f945SEugene Grosbein 
1267a594f945SEugene Grosbein 	/* Clear reorder queue */
1268a594f945SEugene Grosbein 	while (!SLIST_EMPTY(&hpriv->roq)) {
1269a594f945SEugene Grosbein 		np = SLIST_FIRST(&hpriv->roq);
1270a594f945SEugene Grosbein 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1271a594f945SEugene Grosbein 		NG_FREE_ITEM(np->item);
1272*9b8db664SDmitry Lukhtionov 		free(np, M_NETGRAPH_PPTP);
1273a594f945SEugene Grosbein 	}
1274a594f945SEugene Grosbein 	hpriv->roq_len = 0;
1275add85a1dSArchie Cobbs }
1276add85a1dSArchie Cobbs 
1277add85a1dSArchie Cobbs /*
1278add85a1dSArchie Cobbs  * Return the current time scaled & translated to our internally used format.
1279add85a1dSArchie Cobbs  */
1280add85a1dSArchie Cobbs static pptptime_t
ng_pptpgre_time(void)1281489290e9SAlexander Motin ng_pptpgre_time(void)
1282add85a1dSArchie Cobbs {
1283add85a1dSArchie Cobbs 	struct timeval tv;
1284678f9e33SArchie Cobbs 	pptptime_t t;
1285add85a1dSArchie Cobbs 
1286678f9e33SArchie Cobbs 	microuptime(&tv);
1287678f9e33SArchie Cobbs 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1288714f558bSAlexander Motin 	t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1289678f9e33SArchie Cobbs 	return(t);
1290add85a1dSArchie Cobbs }
1291