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