]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_pptpgre.c
Import lib9p 7ddb1164407da19b9b1afb83df83ae65a71a9a66.
[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
604                 /* Check if windowing is enabled */
605                 if (hpriv->conf.enableWindowing) {
606                         /* Is our transmit window full? */
607                         if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
608                             hpriv->recvAck) >= hpriv->xmitWin) {
609                                 priv->stats.xmitDrops++;
610                                 ERROUT(ENOBUFS);
611                         }
612                 }
613
614                 /* Sanity check frame length */
615                 if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
616                         priv->stats.xmitTooBig++;
617                         ERROUT(EMSGSIZE);
618                 }
619         } else {
620                 priv->stats.xmitLoneAcks++;
621         }
622
623         /* Build GRE header */
624         be32enc(gre, PPTP_INIT_VALUE);
625         be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
626         be16enc(&gre->cid, hpriv->conf.peerCid);
627
628         /* Include sequence number if packet contains any data */
629         if (m != NULL) {
630                 gre->hasSeq = 1;
631                 if (hpriv->conf.enableWindowing) {
632                         hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
633                             = ng_pptpgre_time();
634                 }
635                 hpriv->xmitSeq++;
636                 be32enc(&gre->data[0], hpriv->xmitSeq);
637         }
638
639         /* Include acknowledgement (and stop send ack timer) if needed */
640         if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
641                 gre->hasAck = 1;
642                 be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
643                 hpriv->xmitAck = hpriv->recvSeq;
644                 if (hpriv->conf.enableDelayedAck)
645                         ng_uncallout(&hpriv->sackTimer, hpriv->node);
646         }
647
648         /* Prepend GRE header to outgoing frame */
649         grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
650         if (m == NULL) {
651                 MGETHDR(m, M_NOWAIT, MT_DATA);
652                 if (m == NULL) {
653                         priv->stats.memoryFailures++;
654                         ERROUT(ENOBUFS);
655                 }
656                 m->m_len = m->m_pkthdr.len = grelen;
657                 m->m_pkthdr.rcvif = NULL;
658         } else {
659                 M_PREPEND(m, grelen, M_NOWAIT);
660                 if (m == NULL || (m->m_len < grelen
661                     && (m = m_pullup(m, grelen)) == NULL)) {
662                         priv->stats.memoryFailures++;
663                         ERROUT(ENOBUFS);
664                 }
665         }
666         bcopy(gre, mtod(m, u_char *), grelen);
667
668         /* Update stats */
669         priv->stats.xmitPackets++;
670         priv->stats.xmitOctets += m->m_pkthdr.len;
671
672         /*
673          * XXX: we should reset timer only after an item has been sent
674          * successfully.
675          */
676         if (hpriv->conf.enableWindowing &&
677             gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
678                 ng_pptpgre_start_recv_ack_timer(hpriv);
679
680         mtx_unlock(&hpriv->mtx);
681
682         /* Deliver packet */
683         if (item) {
684                 NG_FWD_NEW_DATA(error, item, priv->lower, m);
685         } else {
686                 NG_SEND_DATA_ONLY(error, priv->lower, m);
687         }
688
689         return (error);
690
691 done:
692         mtx_unlock(&hpriv->mtx);
693         NG_FREE_M(m);
694         if (item)
695                 NG_FREE_ITEM(item);
696         return (error);
697 }
698
699 static void
700 ng_pptpgre_ack(const hpriv_p hpriv)
701 {
702         mtx_assert(&hpriv->mtx, MA_OWNED);
703         if (!(callout_pending(&hpriv->sackTimer))) {
704                 /* If delayed ACK is disabled, send it now */
705                 if (!hpriv->conf.enableDelayedAck) {    /* ack now */
706                         ng_pptpgre_xmit(hpriv, NULL);
707                         /* ng_pptpgre_xmit() drops the mutex */
708                         return;
709                 }
710                 /* ack later */
711                 ng_pptpgre_start_send_ack_timer(hpriv);
712                 mtx_unlock(&hpriv->mtx);
713                 return;
714         }
715         mtx_unlock(&hpriv->mtx);
716 }
717
718 /*
719  * Delivers packets from the queue "q" to upper layers. Frees delivered
720  * entries with the exception of one equal to "st" that is allocated
721  * on caller's stack and not on the heap.
722  */
723 static int
724 ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
725 {
726         struct ng_pptpgre_roq *np;
727         struct mbuf *m;
728         int error = 0;
729
730         mtx_assert(&hpriv->mtx, MA_NOTOWNED);
731         while (!SLIST_EMPTY(q)) {
732                 np = SLIST_FIRST(q);
733                 SLIST_REMOVE_HEAD(q, next);
734                 NGI_GET_M(np->item, m);
735                 NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
736                 if (np != st)
737                         free(np, M_NETGRAPH);
738         }
739         return (error);
740 }
741
742 /*
743  * Handle an incoming packet.  The packet includes the IP header.
744  */
745 static int
746 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
747 {
748         hpriv_p hpriv;
749         node_p node = NG_HOOK_NODE(hook);
750         const priv_p priv = NG_NODE_PRIVATE(node);
751         int iphlen, grelen, extralen;
752         const struct greheader *gre;
753         const struct ip *ip;
754         int error = 0;
755         struct mbuf *m;
756
757         roqh sendq = SLIST_HEAD_INITIALIZER(sendq);  /* send queue on stack */
758         struct ng_pptpgre_roq *last = NULL;     /* last packet in the sendq */
759         struct ng_pptpgre_roq *np, *prev;
760         struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
761         long diff;
762         u_int32_t seq;
763
764         m = NGI_M(item);
765         /* Update stats */
766         priv->stats.recvPackets++;
767         priv->stats.recvOctets += m->m_pkthdr.len;
768
769         /* Sanity check packet length */
770         if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
771                 priv->stats.recvRunts++;
772                 ERROUT(EINVAL);
773         }
774
775         /* Safely pull up the complete IP+GRE headers */
776         if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
777                 if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
778                         priv->stats.memoryFailures++;
779                         _NGI_M(item) = NULL;
780                         ERROUT(ENOBUFS);
781                 }
782                 _NGI_M(item) = m;
783         }
784         ip = mtod(m, const struct ip *);
785         iphlen = ip->ip_hl << 2;
786         if (m->m_len < iphlen + sizeof(*gre)) {
787                 if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
788                         priv->stats.memoryFailures++;
789                         _NGI_M(item) = NULL;
790                         ERROUT(ENOBUFS);
791                 }
792                 _NGI_M(item) = m;
793                 ip = mtod(m, const struct ip *);
794         }
795         gre = (const struct greheader *)((const u_char *)ip + iphlen);
796         grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
797         if (m->m_pkthdr.len < iphlen + grelen) {
798                 priv->stats.recvRunts++;
799                 ERROUT(EINVAL);
800         }
801         if (m->m_len < iphlen + grelen) {
802                 if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
803                         priv->stats.memoryFailures++;
804                         _NGI_M(item) = NULL;
805                         ERROUT(ENOBUFS);
806                 }
807                 _NGI_M(item) = m;
808                 ip = mtod(m, const struct ip *);
809                 gre = (const struct greheader *)((const u_char *)ip + iphlen);
810         }
811
812         /* Sanity check packet length and GRE header bits */
813         extralen = m->m_pkthdr.len
814             - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
815         if (extralen < 0) {
816                 priv->stats.recvBadGRE++;
817                 ERROUT(EINVAL);
818         }
819         if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
820                 priv->stats.recvBadGRE++;
821                 ERROUT(EINVAL);
822         }
823
824         hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
825         if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
826                 priv->stats.recvBadCID++;
827                 ERROUT(EINVAL);
828         }
829         mtx_lock(&hpriv->mtx);
830
831         /* Look for peer ack */
832         if (gre->hasAck) {
833                 const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]);
834                 const int index = ack - hpriv->recvAck - 1;
835                 long sample;
836
837                 /* Sanity check ack value */
838                 if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
839                         priv->stats.recvBadAcks++;
840                         goto badAck;            /* we never sent it! */
841                 }
842                 if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
843                         goto badAck;            /* ack already timed out */
844                 hpriv->recvAck = ack;
845
846                 /* Update adaptive timeout stuff */
847                 if (hpriv->conf.enableWindowing) {
848                         sample = ng_pptpgre_time() - hpriv->timeSent[index];
849                         diff = sample - hpriv->rtt;
850                         hpriv->rtt += PPTP_ACK_ALPHA(diff);
851                         if (diff < 0)
852                                 diff = -diff;
853                         hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
854                             /* +2 to compensate low precision of int math */
855                         hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
856                         if (hpriv->ato > PPTP_MAX_TIMEOUT)
857                                 hpriv->ato = PPTP_MAX_TIMEOUT;
858                         else if (hpriv->ato < PPTP_MIN_TIMEOUT)
859                                 hpriv->ato = PPTP_MIN_TIMEOUT;
860
861                         /* Shift packet transmit times in our transmit window */
862                         bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
863                             sizeof(*hpriv->timeSent)
864                               * (PPTP_XMIT_WIN - (index + 1)));
865
866                         /* If we sent an entire window, increase window size */
867                         if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
868                             && hpriv->xmitWin < PPTP_XMIT_WIN) {
869                                 hpriv->xmitWin++;
870                                 hpriv->winAck = ack + hpriv->xmitWin;
871                         }
872
873                         /* Stop/(re)start receive ACK timer as necessary */
874                         ng_uncallout(&hpriv->rackTimer, hpriv->node);
875                         if (hpriv->recvAck != hpriv->xmitSeq)
876                                 ng_pptpgre_start_recv_ack_timer(hpriv);
877                 }
878         }
879 badAck:
880
881         /* See if frame contains any data */
882         if (!gre->hasSeq) {             /* no data to deliver */
883                 priv->stats.recvLoneAcks++;
884                 mtx_unlock(&hpriv->mtx);
885                 ERROUT(0);
886         }
887
888         seq = be32dec(&gre->data[0]);
889
890         diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
891         if (diff <= 0) {                        /* late or duplicate packet */
892                 if (diff < 0 && reorder_max == 0)       /* reorder disabled */
893                         priv->stats.recvOutOfOrder++;   /* late */
894                 else
895                         priv->stats.recvDuplicates++;   /* duplicate */
896                 mtx_unlock(&hpriv->mtx);
897                 ERROUT(EINVAL);
898         }
899
900         /* Trim mbuf down to internal payload */
901         m_adj(m, iphlen + grelen);
902         if (extralen > 0)
903                 m_adj(m, -extralen);
904
905 #define INIT_SENDQ(t) do {                              \
906                 t.item = item;                          \
907                 t.seq = seq;                            \
908                 SLIST_INSERT_HEAD(&sendq, &t, next);    \
909                 last = &t;                              \
910                 hpriv->recvSeq = seq;                   \
911                 goto deliver;                           \
912         } while(0)
913
914         if (diff == 1)
915                 /* the packet came in order, place it at the start of sendq */
916                 INIT_SENDQ(temp);
917
918         /* The packet came too early, try to enqueue it.
919          *
920          * Check for duplicate in the queue. After this loop, "prev" will be
921          * NULL if the packet should become new head of the queue,
922          * or else it should be inserted after the "prev".
923          */
924         prev = SLIST_FIRST(&hpriv->roq);
925         SLIST_FOREACH(np, &hpriv->roq, next) {
926                 diff = PPTP_SEQ_DIFF(np->seq, seq);
927                 if (diff == 0) { /* do not add duplicate, drop it */
928                         priv->stats.recvDuplicates++;
929                         mtx_unlock(&hpriv->mtx);
930                         ERROUT(EINVAL);
931                 }
932                 if (diff > 0) {         /* we found newer packet */
933                         if (np == prev) /* that is the head of the queue */
934                             prev = NULL; /* put current packet to the head */
935                         break;
936                 }
937                 prev = np;
938         }
939
940         priv->stats.recvOutOfOrder++;   /* duplicate not found */
941         if (hpriv->roq_len < reorder_max)
942                 goto enqueue;   /* reorder enabled and there is a room */
943
944         /*
945          * There is no room in the queue or reorder disabled.
946          *
947          * It the latter case, we may still have non-empty reorder queue
948          * if reorder was disabled in process of reordering.
949          * Then we correctly deliver the queue without growing it.
950          *
951          * In both cases, no malloc()'s until the queue is shortened.
952          */
953         priv->stats.recvReorderOverflow++;
954         if (prev == NULL) {       /* new packet goes before the head      */
955                 INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
956         }
957 #undef INIT_SENDQ
958
959         /*
960          * Current packet goes after the head of reorder queue.
961          * Move the head to sendq to make room for current packet.
962          */
963         np = SLIST_FIRST(&hpriv->roq);
964         if (prev == np)
965                 prev = NULL;
966         SLIST_REMOVE_HEAD(&hpriv->roq, next);
967         hpriv->roq_len--;       /* we are allowed to use malloc() now */
968         SLIST_INSERT_HEAD(&sendq, np, next);
969         last = np;
970         hpriv->recvSeq = np->seq;
971
972 enqueue:
973         np = malloc(sizeof(*np), M_NETGRAPH, M_NOWAIT | M_ZERO);
974         if (np == NULL) {
975                 priv->stats.memoryFailures++;
976                 /*
977                  * Emergency: we cannot save new data.
978                  * Flush the queue delivering all queued packets preceeding
979                  * current one despite of gaps.
980                  */
981                 while (!SLIST_EMPTY(&hpriv->roq)) {
982                         np = SLIST_FIRST(&hpriv->roq);
983                         if (np->seq > seq)
984                                 break;
985                         SLIST_REMOVE_HEAD(&hpriv->roq, next);
986                         hpriv->roq_len--;
987                         if (last == NULL)
988                                 SLIST_INSERT_HEAD(&sendq, np, next);
989                         else
990                                 SLIST_INSERT_AFTER(last, np, next);
991                         last = np;
992                 }
993
994                 /*
995                  * Pretend we got all packets till the current one
996                  * and acknowledge it.
997                  */
998                 hpriv->recvSeq = seq;
999                 ng_pptpgre_ack(hpriv);  /* drops lock */
1000                 ng_pptpgre_sendq(hpriv, &sendq, &temp);
1001                 NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1002                 ERROUT(ENOMEM);
1003         }
1004
1005         /* Add current (early) packet to the reorder queue. */
1006         np->item = item;
1007         np->seq = seq;
1008         if (prev == NULL)
1009                 SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1010         else
1011                 SLIST_INSERT_AFTER(prev, np, next);
1012         hpriv->roq_len++;
1013
1014 deliver:
1015         /* Look if we have some packets in sequence after sendq. */
1016         while (!SLIST_EMPTY(&hpriv->roq)) {
1017                 np = SLIST_FIRST(&hpriv->roq);
1018                 if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1019                         break; /* the gap in the sequence */
1020
1021                 /* "np" is in sequence, move it to the sendq. */
1022                 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1023                 hpriv->roq_len--;
1024                 hpriv->recvSeq = np->seq;
1025
1026                 if (last == NULL)
1027                         SLIST_INSERT_HEAD(&sendq, np, next);
1028                 else
1029                         SLIST_INSERT_AFTER(last, np, next);
1030                 last = np;
1031         }
1032
1033         if (SLIST_EMPTY(&hpriv->roq)) {
1034                 if (callout_pending(&hpriv->reorderTimer))
1035                         ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1036         } else {
1037                 if (!callout_pending(&hpriv->reorderTimer))
1038                         ng_pptpgre_start_reorder_timer(hpriv);
1039         }
1040
1041         if (SLIST_EMPTY(&sendq)) {
1042                 /* Current packet has been queued, nothing to free/deliver. */
1043                 mtx_unlock(&hpriv->mtx);
1044                 return (error);
1045         }
1046
1047         /* We need to acknowledge last packet; do it soon... */
1048         ng_pptpgre_ack(hpriv);          /* drops lock */
1049         ng_pptpgre_sendq(hpriv, &sendq, &temp);
1050         return (error);
1051
1052 done:
1053         NG_FREE_ITEM(item);
1054         return (error);
1055 }
1056
1057 /*************************************************************************
1058                     TIMER RELATED FUNCTIONS
1059 *************************************************************************/
1060
1061 /*
1062  * Start a timer for the peer's acknowledging our oldest unacknowledged
1063  * sequence number.  If we get an ack for this sequence number before
1064  * the timer goes off, we cancel the timer.  Resets currently running
1065  * recv ack timer, if any.
1066  */
1067 static void
1068 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1069 {
1070         int remain, ticks;
1071
1072         /* Compute how long until oldest unack'd packet times out,
1073            and reset the timer to that time. */
1074         remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1075         if (remain < 0)
1076                 remain = 0;
1077
1078         /* Be conservative: timeout can happen up to 1 tick early */
1079         ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1080         ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1081             ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
1082 }
1083
1084 /*
1085  * The peer has failed to acknowledge the oldest unacknowledged sequence
1086  * number within the time allotted.  Update our adaptive timeout parameters
1087  * and reset/restart the recv ack timer.
1088  */
1089 static void
1090 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1091 {
1092         const priv_p priv = NG_NODE_PRIVATE(node);
1093         const hpriv_p hpriv = arg1;
1094
1095         /* Update adaptive timeout stuff */
1096         priv->stats.recvAckTimeouts++;
1097         hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1098         hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1099         if (hpriv->ato > PPTP_MAX_TIMEOUT)
1100                 hpriv->ato = PPTP_MAX_TIMEOUT;
1101         else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1102                 hpriv->ato = PPTP_MIN_TIMEOUT;
1103
1104         /* Reset ack and sliding window */
1105         hpriv->recvAck = hpriv->xmitSeq;                /* pretend we got the ack */
1106         hpriv->xmitWin = (hpriv->xmitWin + 1) / 2;      /* shrink transmit window */
1107         hpriv->winAck = hpriv->recvAck + hpriv->xmitWin;        /* reset win expand time */
1108 }
1109
1110 /*
1111  * Start the send ack timer. This assumes the timer is not
1112  * already running.
1113  */
1114 static void
1115 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
1116 {
1117         int ackTimeout, ticks;
1118
1119         /* Take 1/4 of the estimated round trip time */
1120         ackTimeout = (hpriv->rtt >> 2);
1121         if (ackTimeout < PPTP_MIN_ACK_DELAY)
1122                 ackTimeout = PPTP_MIN_ACK_DELAY;
1123         else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1124                 ackTimeout = PPTP_MAX_ACK_DELAY;
1125
1126         /* Be conservative: timeout can happen up to 1 tick early */
1127         ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1128         ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1129             ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
1130 }
1131
1132 /*
1133  * We've waited as long as we're willing to wait before sending an
1134  * acknowledgement to the peer for received frames. We had hoped to
1135  * be able to piggy back our acknowledgement on an outgoing data frame,
1136  * but apparently there haven't been any since. So send the ack now.
1137  */
1138 static void
1139 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1140 {
1141         const hpriv_p hpriv = arg1;
1142
1143         mtx_lock(&hpriv->mtx);
1144         /* Send a frame with an ack but no payload */
1145         ng_pptpgre_xmit(hpriv, NULL);
1146         mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1147 }
1148
1149 /*
1150  * Start a timer for the reorder queue. This assumes the timer is not
1151  * already running.
1152  */
1153 static void
1154 ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1155 {
1156         int ticks;
1157
1158         /* Be conservative: timeout can happen up to 1 tick early */
1159         ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1160         ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1161                 ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1162 }
1163
1164 /*
1165  * The oldest packet spent too much time in the reorder queue.
1166  * Deliver it and next packets in sequence, if any.
1167  */
1168 static void
1169 ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1170 {
1171         const priv_p priv = NG_NODE_PRIVATE(node);
1172         const hpriv_p hpriv = arg1;
1173         roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1174         struct ng_pptpgre_roq *np, *last = NULL;
1175
1176         priv->stats.recvReorderTimeouts++;
1177         mtx_lock(&hpriv->mtx);
1178         if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1179                 mtx_unlock(&hpriv->mtx);
1180                 return;
1181         }
1182
1183         last = np = SLIST_FIRST(&hpriv->roq);
1184         hpriv->roq_len--;
1185         SLIST_REMOVE_HEAD(&hpriv->roq, next);
1186         SLIST_INSERT_HEAD(&sendq, np, next);
1187
1188         /* Look if we have more packets in sequence */
1189         while (!SLIST_EMPTY(&hpriv->roq)) {
1190                 np = SLIST_FIRST(&hpriv->roq);
1191                 if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1192                         break; /* the gap in the sequence */
1193
1194                 /* Next packet is in sequence, move it to the sendq. */
1195                 hpriv->roq_len--;
1196                 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1197                 SLIST_INSERT_AFTER(last, np, next);
1198                 last = np;
1199         }
1200
1201         hpriv->recvSeq = last->seq;
1202         if (!SLIST_EMPTY(&hpriv->roq))
1203                 ng_pptpgre_start_reorder_timer(hpriv);
1204
1205         /* We need to acknowledge last packet; do it soon... */
1206         ng_pptpgre_ack(hpriv);          /* drops lock */
1207         ng_pptpgre_sendq(hpriv, &sendq, NULL);
1208         mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1209 }
1210
1211 /*************************************************************************
1212                     MISC FUNCTIONS
1213 *************************************************************************/
1214
1215 /*
1216  * Find the hook with a given session ID.
1217  */
1218 static hpriv_p
1219 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1220 {
1221         uint16_t        hash = SESSHASH(cid);
1222         hpriv_p hpriv = NULL;
1223
1224         LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1225                 if (hpriv->conf.cid == cid)
1226                         break;
1227         }
1228
1229         return (hpriv);
1230 }
1231
1232 /*
1233  * Reset state (must be called with lock held or from writer)
1234  */
1235 static void
1236 ng_pptpgre_reset(hpriv_p hpriv)
1237 {
1238         struct ng_pptpgre_roq *np;
1239
1240         /* Reset adaptive timeout state */
1241         hpriv->ato = PPTP_MAX_TIMEOUT;
1242         hpriv->rtt = PPTP_TIME_SCALE / 10;
1243         if (hpriv->conf.peerPpd > 1)    /* ppd = 0 treat as = 1 */
1244                 hpriv->rtt *= hpriv->conf.peerPpd;
1245         hpriv->dev = 0;
1246         hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1247         if (hpriv->xmitWin < 2)         /* often the first packet is lost */
1248                 hpriv->xmitWin = 2;             /*   because the peer isn't ready */
1249         else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1250                 hpriv->xmitWin = PPTP_XMIT_WIN;
1251         hpriv->winAck = hpriv->xmitWin;
1252
1253         /* Reset sequence numbers */
1254         hpriv->recvSeq = ~0;
1255         hpriv->recvAck = ~0;
1256         hpriv->xmitSeq = ~0;
1257         hpriv->xmitAck = ~0;
1258
1259         /* Stop timers */
1260         ng_uncallout(&hpriv->sackTimer, hpriv->node);
1261         ng_uncallout(&hpriv->rackTimer, hpriv->node);
1262         ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1263
1264         /* Clear reorder queue */
1265         while (!SLIST_EMPTY(&hpriv->roq)) {
1266                 np = SLIST_FIRST(&hpriv->roq);
1267                 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1268                 NG_FREE_ITEM(np->item);
1269                 free(np, M_NETGRAPH);
1270         }
1271         hpriv->roq_len = 0;
1272 }
1273
1274 /*
1275  * Return the current time scaled & translated to our internally used format.
1276  */
1277 static pptptime_t
1278 ng_pptpgre_time(void)
1279 {
1280         struct timeval tv;
1281         pptptime_t t;
1282
1283         microuptime(&tv);
1284         t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1285         t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1286         return(t);
1287 }