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