6 * Copyright (c) 1996-1999 Whistle Communications, Inc.
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
38 * Author: Archie Cobbs <archie@freebsd.org>
39 * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
43 * PPTP/GRE netgraph node type.
45 * This node type does the GRE encapsulation as specified for the PPTP
46 * protocol (RFC 2637, section 4). This includes sequencing and
47 * retransmission of frames, but not the actual packet delivery nor
48 * any of the TCP control stream protocol.
50 * The "upper" hook of this node is suitable for attaching to a "ppp"
51 * node link hook. The "lower" hook of this node is suitable for attaching
52 * to a "ksocket" node on hook "inet/raw/gre".
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/kernel.h>
60 #include <sys/malloc.h>
62 #include <sys/mutex.h>
63 #include <sys/endian.h>
64 #include <sys/errno.h>
65 #include <sys/sysctl.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 */
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 */
99 #error BYTE_ORDER is not defined properly
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 #define PPTP_REORDER_TIMEOUT 1
128 /* When we receive a packet, we wait to see if there's an outgoing packet
129 we can piggy-back the ACK off of. These parameters determine the minimum
130 and maxmimum length of time we're willing to wait in order to do that.
131 These have no effect unless "enableDelayedAck" is turned on. */
132 #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */
133 #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */
135 /* See RFC 2637 section 4.4 */
136 #define PPTP_ACK_ALPHA(x) (((x) + 4) >> 3) /* alpha = 0.125 */
137 #define PPTP_ACK_BETA(x) (((x) + 2) >> 2) /* beta = 0.25 */
138 #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */
139 #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */
141 #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y))
143 #define SESSHASHSIZE 0x0020
144 #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
146 SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
150 * Reorder queue maximum length. Zero disables reorder.
152 * The node may keep reorder_max queue entries per session
153 * if reorder is enabled, plus allocate one more for short time.
155 * Be conservative in memory consumption by default.
156 * Lots of sessions with large queues can overflow M_NETGRAPH zone.
158 static int reorder_max = 1; /* reorder up to two swapped packets in a row */
159 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
160 &reorder_max, 0, "Reorder queue maximum length");
162 static int reorder_timeout = PPTP_REORDER_TIMEOUT;
163 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
164 &reorder_timeout, 0, "Reorder timeout is milliseconds");
166 /* Packet reorder FIFO queue */
167 struct ng_pptpgre_roq {
168 SLIST_ENTRY(ng_pptpgre_roq) next; /* next entry of the queue */
169 item_p item; /* netgraph item */
170 u_int32_t seq; /* packet sequence number */
172 SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
173 typedef struct ng_pptpgre_roq_head roqh;
175 /* We keep packet retransmit and acknowlegement state in this struct */
176 struct ng_pptpgre_sess {
177 node_p node; /* this node pointer */
178 hook_p hook; /* hook to upper layers */
179 struct ng_pptpgre_conf conf; /* configuration info */
180 struct mtx mtx; /* session mutex */
181 u_int32_t recvSeq; /* last seq # we rcv'd */
182 u_int32_t xmitSeq; /* last seq # we sent */
183 u_int32_t recvAck; /* last seq # peer ack'd */
184 u_int32_t xmitAck; /* last seq # we ack'd */
185 int32_t ato; /* adaptive time-out value */
186 int32_t rtt; /* round trip time estimate */
187 int32_t dev; /* deviation estimate */
188 u_int16_t xmitWin; /* size of xmit window */
189 struct callout sackTimer; /* send ack timer */
190 struct callout rackTimer; /* recv ack timer */
191 u_int32_t winAck; /* seq when xmitWin will grow */
192 pptptime_t timeSent[PPTP_XMIT_WIN];
193 LIST_ENTRY(ng_pptpgre_sess) sessions;
194 roqh roq; /* reorder queue head */
195 u_int8_t roq_len; /* reorder queue length */
196 struct callout reorderTimer; /* reorder timeout handler */
198 typedef struct ng_pptpgre_sess *hpriv_p;
200 /* Node private data */
201 struct ng_pptpgre_private {
202 hook_p upper; /* hook to upper layers */
203 hook_p lower; /* hook to lower layers */
204 struct ng_pptpgre_sess uppersess; /* default session for compat */
205 LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
206 struct ng_pptpgre_stats stats; /* node statistics */
208 typedef struct ng_pptpgre_private *priv_p;
210 /* Netgraph node methods */
211 static ng_constructor_t ng_pptpgre_constructor;
212 static ng_rcvmsg_t ng_pptpgre_rcvmsg;
213 static ng_shutdown_t ng_pptpgre_shutdown;
214 static ng_newhook_t ng_pptpgre_newhook;
215 static ng_rcvdata_t ng_pptpgre_rcvdata;
216 static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
217 static ng_disconnect_t ng_pptpgre_disconnect;
219 /* Helper functions */
220 static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
221 static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
222 static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
223 static void ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
224 static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
225 void *arg1, int arg2);
226 static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
227 void *arg1, int arg2);
228 static void ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
229 void *arg1, int arg2);
230 static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
231 static void ng_pptpgre_reset(hpriv_p hpriv);
232 static pptptime_t ng_pptpgre_time(void);
233 static void ng_pptpgre_ack(const hpriv_p hpriv);
234 static int ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
235 const struct ng_pptpgre_roq *st);
237 /* Parse type for struct ng_pptpgre_conf */
238 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
239 = NG_PPTPGRE_CONF_TYPE_INFO;
240 static const struct ng_parse_type ng_pptpgre_conf_type = {
241 &ng_parse_struct_type,
242 &ng_pptpgre_conf_type_fields,
245 /* Parse type for struct ng_pptpgre_stats */
246 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
247 = NG_PPTPGRE_STATS_TYPE_INFO;
248 static const struct ng_parse_type ng_pptp_stats_type = {
249 &ng_parse_struct_type,
250 &ng_pptpgre_stats_type_fields
253 /* List of commands and how to convert arguments to/from ASCII */
254 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
257 NGM_PPTPGRE_SET_CONFIG,
259 &ng_pptpgre_conf_type,
264 NGM_PPTPGRE_GET_CONFIG,
266 &ng_parse_hint16_type,
267 &ng_pptpgre_conf_type
271 NGM_PPTPGRE_GET_STATS,
278 NGM_PPTPGRE_CLR_STATS,
285 NGM_PPTPGRE_GETCLR_STATS,
293 /* Node type descriptor */
294 static struct ng_type ng_pptpgre_typestruct = {
295 .version = NG_ABI_VERSION,
296 .name = NG_PPTPGRE_NODE_TYPE,
297 .constructor = ng_pptpgre_constructor,
298 .rcvmsg = ng_pptpgre_rcvmsg,
299 .shutdown = ng_pptpgre_shutdown,
300 .newhook = ng_pptpgre_newhook,
301 .rcvdata = ng_pptpgre_rcvdata,
302 .disconnect = ng_pptpgre_disconnect,
303 .cmdlist = ng_pptpgre_cmdlist,
305 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
307 #define ERROUT(x) do { error = (x); goto done; } while (0)
309 /************************************************************************
311 ************************************************************************/
314 * Node type constructor
317 ng_pptpgre_constructor(node_p node)
322 /* Allocate private structure */
323 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
325 NG_NODE_SET_PRIVATE(node, priv);
327 /* Initialize state */
328 mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
329 ng_callout_init(&priv->uppersess.sackTimer);
330 ng_callout_init(&priv->uppersess.rackTimer);
331 priv->uppersess.node = node;
333 SLIST_INIT(&priv->uppersess.roq);
334 priv->uppersess.roq_len = 0;
335 ng_callout_init(&priv->uppersess.reorderTimer);
337 for (i = 0; i < SESSHASHSIZE; i++)
338 LIST_INIT(&priv->sesshash[i]);
340 LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
347 * Give our OK for a hook to be added.
350 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
352 const priv_p priv = NG_NODE_PRIVATE(node);
354 /* Check hook name */
355 if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
357 priv->uppersess.hook = hook;
358 NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
359 } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
361 NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
363 static const char hexdig[16] = "0123456789abcdef";
369 /* Parse hook name to get session ID */
370 if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
371 sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
373 hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
374 for (cid = i = 0; i < 4; i++) {
375 for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
378 cid = (cid << 4) | j;
383 hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
387 /* Initialize state */
388 mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
389 ng_callout_init(&hpriv->sackTimer);
390 ng_callout_init(&hpriv->rackTimer);
391 hpriv->conf.cid = cid;
395 SLIST_INIT(&hpriv->roq);
397 ng_callout_init(&hpriv->reorderTimer);
399 NG_HOOK_SET_PRIVATE(hook, hpriv);
401 hash = SESSHASH(cid);
402 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
409 * Receive a control message.
412 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
414 const priv_p priv = NG_NODE_PRIVATE(node);
415 struct ng_mesg *resp = NULL;
419 NGI_GET_MSG(item, msg);
420 switch (msg->header.typecookie) {
421 case NGM_PPTPGRE_COOKIE:
422 switch (msg->header.cmd) {
423 case NGM_PPTPGRE_SET_CONFIG:
425 struct ng_pptpgre_conf *const newConf =
426 (struct ng_pptpgre_conf *) msg->data;
430 /* Check for invalid or illegal config */
431 if (msg->header.arglen != sizeof(*newConf))
433 /* Try to find session by cid. */
434 hpriv = ng_pptpgre_find_session(priv, newConf->cid);
435 /* If not present - use upper. */
437 hpriv = &priv->uppersess;
438 LIST_REMOVE(hpriv, sessions);
439 hash = SESSHASH(newConf->cid);
440 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
443 ng_pptpgre_reset(hpriv); /* reset on configure */
444 hpriv->conf = *newConf;
447 case NGM_PPTPGRE_GET_CONFIG:
451 if (msg->header.arglen == 2) {
452 /* Try to find session by cid. */
453 hpriv = ng_pptpgre_find_session(priv,
454 *((uint16_t *)msg->data));
457 } else if (msg->header.arglen == 0) {
459 hpriv = &priv->uppersess;
462 NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
465 bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
468 case NGM_PPTPGRE_GET_STATS:
469 case NGM_PPTPGRE_CLR_STATS:
470 case NGM_PPTPGRE_GETCLR_STATS:
472 if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
473 NG_MKRESPONSE(resp, msg,
474 sizeof(priv->stats), M_NOWAIT);
478 resp->data, sizeof(priv->stats));
480 if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
481 bzero(&priv->stats, sizeof(priv->stats));
494 NG_RESPOND_MSG(error, node, item, resp);
500 * Receive incoming data on a hook.
503 ng_pptpgre_rcvdata(hook_p hook, item_p item)
505 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
508 /* If not configured, reject */
509 if (!hpriv->conf.enabled) {
514 mtx_lock(&hpriv->mtx);
516 rval = ng_pptpgre_xmit(hpriv, item);
518 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
527 ng_pptpgre_disconnect(hook_p hook)
529 const node_p node = NG_HOOK_NODE(hook);
530 const priv_p priv = NG_NODE_PRIVATE(node);
531 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
533 /* Zero out hook pointer */
534 if (hook == priv->upper) {
536 priv->uppersess.hook = NULL;
537 } else if (hook == priv->lower) {
540 /* Reset node (stops timers) */
541 ng_pptpgre_reset(hpriv);
543 LIST_REMOVE(hpriv, sessions);
544 mtx_destroy(&hpriv->mtx);
545 free(hpriv, M_NETGRAPH);
548 /* Go away if no longer connected to anything */
549 if ((NG_NODE_NUMHOOKS(node) == 0)
550 && (NG_NODE_IS_VALID(node)))
551 ng_rmnode_self(node);
559 ng_pptpgre_shutdown(node_p node)
561 const priv_p priv = NG_NODE_PRIVATE(node);
563 /* Reset node (stops timers) */
564 ng_pptpgre_reset(&priv->uppersess);
566 LIST_REMOVE(&priv->uppersess, sessions);
567 mtx_destroy(&priv->uppersess.mtx);
569 free(priv, M_NETGRAPH);
571 /* Decrement ref count */
576 /*************************************************************************
577 TRANSMIT AND RECEIVE FUNCTIONS
578 *************************************************************************/
581 * Transmit an outgoing frame, or just an ack if m is NULL.
584 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
586 const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
587 u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
588 struct greheader *const gre = (struct greheader *)buf;
592 mtx_assert(&hpriv->mtx, MA_OWNED);
599 /* Check if there's data */
601 /* Check if windowing is enabled */
602 if (hpriv->conf.enableWindowing) {
603 /* Is our transmit window full? */
604 if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
605 hpriv->recvAck) >= hpriv->xmitWin) {
606 priv->stats.xmitDrops++;
611 /* Sanity check frame length */
612 if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
613 priv->stats.xmitTooBig++;
617 priv->stats.xmitLoneAcks++;
620 /* Build GRE header */
621 be32enc(gre, PPTP_INIT_VALUE);
622 be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
623 be16enc(&gre->cid, hpriv->conf.peerCid);
625 /* Include sequence number if packet contains any data */
628 if (hpriv->conf.enableWindowing) {
629 hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
633 be32enc(&gre->data[0], hpriv->xmitSeq);
636 /* Include acknowledgement (and stop send ack timer) if needed */
637 if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
639 be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
640 hpriv->xmitAck = hpriv->recvSeq;
641 if (hpriv->conf.enableDelayedAck)
642 ng_uncallout(&hpriv->sackTimer, hpriv->node);
645 /* Prepend GRE header to outgoing frame */
646 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
648 MGETHDR(m, M_NOWAIT, MT_DATA);
650 priv->stats.memoryFailures++;
653 m->m_len = m->m_pkthdr.len = grelen;
654 m->m_pkthdr.rcvif = NULL;
656 M_PREPEND(m, grelen, M_NOWAIT);
657 if (m == NULL || (m->m_len < grelen
658 && (m = m_pullup(m, grelen)) == NULL)) {
659 priv->stats.memoryFailures++;
663 bcopy(gre, mtod(m, u_char *), grelen);
666 priv->stats.xmitPackets++;
667 priv->stats.xmitOctets += m->m_pkthdr.len;
670 * XXX: we should reset timer only after an item has been sent
673 if (hpriv->conf.enableWindowing &&
674 gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
675 ng_pptpgre_start_recv_ack_timer(hpriv);
677 mtx_unlock(&hpriv->mtx);
681 NG_FWD_NEW_DATA(error, item, priv->lower, m);
683 NG_SEND_DATA_ONLY(error, priv->lower, m);
689 mtx_unlock(&hpriv->mtx);
697 ng_pptpgre_ack(const hpriv_p hpriv)
699 mtx_assert(&hpriv->mtx, MA_OWNED);
700 if (!(callout_pending(&hpriv->sackTimer))) {
701 /* If delayed ACK is disabled, send it now */
702 if (!hpriv->conf.enableDelayedAck) { /* ack now */
703 ng_pptpgre_xmit(hpriv, NULL);
704 /* ng_pptpgre_xmit() drops the mutex */
708 ng_pptpgre_start_send_ack_timer(hpriv);
709 mtx_unlock(&hpriv->mtx);
712 mtx_unlock(&hpriv->mtx);
716 * Delivers packets from the queue "q" to upper layers. Frees delivered
717 * entries with the exception of one equal to "st" that is allocated
718 * on caller's stack and not on the heap.
721 ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
723 struct ng_pptpgre_roq *np;
727 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
728 while (!SLIST_EMPTY(q)) {
730 SLIST_REMOVE_HEAD(q, next);
731 NGI_GET_M(np->item, m);
732 NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
734 free(np, M_NETGRAPH);
740 * Handle an incoming packet. The packet includes the IP header.
743 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
746 node_p node = NG_HOOK_NODE(hook);
747 const priv_p priv = NG_NODE_PRIVATE(node);
748 int iphlen, grelen, extralen;
749 const struct greheader *gre;
754 roqh sendq = SLIST_HEAD_INITIALIZER(sendq); /* send queue on stack */
755 struct ng_pptpgre_roq *last = NULL; /* last packet in the sendq */
756 struct ng_pptpgre_roq *np, *prev;
757 struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
763 priv->stats.recvPackets++;
764 priv->stats.recvOctets += m->m_pkthdr.len;
766 /* Sanity check packet length */
767 if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
768 priv->stats.recvRunts++;
772 /* Safely pull up the complete IP+GRE headers */
773 if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
774 if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
775 priv->stats.memoryFailures++;
781 ip = mtod(m, const struct ip *);
782 iphlen = ip->ip_hl << 2;
783 if (m->m_len < iphlen + sizeof(*gre)) {
784 if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
785 priv->stats.memoryFailures++;
790 ip = mtod(m, const struct ip *);
792 gre = (const struct greheader *)((const u_char *)ip + iphlen);
793 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
794 if (m->m_pkthdr.len < iphlen + grelen) {
795 priv->stats.recvRunts++;
798 if (m->m_len < iphlen + grelen) {
799 if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
800 priv->stats.memoryFailures++;
805 ip = mtod(m, const struct ip *);
806 gre = (const struct greheader *)((const u_char *)ip + iphlen);
809 /* Sanity check packet length and GRE header bits */
810 extralen = m->m_pkthdr.len
811 - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
813 priv->stats.recvBadGRE++;
816 if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
817 priv->stats.recvBadGRE++;
821 hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
822 if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
823 priv->stats.recvBadCID++;
826 mtx_lock(&hpriv->mtx);
828 /* Look for peer ack */
830 const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]);
831 const int index = ack - hpriv->recvAck - 1;
834 /* Sanity check ack value */
835 if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
836 priv->stats.recvBadAcks++;
837 goto badAck; /* we never sent it! */
839 if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
840 goto badAck; /* ack already timed out */
841 hpriv->recvAck = ack;
843 /* Update adaptive timeout stuff */
844 if (hpriv->conf.enableWindowing) {
845 sample = ng_pptpgre_time() - hpriv->timeSent[index];
846 diff = sample - hpriv->rtt;
847 hpriv->rtt += PPTP_ACK_ALPHA(diff);
850 hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
851 /* +2 to compensate low precision of int math */
852 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
853 if (hpriv->ato > PPTP_MAX_TIMEOUT)
854 hpriv->ato = PPTP_MAX_TIMEOUT;
855 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
856 hpriv->ato = PPTP_MIN_TIMEOUT;
858 /* Shift packet transmit times in our transmit window */
859 bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
860 sizeof(*hpriv->timeSent)
861 * (PPTP_XMIT_WIN - (index + 1)));
863 /* If we sent an entire window, increase window size */
864 if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
865 && hpriv->xmitWin < PPTP_XMIT_WIN) {
867 hpriv->winAck = ack + hpriv->xmitWin;
870 /* Stop/(re)start receive ACK timer as necessary */
871 ng_uncallout(&hpriv->rackTimer, hpriv->node);
872 if (hpriv->recvAck != hpriv->xmitSeq)
873 ng_pptpgre_start_recv_ack_timer(hpriv);
878 /* See if frame contains any data */
879 if (!gre->hasSeq) { /* no data to deliver */
880 priv->stats.recvLoneAcks++;
881 mtx_unlock(&hpriv->mtx);
885 seq = be32dec(&gre->data[0]);
887 diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
888 if (diff <= 0) { /* late or duplicate packet */
889 if (diff < 0 && reorder_max == 0) /* reorder disabled */
890 priv->stats.recvOutOfOrder++; /* late */
892 priv->stats.recvDuplicates++; /* duplicate */
893 mtx_unlock(&hpriv->mtx);
897 /* Trim mbuf down to internal payload */
898 m_adj(m, iphlen + grelen);
902 #define INIT_SENDQ(t) do { \
905 SLIST_INSERT_HEAD(&sendq, &t, next); \
907 hpriv->recvSeq = seq; \
912 /* the packet came in order, place it at the start of sendq */
915 /* The packet came too early, try to enqueue it.
917 * Check for duplicate in the queue. After this loop, "prev" will be
918 * NULL if the packet should become new head of the queue,
919 * or else it should be inserted after the "prev".
921 prev = SLIST_FIRST(&hpriv->roq);
922 SLIST_FOREACH(np, &hpriv->roq, next) {
923 diff = PPTP_SEQ_DIFF(np->seq, seq);
924 if (diff == 0) { /* do not add duplicate, drop it */
925 priv->stats.recvDuplicates++;
926 mtx_unlock(&hpriv->mtx);
929 if (diff > 0) { /* we found newer packet */
930 if (np == prev) /* that is the head of the queue */
931 prev = NULL; /* put current packet to the head */
937 priv->stats.recvOutOfOrder++; /* duplicate not found */
938 if (hpriv->roq_len < reorder_max)
939 goto enqueue; /* reorder enabled and there is a room */
942 * There is no room in the queue or reorder disabled.
944 * It the latter case, we may still have non-empty reorder queue
945 * if reorder was disabled in process of reordering.
946 * Then we correctly deliver the queue without growing it.
948 * In both cases, no malloc()'s until the queue is shortened.
950 priv->stats.recvReorderOverflow++;
951 if (prev == NULL) { /* new packet goes before the head */
952 INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
957 * Current packet goes after the head of reorder queue.
958 * Move the head to sendq to make room for current packet.
960 np = SLIST_FIRST(&hpriv->roq);
963 SLIST_REMOVE_HEAD(&hpriv->roq, next);
964 hpriv->roq_len--; /* we are allowed to use malloc() now */
965 SLIST_INSERT_HEAD(&sendq, np, next);
967 hpriv->recvSeq = np->seq;
970 np = malloc(sizeof(*np), M_NETGRAPH, M_NOWAIT | M_ZERO);
972 priv->stats.memoryFailures++;
974 * Emergency: we cannot save new data.
975 * Flush the queue delivering all queued packets preceeding
976 * current one despite of gaps.
978 while (!SLIST_EMPTY(&hpriv->roq)) {
979 np = SLIST_FIRST(&hpriv->roq);
982 SLIST_REMOVE_HEAD(&hpriv->roq, next);
985 SLIST_INSERT_HEAD(&sendq, np, next);
987 SLIST_INSERT_AFTER(last, np, next);
992 * Pretend we got all packets till the current one
993 * and acknowledge it.
995 hpriv->recvSeq = seq;
996 ng_pptpgre_ack(hpriv); /* drops lock */
997 ng_pptpgre_sendq(hpriv, &sendq, &temp);
998 NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1002 /* Add current (early) packet to the reorder queue. */
1006 SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1008 SLIST_INSERT_AFTER(prev, np, next);
1012 /* Look if we have some packets in sequence after sendq. */
1013 while (!SLIST_EMPTY(&hpriv->roq)) {
1014 np = SLIST_FIRST(&hpriv->roq);
1015 if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1016 break; /* the gap in the sequence */
1018 /* "np" is in sequence, move it to the sendq. */
1019 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1021 hpriv->recvSeq = np->seq;
1024 SLIST_INSERT_HEAD(&sendq, np, next);
1026 SLIST_INSERT_AFTER(last, np, next);
1030 if (SLIST_EMPTY(&hpriv->roq)) {
1031 if (callout_pending(&hpriv->reorderTimer))
1032 ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1034 if (!callout_pending(&hpriv->reorderTimer))
1035 ng_pptpgre_start_reorder_timer(hpriv);
1038 if (SLIST_EMPTY(&sendq)) {
1039 /* Current packet has been queued, nothing to free/deliver. */
1040 mtx_unlock(&hpriv->mtx);
1044 /* We need to acknowledge last packet; do it soon... */
1045 ng_pptpgre_ack(hpriv); /* drops lock */
1046 ng_pptpgre_sendq(hpriv, &sendq, &temp);
1054 /*************************************************************************
1055 TIMER RELATED FUNCTIONS
1056 *************************************************************************/
1059 * Start a timer for the peer's acknowledging our oldest unacknowledged
1060 * sequence number. If we get an ack for this sequence number before
1061 * the timer goes off, we cancel the timer. Resets currently running
1062 * recv ack timer, if any.
1065 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1069 /* Compute how long until oldest unack'd packet times out,
1070 and reset the timer to that time. */
1071 remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1075 /* Be conservative: timeout can happen up to 1 tick early */
1076 ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1077 ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1078 ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
1082 * The peer has failed to acknowledge the oldest unacknowledged sequence
1083 * number within the time allotted. Update our adaptive timeout parameters
1084 * and reset/restart the recv ack timer.
1087 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1089 const priv_p priv = NG_NODE_PRIVATE(node);
1090 const hpriv_p hpriv = arg1;
1092 /* Update adaptive timeout stuff */
1093 priv->stats.recvAckTimeouts++;
1094 hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1095 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1096 if (hpriv->ato > PPTP_MAX_TIMEOUT)
1097 hpriv->ato = PPTP_MAX_TIMEOUT;
1098 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1099 hpriv->ato = PPTP_MIN_TIMEOUT;
1101 /* Reset ack and sliding window */
1102 hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */
1103 hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */
1104 hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */
1108 * Start the send ack timer. This assumes the timer is not
1112 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
1114 int ackTimeout, ticks;
1116 /* Take 1/4 of the estimated round trip time */
1117 ackTimeout = (hpriv->rtt >> 2);
1118 if (ackTimeout < PPTP_MIN_ACK_DELAY)
1119 ackTimeout = PPTP_MIN_ACK_DELAY;
1120 else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1121 ackTimeout = PPTP_MAX_ACK_DELAY;
1123 /* Be conservative: timeout can happen up to 1 tick early */
1124 ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1125 ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1126 ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
1130 * We've waited as long as we're willing to wait before sending an
1131 * acknowledgement to the peer for received frames. We had hoped to
1132 * be able to piggy back our acknowledgement on an outgoing data frame,
1133 * but apparently there haven't been any since. So send the ack now.
1136 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1138 const hpriv_p hpriv = arg1;
1140 mtx_lock(&hpriv->mtx);
1141 /* Send a frame with an ack but no payload */
1142 ng_pptpgre_xmit(hpriv, NULL);
1143 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1147 * Start a timer for the reorder queue. This assumes the timer is not
1151 ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1155 /* Be conservative: timeout can happen up to 1 tick early */
1156 ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1157 ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1158 ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1162 * The oldest packet spent too much time in the reorder queue.
1163 * Deliver it and next packets in sequence, if any.
1166 ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1168 const priv_p priv = NG_NODE_PRIVATE(node);
1169 const hpriv_p hpriv = arg1;
1170 roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1171 struct ng_pptpgre_roq *np, *last = NULL;
1173 priv->stats.recvReorderTimeouts++;
1174 mtx_lock(&hpriv->mtx);
1175 if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1176 mtx_unlock(&hpriv->mtx);
1180 last = np = SLIST_FIRST(&hpriv->roq);
1182 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1183 SLIST_INSERT_HEAD(&sendq, np, next);
1185 /* Look if we have more packets in sequence */
1186 while (!SLIST_EMPTY(&hpriv->roq)) {
1187 np = SLIST_FIRST(&hpriv->roq);
1188 if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1189 break; /* the gap in the sequence */
1191 /* Next packet is in sequence, move it to the sendq. */
1193 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1194 SLIST_INSERT_AFTER(last, np, next);
1198 hpriv->recvSeq = last->seq;
1199 if (!SLIST_EMPTY(&hpriv->roq))
1200 ng_pptpgre_start_reorder_timer(hpriv);
1202 /* We need to acknowledge last packet; do it soon... */
1203 ng_pptpgre_ack(hpriv); /* drops lock */
1204 ng_pptpgre_sendq(hpriv, &sendq, NULL);
1205 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1208 /*************************************************************************
1210 *************************************************************************/
1213 * Find the hook with a given session ID.
1216 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1218 uint16_t hash = SESSHASH(cid);
1219 hpriv_p hpriv = NULL;
1221 LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1222 if (hpriv->conf.cid == cid)
1230 * Reset state (must be called with lock held or from writer)
1233 ng_pptpgre_reset(hpriv_p hpriv)
1235 struct ng_pptpgre_roq *np;
1237 /* Reset adaptive timeout state */
1238 hpriv->ato = PPTP_MAX_TIMEOUT;
1239 hpriv->rtt = PPTP_TIME_SCALE / 10;
1240 if (hpriv->conf.peerPpd > 1) /* ppd = 0 treat as = 1 */
1241 hpriv->rtt *= hpriv->conf.peerPpd;
1243 hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1244 if (hpriv->xmitWin < 2) /* often the first packet is lost */
1245 hpriv->xmitWin = 2; /* because the peer isn't ready */
1246 else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1247 hpriv->xmitWin = PPTP_XMIT_WIN;
1248 hpriv->winAck = hpriv->xmitWin;
1250 /* Reset sequence numbers */
1251 hpriv->recvSeq = ~0;
1252 hpriv->recvAck = ~0;
1253 hpriv->xmitSeq = ~0;
1254 hpriv->xmitAck = ~0;
1257 ng_uncallout(&hpriv->sackTimer, hpriv->node);
1258 ng_uncallout(&hpriv->rackTimer, hpriv->node);
1259 ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1261 /* Clear reorder queue */
1262 while (!SLIST_EMPTY(&hpriv->roq)) {
1263 np = SLIST_FIRST(&hpriv->roq);
1264 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1265 NG_FREE_ITEM(np->item);
1266 free(np, M_NETGRAPH);
1272 * Return the current time scaled & translated to our internally used format.
1275 ng_pptpgre_time(void)
1281 t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1282 t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);