xref: /freebsd/sys/netgraph/ng_pptpgre.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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  *
40  * $FreeBSD$
41  * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
42  */
43 
44 /*
45  * PPTP/GRE netgraph node type.
46  *
47  * This node type does the GRE encapsulation as specified for the PPTP
48  * protocol (RFC 2637, section 4).  This includes sequencing and
49  * retransmission of frames, but not the actual packet delivery nor
50  * any of the TCP control stream protocol.
51  *
52  * The "upper" hook of this node is suitable for attaching to a "ppp"
53  * node link hook.  The "lower" hook of this node is suitable for attaching
54  * to a "ksocket" node on hook "inet/raw/gre".
55  */
56 
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/kernel.h>
60 #include <sys/time.h>
61 #include <sys/lock.h>
62 #include <sys/malloc.h>
63 #include <sys/mbuf.h>
64 #include <sys/mutex.h>
65 #include <sys/errno.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 /* GRE packet format, as used by PPTP */
77 struct greheader {
78 #if BYTE_ORDER == LITTLE_ENDIAN
79 	u_char		recursion:3;		/* recursion control */
80 	u_char		ssr:1;			/* strict source route */
81 	u_char		hasSeq:1;		/* sequence number present */
82 	u_char		hasKey:1;		/* key present */
83 	u_char		hasRoute:1;		/* routing present */
84 	u_char		hasSum:1;		/* checksum present */
85 	u_char		vers:3;			/* version */
86 	u_char		flags:4;		/* flags */
87 	u_char		hasAck:1;		/* acknowlege number present */
88 #elif BYTE_ORDER == BIG_ENDIAN
89 	u_char		hasSum:1;		/* checksum present */
90 	u_char		hasRoute:1;		/* routing present */
91 	u_char		hasKey:1;		/* key present */
92 	u_char		hasSeq:1;		/* sequence number present */
93 	u_char		ssr:1;			/* strict source route */
94 	u_char		recursion:3;		/* recursion control */
95 	u_char		hasAck:1;		/* acknowlege number present */
96 	u_char		flags:4;		/* flags */
97 	u_char		vers:3;			/* version */
98 #else
99 #error BYTE_ORDER is not defined properly
100 #endif
101 	u_int16_t	proto;			/* protocol (ethertype) */
102 	u_int16_t	length;			/* payload length */
103 	u_int16_t	cid;			/* call id */
104 	u_int32_t	data[0];		/* opt. seq, ack, then data */
105 };
106 
107 /* The PPTP protocol ID used in the GRE 'proto' field */
108 #define PPTP_GRE_PROTO		0x880b
109 
110 /* Bits that must be set a certain way in all PPTP/GRE packets */
111 #define PPTP_INIT_VALUE		((0x2001 << 16) | PPTP_GRE_PROTO)
112 #define PPTP_INIT_MASK		0xef7fffff
113 
114 /* Min and max packet length */
115 #define PPTP_MAX_PAYLOAD	(0xffff - sizeof(struct greheader) - 8)
116 
117 /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
118 #define PPTP_TIME_SCALE		1024			/* milliseconds */
119 typedef u_int64_t		pptptime_t;
120 
121 /* Acknowledgment timeout parameters and functions */
122 #define PPTP_XMIT_WIN		16			/* max xmit window */
123 #define PPTP_MIN_TIMEOUT	(PPTP_TIME_SCALE / 83)	/* 12 milliseconds */
124 #define PPTP_MAX_TIMEOUT	(3 * PPTP_TIME_SCALE)	/* 3 seconds */
125 
126 /* When we recieve a packet, we wait to see if there's an outgoing packet
127    we can piggy-back the ACK off of. These parameters determine the mimimum
128    and maxmimum length of time we're willing to wait in order to do that.
129    These have no effect unless "enableDelayedAck" is turned on. */
130 #define PPTP_MIN_ACK_DELAY	(PPTP_TIME_SCALE / 500)	/* 2 milliseconds */
131 #define PPTP_MAX_ACK_DELAY	(PPTP_TIME_SCALE / 2)	/* 500 milliseconds */
132 
133 /* See RFC 2637 section 4.4 */
134 #define PPTP_ACK_ALPHA(x)	(((x) + 4) >> 3)	/* alpha = 0.125 */
135 #define PPTP_ACK_BETA(x)	(((x) + 2) >> 2)	/* beta = 0.25 */
136 #define PPTP_ACK_CHI(x) 	((x) << 2)	/* chi = 4 */
137 #define PPTP_ACK_DELTA(x) 	((x) << 1)	/* delta = 2 */
138 
139 #define PPTP_SEQ_DIFF(x,y)	((int32_t)(x) - (int32_t)(y))
140 
141 #define SESSHASHSIZE		0x0020
142 #define SESSHASH(x)		(((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
143 
144 /* We keep packet retransmit and acknowlegement state in this struct */
145 struct ng_pptpgre_sess {
146 	node_p			node;		/* this node pointer */
147 	hook_p			hook;		/* hook to upper layers */
148 	struct ng_pptpgre_conf	conf;		/* configuration info */
149 	struct mtx		mtx;		/* session mutex */
150 	u_int32_t		recvSeq;	/* last seq # we rcv'd */
151 	u_int32_t		xmitSeq;	/* last seq # we sent */
152 	u_int32_t		recvAck;	/* last seq # peer ack'd */
153 	u_int32_t		xmitAck;	/* last seq # we ack'd */
154 	int32_t			ato;		/* adaptive time-out value */
155 	int32_t			rtt;		/* round trip time estimate */
156 	int32_t			dev;		/* deviation estimate */
157 	u_int16_t		xmitWin;	/* size of xmit window */
158 	struct callout		sackTimer;	/* send ack timer */
159 	struct callout		rackTimer;	/* recv ack timer */
160 	u_int32_t		winAck;		/* seq when xmitWin will grow */
161 	pptptime_t		timeSent[PPTP_XMIT_WIN];
162 	LIST_ENTRY(ng_pptpgre_sess) sessions;
163 };
164 typedef struct ng_pptpgre_sess *hpriv_p;
165 
166 /* Node private data */
167 struct ng_pptpgre_private {
168 	hook_p			upper;		/* hook to upper layers */
169 	hook_p			lower;		/* hook to lower layers */
170 	struct ng_pptpgre_sess	uppersess;	/* default session for compat */
171 	LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
172 	struct ng_pptpgre_stats	stats;		/* node statistics */
173 };
174 typedef struct ng_pptpgre_private *priv_p;
175 
176 /* Netgraph node methods */
177 static ng_constructor_t	ng_pptpgre_constructor;
178 static ng_rcvmsg_t	ng_pptpgre_rcvmsg;
179 static ng_shutdown_t	ng_pptpgre_shutdown;
180 static ng_newhook_t	ng_pptpgre_newhook;
181 static ng_rcvdata_t	ng_pptpgre_rcvdata;
182 static ng_rcvdata_t	ng_pptpgre_rcvdata_lower;
183 static ng_disconnect_t	ng_pptpgre_disconnect;
184 
185 /* Helper functions */
186 static int	ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
187 static void	ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
188 static void	ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
189 static void	ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
190 		    void *arg1, int arg2);
191 static void	ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
192 		    void *arg1, int arg2);
193 static hpriv_p	ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
194 static void	ng_pptpgre_reset(hpriv_p hpriv);
195 static pptptime_t ng_pptpgre_time(void);
196 
197 /* Parse type for struct ng_pptpgre_conf */
198 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
199 	= NG_PPTPGRE_CONF_TYPE_INFO;
200 static const struct ng_parse_type ng_pptpgre_conf_type = {
201 	&ng_parse_struct_type,
202 	&ng_pptpgre_conf_type_fields,
203 };
204 
205 /* Parse type for struct ng_pptpgre_stats */
206 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
207 	= NG_PPTPGRE_STATS_TYPE_INFO;
208 static const struct ng_parse_type ng_pptp_stats_type = {
209 	&ng_parse_struct_type,
210 	&ng_pptpgre_stats_type_fields
211 };
212 
213 /* List of commands and how to convert arguments to/from ASCII */
214 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
215 	{
216 	  NGM_PPTPGRE_COOKIE,
217 	  NGM_PPTPGRE_SET_CONFIG,
218 	  "setconfig",
219 	  &ng_pptpgre_conf_type,
220 	  NULL
221 	},
222 	{
223 	  NGM_PPTPGRE_COOKIE,
224 	  NGM_PPTPGRE_GET_CONFIG,
225 	  "getconfig",
226 	  &ng_parse_hint16_type,
227 	  &ng_pptpgre_conf_type
228 	},
229 	{
230 	  NGM_PPTPGRE_COOKIE,
231 	  NGM_PPTPGRE_GET_STATS,
232 	  "getstats",
233 	  NULL,
234 	  &ng_pptp_stats_type
235 	},
236 	{
237 	  NGM_PPTPGRE_COOKIE,
238 	  NGM_PPTPGRE_CLR_STATS,
239 	  "clrstats",
240 	  NULL,
241 	  NULL
242 	},
243 	{
244 	  NGM_PPTPGRE_COOKIE,
245 	  NGM_PPTPGRE_GETCLR_STATS,
246 	  "getclrstats",
247 	  NULL,
248 	  &ng_pptp_stats_type
249 	},
250 	{ 0 }
251 };
252 
253 /* Node type descriptor */
254 static struct ng_type ng_pptpgre_typestruct = {
255 	.version =	NG_ABI_VERSION,
256 	.name =		NG_PPTPGRE_NODE_TYPE,
257 	.constructor =	ng_pptpgre_constructor,
258 	.rcvmsg =	ng_pptpgre_rcvmsg,
259 	.shutdown =	ng_pptpgre_shutdown,
260 	.newhook =	ng_pptpgre_newhook,
261 	.rcvdata =	ng_pptpgre_rcvdata,
262 	.disconnect =	ng_pptpgre_disconnect,
263 	.cmdlist =	ng_pptpgre_cmdlist,
264 };
265 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
266 
267 #define ERROUT(x)	do { error = (x); goto done; } while (0)
268 
269 /************************************************************************
270 			NETGRAPH NODE STUFF
271  ************************************************************************/
272 
273 /*
274  * Node type constructor
275  */
276 static int
277 ng_pptpgre_constructor(node_p node)
278 {
279 	priv_p priv;
280 	int i;
281 
282 	/* Allocate private structure */
283 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
284 	if (priv == NULL)
285 		return (ENOMEM);
286 
287 	NG_NODE_SET_PRIVATE(node, priv);
288 
289 	/* Initialize state */
290 	mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
291 	ng_callout_init(&priv->uppersess.sackTimer);
292 	ng_callout_init(&priv->uppersess.rackTimer);
293 	priv->uppersess.node = node;
294 
295 	for (i = 0; i < SESSHASHSIZE; i++)
296 	    LIST_INIT(&priv->sesshash[i]);
297 
298 	LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
299 
300 	/* Done */
301 	return (0);
302 }
303 
304 /*
305  * Give our OK for a hook to be added.
306  */
307 static int
308 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
309 {
310 	const priv_p priv = NG_NODE_PRIVATE(node);
311 
312 	/* Check hook name */
313 	if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
314 		priv->upper = hook;
315 		priv->uppersess.hook = hook;
316 		NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
317 	} else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
318 		priv->lower = hook;
319 		NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
320 	} else {
321 		static const char hexdig[16] = "0123456789abcdef";
322 		const char *hex;
323 		hpriv_p hpriv;
324 		int i, j;
325 		uint16_t cid, hash;
326 
327 		/* Parse hook name to get session ID */
328 		if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
329 		    sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
330 			return (EINVAL);
331 		hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
332 		for (cid = i = 0; i < 4; i++) {
333 			for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
334 			if (j == 16)
335 				return (EINVAL);
336 			cid = (cid << 4) | j;
337 		}
338 		if (hex[i] != '\0')
339 			return (EINVAL);
340 
341 		hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
342 		if (hpriv == NULL)
343 			return (ENOMEM);
344 
345 		/* Initialize state */
346 		mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
347 		ng_callout_init(&hpriv->sackTimer);
348 		ng_callout_init(&hpriv->rackTimer);
349 		hpriv->conf.cid = cid;
350 		hpriv->node = node;
351 		hpriv->hook = hook;
352 		NG_HOOK_SET_PRIVATE(hook, hpriv);
353 
354 		hash = SESSHASH(cid);
355 		LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
356 	}
357 
358 	return (0);
359 }
360 
361 /*
362  * Receive a control message.
363  */
364 static int
365 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
366 {
367 	const priv_p priv = NG_NODE_PRIVATE(node);
368 	struct ng_mesg *resp = NULL;
369 	int error = 0;
370 	struct ng_mesg *msg;
371 
372 	NGI_GET_MSG(item, msg);
373 	switch (msg->header.typecookie) {
374 	case NGM_PPTPGRE_COOKIE:
375 		switch (msg->header.cmd) {
376 		case NGM_PPTPGRE_SET_CONFIG:
377 		    {
378 			struct ng_pptpgre_conf *const newConf =
379 				(struct ng_pptpgre_conf *) msg->data;
380 			hpriv_p hpriv;
381 			uint16_t hash;
382 
383 			/* Check for invalid or illegal config */
384 			if (msg->header.arglen != sizeof(*newConf))
385 				ERROUT(EINVAL);
386 			/* Try to find session by cid. */
387 			hpriv = ng_pptpgre_find_session(priv, newConf->cid);
388 			/* If not present - use upper. */
389 			if (hpriv == NULL) {
390 				hpriv = &priv->uppersess;
391 				LIST_REMOVE(hpriv, sessions);
392 				hash = SESSHASH(newConf->cid);
393 				LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
394 				    sessions);
395 			}
396 			ng_pptpgre_reset(hpriv);	/* reset on configure */
397 			hpriv->conf = *newConf;
398 			break;
399 		    }
400 		case NGM_PPTPGRE_GET_CONFIG:
401 		    {
402 			hpriv_p hpriv;
403 
404 			if (msg->header.arglen == 2) {
405 				/* Try to find session by cid. */
406 	    			hpriv = ng_pptpgre_find_session(priv,
407 				    *((uint16_t *)msg->data));
408 				if (hpriv == NULL)
409 					ERROUT(EINVAL);
410 			} else if (msg->header.arglen == 0) {
411 				/* Use upper. */
412 				hpriv = &priv->uppersess;
413 			} else
414 				ERROUT(EINVAL);
415 			NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
416 			if (resp == NULL)
417 				ERROUT(ENOMEM);
418 			bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
419 			break;
420 		    }
421 		case NGM_PPTPGRE_GET_STATS:
422 		case NGM_PPTPGRE_CLR_STATS:
423 		case NGM_PPTPGRE_GETCLR_STATS:
424 		    {
425 			if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
426 				NG_MKRESPONSE(resp, msg,
427 				    sizeof(priv->stats), M_NOWAIT);
428 				if (resp == NULL)
429 					ERROUT(ENOMEM);
430 				bcopy(&priv->stats,
431 				    resp->data, sizeof(priv->stats));
432 			}
433 			if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
434 				bzero(&priv->stats, sizeof(priv->stats));
435 			break;
436 		    }
437 		default:
438 			error = EINVAL;
439 			break;
440 		}
441 		break;
442 	default:
443 		error = EINVAL;
444 		break;
445 	}
446 done:
447 	NG_RESPOND_MSG(error, node, item, resp);
448 	NG_FREE_MSG(msg);
449 	return (error);
450 }
451 
452 /*
453  * Receive incoming data on a hook.
454  */
455 static int
456 ng_pptpgre_rcvdata(hook_p hook, item_p item)
457 {
458 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
459 	int rval;
460 
461 	/* If not configured, reject */
462 	if (!hpriv->conf.enabled) {
463 		NG_FREE_ITEM(item);
464 		return (ENXIO);
465 	}
466 
467 	mtx_lock(&hpriv->mtx);
468 
469 	rval = ng_pptpgre_xmit(hpriv, item);
470 
471 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
472 
473 	return (rval);
474 }
475 
476 /*
477  * Hook disconnection
478  */
479 static int
480 ng_pptpgre_disconnect(hook_p hook)
481 {
482 	const node_p node = NG_HOOK_NODE(hook);
483 	const priv_p priv = NG_NODE_PRIVATE(node);
484 	const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
485 
486 	/* Zero out hook pointer */
487 	if (hook == priv->upper) {
488 		priv->upper = NULL;
489 		priv->uppersess.hook = NULL;
490 	} else if (hook == priv->lower) {
491 		priv->lower = NULL;
492 	} else {
493 		/* Reset node (stops timers) */
494 		ng_pptpgre_reset(hpriv);
495 
496 		LIST_REMOVE(hpriv, sessions);
497 		mtx_destroy(&hpriv->mtx);
498 		free(hpriv, M_NETGRAPH);
499 	}
500 
501 	/* Go away if no longer connected to anything */
502 	if ((NG_NODE_NUMHOOKS(node) == 0)
503 	&& (NG_NODE_IS_VALID(node)))
504 		ng_rmnode_self(node);
505 	return (0);
506 }
507 
508 /*
509  * Destroy node
510  */
511 static int
512 ng_pptpgre_shutdown(node_p node)
513 {
514 	const priv_p priv = NG_NODE_PRIVATE(node);
515 
516 	/* Reset node (stops timers) */
517 	ng_pptpgre_reset(&priv->uppersess);
518 
519 	LIST_REMOVE(&priv->uppersess, sessions);
520 	mtx_destroy(&priv->uppersess.mtx);
521 
522 	free(priv, M_NETGRAPH);
523 
524 	/* Decrement ref count */
525 	NG_NODE_UNREF(node);
526 	return (0);
527 }
528 
529 /*************************************************************************
530 		    TRANSMIT AND RECEIVE FUNCTIONS
531 *************************************************************************/
532 
533 /*
534  * Transmit an outgoing frame, or just an ack if m is NULL.
535  */
536 static int
537 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
538 {
539 	const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
540 	u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
541 	struct greheader *const gre = (struct greheader *)buf;
542 	int grelen, error;
543 	struct mbuf *m;
544 
545 	mtx_assert(&hpriv->mtx, MA_OWNED);
546 
547 	if (item) {
548 		NGI_GET_M(item, m);
549 	} else {
550 		m = NULL;
551 	}
552 	/* Check if there's data */
553 	if (m != NULL) {
554 
555 		/* Check if windowing is enabled */
556 		if (hpriv->conf.enableWindowing) {
557 			/* Is our transmit window full? */
558 			if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
559 			    hpriv->recvAck) >= hpriv->xmitWin) {
560 				priv->stats.xmitDrops++;
561 				ERROUT(ENOBUFS);
562 			}
563 		}
564 
565 		/* Sanity check frame length */
566 		if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
567 			priv->stats.xmitTooBig++;
568 			ERROUT(EMSGSIZE);
569 		}
570 	} else {
571 		priv->stats.xmitLoneAcks++;
572 	}
573 
574 	/* Build GRE header */
575 	((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE);
576 	gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0;
577 	gre->cid = htons(hpriv->conf.peerCid);
578 
579 	/* Include sequence number if packet contains any data */
580 	if (m != NULL) {
581 		gre->hasSeq = 1;
582 		if (hpriv->conf.enableWindowing) {
583 			hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
584 			    = ng_pptpgre_time();
585 		}
586 		hpriv->xmitSeq++;
587 		gre->data[0] = htonl(hpriv->xmitSeq);
588 	}
589 
590 	/* Include acknowledgement (and stop send ack timer) if needed */
591 	if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
592 		gre->hasAck = 1;
593 		gre->data[gre->hasSeq] = htonl(hpriv->recvSeq);
594 		hpriv->xmitAck = hpriv->recvSeq;
595 		if (hpriv->conf.enableDelayedAck)
596 			ng_uncallout(&hpriv->sackTimer, hpriv->node);
597 	}
598 
599 	/* Prepend GRE header to outgoing frame */
600 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
601 	if (m == NULL) {
602 		MGETHDR(m, M_DONTWAIT, MT_DATA);
603 		if (m == NULL) {
604 			priv->stats.memoryFailures++;
605 			ERROUT(ENOBUFS);
606 		}
607 		m->m_len = m->m_pkthdr.len = grelen;
608 		m->m_pkthdr.rcvif = NULL;
609 	} else {
610 		M_PREPEND(m, grelen, M_DONTWAIT);
611 		if (m == NULL || (m->m_len < grelen
612 		    && (m = m_pullup(m, grelen)) == NULL)) {
613 			priv->stats.memoryFailures++;
614 			ERROUT(ENOBUFS);
615 		}
616 	}
617 	bcopy(gre, mtod(m, u_char *), grelen);
618 
619 	/* Update stats */
620 	priv->stats.xmitPackets++;
621 	priv->stats.xmitOctets += m->m_pkthdr.len;
622 
623 	/*
624 	 * XXX: we should reset timer only after an item has been sent
625 	 * successfully.
626 	 */
627 	if (hpriv->conf.enableWindowing &&
628 	    gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
629 		ng_pptpgre_start_recv_ack_timer(hpriv);
630 
631 	mtx_unlock(&hpriv->mtx);
632 
633 	/* Deliver packet */
634 	if (item) {
635 		NG_FWD_NEW_DATA(error, item, priv->lower, m);
636 	} else {
637 		NG_SEND_DATA_ONLY(error, priv->lower, m);
638 	}
639 
640 	return (error);
641 
642 done:
643 	mtx_unlock(&hpriv->mtx);
644 	NG_FREE_M(m);
645 	if (item)
646 		NG_FREE_ITEM(item);
647 	return (error);
648 }
649 
650 /*
651  * Handle an incoming packet.  The packet includes the IP header.
652  */
653 static int
654 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
655 {
656 	hpriv_p hpriv;
657 	node_p node = NG_HOOK_NODE(hook);
658 	const priv_p priv = NG_NODE_PRIVATE(node);
659 	int iphlen, grelen, extralen;
660 	const struct greheader *gre;
661 	const struct ip *ip;
662 	int error = 0;
663 	struct mbuf *m;
664 
665 	NGI_GET_M(item, m);
666 	/* Update stats */
667 	priv->stats.recvPackets++;
668 	priv->stats.recvOctets += m->m_pkthdr.len;
669 
670 	/* Sanity check packet length */
671 	if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
672 		priv->stats.recvRunts++;
673 		ERROUT(EINVAL);
674 	}
675 
676 	/* Safely pull up the complete IP+GRE headers */
677 	if (m->m_len < sizeof(*ip) + sizeof(*gre)
678 	    && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
679 		priv->stats.memoryFailures++;
680 		ERROUT(ENOBUFS);
681 	}
682 	ip = mtod(m, const struct ip *);
683 	iphlen = ip->ip_hl << 2;
684 	if (m->m_len < iphlen + sizeof(*gre)) {
685 		if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
686 			priv->stats.memoryFailures++;
687 			ERROUT(ENOBUFS);
688 		}
689 		ip = mtod(m, const struct ip *);
690 	}
691 	gre = (const struct greheader *)((const u_char *)ip + iphlen);
692 	grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
693 	if (m->m_pkthdr.len < iphlen + grelen) {
694 		priv->stats.recvRunts++;
695 		ERROUT(EINVAL);
696 	}
697 	if (m->m_len < iphlen + grelen) {
698 		if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
699 			priv->stats.memoryFailures++;
700 			ERROUT(ENOBUFS);
701 		}
702 		ip = mtod(m, const struct ip *);
703 		gre = (const struct greheader *)((const u_char *)ip + iphlen);
704 	}
705 
706 	/* Sanity check packet length and GRE header bits */
707 	extralen = m->m_pkthdr.len
708 	    - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length));
709 	if (extralen < 0) {
710 		priv->stats.recvBadGRE++;
711 		ERROUT(EINVAL);
712 	}
713 	if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK)
714 	    != PPTP_INIT_VALUE) {
715 		priv->stats.recvBadGRE++;
716 		ERROUT(EINVAL);
717 	}
718 
719 	hpriv = ng_pptpgre_find_session(priv, ntohs(gre->cid));
720 	if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
721 		priv->stats.recvBadCID++;
722 		ERROUT(EINVAL);
723 	}
724 	mtx_lock(&hpriv->mtx);
725 
726 	/* Look for peer ack */
727 	if (gre->hasAck) {
728 		const u_int32_t	ack = ntohl(gre->data[gre->hasSeq]);
729 		const int index = ack - hpriv->recvAck - 1;
730 		long sample;
731 		long diff;
732 
733 		/* Sanity check ack value */
734 		if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
735 			priv->stats.recvBadAcks++;
736 			goto badAck;		/* we never sent it! */
737 		}
738 		if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
739 			goto badAck;		/* ack already timed out */
740 		hpriv->recvAck = ack;
741 
742 		/* Update adaptive timeout stuff */
743 		if (hpriv->conf.enableWindowing) {
744 			sample = ng_pptpgre_time() - hpriv->timeSent[index];
745 			diff = sample - hpriv->rtt;
746 			hpriv->rtt += PPTP_ACK_ALPHA(diff);
747 			if (diff < 0)
748 				diff = -diff;
749 			hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
750 			    /* +2 to compensate low precision of int math */
751 			hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
752 			if (hpriv->ato > PPTP_MAX_TIMEOUT)
753 				hpriv->ato = PPTP_MAX_TIMEOUT;
754 			else if (hpriv->ato < PPTP_MIN_TIMEOUT)
755 				hpriv->ato = PPTP_MIN_TIMEOUT;
756 
757 			/* Shift packet transmit times in our transmit window */
758 			bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
759 			    sizeof(*hpriv->timeSent)
760 			      * (PPTP_XMIT_WIN - (index + 1)));
761 
762 			/* If we sent an entire window, increase window size */
763 			if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
764 			    && hpriv->xmitWin < PPTP_XMIT_WIN) {
765 				hpriv->xmitWin++;
766 				hpriv->winAck = ack + hpriv->xmitWin;
767 			}
768 
769 			/* Stop/(re)start receive ACK timer as necessary */
770 			ng_uncallout(&hpriv->rackTimer, hpriv->node);
771 			if (hpriv->recvAck != hpriv->xmitSeq)
772 				ng_pptpgre_start_recv_ack_timer(hpriv);
773 		}
774 	}
775 badAck:
776 
777 	/* See if frame contains any data */
778 	if (gre->hasSeq) {
779 		const u_int32_t seq = ntohl(gre->data[0]);
780 
781 		/* Sanity check sequence number */
782 		if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) {
783 			if (seq == hpriv->recvSeq)
784 				priv->stats.recvDuplicates++;
785 			else
786 				priv->stats.recvOutOfOrder++;
787 			mtx_unlock(&hpriv->mtx);
788 			ERROUT(EINVAL);
789 		}
790 		hpriv->recvSeq = seq;
791 
792 		/* We need to acknowledge this packet; do it soon... */
793 		if (!(callout_pending(&hpriv->sackTimer))) {
794 			/* If delayed ACK is disabled, send it now */
795 			if (!hpriv->conf.enableDelayedAck) {	/* ack now */
796 				ng_pptpgre_xmit(hpriv, NULL);
797 				/* ng_pptpgre_xmit() drops the mutex */
798 			} else {				/* ack later */
799 				ng_pptpgre_start_send_ack_timer(hpriv);
800 				mtx_unlock(&hpriv->mtx);
801 			}
802 		} else
803 			mtx_unlock(&hpriv->mtx);
804 
805 		/* Trim mbuf down to internal payload */
806 		m_adj(m, iphlen + grelen);
807 		if (extralen > 0)
808 			m_adj(m, -extralen);
809 
810 		mtx_assert(&hpriv->mtx, MA_NOTOWNED);
811 
812 		/* Deliver frame to upper layers */
813 		NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
814 	} else {
815 		priv->stats.recvLoneAcks++;
816 		mtx_unlock(&hpriv->mtx);
817 		NG_FREE_ITEM(item);
818 		NG_FREE_M(m);		/* no data to deliver */
819 	}
820 
821 	return (error);
822 
823 done:
824 	NG_FREE_ITEM(item);
825 	NG_FREE_M(m);
826 	return (error);
827 }
828 
829 /*************************************************************************
830 		    TIMER RELATED FUNCTIONS
831 *************************************************************************/
832 
833 /*
834  * Start a timer for the peer's acknowledging our oldest unacknowledged
835  * sequence number.  If we get an ack for this sequence number before
836  * the timer goes off, we cancel the timer.  Resets currently running
837  * recv ack timer, if any.
838  */
839 static void
840 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
841 {
842 	int remain, ticks;
843 
844 	/* Compute how long until oldest unack'd packet times out,
845 	   and reset the timer to that time. */
846 	remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
847 	if (remain < 0)
848 		remain = 0;
849 
850 	/* Be conservative: timeout can happen up to 1 tick early */
851 	ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
852 	ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
853 	    ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
854 }
855 
856 /*
857  * The peer has failed to acknowledge the oldest unacknowledged sequence
858  * number within the time allotted.  Update our adaptive timeout parameters
859  * and reset/restart the recv ack timer.
860  */
861 static void
862 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
863 {
864 	const priv_p priv = NG_NODE_PRIVATE(node);
865 	const hpriv_p hpriv = arg1;
866 
867 	/* Update adaptive timeout stuff */
868 	priv->stats.recvAckTimeouts++;
869 	hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
870 	hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
871 	if (hpriv->ato > PPTP_MAX_TIMEOUT)
872 		hpriv->ato = PPTP_MAX_TIMEOUT;
873 	else if (hpriv->ato < PPTP_MIN_TIMEOUT)
874 		hpriv->ato = PPTP_MIN_TIMEOUT;
875 
876 	/* Reset ack and sliding window */
877 	hpriv->recvAck = hpriv->xmitSeq;		/* pretend we got the ack */
878 	hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;	/* shrink transmit window */
879 	hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;	/* reset win expand time */
880 }
881 
882 /*
883  * Start the send ack timer. This assumes the timer is not
884  * already running.
885  */
886 static void
887 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
888 {
889 	int ackTimeout, ticks;
890 
891 	/* Take 1/4 of the estimated round trip time */
892 	ackTimeout = (hpriv->rtt >> 2);
893 	if (ackTimeout < PPTP_MIN_ACK_DELAY)
894 		ackTimeout = PPTP_MIN_ACK_DELAY;
895 	else if (ackTimeout > PPTP_MAX_ACK_DELAY)
896 		ackTimeout = PPTP_MAX_ACK_DELAY;
897 
898 	/* Be conservative: timeout can happen up to 1 tick early */
899 	ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
900 	ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
901 	    ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
902 }
903 
904 /*
905  * We've waited as long as we're willing to wait before sending an
906  * acknowledgement to the peer for received frames. We had hoped to
907  * be able to piggy back our acknowledgement on an outgoing data frame,
908  * but apparently there haven't been any since. So send the ack now.
909  */
910 static void
911 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
912 {
913 	const hpriv_p hpriv = arg1;
914 
915 	mtx_lock(&hpriv->mtx);
916 	/* Send a frame with an ack but no payload */
917   	ng_pptpgre_xmit(hpriv, NULL);
918 	mtx_assert(&hpriv->mtx, MA_NOTOWNED);
919 }
920 
921 /*************************************************************************
922 		    MISC FUNCTIONS
923 *************************************************************************/
924 
925 /*
926  * Find the hook with a given session ID.
927  */
928 static hpriv_p
929 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
930 {
931 	uint16_t	hash = SESSHASH(cid);
932 	hpriv_p	hpriv = NULL;
933 
934 	LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
935 		if (hpriv->conf.cid == cid)
936 			break;
937 	}
938 
939 	return (hpriv);
940 }
941 
942 /*
943  * Reset state (must be called with lock held or from writer)
944  */
945 static void
946 ng_pptpgre_reset(hpriv_p hpriv)
947 {
948 	/* Reset adaptive timeout state */
949 	hpriv->ato = PPTP_MAX_TIMEOUT;
950 	hpriv->rtt = PPTP_TIME_SCALE / 10;
951 	if (hpriv->conf.peerPpd > 1)	/* ppd = 0 treat as = 1 */
952 		hpriv->rtt *= hpriv->conf.peerPpd;
953 	hpriv->dev = 0;
954 	hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
955 	if (hpriv->xmitWin < 2)		/* often the first packet is lost */
956 		hpriv->xmitWin = 2;		/*   because the peer isn't ready */
957 	else if (hpriv->xmitWin > PPTP_XMIT_WIN)
958 		hpriv->xmitWin = PPTP_XMIT_WIN;
959 	hpriv->winAck = hpriv->xmitWin;
960 
961 	/* Reset sequence numbers */
962 	hpriv->recvSeq = ~0;
963 	hpriv->recvAck = ~0;
964 	hpriv->xmitSeq = ~0;
965 	hpriv->xmitAck = ~0;
966 
967 	/* Stop timers */
968 	ng_uncallout(&hpriv->sackTimer, hpriv->node);
969 	ng_uncallout(&hpriv->rackTimer, hpriv->node);
970 }
971 
972 /*
973  * Return the current time scaled & translated to our internally used format.
974  */
975 static pptptime_t
976 ng_pptpgre_time(void)
977 {
978 	struct timeval tv;
979 	pptptime_t t;
980 
981 	microuptime(&tv);
982 	t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
983 	t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
984 	return(t);
985 }
986