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>
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>
62 #include <sys/malloc.h>
64 #include <sys/mutex.h>
65 #include <sys/endian.h>
66 #include <sys/errno.h>
67 #include <sys/sysctl.h>
69 #include <netinet/in.h>
70 #include <netinet/in_systm.h>
71 #include <netinet/ip.h>
73 #include <netgraph/ng_message.h>
74 #include <netgraph/netgraph.h>
75 #include <netgraph/ng_parse.h>
76 #include <netgraph/ng_pptpgre.h>
78 /* GRE packet format, as used by PPTP */
80 #if BYTE_ORDER == LITTLE_ENDIAN
81 u_char recursion:3; /* recursion control */
82 u_char ssr:1; /* strict source route */
83 u_char hasSeq:1; /* sequence number present */
84 u_char hasKey:1; /* key present */
85 u_char hasRoute:1; /* routing present */
86 u_char hasSum:1; /* checksum present */
87 u_char vers:3; /* version */
88 u_char flags:4; /* flags */
89 u_char hasAck:1; /* acknowlege number present */
90 #elif BYTE_ORDER == BIG_ENDIAN
91 u_char hasSum:1; /* checksum present */
92 u_char hasRoute:1; /* routing present */
93 u_char hasKey:1; /* key present */
94 u_char hasSeq:1; /* sequence number present */
95 u_char ssr:1; /* strict source route */
96 u_char recursion:3; /* recursion control */
97 u_char hasAck:1; /* acknowlege number present */
98 u_char flags:4; /* flags */
99 u_char vers:3; /* version */
101 #error BYTE_ORDER is not defined properly
103 u_int16_t proto; /* protocol (ethertype) */
104 u_int16_t length; /* payload length */
105 u_int16_t cid; /* call id */
106 u_int32_t data[0]; /* opt. seq, ack, then data */
109 /* The PPTP protocol ID used in the GRE 'proto' field */
110 #define PPTP_GRE_PROTO 0x880b
112 /* Bits that must be set a certain way in all PPTP/GRE packets */
113 #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO)
114 #define PPTP_INIT_MASK 0xef7fffff
116 /* Min and max packet length */
117 #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8)
119 /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
120 #define PPTP_TIME_SCALE 1024 /* milliseconds */
121 typedef u_int64_t pptptime_t;
123 /* Acknowledgment timeout parameters and functions */
124 #define PPTP_XMIT_WIN 16 /* max xmit window */
125 #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 83) /* 12 milliseconds */
126 #define PPTP_MAX_TIMEOUT (3 * PPTP_TIME_SCALE) /* 3 seconds */
128 #define PPTP_REORDER_TIMEOUT 1
130 /* When we receive a packet, we wait to see if there's an outgoing packet
131 we can piggy-back the ACK off of. These parameters determine the mimimum
132 and maxmimum length of time we're willing to wait in order to do that.
133 These have no effect unless "enableDelayedAck" is turned on. */
134 #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */
135 #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */
137 /* See RFC 2637 section 4.4 */
138 #define PPTP_ACK_ALPHA(x) (((x) + 4) >> 3) /* alpha = 0.125 */
139 #define PPTP_ACK_BETA(x) (((x) + 2) >> 2) /* beta = 0.25 */
140 #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */
141 #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */
143 #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y))
145 #define SESSHASHSIZE 0x0020
146 #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
148 SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
152 * Reorder queue maximum length. Zero disables reorder.
154 * The node may keep reorder_max queue entries per session
155 * if reorder is enabled, plus allocate one more for short time.
157 * Be conservative in memory consumption by default.
158 * Lots of sessions with large queues can overflow M_NETGRAPH zone.
160 static int reorder_max = 1; /* reorder up to two swapped packets in a row */
161 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
162 &reorder_max, 0, "Reorder queue maximum length");
164 static int reorder_timeout = PPTP_REORDER_TIMEOUT;
165 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
166 &reorder_timeout, 0, "Reorder timeout is milliseconds");
168 /* Packet reorder FIFO queue */
169 struct ng_pptpgre_roq {
170 SLIST_ENTRY(ng_pptpgre_roq) next; /* next entry of the queue */
171 item_p item; /* netgraph item */
172 u_int32_t seq; /* packet sequence number */
174 SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
175 typedef struct ng_pptpgre_roq_head roqh;
177 /* We keep packet retransmit and acknowlegement state in this struct */
178 struct ng_pptpgre_sess {
179 node_p node; /* this node pointer */
180 hook_p hook; /* hook to upper layers */
181 struct ng_pptpgre_conf conf; /* configuration info */
182 struct mtx mtx; /* session mutex */
183 u_int32_t recvSeq; /* last seq # we rcv'd */
184 u_int32_t xmitSeq; /* last seq # we sent */
185 u_int32_t recvAck; /* last seq # peer ack'd */
186 u_int32_t xmitAck; /* last seq # we ack'd */
187 int32_t ato; /* adaptive time-out value */
188 int32_t rtt; /* round trip time estimate */
189 int32_t dev; /* deviation estimate */
190 u_int16_t xmitWin; /* size of xmit window */
191 struct callout sackTimer; /* send ack timer */
192 struct callout rackTimer; /* recv ack timer */
193 u_int32_t winAck; /* seq when xmitWin will grow */
194 pptptime_t timeSent[PPTP_XMIT_WIN];
195 LIST_ENTRY(ng_pptpgre_sess) sessions;
196 roqh roq; /* reorder queue head */
197 u_int8_t roq_len; /* reorder queue length */
198 struct callout reorderTimer; /* reorder timeout handler */
200 typedef struct ng_pptpgre_sess *hpriv_p;
202 /* Node private data */
203 struct ng_pptpgre_private {
204 hook_p upper; /* hook to upper layers */
205 hook_p lower; /* hook to lower layers */
206 struct ng_pptpgre_sess uppersess; /* default session for compat */
207 LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
208 struct ng_pptpgre_stats stats; /* node statistics */
210 typedef struct ng_pptpgre_private *priv_p;
212 /* Netgraph node methods */
213 static ng_constructor_t ng_pptpgre_constructor;
214 static ng_rcvmsg_t ng_pptpgre_rcvmsg;
215 static ng_shutdown_t ng_pptpgre_shutdown;
216 static ng_newhook_t ng_pptpgre_newhook;
217 static ng_rcvdata_t ng_pptpgre_rcvdata;
218 static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
219 static ng_disconnect_t ng_pptpgre_disconnect;
221 /* Helper functions */
222 static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
223 static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
224 static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
225 static void ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
226 static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
227 void *arg1, int arg2);
228 static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
229 void *arg1, int arg2);
230 static void ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
231 void *arg1, int arg2);
232 static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
233 static void ng_pptpgre_reset(hpriv_p hpriv);
234 static pptptime_t ng_pptpgre_time(void);
235 static void ng_pptpgre_ack(const hpriv_p hpriv);
236 static int ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
237 const struct ng_pptpgre_roq *st);
239 /* Parse type for struct ng_pptpgre_conf */
240 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
241 = NG_PPTPGRE_CONF_TYPE_INFO;
242 static const struct ng_parse_type ng_pptpgre_conf_type = {
243 &ng_parse_struct_type,
244 &ng_pptpgre_conf_type_fields,
247 /* Parse type for struct ng_pptpgre_stats */
248 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
249 = NG_PPTPGRE_STATS_TYPE_INFO;
250 static const struct ng_parse_type ng_pptp_stats_type = {
251 &ng_parse_struct_type,
252 &ng_pptpgre_stats_type_fields
255 /* List of commands and how to convert arguments to/from ASCII */
256 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
259 NGM_PPTPGRE_SET_CONFIG,
261 &ng_pptpgre_conf_type,
266 NGM_PPTPGRE_GET_CONFIG,
268 &ng_parse_hint16_type,
269 &ng_pptpgre_conf_type
273 NGM_PPTPGRE_GET_STATS,
280 NGM_PPTPGRE_CLR_STATS,
287 NGM_PPTPGRE_GETCLR_STATS,
295 /* Node type descriptor */
296 static struct ng_type ng_pptpgre_typestruct = {
297 .version = NG_ABI_VERSION,
298 .name = NG_PPTPGRE_NODE_TYPE,
299 .constructor = ng_pptpgre_constructor,
300 .rcvmsg = ng_pptpgre_rcvmsg,
301 .shutdown = ng_pptpgre_shutdown,
302 .newhook = ng_pptpgre_newhook,
303 .rcvdata = ng_pptpgre_rcvdata,
304 .disconnect = ng_pptpgre_disconnect,
305 .cmdlist = ng_pptpgre_cmdlist,
307 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
309 #define ERROUT(x) do { error = (x); goto done; } while (0)
311 /************************************************************************
313 ************************************************************************/
316 * Node type constructor
319 ng_pptpgre_constructor(node_p node)
324 /* Allocate private structure */
325 priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
327 NG_NODE_SET_PRIVATE(node, priv);
329 /* Initialize state */
330 mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
331 ng_callout_init(&priv->uppersess.sackTimer);
332 ng_callout_init(&priv->uppersess.rackTimer);
333 priv->uppersess.node = node;
335 SLIST_INIT(&priv->uppersess.roq);
336 priv->uppersess.roq_len = 0;
337 ng_callout_init(&priv->uppersess.reorderTimer);
339 for (i = 0; i < SESSHASHSIZE; i++)
340 LIST_INIT(&priv->sesshash[i]);
342 LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
349 * Give our OK for a hook to be added.
352 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
354 const priv_p priv = NG_NODE_PRIVATE(node);
356 /* Check hook name */
357 if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
359 priv->uppersess.hook = hook;
360 NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
361 } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
363 NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
365 static const char hexdig[16] = "0123456789abcdef";
371 /* Parse hook name to get session ID */
372 if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
373 sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
375 hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
376 for (cid = i = 0; i < 4; i++) {
377 for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
380 cid = (cid << 4) | j;
385 hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
389 /* Initialize state */
390 mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
391 ng_callout_init(&hpriv->sackTimer);
392 ng_callout_init(&hpriv->rackTimer);
393 hpriv->conf.cid = cid;
397 SLIST_INIT(&hpriv->roq);
399 ng_callout_init(&hpriv->reorderTimer);
401 NG_HOOK_SET_PRIVATE(hook, hpriv);
403 hash = SESSHASH(cid);
404 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
411 * Receive a control message.
414 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
416 const priv_p priv = NG_NODE_PRIVATE(node);
417 struct ng_mesg *resp = NULL;
421 NGI_GET_MSG(item, msg);
422 switch (msg->header.typecookie) {
423 case NGM_PPTPGRE_COOKIE:
424 switch (msg->header.cmd) {
425 case NGM_PPTPGRE_SET_CONFIG:
427 struct ng_pptpgre_conf *const newConf =
428 (struct ng_pptpgre_conf *) msg->data;
432 /* Check for invalid or illegal config */
433 if (msg->header.arglen != sizeof(*newConf))
435 /* Try to find session by cid. */
436 hpriv = ng_pptpgre_find_session(priv, newConf->cid);
437 /* If not present - use upper. */
439 hpriv = &priv->uppersess;
440 LIST_REMOVE(hpriv, sessions);
441 hash = SESSHASH(newConf->cid);
442 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
445 ng_pptpgre_reset(hpriv); /* reset on configure */
446 hpriv->conf = *newConf;
449 case NGM_PPTPGRE_GET_CONFIG:
453 if (msg->header.arglen == 2) {
454 /* Try to find session by cid. */
455 hpriv = ng_pptpgre_find_session(priv,
456 *((uint16_t *)msg->data));
459 } else if (msg->header.arglen == 0) {
461 hpriv = &priv->uppersess;
464 NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
467 bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
470 case NGM_PPTPGRE_GET_STATS:
471 case NGM_PPTPGRE_CLR_STATS:
472 case NGM_PPTPGRE_GETCLR_STATS:
474 if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
475 NG_MKRESPONSE(resp, msg,
476 sizeof(priv->stats), M_NOWAIT);
480 resp->data, sizeof(priv->stats));
482 if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
483 bzero(&priv->stats, sizeof(priv->stats));
496 NG_RESPOND_MSG(error, node, item, resp);
502 * Receive incoming data on a hook.
505 ng_pptpgre_rcvdata(hook_p hook, item_p item)
507 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
510 /* If not configured, reject */
511 if (!hpriv->conf.enabled) {
516 mtx_lock(&hpriv->mtx);
518 rval = ng_pptpgre_xmit(hpriv, item);
520 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
529 ng_pptpgre_disconnect(hook_p hook)
531 const node_p node = NG_HOOK_NODE(hook);
532 const priv_p priv = NG_NODE_PRIVATE(node);
533 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
535 /* Zero out hook pointer */
536 if (hook == priv->upper) {
538 priv->uppersess.hook = NULL;
539 } else if (hook == priv->lower) {
542 /* Reset node (stops timers) */
543 ng_pptpgre_reset(hpriv);
545 LIST_REMOVE(hpriv, sessions);
546 mtx_destroy(&hpriv->mtx);
547 free(hpriv, M_NETGRAPH);
550 /* Go away if no longer connected to anything */
551 if ((NG_NODE_NUMHOOKS(node) == 0)
552 && (NG_NODE_IS_VALID(node)))
553 ng_rmnode_self(node);
561 ng_pptpgre_shutdown(node_p node)
563 const priv_p priv = NG_NODE_PRIVATE(node);
565 /* Reset node (stops timers) */
566 ng_pptpgre_reset(&priv->uppersess);
568 LIST_REMOVE(&priv->uppersess, sessions);
569 mtx_destroy(&priv->uppersess.mtx);
571 free(priv, M_NETGRAPH);
573 /* Decrement ref count */
578 /*************************************************************************
579 TRANSMIT AND RECEIVE FUNCTIONS
580 *************************************************************************/
583 * Transmit an outgoing frame, or just an ack if m is NULL.
586 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
588 const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
589 u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
590 struct greheader *const gre = (struct greheader *)buf;
594 mtx_assert(&hpriv->mtx, MA_OWNED);
601 /* Check if there's data */
603 /* Check if windowing is enabled */
604 if (hpriv->conf.enableWindowing) {
605 /* Is our transmit window full? */
606 if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
607 hpriv->recvAck) >= hpriv->xmitWin) {
608 priv->stats.xmitDrops++;
613 /* Sanity check frame length */
614 if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
615 priv->stats.xmitTooBig++;
619 priv->stats.xmitLoneAcks++;
622 /* Build GRE header */
623 be32enc(gre, PPTP_INIT_VALUE);
624 be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
625 be16enc(&gre->cid, hpriv->conf.peerCid);
627 /* Include sequence number if packet contains any data */
630 if (hpriv->conf.enableWindowing) {
631 hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
635 be32enc(&gre->data[0], hpriv->xmitSeq);
638 /* Include acknowledgement (and stop send ack timer) if needed */
639 if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
641 be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
642 hpriv->xmitAck = hpriv->recvSeq;
643 if (hpriv->conf.enableDelayedAck)
644 ng_uncallout(&hpriv->sackTimer, hpriv->node);
647 /* Prepend GRE header to outgoing frame */
648 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
650 MGETHDR(m, M_NOWAIT, MT_DATA);
652 priv->stats.memoryFailures++;
655 m->m_len = m->m_pkthdr.len = grelen;
656 m->m_pkthdr.rcvif = NULL;
658 M_PREPEND(m, grelen, M_NOWAIT);
659 if (m == NULL || (m->m_len < grelen
660 && (m = m_pullup(m, grelen)) == NULL)) {
661 priv->stats.memoryFailures++;
665 bcopy(gre, mtod(m, u_char *), grelen);
668 priv->stats.xmitPackets++;
669 priv->stats.xmitOctets += m->m_pkthdr.len;
672 * XXX: we should reset timer only after an item has been sent
675 if (hpriv->conf.enableWindowing &&
676 gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
677 ng_pptpgre_start_recv_ack_timer(hpriv);
679 mtx_unlock(&hpriv->mtx);
683 NG_FWD_NEW_DATA(error, item, priv->lower, m);
685 NG_SEND_DATA_ONLY(error, priv->lower, m);
691 mtx_unlock(&hpriv->mtx);
699 ng_pptpgre_ack(const hpriv_p hpriv)
701 mtx_assert(&hpriv->mtx, MA_OWNED);
702 if (!(callout_pending(&hpriv->sackTimer))) {
703 /* If delayed ACK is disabled, send it now */
704 if (!hpriv->conf.enableDelayedAck) { /* ack now */
705 ng_pptpgre_xmit(hpriv, NULL);
706 /* ng_pptpgre_xmit() drops the mutex */
710 ng_pptpgre_start_send_ack_timer(hpriv);
711 mtx_unlock(&hpriv->mtx);
714 mtx_unlock(&hpriv->mtx);
718 * Delivers packets from the queue "q" to upper layers. Frees delivered
719 * entries with the exception of one equal to "st" that is allocated
720 * on caller's stack and not on the heap.
723 ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
725 struct ng_pptpgre_roq *np;
729 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
730 while (!SLIST_EMPTY(q)) {
732 SLIST_REMOVE_HEAD(q, next);
733 NGI_GET_M(np->item, m);
734 NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
736 free(np, M_NETGRAPH);
742 * Handle an incoming packet. The packet includes the IP header.
745 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
748 node_p node = NG_HOOK_NODE(hook);
749 const priv_p priv = NG_NODE_PRIVATE(node);
750 int iphlen, grelen, extralen;
751 const struct greheader *gre;
756 roqh sendq = SLIST_HEAD_INITIALIZER(sendq); /* send queue on stack */
757 struct ng_pptpgre_roq *last = NULL; /* last packet in the sendq */
758 struct ng_pptpgre_roq *np, *prev;
759 struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
765 priv->stats.recvPackets++;
766 priv->stats.recvOctets += m->m_pkthdr.len;
768 /* Sanity check packet length */
769 if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
770 priv->stats.recvRunts++;
774 /* Safely pull up the complete IP+GRE headers */
775 if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
776 if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
777 priv->stats.memoryFailures++;
783 ip = mtod(m, const struct ip *);
784 iphlen = ip->ip_hl << 2;
785 if (m->m_len < iphlen + sizeof(*gre)) {
786 if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
787 priv->stats.memoryFailures++;
792 ip = mtod(m, const struct ip *);
794 gre = (const struct greheader *)((const u_char *)ip + iphlen);
795 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
796 if (m->m_pkthdr.len < iphlen + grelen) {
797 priv->stats.recvRunts++;
800 if (m->m_len < iphlen + grelen) {
801 if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
802 priv->stats.memoryFailures++;
807 ip = mtod(m, const struct ip *);
808 gre = (const struct greheader *)((const u_char *)ip + iphlen);
811 /* Sanity check packet length and GRE header bits */
812 extralen = m->m_pkthdr.len
813 - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
815 priv->stats.recvBadGRE++;
818 if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
819 priv->stats.recvBadGRE++;
823 hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
824 if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
825 priv->stats.recvBadCID++;
828 mtx_lock(&hpriv->mtx);
830 /* Look for peer ack */
832 const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]);
833 const int index = ack - hpriv->recvAck - 1;
836 /* Sanity check ack value */
837 if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
838 priv->stats.recvBadAcks++;
839 goto badAck; /* we never sent it! */
841 if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
842 goto badAck; /* ack already timed out */
843 hpriv->recvAck = ack;
845 /* Update adaptive timeout stuff */
846 if (hpriv->conf.enableWindowing) {
847 sample = ng_pptpgre_time() - hpriv->timeSent[index];
848 diff = sample - hpriv->rtt;
849 hpriv->rtt += PPTP_ACK_ALPHA(diff);
852 hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
853 /* +2 to compensate low precision of int math */
854 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
855 if (hpriv->ato > PPTP_MAX_TIMEOUT)
856 hpriv->ato = PPTP_MAX_TIMEOUT;
857 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
858 hpriv->ato = PPTP_MIN_TIMEOUT;
860 /* Shift packet transmit times in our transmit window */
861 bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
862 sizeof(*hpriv->timeSent)
863 * (PPTP_XMIT_WIN - (index + 1)));
865 /* If we sent an entire window, increase window size */
866 if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
867 && hpriv->xmitWin < PPTP_XMIT_WIN) {
869 hpriv->winAck = ack + hpriv->xmitWin;
872 /* Stop/(re)start receive ACK timer as necessary */
873 ng_uncallout(&hpriv->rackTimer, hpriv->node);
874 if (hpriv->recvAck != hpriv->xmitSeq)
875 ng_pptpgre_start_recv_ack_timer(hpriv);
880 /* See if frame contains any data */
881 if (!gre->hasSeq) { /* no data to deliver */
882 priv->stats.recvLoneAcks++;
883 mtx_unlock(&hpriv->mtx);
887 seq = be32dec(&gre->data[0]);
889 diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
890 if (diff <= 0) { /* late or duplicate packet */
891 if (diff < 0 && reorder_max == 0) /* reorder disabled */
892 priv->stats.recvOutOfOrder++; /* late */
894 priv->stats.recvDuplicates++; /* duplicate */
895 mtx_unlock(&hpriv->mtx);
899 /* Trim mbuf down to internal payload */
900 m_adj(m, iphlen + grelen);
904 #define INIT_SENDQ(t) do { \
907 SLIST_INSERT_HEAD(&sendq, &t, next); \
909 hpriv->recvSeq = seq; \
914 /* the packet came in order, place it at the start of sendq */
917 /* The packet came too early, try to enqueue it.
919 * Check for duplicate in the queue. After this loop, "prev" will be
920 * NULL if the packet should become new head of the queue,
921 * or else it should be inserted after the "prev".
923 prev = SLIST_FIRST(&hpriv->roq);
924 SLIST_FOREACH(np, &hpriv->roq, next) {
925 diff = PPTP_SEQ_DIFF(np->seq, seq);
926 if (diff == 0) { /* do not add duplicate, drop it */
927 priv->stats.recvDuplicates++;
928 mtx_unlock(&hpriv->mtx);
931 if (diff > 0) { /* we found newer packet */
932 if (np == prev) /* that is the head of the queue */
933 prev = NULL; /* put current packet to the head */
939 priv->stats.recvOutOfOrder++; /* duplicate not found */
940 if (hpriv->roq_len < reorder_max)
941 goto enqueue; /* reorder enabled and there is a room */
944 * There is no room in the queue or reorder disabled.
946 * It the latter case, we may still have non-empty reorder queue
947 * if reorder was disabled in process of reordering.
948 * Then we correctly deliver the queue without growing it.
950 * In both cases, no malloc()'s until the queue is shortened.
952 priv->stats.recvReorderOverflow++;
953 if (prev == NULL) { /* new packet goes before the head */
954 INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
959 * Current packet goes after the head of reorder queue.
960 * Move the head to sendq to make room for current packet.
962 np = SLIST_FIRST(&hpriv->roq);
965 SLIST_REMOVE_HEAD(&hpriv->roq, next);
966 hpriv->roq_len--; /* we are allowed to use malloc() now */
967 SLIST_INSERT_HEAD(&sendq, np, next);
969 hpriv->recvSeq = np->seq;
972 np = malloc(sizeof(*np), M_NETGRAPH, M_NOWAIT | M_ZERO);
974 priv->stats.memoryFailures++;
976 * Emergency: we cannot save new data.
977 * Flush the queue delivering all queued packets preceeding
978 * current one despite of gaps.
980 while (!SLIST_EMPTY(&hpriv->roq)) {
981 np = SLIST_FIRST(&hpriv->roq);
984 SLIST_REMOVE_HEAD(&hpriv->roq, next);
987 SLIST_INSERT_HEAD(&sendq, np, next);
989 SLIST_INSERT_AFTER(last, np, next);
994 * Pretend we got all packets till the current one
995 * and acknowledge it.
997 hpriv->recvSeq = seq;
998 ng_pptpgre_ack(hpriv); /* drops lock */
999 ng_pptpgre_sendq(hpriv, &sendq, &temp);
1000 NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1004 /* Add current (early) packet to the reorder queue. */
1008 SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1010 SLIST_INSERT_AFTER(prev, np, next);
1014 /* Look if we have some packets in sequence after sendq. */
1015 while (!SLIST_EMPTY(&hpriv->roq)) {
1016 np = SLIST_FIRST(&hpriv->roq);
1017 if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1018 break; /* the gap in the sequence */
1020 /* "np" is in sequence, move it to the sendq. */
1021 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1023 hpriv->recvSeq = np->seq;
1026 SLIST_INSERT_HEAD(&sendq, np, next);
1028 SLIST_INSERT_AFTER(last, np, next);
1032 if (SLIST_EMPTY(&hpriv->roq)) {
1033 if (callout_pending(&hpriv->reorderTimer))
1034 ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1036 if (!callout_pending(&hpriv->reorderTimer))
1037 ng_pptpgre_start_reorder_timer(hpriv);
1040 if (SLIST_EMPTY(&sendq)) {
1041 /* Current packet has been queued, nothing to free/deliver. */
1042 mtx_unlock(&hpriv->mtx);
1046 /* We need to acknowledge last packet; do it soon... */
1047 ng_pptpgre_ack(hpriv); /* drops lock */
1048 ng_pptpgre_sendq(hpriv, &sendq, &temp);
1056 /*************************************************************************
1057 TIMER RELATED FUNCTIONS
1058 *************************************************************************/
1061 * Start a timer for the peer's acknowledging our oldest unacknowledged
1062 * sequence number. If we get an ack for this sequence number before
1063 * the timer goes off, we cancel the timer. Resets currently running
1064 * recv ack timer, if any.
1067 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1071 /* Compute how long until oldest unack'd packet times out,
1072 and reset the timer to that time. */
1073 remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1077 /* Be conservative: timeout can happen up to 1 tick early */
1078 ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1079 ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1080 ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
1084 * The peer has failed to acknowledge the oldest unacknowledged sequence
1085 * number within the time allotted. Update our adaptive timeout parameters
1086 * and reset/restart the recv ack timer.
1089 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1091 const priv_p priv = NG_NODE_PRIVATE(node);
1092 const hpriv_p hpriv = arg1;
1094 /* Update adaptive timeout stuff */
1095 priv->stats.recvAckTimeouts++;
1096 hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1097 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1098 if (hpriv->ato > PPTP_MAX_TIMEOUT)
1099 hpriv->ato = PPTP_MAX_TIMEOUT;
1100 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1101 hpriv->ato = PPTP_MIN_TIMEOUT;
1103 /* Reset ack and sliding window */
1104 hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */
1105 hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */
1106 hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */
1110 * Start the send ack timer. This assumes the timer is not
1114 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
1116 int ackTimeout, ticks;
1118 /* Take 1/4 of the estimated round trip time */
1119 ackTimeout = (hpriv->rtt >> 2);
1120 if (ackTimeout < PPTP_MIN_ACK_DELAY)
1121 ackTimeout = PPTP_MIN_ACK_DELAY;
1122 else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1123 ackTimeout = PPTP_MAX_ACK_DELAY;
1125 /* Be conservative: timeout can happen up to 1 tick early */
1126 ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1127 ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1128 ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
1132 * We've waited as long as we're willing to wait before sending an
1133 * acknowledgement to the peer for received frames. We had hoped to
1134 * be able to piggy back our acknowledgement on an outgoing data frame,
1135 * but apparently there haven't been any since. So send the ack now.
1138 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1140 const hpriv_p hpriv = arg1;
1142 mtx_lock(&hpriv->mtx);
1143 /* Send a frame with an ack but no payload */
1144 ng_pptpgre_xmit(hpriv, NULL);
1145 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1149 * Start a timer for the reorder queue. This assumes the timer is not
1153 ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1157 /* Be conservative: timeout can happen up to 1 tick early */
1158 ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1159 ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1160 ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1164 * The oldest packet spent too much time in the reorder queue.
1165 * Deliver it and next packets in sequence, if any.
1168 ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1170 const priv_p priv = NG_NODE_PRIVATE(node);
1171 const hpriv_p hpriv = arg1;
1172 roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1173 struct ng_pptpgre_roq *np, *last = NULL;
1175 priv->stats.recvReorderTimeouts++;
1176 mtx_lock(&hpriv->mtx);
1177 if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1178 mtx_unlock(&hpriv->mtx);
1182 last = np = SLIST_FIRST(&hpriv->roq);
1184 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1185 SLIST_INSERT_HEAD(&sendq, np, next);
1187 /* Look if we have more packets in sequence */
1188 while (!SLIST_EMPTY(&hpriv->roq)) {
1189 np = SLIST_FIRST(&hpriv->roq);
1190 if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1191 break; /* the gap in the sequence */
1193 /* Next packet is in sequence, move it to the sendq. */
1195 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1196 SLIST_INSERT_AFTER(last, np, next);
1200 hpriv->recvSeq = last->seq;
1201 if (!SLIST_EMPTY(&hpriv->roq))
1202 ng_pptpgre_start_reorder_timer(hpriv);
1204 /* We need to acknowledge last packet; do it soon... */
1205 ng_pptpgre_ack(hpriv); /* drops lock */
1206 ng_pptpgre_sendq(hpriv, &sendq, NULL);
1207 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1210 /*************************************************************************
1212 *************************************************************************/
1215 * Find the hook with a given session ID.
1218 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1220 uint16_t hash = SESSHASH(cid);
1221 hpriv_p hpriv = NULL;
1223 LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1224 if (hpriv->conf.cid == cid)
1232 * Reset state (must be called with lock held or from writer)
1235 ng_pptpgre_reset(hpriv_p hpriv)
1237 struct ng_pptpgre_roq *np;
1239 /* Reset adaptive timeout state */
1240 hpriv->ato = PPTP_MAX_TIMEOUT;
1241 hpriv->rtt = PPTP_TIME_SCALE / 10;
1242 if (hpriv->conf.peerPpd > 1) /* ppd = 0 treat as = 1 */
1243 hpriv->rtt *= hpriv->conf.peerPpd;
1245 hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1246 if (hpriv->xmitWin < 2) /* often the first packet is lost */
1247 hpriv->xmitWin = 2; /* because the peer isn't ready */
1248 else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1249 hpriv->xmitWin = PPTP_XMIT_WIN;
1250 hpriv->winAck = hpriv->xmitWin;
1252 /* Reset sequence numbers */
1253 hpriv->recvSeq = ~0;
1254 hpriv->recvAck = ~0;
1255 hpriv->xmitSeq = ~0;
1256 hpriv->xmitAck = ~0;
1259 ng_uncallout(&hpriv->sackTimer, hpriv->node);
1260 ng_uncallout(&hpriv->rackTimer, hpriv->node);
1261 ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1263 /* Clear reorder queue */
1264 while (!SLIST_EMPTY(&hpriv->roq)) {
1265 np = SLIST_FIRST(&hpriv->roq);
1266 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1267 NG_FREE_ITEM(np->item);
1268 free(np, M_NETGRAPH);
1274 * Return the current time scaled & translated to our internally used format.
1277 ng_pptpgre_time(void)
1283 t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1284 t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);