xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision 9b8db66402e94919ab47fc2d69863968e49e305a)
1 /*
2  * ng_pptpgre.c
3  */
4 
5 /*-
6  * Copyright (c) 1996-1999 Whistle Communications, Inc.
7  * All rights reserved.
8  *
9  * Subject to the following obligations and disclaimer of warranty, use and
10  * redistribution of this software, in source or object code forms, with or
11  * without modifications are expressly permitted by Whistle Communications;
12  * provided, however, that:
13  * 1. Any and all reproductions of the source or object code must include the
14  *    copyright notice above and the following disclaimer of warranties; and
15  * 2. No rights are granted, in any manner or form, to use Whistle
16  *    Communications, Inc. trademarks, including the mark "WHISTLE
17  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18  *    such appears in the above copyright notice or in the software.
19  *
20  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  *
38  * Author: Archie Cobbs <archie@freebsd.org>
39  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
40  */
41 
42 /*
43  * PPTP/GRE netgraph node type.
44  *
45  * This node type does the GRE encapsulation as specified for the PPTP
46  * protocol (RFC 2637, section 4).  This includes sequencing and
47  * retransmission of frames, but not the actual packet delivery nor
48  * any of the TCP control stream protocol.
49  *
50  * The "upper" hook of this node is suitable for attaching to a "ppp"
51  * node link hook.  The "lower" hook of this node is suitable for attaching
52  * to a "ksocket" node on hook "inet/raw/gre".
53  */
54 
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/kernel.h>
58 #include <sys/time.h>
59 #include <sys/lock.h>
60 #include <sys/malloc.h>
61 #include <sys/mbuf.h>
62 #include <sys/mutex.h>
63 #include <sys/endian.h>
64 #include <sys/errno.h>
65 #include <sys/sysctl.h>
66 
67 #include <netinet/in.h>
68 #include <netinet/in_systm.h>
69 #include <netinet/ip.h>
70 
71 #include <netgraph/ng_message.h>
72 #include <netgraph/netgraph.h>
73 #include <netgraph/ng_parse.h>
74 #include <netgraph/ng_pptpgre.h>
75 
76 #ifdef NG_SEPARATE_MALLOC
77 static MALLOC_DEFINE(M_NETGRAPH_PPTP, "netgraph_pptp", "netgraph pptpgre node");
78 #else
79 #define M_NETGRAPH_PPTP M_NETGRAPH
80 #endif
81 
82 /* GRE packet format, as used by PPTP */
83 struct greheader {
84 #if BYTE_ORDER == LITTLE_ENDIAN
85 	u_char		recursion:3;		/* recursion control */
86 	u_char		ssr:1;			/* strict source route */
87 	u_char		hasSeq:1;		/* sequence number present */
88 	u_char		hasKey:1;		/* key present */
89 	u_char		hasRoute:1;		/* routing present */
90 	u_char		hasSum:1;		/* checksum present */
91 	u_char		vers:3;			/* version */
92 	u_char		flags:4;		/* flags */
93 	u_char		hasAck:1;		/* acknowlege number present */
94 #elif BYTE_ORDER == BIG_ENDIAN
95 	u_char		hasSum:1;		/* checksum present */
96 	u_char		hasRoute:1;		/* routing present */
97 	u_char		hasKey:1;		/* key present */
98 	u_char		hasSeq:1;		/* sequence number present */
99 	u_char		ssr:1;			/* strict source route */
100 	u_char		recursion:3;		/* recursion control */
101 	u_char		hasAck:1;		/* acknowlege number present */
102 	u_char		flags:4;		/* flags */
103 	u_char		vers:3;			/* version */
104 #else
105 #error BYTE_ORDER is not defined properly
106 #endif
107 	u_int16_t	proto;			/* protocol (ethertype) */
108 	u_int16_t	length;			/* payload length */
109 	u_int16_t	cid;			/* call id */
110 	u_int32_t	data[0];		/* opt. seq, ack, then data */
111 };
112 
113 /* The PPTP protocol ID used in the GRE 'proto' field */
114 #define PPTP_GRE_PROTO		0x880b
115 
116 /* Bits that must be set a certain way in all PPTP/GRE packets */
117 #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
118 #define PPTP_INIT_MASK		0xef7fffff
119 
120 /* Min and max packet length */
121 #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
122 
123 /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
124 #define PPTP_TIME_SCALE		1024			/* milliseconds */
125 typedef u_int64_t		pptptime_t;
126 
127 /* Acknowledgment timeout parameters and functions */
128 #define PPTP_XMIT_WIN		16			/* max xmit window */
129 #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
130 #define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 seconds */
131 
132 #define PPTP_REORDER_TIMEOUT	1
133 
134 /* When we receive a packet, we wait to see if there's an outgoing packet
135    we can piggy-back the ACK off of. These parameters determine the minimum
136    and maxmimum length of time we're willing to wait in order to do that.
137    These have no effect unless "enableDelayedAck" is turned on. */
138 #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
139 #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
140 
141 /* See RFC 2637 section 4.4 */
142 #define PPTP_ACK_ALPHA(x)	(((x) + 4) >> 3)	/* alpha = 0.125 */
143 #define PPTP_ACK_BETA(x)	(((x) + 2) >> 2)	/* beta = 0.25 */
144 #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
145 #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
146 
147 #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
148 
149 #define SESSHASHSIZE		0x0020
150 #define SESSHASH(x)		(((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
151 
152 SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
153     "PPTPGRE");
154 
155 /*
156  * Reorder queue maximum length. Zero disables reorder.
157  *
158  * The node may keep reorder_max queue entries per session
159  * if reorder is enabled, plus allocate one more for short time.
160  *
161  * Be conservative in memory consumption by default.
162  * Lots of sessions with large queues can overflow M_NETGRAPH zone.
163  */
164 static int reorder_max = 1; /* reorder up to two swapped packets in a row */
165 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
166 	&reorder_max, 0, "Reorder queue maximum length");
167 
168 static int reorder_timeout = PPTP_REORDER_TIMEOUT;
169 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
170 	&reorder_timeout, 0, "Reorder timeout is milliseconds");
171 
172 /* Packet reorder FIFO queue */
173 struct ng_pptpgre_roq {
174 	SLIST_ENTRY(ng_pptpgre_roq)  next;	/* next entry of the queue */
175 	item_p			item;		/* netgraph item */
176 	u_int32_t		seq;		/* packet sequence number */
177 };
178 SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
179 typedef struct ng_pptpgre_roq_head roqh;
180 
181 /* We keep packet retransmit and acknowlegement state in this struct */
182 struct ng_pptpgre_sess {
183 	node_p			node;		/* this node pointer */
184 	hook_p			hook;		/* hook to upper layers */
185 	struct ng_pptpgre_conf	conf;		/* configuration info */
186 	struct mtx		mtx;		/* session mutex */
187 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
188 	u_int32_t		xmitSeq;	/* last seq # we sent */
189 	u_int32_t		recvAck;	/* last seq # peer ack'd */
190 	u_int32_t		xmitAck;	/* last seq # we ack'd */
191 	int32_t			ato;		/* adaptive time-out value */
192 	int32_t			rtt;		/* round trip time estimate */
193 	int32_t			dev;		/* deviation estimate */
194 	u_int16_t		xmitWin;	/* size of xmit window */
195 	struct callout		sackTimer;	/* send ack timer */
196 	struct callout		rackTimer;	/* recv ack timer */
197 	u_int32_t		winAck;		/* seq when xmitWin will grow */
198 	pptptime_t		timeSent[PPTP_XMIT_WIN];
199 	LIST_ENTRY(ng_pptpgre_sess) sessions;
200 	roqh			roq;		/* reorder queue head */
201 	u_int8_t		roq_len;	/* reorder queue length */
202 	struct callout		reorderTimer;	/* reorder timeout handler */
203 };
204 typedef struct ng_pptpgre_sess *hpriv_p;
205 
206 /* Node private data */
207 struct ng_pptpgre_private {
208 	hook_p			upper;		/* hook to upper layers */
209 	hook_p			lower;		/* hook to lower layers */
210 	struct ng_pptpgre_sess	uppersess;	/* default session for compat */
211 	LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
212 	struct ng_pptpgre_stats	stats;		/* node statistics */
213 };
214 typedef struct ng_pptpgre_private *priv_p;
215 
216 /* Netgraph node methods */
217 static ng_constructor_t	ng_pptpgre_constructor;
218 static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
219 static ng_shutdown_t	ng_pptpgre_shutdown;
220 static ng_newhook_t	ng_pptpgre_newhook;
221 static ng_rcvdata_t	ng_pptpgre_rcvdata;
222 static ng_rcvdata_t	ng_pptpgre_rcvdata_lower;
223 static ng_disconnect_t	ng_pptpgre_disconnect;
224 
225 /* Helper functions */
226 static int	ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
227 static void	ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
228 static void	ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
229 static void	ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
230 static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
231 		    void *arg1, int arg2);
232 static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
233 		    void *arg1, int arg2);
234 static void	ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
235 		    void *arg1, int arg2);
236 static hpriv_p	ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
237 static void	ng_pptpgre_reset(hpriv_p hpriv);
238 static pptptime_t ng_pptpgre_time(void);
239 static void	ng_pptpgre_ack(const hpriv_p hpriv);
240 static int	ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
241 		    const struct ng_pptpgre_roq *st);
242 
243 /* Parse type for struct ng_pptpgre_conf */
244 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
245 	= NG_PPTPGRE_CONF_TYPE_INFO;
246 static const struct ng_parse_type ng_pptpgre_conf_type = {
247 	&ng_parse_struct_type,
248 	&ng_pptpgre_conf_type_fields,
249 };
250 
251 /* Parse type for struct ng_pptpgre_stats */
252 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
253 	= NG_PPTPGRE_STATS_TYPE_INFO;
254 static const struct ng_parse_type ng_pptp_stats_type = {
255 	&ng_parse_struct_type,
256 	&ng_pptpgre_stats_type_fields
257 };
258 
259 /* List of commands and how to convert arguments to/from ASCII */
260 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
261 	{
262 	  NGM_PPTPGRE_COOKIE,
263 	  NGM_PPTPGRE_SET_CONFIG,
264 	  "setconfig",
265 	  &ng_pptpgre_conf_type,
266 	  NULL
267 	},
268 	{
269 	  NGM_PPTPGRE_COOKIE,
270 	  NGM_PPTPGRE_GET_CONFIG,
271 	  "getconfig",
272 	  &ng_parse_hint16_type,
273 	  &ng_pptpgre_conf_type
274 	},
275 	{
276 	  NGM_PPTPGRE_COOKIE,
277 	  NGM_PPTPGRE_GET_STATS,
278 	  "getstats",
279 	  NULL,
280 	  &ng_pptp_stats_type
281 	},
282 	{
283 	  NGM_PPTPGRE_COOKIE,
284 	  NGM_PPTPGRE_CLR_STATS,
285 	  "clrstats",
286 	  NULL,
287 	  NULL
288 	},
289 	{
290 	  NGM_PPTPGRE_COOKIE,
291 	  NGM_PPTPGRE_GETCLR_STATS,
292 	  "getclrstats",
293 	  NULL,
294 	  &ng_pptp_stats_type
295 	},
296 	{ 0 }
297 };
298 
299 /* Node type descriptor */
300 static struct ng_type ng_pptpgre_typestruct = {
301 	.version =	NG_ABI_VERSION,
302 	.name =		NG_PPTPGRE_NODE_TYPE,
303 	.constructor =	ng_pptpgre_constructor,
304 	.rcvmsg =	ng_pptpgre_rcvmsg,
305 	.shutdown =	ng_pptpgre_shutdown,
306 	.newhook =	ng_pptpgre_newhook,
307 	.rcvdata =	ng_pptpgre_rcvdata,
308 	.disconnect =	ng_pptpgre_disconnect,
309 	.cmdlist =	ng_pptpgre_cmdlist,
310 };
311 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
312 
313 #define ERROUT(x)	do { error = (x); goto done; } while (0)
314 
315 /************************************************************************
316 			NETGRAPH NODE STUFF
317  ************************************************************************/
318 
319 /*
320  * Node type constructor
321  */
322 static int
ng_pptpgre_constructor(node_p node)323 ng_pptpgre_constructor(node_p node)
324 {
325 	priv_p priv;
326 	int i;
327 
328 	/* Allocate private structure */
329 	priv = malloc(sizeof(*priv), M_NETGRAPH_PPTP, M_WAITOK | M_ZERO);
330 
331 	NG_NODE_SET_PRIVATE(node, priv);
332 
333 	/* Initialize state */
334 	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
335 	ng_callout_init(&priv->uppersess.sackTimer);
336 	ng_callout_init(&priv->uppersess.rackTimer);
337 	priv->uppersess.node = node;
338 
339 	SLIST_INIT(&priv->uppersess.roq);
340 	priv->uppersess.roq_len = 0;
341 	ng_callout_init(&priv->uppersess.reorderTimer);
342 
343 	for (i = 0; i < SESSHASHSIZE; i++)
344 	    LIST_INIT(&priv->sesshash[i]);
345 
346 	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
347 
348 	/* Done */
349 	return (0);
350 }
351 
352 /*
353  * Give our OK for a hook to be added.
354  */
355 static int
ng_pptpgre_newhook(node_p node,hook_p hook,const char * name)356 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
357 {
358 	const priv_p priv = NG_NODE_PRIVATE(node);
359 
360 	/* Check hook name */
361 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
362 		priv->upper = hook;
363 		priv->uppersess.hook = hook;
364 		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
365 	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
366 		priv->lower = hook;
367 		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
368 	} else {
369 		static const char hexdig[16] = "0123456789abcdef";
370 		const char *hex;
371 		hpriv_p hpriv;
372 		int i, j;
373 		uint16_t cid, hash;
374 
375 		/* Parse hook name to get session ID */
376 		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
377 		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
378 			return (EINVAL);
379 		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
380 		for (cid = i = 0; i < 4; i++) {
381 			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
382 			if (j == 16)
383 				return (EINVAL);
384 			cid = (cid << 4) | j;
385 		}
386 		if (hex[i] != '\0')
387 			return (EINVAL);
388 
389 		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
390 		if (hpriv == NULL)
391 			return (ENOMEM);
392 
393 		/* Initialize state */
394 		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
395 		ng_callout_init(&hpriv->sackTimer);
396 		ng_callout_init(&hpriv->rackTimer);
397 		hpriv->conf.cid = cid;
398 		hpriv->node = node;
399 		hpriv->hook = hook;
400 
401 		SLIST_INIT(&hpriv->roq);
402 		hpriv->roq_len = 0;
403 		ng_callout_init(&hpriv->reorderTimer);
404 
405 		NG_HOOK_SET_PRIVATE(hook, hpriv);
406 
407 		hash = SESSHASH(cid);
408 		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
409 	}
410 
411 	return (0);
412 }
413 
414 /*
415  * Receive a control message.
416  */
417 static int
ng_pptpgre_rcvmsg(node_p node,item_p item,hook_p lasthook)418 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
419 {
420 	const priv_p priv = NG_NODE_PRIVATE(node);
421 	struct ng_mesg *resp = NULL;
422 	int error = 0;
423 	struct ng_mesg *msg;
424 
425 	NGI_GET_MSG(item, msg);
426 	switch (msg->header.typecookie) {
427 	case NGM_PPTPGRE_COOKIE:
428 		switch (msg->header.cmd) {
429 		case NGM_PPTPGRE_SET_CONFIG:
430 		    {
431 			struct ng_pptpgre_conf *const newConf =
432 				(struct ng_pptpgre_conf *) msg->data;
433 			hpriv_p hpriv;
434 			uint16_t hash;
435 
436 			/* Check for invalid or illegal config */
437 			if (msg->header.arglen != sizeof(*newConf))
438 				ERROUT(EINVAL);
439 			/* Try to find session by cid. */
440 			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
441 			/* If not present - use upper. */
442 			if (hpriv == NULL) {
443 				hpriv = &priv->uppersess;
444 				LIST_REMOVE(hpriv, sessions);
445 				hash = SESSHASH(newConf->cid);
446 				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
447 				    sessions);
448 			}
449 			ng_pptpgre_reset(hpriv);	/* reset on configure */
450 			hpriv->conf = *newConf;
451 			break;
452 		    }
453 		case NGM_PPTPGRE_GET_CONFIG:
454 		    {
455 			hpriv_p hpriv;
456 
457 			if (msg->header.arglen == 2) {
458 				/* Try to find session by cid. */
459 	    			hpriv = ng_pptpgre_find_session(priv,
460 				    *((uint16_t *)msg->data));
461 				if (hpriv == NULL)
462 					ERROUT(EINVAL);
463 			} else if (msg->header.arglen == 0) {
464 				/* Use upper. */
465 				hpriv = &priv->uppersess;
466 			} else
467 				ERROUT(EINVAL);
468 			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
469 			if (resp == NULL)
470 				ERROUT(ENOMEM);
471 			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
472 			break;
473 		    }
474 		case NGM_PPTPGRE_GET_STATS:
475 		case NGM_PPTPGRE_CLR_STATS:
476 		case NGM_PPTPGRE_GETCLR_STATS:
477 		    {
478 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
479 				NG_MKRESPONSE(resp, msg,
480 				    sizeof(priv->stats), M_NOWAIT);
481 				if (resp == NULL)
482 					ERROUT(ENOMEM);
483 				bcopy(&priv->stats,
484 				    resp->data, sizeof(priv->stats));
485 			}
486 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
487 				bzero(&priv->stats, sizeof(priv->stats));
488 			break;
489 		    }
490 		default:
491 			error = EINVAL;
492 			break;
493 		}
494 		break;
495 	default:
496 		error = EINVAL;
497 		break;
498 	}
499 done:
500 	NG_RESPOND_MSG(error, node, item, resp);
501 	NG_FREE_MSG(msg);
502 	return (error);
503 }
504 
505 /*
506  * Receive incoming data on a hook.
507  */
508 static int
ng_pptpgre_rcvdata(hook_p hook,item_p item)509 ng_pptpgre_rcvdata(hook_p hook, item_p item)
510 {
511 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
512 	int rval;
513 
514 	/* If not configured, reject */
515 	if (!hpriv->conf.enabled) {
516 		NG_FREE_ITEM(item);
517 		return (ENXIO);
518 	}
519 
520 	mtx_lock(&hpriv->mtx);
521 
522 	rval = ng_pptpgre_xmit(hpriv, item);
523 
524 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
525 
526 	return (rval);
527 }
528 
529 /*
530  * Hook disconnection
531  */
532 static int
ng_pptpgre_disconnect(hook_p hook)533 ng_pptpgre_disconnect(hook_p hook)
534 {
535 	const node_p node = NG_HOOK_NODE(hook);
536 	const priv_p priv = NG_NODE_PRIVATE(node);
537 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
538 
539 	/* Zero out hook pointer */
540 	if (hook == priv->upper) {
541 		priv->upper = NULL;
542 		priv->uppersess.hook = NULL;
543 	} else if (hook == priv->lower) {
544 		priv->lower = NULL;
545 	} else {
546 		/* Reset node (stops timers) */
547 		ng_pptpgre_reset(hpriv);
548 
549 		LIST_REMOVE(hpriv, sessions);
550 		mtx_destroy(&hpriv->mtx);
551 		free(hpriv, M_NETGRAPH_PPTP);
552 	}
553 
554 	/* Go away if no longer connected to anything */
555 	if ((NG_NODE_NUMHOOKS(node) == 0)
556 	&& (NG_NODE_IS_VALID(node)))
557 		ng_rmnode_self(node);
558 	return (0);
559 }
560 
561 /*
562  * Destroy node
563  */
564 static int
ng_pptpgre_shutdown(node_p node)565 ng_pptpgre_shutdown(node_p node)
566 {
567 	const priv_p priv = NG_NODE_PRIVATE(node);
568 
569 	/* Reset node (stops timers) */
570 	ng_pptpgre_reset(&priv->uppersess);
571 
572 	LIST_REMOVE(&priv->uppersess, sessions);
573 	mtx_destroy(&priv->uppersess.mtx);
574 
575 	free(priv, M_NETGRAPH_PPTP);
576 
577 	/* Decrement ref count */
578 	NG_NODE_UNREF(node);
579 	return (0);
580 }
581 
582 /*************************************************************************
583 		    TRANSMIT AND RECEIVE FUNCTIONS
584 *************************************************************************/
585 
586 /*
587  * Transmit an outgoing frame, or just an ack if m is NULL.
588  */
589 static int
ng_pptpgre_xmit(hpriv_p hpriv,item_p item)590 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
591 {
592 	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
593 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
594 	struct greheader *const gre = (struct greheader *)buf;
595 	int grelen, error;
596 	struct mbuf *m;
597 
598 	mtx_assert(&hpriv->mtx, MA_OWNED);
599 
600 	if (item) {
601 		NGI_GET_M(item, m);
602 	} else {
603 		m = NULL;
604 	}
605 	/* Check if there's data */
606 	if (m != NULL) {
607 		/* Check if windowing is enabled */
608 		if (hpriv->conf.enableWindowing) {
609 			/* Is our transmit window full? */
610 			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
611 			    hpriv->recvAck) >= hpriv->xmitWin) {
612 				priv->stats.xmitDrops++;
613 				ERROUT(ENOBUFS);
614 			}
615 		}
616 
617 		/* Sanity check frame length */
618 		if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
619 			priv->stats.xmitTooBig++;
620 			ERROUT(EMSGSIZE);
621 		}
622 	} else {
623 		priv->stats.xmitLoneAcks++;
624 	}
625 
626 	/* Build GRE header */
627 	be32enc(gre, PPTP_INIT_VALUE);
628 	be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
629 	be16enc(&gre->cid, hpriv->conf.peerCid);
630 
631 	/* Include sequence number if packet contains any data */
632 	if (m != NULL) {
633 		gre->hasSeq = 1;
634 		if (hpriv->conf.enableWindowing) {
635 			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
636 			    = ng_pptpgre_time();
637 		}
638 		hpriv->xmitSeq++;
639 		be32enc(&gre->data[0], hpriv->xmitSeq);
640 	}
641 
642 	/* Include acknowledgement (and stop send ack timer) if needed */
643 	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
644 		gre->hasAck = 1;
645 		be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
646 		hpriv->xmitAck = hpriv->recvSeq;
647 		if (hpriv->conf.enableDelayedAck)
648 			ng_uncallout(&hpriv->sackTimer, hpriv->node);
649 	}
650 
651 	/* Prepend GRE header to outgoing frame */
652 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
653 	if (m == NULL) {
654 		MGETHDR(m, M_NOWAIT, MT_DATA);
655 		if (m == NULL) {
656 			priv->stats.memoryFailures++;
657 			ERROUT(ENOBUFS);
658 		}
659 		m->m_len = m->m_pkthdr.len = grelen;
660 		m->m_pkthdr.rcvif = NULL;
661 	} else {
662 		M_PREPEND(m, grelen, M_NOWAIT);
663 		if (m == NULL || (m->m_len < grelen
664 		    && (m = m_pullup(m, grelen)) == NULL)) {
665 			priv->stats.memoryFailures++;
666 			ERROUT(ENOBUFS);
667 		}
668 	}
669 	bcopy(gre, mtod(m, u_char *), grelen);
670 
671 	/* Update stats */
672 	priv->stats.xmitPackets++;
673 	priv->stats.xmitOctets += m->m_pkthdr.len;
674 
675 	/*
676 	 * XXX: we should reset timer only after an item has been sent
677 	 * successfully.
678 	 */
679 	if (hpriv->conf.enableWindowing &&
680 	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
681 		ng_pptpgre_start_recv_ack_timer(hpriv);
682 
683 	mtx_unlock(&hpriv->mtx);
684 
685 	/* Deliver packet */
686 	if (item) {
687 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
688 	} else {
689 		NG_SEND_DATA_ONLY(error, priv->lower, m);
690 	}
691 
692 	return (error);
693 
694 done:
695 	mtx_unlock(&hpriv->mtx);
696 	NG_FREE_M(m);
697 	if (item)
698 		NG_FREE_ITEM(item);
699 	return (error);
700 }
701 
702 static void
ng_pptpgre_ack(const hpriv_p hpriv)703 ng_pptpgre_ack(const hpriv_p hpriv)
704 {
705 	mtx_assert(&hpriv->mtx, MA_OWNED);
706 	if (!(callout_pending(&hpriv->sackTimer))) {
707 		/* If delayed ACK is disabled, send it now */
708 		if (!hpriv->conf.enableDelayedAck) {	/* ack now */
709 			ng_pptpgre_xmit(hpriv, NULL);
710 			/* ng_pptpgre_xmit() drops the mutex */
711 			return;
712 		}
713 		/* ack later */
714 		ng_pptpgre_start_send_ack_timer(hpriv);
715 		mtx_unlock(&hpriv->mtx);
716 		return;
717 	}
718 	mtx_unlock(&hpriv->mtx);
719 }
720 
721 /*
722  * Delivers packets from the queue "q" to upper layers. Frees delivered
723  * entries with the exception of one equal to "st" that is allocated
724  * on caller's stack and not on the heap.
725  */
726 static int
ng_pptpgre_sendq(const hpriv_p hpriv,roqh * q,const struct ng_pptpgre_roq * st)727 ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
728 {
729 	struct ng_pptpgre_roq *np;
730 	struct mbuf *m;
731 	int error = 0;
732 
733 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
734 	while (!SLIST_EMPTY(q)) {
735 		np = SLIST_FIRST(q);
736 		SLIST_REMOVE_HEAD(q, next);
737 		NGI_GET_M(np->item, m);
738 		NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
739 		if (np != st)
740 			free(np, M_NETGRAPH_PPTP);
741 	}
742 	return (error);
743 }
744 
745 /*
746  * Handle an incoming packet.  The packet includes the IP header.
747  */
748 static int
ng_pptpgre_rcvdata_lower(hook_p hook,item_p item)749 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
750 {
751 	hpriv_p hpriv;
752 	node_p node = NG_HOOK_NODE(hook);
753 	const priv_p priv = NG_NODE_PRIVATE(node);
754 	int iphlen, grelen, extralen;
755 	const struct greheader *gre;
756 	const struct ip *ip;
757 	int error = 0;
758 	struct mbuf *m;
759 
760 	roqh sendq = SLIST_HEAD_INITIALIZER(sendq);  /* send queue on stack */
761 	struct ng_pptpgre_roq *last = NULL;	/* last packet in the sendq */
762 	struct ng_pptpgre_roq *np, *prev;
763 	struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
764 	long diff;
765 	u_int32_t seq;
766 
767 	m = NGI_M(item);
768 	/* Update stats */
769 	priv->stats.recvPackets++;
770 	priv->stats.recvOctets += m->m_pkthdr.len;
771 
772 	/* Sanity check packet length */
773 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
774 		priv->stats.recvRunts++;
775 		ERROUT(EINVAL);
776 	}
777 
778 	/* Safely pull up the complete IP+GRE headers */
779 	if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
780 		if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
781 			priv->stats.memoryFailures++;
782 			_NGI_M(item) = NULL;
783 			ERROUT(ENOBUFS);
784 		}
785 		_NGI_M(item) = m;
786 	}
787 	ip = mtod(m, const struct ip *);
788 	iphlen = ip->ip_hl << 2;
789 	if (m->m_len < iphlen + sizeof(*gre)) {
790 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
791 			priv->stats.memoryFailures++;
792 			_NGI_M(item) = NULL;
793 			ERROUT(ENOBUFS);
794 		}
795 		_NGI_M(item) = m;
796 		ip = mtod(m, const struct ip *);
797 	}
798 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
799 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
800 	if (m->m_pkthdr.len < iphlen + grelen) {
801 		priv->stats.recvRunts++;
802 		ERROUT(EINVAL);
803 	}
804 	if (m->m_len < iphlen + grelen) {
805 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
806 			priv->stats.memoryFailures++;
807 			_NGI_M(item) = NULL;
808 			ERROUT(ENOBUFS);
809 		}
810 		_NGI_M(item) = m;
811 		ip = mtod(m, const struct ip *);
812 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
813 	}
814 
815 	/* Sanity check packet length and GRE header bits */
816 	extralen = m->m_pkthdr.len
817 	    - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
818 	if (extralen < 0) {
819 		priv->stats.recvBadGRE++;
820 		ERROUT(EINVAL);
821 	}
822 	if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
823 		priv->stats.recvBadGRE++;
824 		ERROUT(EINVAL);
825 	}
826 
827 	hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
828 	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
829 		priv->stats.recvBadCID++;
830 		ERROUT(EINVAL);
831 	}
832 	mtx_lock(&hpriv->mtx);
833 
834 	/* Look for peer ack */
835 	if (gre->hasAck) {
836 		const u_int32_t	ack = be32dec(&gre->data[gre->hasSeq]);
837 		const int index = ack - hpriv->recvAck - 1;
838 		long sample;
839 
840 		/* Sanity check ack value */
841 		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
842 			priv->stats.recvBadAcks++;
843 			goto badAck;		/* we never sent it! */
844 		}
845 		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
846 			goto badAck;		/* ack already timed out */
847 		hpriv->recvAck = ack;
848 
849 		/* Update adaptive timeout stuff */
850 		if (hpriv->conf.enableWindowing) {
851 			sample = ng_pptpgre_time() - hpriv->timeSent[index];
852 			diff = sample - hpriv->rtt;
853 			hpriv->rtt += PPTP_ACK_ALPHA(diff);
854 			if (diff < 0)
855 				diff = -diff;
856 			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
857 			    /* +2 to compensate low precision of int math */
858 			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
859 			if (hpriv->ato > PPTP_MAX_TIMEOUT)
860 				hpriv->ato = PPTP_MAX_TIMEOUT;
861 			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
862 				hpriv->ato = PPTP_MIN_TIMEOUT;
863 
864 			/* Shift packet transmit times in our transmit window */
865 			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
866 			    sizeof(*hpriv->timeSent)
867 			      * (PPTP_XMIT_WIN - (index + 1)));
868 
869 			/* If we sent an entire window, increase window size */
870 			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
871 			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
872 				hpriv->xmitWin++;
873 				hpriv->winAck = ack + hpriv->xmitWin;
874 			}
875 
876 			/* Stop/(re)start receive ACK timer as necessary */
877 			ng_uncallout(&hpriv->rackTimer, hpriv->node);
878 			if (hpriv->recvAck != hpriv->xmitSeq)
879 				ng_pptpgre_start_recv_ack_timer(hpriv);
880 		}
881 	}
882 badAck:
883 
884 	/* See if frame contains any data */
885 	if (!gre->hasSeq) {		/* no data to deliver */
886 		priv->stats.recvLoneAcks++;
887 		mtx_unlock(&hpriv->mtx);
888 		ERROUT(0);
889 	}
890 
891 	seq = be32dec(&gre->data[0]);
892 
893 	diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
894 	if (diff <= 0) {			/* late or duplicate packet */
895 		if (diff < 0 && reorder_max == 0)	/* reorder disabled */
896 			priv->stats.recvOutOfOrder++;	/* late */
897 		else
898 			priv->stats.recvDuplicates++;	/* duplicate */
899 		mtx_unlock(&hpriv->mtx);
900 		ERROUT(EINVAL);
901 	}
902 
903 	/* Trim mbuf down to internal payload */
904 	m_adj(m, iphlen + grelen);
905 	if (extralen > 0)
906 		m_adj(m, -extralen);
907 
908 #define INIT_SENDQ(t) do {				\
909 		t.item = item;				\
910 		t.seq = seq;				\
911 		SLIST_INSERT_HEAD(&sendq, &t, next);	\
912 		last = &t;				\
913 		hpriv->recvSeq = seq;			\
914 		goto deliver;				\
915 	} while(0)
916 
917 	if (diff == 1)
918 		/* the packet came in order, place it at the start of sendq */
919 		INIT_SENDQ(temp);
920 
921 	/* The packet came too early, try to enqueue it.
922 	 *
923 	 * Check for duplicate in the queue. After this loop, "prev" will be
924 	 * NULL if the packet should become new head of the queue,
925 	 * or else it should be inserted after the "prev".
926 	 */
927 	prev = SLIST_FIRST(&hpriv->roq);
928 	SLIST_FOREACH(np, &hpriv->roq, next) {
929 		diff = PPTP_SEQ_DIFF(np->seq, seq);
930 		if (diff == 0) { /* do not add duplicate, drop it */
931 			priv->stats.recvDuplicates++;
932 			mtx_unlock(&hpriv->mtx);
933 			ERROUT(EINVAL);
934 		}
935 		if (diff > 0) {		/* we found newer packet */
936 			if (np == prev)	/* that is the head of the queue */
937 			    prev = NULL; /* put current packet to the head */
938 			break;
939 		}
940 		prev = np;
941 	}
942 
943 	priv->stats.recvOutOfOrder++;	/* duplicate not found */
944 	if (hpriv->roq_len < reorder_max)
945 		goto enqueue;	/* reorder enabled and there is a room */
946 
947 	/*
948 	 * There is no room in the queue or reorder disabled.
949 	 *
950 	 * It the latter case, we may still have non-empty reorder queue
951 	 * if reorder was disabled in process of reordering.
952 	 * Then we correctly deliver the queue without growing it.
953 	 *
954 	 * In both cases, no malloc()'s until the queue is shortened.
955 	 */
956 	priv->stats.recvReorderOverflow++;
957 	if (prev == NULL) {	  /* new packet goes before the head      */
958 		INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
959 	}
960 #undef INIT_SENDQ
961 
962 	/*
963 	 * Current packet goes after the head of reorder queue.
964 	 * Move the head to sendq to make room for current packet.
965 	 */
966 	np = SLIST_FIRST(&hpriv->roq);
967 	if (prev == np)
968 		prev = NULL;
969 	SLIST_REMOVE_HEAD(&hpriv->roq, next);
970 	hpriv->roq_len--;	/* we are allowed to use malloc() now */
971 	SLIST_INSERT_HEAD(&sendq, np, next);
972 	last = np;
973 	hpriv->recvSeq = np->seq;
974 
975 enqueue:
976 	np = malloc(sizeof(*np), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
977 	if (np == NULL) {
978 		priv->stats.memoryFailures++;
979 		/*
980 		 * Emergency: we cannot save new data.
981 		 * Flush the queue delivering all queued packets preceeding
982 		 * current one despite of gaps.
983 		 */
984 		while (!SLIST_EMPTY(&hpriv->roq)) {
985 			np = SLIST_FIRST(&hpriv->roq);
986 			if (np->seq > seq)
987 				break;
988 			SLIST_REMOVE_HEAD(&hpriv->roq, next);
989 			hpriv->roq_len--;
990 			if (last == NULL)
991 				SLIST_INSERT_HEAD(&sendq, np, next);
992 			else
993 				SLIST_INSERT_AFTER(last, np, next);
994 			last = np;
995 		}
996 
997 		/*
998 		 * Pretend we got all packets till the current one
999 		 * and acknowledge it.
1000 		 */
1001 		hpriv->recvSeq = seq;
1002 		ng_pptpgre_ack(hpriv);	/* drops lock */
1003 		ng_pptpgre_sendq(hpriv, &sendq, &temp);
1004 		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1005 		ERROUT(ENOMEM);
1006 	}
1007 
1008 	/* Add current (early) packet to the reorder queue. */
1009 	np->item = item;
1010 	np->seq = seq;
1011 	if (prev == NULL)
1012 		SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1013 	else
1014 		SLIST_INSERT_AFTER(prev, np, next);
1015 	hpriv->roq_len++;
1016 
1017 deliver:
1018 	/* Look if we have some packets in sequence after sendq. */
1019 	while (!SLIST_EMPTY(&hpriv->roq)) {
1020 		np = SLIST_FIRST(&hpriv->roq);
1021 		if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1022 			break; /* the gap in the sequence */
1023 
1024 		/* "np" is in sequence, move it to the sendq. */
1025 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1026 		hpriv->roq_len--;
1027 		hpriv->recvSeq = np->seq;
1028 
1029 		if (last == NULL)
1030 			SLIST_INSERT_HEAD(&sendq, np, next);
1031 		else
1032 			SLIST_INSERT_AFTER(last, np, next);
1033 		last = np;
1034 	}
1035 
1036 	if (SLIST_EMPTY(&hpriv->roq)) {
1037 		if (callout_pending(&hpriv->reorderTimer))
1038 			ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1039 	} else {
1040 		if (!callout_pending(&hpriv->reorderTimer))
1041 			ng_pptpgre_start_reorder_timer(hpriv);
1042 	}
1043 
1044 	if (SLIST_EMPTY(&sendq)) {
1045 		/* Current packet has been queued, nothing to free/deliver. */
1046 		mtx_unlock(&hpriv->mtx);
1047 		return (error);
1048 	}
1049 
1050 	/* We need to acknowledge last packet; do it soon... */
1051 	ng_pptpgre_ack(hpriv);		/* drops lock */
1052 	ng_pptpgre_sendq(hpriv, &sendq, &temp);
1053 	return (error);
1054 
1055 done:
1056 	NG_FREE_ITEM(item);
1057 	return (error);
1058 }
1059 
1060 /*************************************************************************
1061 		    TIMER RELATED FUNCTIONS
1062 *************************************************************************/
1063 
1064 /*
1065  * Start a timer for the peer's acknowledging our oldest unacknowledged
1066  * sequence number.  If we get an ack for this sequence number before
1067  * the timer goes off, we cancel the timer.  Resets currently running
1068  * recv ack timer, if any.
1069  */
1070 static void
ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)1071 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1072 {
1073 	int remain, ticks;
1074 
1075 	/* Compute how long until oldest unack'd packet times out,
1076 	   and reset the timer to that time. */
1077 	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1078 	if (remain < 0)
1079 		remain = 0;
1080 
1081 	/* Be conservative: timeout can happen up to 1 tick early */
1082 	ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1083 	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1084 	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
1085 }
1086 
1087 /*
1088  * The peer has failed to acknowledge the oldest unacknowledged sequence
1089  * number within the time allotted.  Update our adaptive timeout parameters
1090  * and reset/restart the recv ack timer.
1091  */
1092 static void
ng_pptpgre_recv_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1093 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1094 {
1095 	const priv_p priv = NG_NODE_PRIVATE(node);
1096 	const hpriv_p hpriv = arg1;
1097 
1098 	/* Update adaptive timeout stuff */
1099 	priv->stats.recvAckTimeouts++;
1100 	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1101 	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1102 	if (hpriv->ato > PPTP_MAX_TIMEOUT)
1103 		hpriv->ato = PPTP_MAX_TIMEOUT;
1104 	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1105 		hpriv->ato = PPTP_MIN_TIMEOUT;
1106 
1107 	/* Reset ack and sliding window */
1108 	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
1109 	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
1110 	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
1111 }
1112 
1113 /*
1114  * Start the send ack timer. This assumes the timer is not
1115  * already running.
1116  */
1117 static void
ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)1118 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
1119 {
1120 	int ackTimeout, ticks;
1121 
1122 	/* Take 1/4 of the estimated round trip time */
1123 	ackTimeout = (hpriv->rtt >> 2);
1124 	if (ackTimeout < PPTP_MIN_ACK_DELAY)
1125 		ackTimeout = PPTP_MIN_ACK_DELAY;
1126 	else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1127 		ackTimeout = PPTP_MAX_ACK_DELAY;
1128 
1129 	/* Be conservative: timeout can happen up to 1 tick early */
1130 	ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1131 	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1132 	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
1133 }
1134 
1135 /*
1136  * We've waited as long as we're willing to wait before sending an
1137  * acknowledgement to the peer for received frames. We had hoped to
1138  * be able to piggy back our acknowledgement on an outgoing data frame,
1139  * but apparently there haven't been any since. So send the ack now.
1140  */
1141 static void
ng_pptpgre_send_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1142 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1143 {
1144 	const hpriv_p hpriv = arg1;
1145 
1146 	mtx_lock(&hpriv->mtx);
1147 	/* Send a frame with an ack but no payload */
1148   	ng_pptpgre_xmit(hpriv, NULL);
1149 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1150 }
1151 
1152 /*
1153  * Start a timer for the reorder queue. This assumes the timer is not
1154  * already running.
1155  */
1156 static void
ng_pptpgre_start_reorder_timer(hpriv_p hpriv)1157 ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1158 {
1159 	int ticks;
1160 
1161 	/* Be conservative: timeout can happen up to 1 tick early */
1162 	ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1163 	ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1164 		ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1165 }
1166 
1167 /*
1168  * The oldest packet spent too much time in the reorder queue.
1169  * Deliver it and next packets in sequence, if any.
1170  */
1171 static void
ng_pptpgre_reorder_timeout(node_p node,hook_p hook,void * arg1,int arg2)1172 ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1173 {
1174 	const priv_p priv = NG_NODE_PRIVATE(node);
1175 	const hpriv_p hpriv = arg1;
1176 	roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1177 	struct ng_pptpgre_roq *np, *last = NULL;
1178 
1179 	priv->stats.recvReorderTimeouts++;
1180 	mtx_lock(&hpriv->mtx);
1181 	if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1182 		mtx_unlock(&hpriv->mtx);
1183 		return;
1184 	}
1185 
1186 	last = np = SLIST_FIRST(&hpriv->roq);
1187 	hpriv->roq_len--;
1188 	SLIST_REMOVE_HEAD(&hpriv->roq, next);
1189 	SLIST_INSERT_HEAD(&sendq, np, next);
1190 
1191 	/* Look if we have more packets in sequence */
1192 	while (!SLIST_EMPTY(&hpriv->roq)) {
1193 		np = SLIST_FIRST(&hpriv->roq);
1194 		if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1195 			break; /* the gap in the sequence */
1196 
1197 		/* Next packet is in sequence, move it to the sendq. */
1198 		hpriv->roq_len--;
1199 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1200 		SLIST_INSERT_AFTER(last, np, next);
1201 		last = np;
1202 	}
1203 
1204 	hpriv->recvSeq = last->seq;
1205 	if (!SLIST_EMPTY(&hpriv->roq))
1206 		ng_pptpgre_start_reorder_timer(hpriv);
1207 
1208 	/* We need to acknowledge last packet; do it soon... */
1209 	ng_pptpgre_ack(hpriv);		/* drops lock */
1210 	ng_pptpgre_sendq(hpriv, &sendq, NULL);
1211 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1212 }
1213 
1214 /*************************************************************************
1215 		    MISC FUNCTIONS
1216 *************************************************************************/
1217 
1218 /*
1219  * Find the hook with a given session ID.
1220  */
1221 static hpriv_p
ng_pptpgre_find_session(priv_p privp,u_int16_t cid)1222 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1223 {
1224 	uint16_t	hash = SESSHASH(cid);
1225 	hpriv_p	hpriv = NULL;
1226 
1227 	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1228 		if (hpriv->conf.cid == cid)
1229 			break;
1230 	}
1231 
1232 	return (hpriv);
1233 }
1234 
1235 /*
1236  * Reset state (must be called with lock held or from writer)
1237  */
1238 static void
ng_pptpgre_reset(hpriv_p hpriv)1239 ng_pptpgre_reset(hpriv_p hpriv)
1240 {
1241 	struct ng_pptpgre_roq *np;
1242 
1243 	/* Reset adaptive timeout state */
1244 	hpriv->ato = PPTP_MAX_TIMEOUT;
1245 	hpriv->rtt = PPTP_TIME_SCALE / 10;
1246 	if (hpriv->conf.peerPpd > 1)	/* ppd = 0 treat as = 1 */
1247 		hpriv->rtt *= hpriv->conf.peerPpd;
1248 	hpriv->dev = 0;
1249 	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1250 	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
1251 		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
1252 	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1253 		hpriv->xmitWin = PPTP_XMIT_WIN;
1254 	hpriv->winAck = hpriv->xmitWin;
1255 
1256 	/* Reset sequence numbers */
1257 	hpriv->recvSeq = ~0;
1258 	hpriv->recvAck = ~0;
1259 	hpriv->xmitSeq = ~0;
1260 	hpriv->xmitAck = ~0;
1261 
1262 	/* Stop timers */
1263 	ng_uncallout(&hpriv->sackTimer, hpriv->node);
1264 	ng_uncallout(&hpriv->rackTimer, hpriv->node);
1265 	ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1266 
1267 	/* Clear reorder queue */
1268 	while (!SLIST_EMPTY(&hpriv->roq)) {
1269 		np = SLIST_FIRST(&hpriv->roq);
1270 		SLIST_REMOVE_HEAD(&hpriv->roq, next);
1271 		NG_FREE_ITEM(np->item);
1272 		free(np, M_NETGRAPH_PPTP);
1273 	}
1274 	hpriv->roq_len = 0;
1275 }
1276 
1277 /*
1278  * Return the current time scaled & translated to our internally used format.
1279  */
1280 static pptptime_t
ng_pptpgre_time(void)1281 ng_pptpgre_time(void)
1282 {
1283 	struct timeval tv;
1284 	pptptime_t t;
1285 
1286 	microuptime(&tv);
1287 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1288 	t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1289 	return(t);
1290 }
1291