Deleted Added
full compact
ng_pptpgre.c (243882) ng_pptpgre.c (298649)
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 *
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: head/sys/netgraph/ng_pptpgre.c 243882 2012-12-05 08:04:20Z glebius $
40 * $FreeBSD: head/sys/netgraph/ng_pptpgre.c 298649 2016-04-26 15:38:17Z pfg $
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 */
78struct 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 */
120typedef 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 */
146struct 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};
165typedef struct ng_pptpgre_sess *hpriv_p;
166
167/* Node private data */
168struct 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};
175typedef struct ng_pptpgre_private *priv_p;
176
177/* Netgraph node methods */
178static ng_constructor_t ng_pptpgre_constructor;
179static ng_rcvmsg_t ng_pptpgre_rcvmsg;
180static ng_shutdown_t ng_pptpgre_shutdown;
181static ng_newhook_t ng_pptpgre_newhook;
182static ng_rcvdata_t ng_pptpgre_rcvdata;
183static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
184static ng_disconnect_t ng_pptpgre_disconnect;
185
186/* Helper functions */
187static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
188static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
189static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
190static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
191 void *arg1, int arg2);
192static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
193 void *arg1, int arg2);
194static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
195static void ng_pptpgre_reset(hpriv_p hpriv);
196static pptptime_t ng_pptpgre_time(void);
197
198/* Parse type for struct ng_pptpgre_conf */
199static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
200 = NG_PPTPGRE_CONF_TYPE_INFO;
201static 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 */
207static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
208 = NG_PPTPGRE_STATS_TYPE_INFO;
209static 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 */
215static 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 */
255static 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};
266NETGRAPH_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 */
277static int
278ng_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 */
306static int
307ng_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 */
363static int
364ng_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 }
445done:
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 */
454static int
455ng_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 */
478static int
479ng_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 */
510static int
511ng_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 */
535static int
536ng_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
641done:
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 */
652static int
653ng_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 }
773badAck:
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
821done:
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 */
837static void
838ng_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 */
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 */
78struct 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 */
120typedef 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 */
146struct 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};
165typedef struct ng_pptpgre_sess *hpriv_p;
166
167/* Node private data */
168struct 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};
175typedef struct ng_pptpgre_private *priv_p;
176
177/* Netgraph node methods */
178static ng_constructor_t ng_pptpgre_constructor;
179static ng_rcvmsg_t ng_pptpgre_rcvmsg;
180static ng_shutdown_t ng_pptpgre_shutdown;
181static ng_newhook_t ng_pptpgre_newhook;
182static ng_rcvdata_t ng_pptpgre_rcvdata;
183static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
184static ng_disconnect_t ng_pptpgre_disconnect;
185
186/* Helper functions */
187static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
188static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
189static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
190static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
191 void *arg1, int arg2);
192static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
193 void *arg1, int arg2);
194static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
195static void ng_pptpgre_reset(hpriv_p hpriv);
196static pptptime_t ng_pptpgre_time(void);
197
198/* Parse type for struct ng_pptpgre_conf */
199static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
200 = NG_PPTPGRE_CONF_TYPE_INFO;
201static 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 */
207static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
208 = NG_PPTPGRE_STATS_TYPE_INFO;
209static 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 */
215static 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 */
255static 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};
266NETGRAPH_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 */
277static int
278ng_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 */
306static int
307ng_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 */
363static int
364ng_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 }
445done:
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 */
454static int
455ng_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 */
478static int
479ng_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 */
510static int
511ng_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 */
535static int
536ng_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
641done:
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 */
652static int
653ng_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 }
773badAck:
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
821done:
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 */
837static void
838ng_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;
849 ticks = howmany(remain * hz, 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 */
859static void
860ng_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 */
884static void
885ng_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 */
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 */
859static void
860ng_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 */
884static void
885ng_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);
897 ticks = howmany(ackTimeout * hz, 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 */
908static void
909ng_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 */
926static hpriv_p
927ng_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 */
943static void
944ng_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 */
973static pptptime_t
974ng_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}
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 */
908static void
909ng_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 */
926static hpriv_p
927ng_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 */
943static void
944ng_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 */
973static pptptime_t
974ng_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}