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