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