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