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