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