kernel/netgraph7: Replace usage of MALLOC/FREE with kmalloc/kfree here too.
[dragonfly.git] / sys / netgraph7 / ng_pptpgre.c
CommitLineData
b06ebda0
MD
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 $
5a975a3d 41 * $DragonFly: src/sys/netgraph7/ng_pptpgre.c,v 1.2 2008/06/26 23:05:35 dillon Exp $
b06ebda0
MD
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
5a975a3d
MD
72#include "ng_message.h"
73#include "netgraph.h"
74#include "ng_parse.h"
75#include "ng_pptpgre.h"
b06ebda0
MD
76
77/* GRE packet format, as used by PPTP */
78struct 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 */
120typedef 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 */
146struct 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};
165typedef struct ng_pptpgre_sess *hpriv_p;
166
167/* Node private data */
168struct 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};
175typedef struct ng_pptpgre_private *priv_p;
176
177/* Netgraph node methods */
178static ng_constructor_t ng_pptpgre_constructor;
179static ng_rcvmsg_t ng_pptpgre_rcvmsg;
180static ng_shutdown_t ng_pptpgre_shutdown;
181static ng_newhook_t ng_pptpgre_newhook;
182static ng_rcvdata_t ng_pptpgre_rcvdata;
183static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
184static ng_disconnect_t ng_pptpgre_disconnect;
185
186/* Helper functions */
187static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
188static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
189static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
190static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
191 void *arg1, int arg2);
192static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
193 void *arg1, int arg2);
194static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
195static void ng_pptpgre_reset(hpriv_p hpriv);
196static pptptime_t ng_pptpgre_time(void);
197
198/* Parse type for struct ng_pptpgre_conf */
199static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
200 = NG_PPTPGRE_CONF_TYPE_INFO;
201static 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 */
207static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
208 = NG_PPTPGRE_STATS_TYPE_INFO;
209static 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 */
215static 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 */
255static 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};
266NETGRAPH_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 */
277static int
278ng_pptpgre_constructor(node_p node)
279{
280 priv_p priv;
281 int i;
282
283 /* Allocate private structure */
fc025606
SW
284 priv = kmalloc(sizeof(*priv), M_NETGRAPH,
285 M_WAITOK | M_NULLOK | M_ZERO);
b06ebda0
MD
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 */
309static int
310ng_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
5a975a3d 343 hpriv = kmalloc(sizeof(*hpriv), M_NETGRAPH, M_WAITOK | M_NULLOK | M_ZERO);
b06ebda0
MD
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 */
366static int
367ng_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);
5a975a3d 417 NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_WAITOK | M_NULLOK);
b06ebda0
MD
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,
5a975a3d 429 sizeof(priv->stats), M_WAITOK | M_NULLOK);
b06ebda0
MD
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 }
448done:
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 */
457static int
458ng_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 */
481static int
482ng_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);
5a975a3d 500 kfree(hpriv, M_NETGRAPH);
b06ebda0
MD
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 */
513static int
514ng_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
fc025606 524 kfree(priv, M_NETGRAPH);
b06ebda0
MD
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 */
538static int
539ng_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) {
5a975a3d 604 MGETHDR(m, MB_DONTWAIT, MT_DATA);
b06ebda0
MD
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 {
5a975a3d 612 M_PREPEND(m, grelen, MB_DONTWAIT);
b06ebda0
MD
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
644done:
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 */
655static int
656ng_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 }
777badAck:
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
825done:
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 */
841static void
842ng_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 */
863static void
864ng_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 */
888static void
889ng_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 */
912static void
913ng_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 */
930static hpriv_p
931ng_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 */
947static void
948ng_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 */
977static pptptime_t
978ng_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}