1 /*
2 * ng_pptpgre.c
3 */
4
5 /*-
6 * Copyright (c) 1996-1999 Whistle Communications, Inc.
7 * All rights reserved.
8 *
9 * Subject to the following obligations and disclaimer of warranty, use and
10 * redistribution of this software, in source or object code forms, with or
11 * without modifications are expressly permitted by Whistle Communications;
12 * provided, however, that:
13 * 1. Any and all reproductions of the source or object code must include the
14 * copyright notice above and the following disclaimer of warranties; and
15 * 2. No rights are granted, in any manner or form, to use Whistle
16 * Communications, Inc. trademarks, including the mark "WHISTLE
17 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18 * such appears in the above copyright notice or in the software.
19 *
20 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36 * OF SUCH DAMAGE.
37 *
38 * Author: Archie Cobbs <archie@freebsd.org>
39 * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
40 */
41
42 /*
43 * PPTP/GRE netgraph node type.
44 *
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.
49 *
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".
53 */
54
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/kernel.h>
58 #include <sys/time.h>
59 #include <sys/lock.h>
60 #include <sys/malloc.h>
61 #include <sys/mbuf.h>
62 #include <sys/mutex.h>
63 #include <sys/endian.h>
64 #include <sys/errno.h>
65 #include <sys/sysctl.h>
66
67 #include <netinet/in.h>
68 #include <netinet/in_systm.h>
69 #include <netinet/ip.h>
70
71 #include <netgraph/ng_message.h>
72 #include <netgraph/netgraph.h>
73 #include <netgraph/ng_parse.h>
74 #include <netgraph/ng_pptpgre.h>
75
76 #ifdef NG_SEPARATE_MALLOC
77 static MALLOC_DEFINE(M_NETGRAPH_PPTP, "netgraph_pptp", "netgraph pptpgre node");
78 #else
79 #define M_NETGRAPH_PPTP M_NETGRAPH
80 #endif
81
82 /* GRE packet format, as used by PPTP */
83 struct greheader {
84 #if BYTE_ORDER == LITTLE_ENDIAN
85 u_char recursion:3; /* recursion control */
86 u_char ssr:1; /* strict source route */
87 u_char hasSeq:1; /* sequence number present */
88 u_char hasKey:1; /* key present */
89 u_char hasRoute:1; /* routing present */
90 u_char hasSum:1; /* checksum present */
91 u_char vers:3; /* version */
92 u_char flags:4; /* flags */
93 u_char hasAck:1; /* acknowlege number present */
94 #elif BYTE_ORDER == BIG_ENDIAN
95 u_char hasSum:1; /* checksum present */
96 u_char hasRoute:1; /* routing present */
97 u_char hasKey:1; /* key present */
98 u_char hasSeq:1; /* sequence number present */
99 u_char ssr:1; /* strict source route */
100 u_char recursion:3; /* recursion control */
101 u_char hasAck:1; /* acknowlege number present */
102 u_char flags:4; /* flags */
103 u_char vers:3; /* version */
104 #else
105 #error BYTE_ORDER is not defined properly
106 #endif
107 u_int16_t proto; /* protocol (ethertype) */
108 u_int16_t length; /* payload length */
109 u_int16_t cid; /* call id */
110 u_int32_t data[0]; /* opt. seq, ack, then data */
111 };
112
113 /* The PPTP protocol ID used in the GRE 'proto' field */
114 #define PPTP_GRE_PROTO 0x880b
115
116 /* Bits that must be set a certain way in all PPTP/GRE packets */
117 #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO)
118 #define PPTP_INIT_MASK 0xef7fffff
119
120 /* Min and max packet length */
121 #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8)
122
123 /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
124 #define PPTP_TIME_SCALE 1024 /* milliseconds */
125 typedef u_int64_t pptptime_t;
126
127 /* Acknowledgment timeout parameters and functions */
128 #define PPTP_XMIT_WIN 16 /* max xmit window */
129 #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 83) /* 12 milliseconds */
130 #define PPTP_MAX_TIMEOUT (3 * PPTP_TIME_SCALE) /* 3 seconds */
131
132 #define PPTP_REORDER_TIMEOUT 1
133
134 /* When we receive a packet, we wait to see if there's an outgoing packet
135 we can piggy-back the ACK off of. These parameters determine the minimum
136 and maxmimum length of time we're willing to wait in order to do that.
137 These have no effect unless "enableDelayedAck" is turned on. */
138 #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */
139 #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */
140
141 /* See RFC 2637 section 4.4 */
142 #define PPTP_ACK_ALPHA(x) (((x) + 4) >> 3) /* alpha = 0.125 */
143 #define PPTP_ACK_BETA(x) (((x) + 2) >> 2) /* beta = 0.25 */
144 #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */
145 #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */
146
147 #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y))
148
149 #define SESSHASHSIZE 0x0020
150 #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
151
152 SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
153 "PPTPGRE");
154
155 /*
156 * Reorder queue maximum length. Zero disables reorder.
157 *
158 * The node may keep reorder_max queue entries per session
159 * if reorder is enabled, plus allocate one more for short time.
160 *
161 * Be conservative in memory consumption by default.
162 * Lots of sessions with large queues can overflow M_NETGRAPH zone.
163 */
164 static int reorder_max = 1; /* reorder up to two swapped packets in a row */
165 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
166 &reorder_max, 0, "Reorder queue maximum length");
167
168 static int reorder_timeout = PPTP_REORDER_TIMEOUT;
169 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
170 &reorder_timeout, 0, "Reorder timeout is milliseconds");
171
172 /* Packet reorder FIFO queue */
173 struct ng_pptpgre_roq {
174 SLIST_ENTRY(ng_pptpgre_roq) next; /* next entry of the queue */
175 item_p item; /* netgraph item */
176 u_int32_t seq; /* packet sequence number */
177 };
178 SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
179 typedef struct ng_pptpgre_roq_head roqh;
180
181 /* We keep packet retransmit and acknowlegement state in this struct */
182 struct ng_pptpgre_sess {
183 node_p node; /* this node pointer */
184 hook_p hook; /* hook to upper layers */
185 struct ng_pptpgre_conf conf; /* configuration info */
186 struct mtx mtx; /* session mutex */
187 u_int32_t recvSeq; /* last seq # we rcv'd */
188 u_int32_t xmitSeq; /* last seq # we sent */
189 u_int32_t recvAck; /* last seq # peer ack'd */
190 u_int32_t xmitAck; /* last seq # we ack'd */
191 int32_t ato; /* adaptive time-out value */
192 int32_t rtt; /* round trip time estimate */
193 int32_t dev; /* deviation estimate */
194 u_int16_t xmitWin; /* size of xmit window */
195 struct callout sackTimer; /* send ack timer */
196 struct callout rackTimer; /* recv ack timer */
197 u_int32_t winAck; /* seq when xmitWin will grow */
198 pptptime_t timeSent[PPTP_XMIT_WIN];
199 LIST_ENTRY(ng_pptpgre_sess) sessions;
200 roqh roq; /* reorder queue head */
201 u_int8_t roq_len; /* reorder queue length */
202 struct callout reorderTimer; /* reorder timeout handler */
203 };
204 typedef struct ng_pptpgre_sess *hpriv_p;
205
206 /* Node private data */
207 struct ng_pptpgre_private {
208 hook_p upper; /* hook to upper layers */
209 hook_p lower; /* hook to lower layers */
210 struct ng_pptpgre_sess uppersess; /* default session for compat */
211 LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
212 struct ng_pptpgre_stats stats; /* node statistics */
213 };
214 typedef struct ng_pptpgre_private *priv_p;
215
216 /* Netgraph node methods */
217 static ng_constructor_t ng_pptpgre_constructor;
218 static ng_rcvmsg_t ng_pptpgre_rcvmsg;
219 static ng_shutdown_t ng_pptpgre_shutdown;
220 static ng_newhook_t ng_pptpgre_newhook;
221 static ng_rcvdata_t ng_pptpgre_rcvdata;
222 static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
223 static ng_disconnect_t ng_pptpgre_disconnect;
224
225 /* Helper functions */
226 static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
227 static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
228 static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
229 static void ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
230 static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
231 void *arg1, int arg2);
232 static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
233 void *arg1, int arg2);
234 static void ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
235 void *arg1, int arg2);
236 static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
237 static void ng_pptpgre_reset(hpriv_p hpriv);
238 static pptptime_t ng_pptpgre_time(void);
239 static void ng_pptpgre_ack(const hpriv_p hpriv);
240 static int ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
241 const struct ng_pptpgre_roq *st);
242
243 /* Parse type for struct ng_pptpgre_conf */
244 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
245 = NG_PPTPGRE_CONF_TYPE_INFO;
246 static const struct ng_parse_type ng_pptpgre_conf_type = {
247 &ng_parse_struct_type,
248 &ng_pptpgre_conf_type_fields,
249 };
250
251 /* Parse type for struct ng_pptpgre_stats */
252 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
253 = NG_PPTPGRE_STATS_TYPE_INFO;
254 static const struct ng_parse_type ng_pptp_stats_type = {
255 &ng_parse_struct_type,
256 &ng_pptpgre_stats_type_fields
257 };
258
259 /* List of commands and how to convert arguments to/from ASCII */
260 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
261 {
262 NGM_PPTPGRE_COOKIE,
263 NGM_PPTPGRE_SET_CONFIG,
264 "setconfig",
265 &ng_pptpgre_conf_type,
266 NULL
267 },
268 {
269 NGM_PPTPGRE_COOKIE,
270 NGM_PPTPGRE_GET_CONFIG,
271 "getconfig",
272 &ng_parse_hint16_type,
273 &ng_pptpgre_conf_type
274 },
275 {
276 NGM_PPTPGRE_COOKIE,
277 NGM_PPTPGRE_GET_STATS,
278 "getstats",
279 NULL,
280 &ng_pptp_stats_type
281 },
282 {
283 NGM_PPTPGRE_COOKIE,
284 NGM_PPTPGRE_CLR_STATS,
285 "clrstats",
286 NULL,
287 NULL
288 },
289 {
290 NGM_PPTPGRE_COOKIE,
291 NGM_PPTPGRE_GETCLR_STATS,
292 "getclrstats",
293 NULL,
294 &ng_pptp_stats_type
295 },
296 { 0 }
297 };
298
299 /* Node type descriptor */
300 static struct ng_type ng_pptpgre_typestruct = {
301 .version = NG_ABI_VERSION,
302 .name = NG_PPTPGRE_NODE_TYPE,
303 .constructor = ng_pptpgre_constructor,
304 .rcvmsg = ng_pptpgre_rcvmsg,
305 .shutdown = ng_pptpgre_shutdown,
306 .newhook = ng_pptpgre_newhook,
307 .rcvdata = ng_pptpgre_rcvdata,
308 .disconnect = ng_pptpgre_disconnect,
309 .cmdlist = ng_pptpgre_cmdlist,
310 };
311 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
312
313 #define ERROUT(x) do { error = (x); goto done; } while (0)
314
315 /************************************************************************
316 NETGRAPH NODE STUFF
317 ************************************************************************/
318
319 /*
320 * Node type constructor
321 */
322 static int
ng_pptpgre_constructor(node_p node)323 ng_pptpgre_constructor(node_p node)
324 {
325 priv_p priv;
326 int i;
327
328 /* Allocate private structure */
329 priv = malloc(sizeof(*priv), M_NETGRAPH_PPTP, M_WAITOK | M_ZERO);
330
331 NG_NODE_SET_PRIVATE(node, priv);
332
333 /* Initialize state */
334 mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
335 ng_callout_init(&priv->uppersess.sackTimer);
336 ng_callout_init(&priv->uppersess.rackTimer);
337 priv->uppersess.node = node;
338
339 SLIST_INIT(&priv->uppersess.roq);
340 priv->uppersess.roq_len = 0;
341 ng_callout_init(&priv->uppersess.reorderTimer);
342
343 for (i = 0; i < SESSHASHSIZE; i++)
344 LIST_INIT(&priv->sesshash[i]);
345
346 LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
347
348 /* Done */
349 return (0);
350 }
351
352 /*
353 * Give our OK for a hook to be added.
354 */
355 static int
ng_pptpgre_newhook(node_p node,hook_p hook,const char * name)356 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
357 {
358 const priv_p priv = NG_NODE_PRIVATE(node);
359
360 /* Check hook name */
361 if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
362 priv->upper = hook;
363 priv->uppersess.hook = hook;
364 NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
365 } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
366 priv->lower = hook;
367 NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
368 } else {
369 static const char hexdig[16] = "0123456789abcdef";
370 const char *hex;
371 hpriv_p hpriv;
372 int i, j;
373 uint16_t cid, hash;
374
375 /* Parse hook name to get session ID */
376 if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
377 sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
378 return (EINVAL);
379 hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
380 for (cid = i = 0; i < 4; i++) {
381 for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
382 if (j == 16)
383 return (EINVAL);
384 cid = (cid << 4) | j;
385 }
386 if (hex[i] != '\0')
387 return (EINVAL);
388
389 hpriv = malloc(sizeof(*hpriv), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
390 if (hpriv == NULL)
391 return (ENOMEM);
392
393 /* Initialize state */
394 mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
395 ng_callout_init(&hpriv->sackTimer);
396 ng_callout_init(&hpriv->rackTimer);
397 hpriv->conf.cid = cid;
398 hpriv->node = node;
399 hpriv->hook = hook;
400
401 SLIST_INIT(&hpriv->roq);
402 hpriv->roq_len = 0;
403 ng_callout_init(&hpriv->reorderTimer);
404
405 NG_HOOK_SET_PRIVATE(hook, hpriv);
406
407 hash = SESSHASH(cid);
408 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
409 }
410
411 return (0);
412 }
413
414 /*
415 * Receive a control message.
416 */
417 static int
ng_pptpgre_rcvmsg(node_p node,item_p item,hook_p lasthook)418 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
419 {
420 const priv_p priv = NG_NODE_PRIVATE(node);
421 struct ng_mesg *resp = NULL;
422 int error = 0;
423 struct ng_mesg *msg;
424
425 NGI_GET_MSG(item, msg);
426 switch (msg->header.typecookie) {
427 case NGM_PPTPGRE_COOKIE:
428 switch (msg->header.cmd) {
429 case NGM_PPTPGRE_SET_CONFIG:
430 {
431 struct ng_pptpgre_conf *const newConf =
432 (struct ng_pptpgre_conf *) msg->data;
433 hpriv_p hpriv;
434 uint16_t hash;
435
436 /* Check for invalid or illegal config */
437 if (msg->header.arglen != sizeof(*newConf))
438 ERROUT(EINVAL);
439 /* Try to find session by cid. */
440 hpriv = ng_pptpgre_find_session(priv, newConf->cid);
441 /* If not present - use upper. */
442 if (hpriv == NULL) {
443 hpriv = &priv->uppersess;
444 LIST_REMOVE(hpriv, sessions);
445 hash = SESSHASH(newConf->cid);
446 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
447 sessions);
448 }
449 ng_pptpgre_reset(hpriv); /* reset on configure */
450 hpriv->conf = *newConf;
451 break;
452 }
453 case NGM_PPTPGRE_GET_CONFIG:
454 {
455 hpriv_p hpriv;
456
457 if (msg->header.arglen == 2) {
458 /* Try to find session by cid. */
459 hpriv = ng_pptpgre_find_session(priv,
460 *((uint16_t *)msg->data));
461 if (hpriv == NULL)
462 ERROUT(EINVAL);
463 } else if (msg->header.arglen == 0) {
464 /* Use upper. */
465 hpriv = &priv->uppersess;
466 } else
467 ERROUT(EINVAL);
468 NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
469 if (resp == NULL)
470 ERROUT(ENOMEM);
471 bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
472 break;
473 }
474 case NGM_PPTPGRE_GET_STATS:
475 case NGM_PPTPGRE_CLR_STATS:
476 case NGM_PPTPGRE_GETCLR_STATS:
477 {
478 if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
479 NG_MKRESPONSE(resp, msg,
480 sizeof(priv->stats), M_NOWAIT);
481 if (resp == NULL)
482 ERROUT(ENOMEM);
483 bcopy(&priv->stats,
484 resp->data, sizeof(priv->stats));
485 }
486 if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
487 bzero(&priv->stats, sizeof(priv->stats));
488 break;
489 }
490 default:
491 error = EINVAL;
492 break;
493 }
494 break;
495 default:
496 error = EINVAL;
497 break;
498 }
499 done:
500 NG_RESPOND_MSG(error, node, item, resp);
501 NG_FREE_MSG(msg);
502 return (error);
503 }
504
505 /*
506 * Receive incoming data on a hook.
507 */
508 static int
ng_pptpgre_rcvdata(hook_p hook,item_p item)509 ng_pptpgre_rcvdata(hook_p hook, item_p item)
510 {
511 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
512 int rval;
513
514 /* If not configured, reject */
515 if (!hpriv->conf.enabled) {
516 NG_FREE_ITEM(item);
517 return (ENXIO);
518 }
519
520 mtx_lock(&hpriv->mtx);
521
522 rval = ng_pptpgre_xmit(hpriv, item);
523
524 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
525
526 return (rval);
527 }
528
529 /*
530 * Hook disconnection
531 */
532 static int
ng_pptpgre_disconnect(hook_p hook)533 ng_pptpgre_disconnect(hook_p hook)
534 {
535 const node_p node = NG_HOOK_NODE(hook);
536 const priv_p priv = NG_NODE_PRIVATE(node);
537 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
538
539 /* Zero out hook pointer */
540 if (hook == priv->upper) {
541 priv->upper = NULL;
542 priv->uppersess.hook = NULL;
543 } else if (hook == priv->lower) {
544 priv->lower = NULL;
545 } else {
546 /* Reset node (stops timers) */
547 ng_pptpgre_reset(hpriv);
548
549 LIST_REMOVE(hpriv, sessions);
550 mtx_destroy(&hpriv->mtx);
551 free(hpriv, M_NETGRAPH_PPTP);
552 }
553
554 /* Go away if no longer connected to anything */
555 if ((NG_NODE_NUMHOOKS(node) == 0)
556 && (NG_NODE_IS_VALID(node)))
557 ng_rmnode_self(node);
558 return (0);
559 }
560
561 /*
562 * Destroy node
563 */
564 static int
ng_pptpgre_shutdown(node_p node)565 ng_pptpgre_shutdown(node_p node)
566 {
567 const priv_p priv = NG_NODE_PRIVATE(node);
568
569 /* Reset node (stops timers) */
570 ng_pptpgre_reset(&priv->uppersess);
571
572 LIST_REMOVE(&priv->uppersess, sessions);
573 mtx_destroy(&priv->uppersess.mtx);
574
575 free(priv, M_NETGRAPH_PPTP);
576
577 /* Decrement ref count */
578 NG_NODE_UNREF(node);
579 return (0);
580 }
581
582 /*************************************************************************
583 TRANSMIT AND RECEIVE FUNCTIONS
584 *************************************************************************/
585
586 /*
587 * Transmit an outgoing frame, or just an ack if m is NULL.
588 */
589 static int
ng_pptpgre_xmit(hpriv_p hpriv,item_p item)590 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
591 {
592 const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
593 u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
594 struct greheader *const gre = (struct greheader *)buf;
595 int grelen, error;
596 struct mbuf *m;
597
598 mtx_assert(&hpriv->mtx, MA_OWNED);
599
600 if (item) {
601 NGI_GET_M(item, m);
602 } else {
603 m = NULL;
604 }
605 /* Check if there's data */
606 if (m != NULL) {
607 /* Check if windowing is enabled */
608 if (hpriv->conf.enableWindowing) {
609 /* Is our transmit window full? */
610 if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
611 hpriv->recvAck) >= hpriv->xmitWin) {
612 priv->stats.xmitDrops++;
613 ERROUT(ENOBUFS);
614 }
615 }
616
617 /* Sanity check frame length */
618 if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
619 priv->stats.xmitTooBig++;
620 ERROUT(EMSGSIZE);
621 }
622 } else {
623 priv->stats.xmitLoneAcks++;
624 }
625
626 /* Build GRE header */
627 be32enc(gre, PPTP_INIT_VALUE);
628 be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
629 be16enc(&gre->cid, hpriv->conf.peerCid);
630
631 /* Include sequence number if packet contains any data */
632 if (m != NULL) {
633 gre->hasSeq = 1;
634 if (hpriv->conf.enableWindowing) {
635 hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
636 = ng_pptpgre_time();
637 }
638 hpriv->xmitSeq++;
639 be32enc(&gre->data[0], hpriv->xmitSeq);
640 }
641
642 /* Include acknowledgement (and stop send ack timer) if needed */
643 if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
644 gre->hasAck = 1;
645 be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
646 hpriv->xmitAck = hpriv->recvSeq;
647 if (hpriv->conf.enableDelayedAck)
648 ng_uncallout(&hpriv->sackTimer, hpriv->node);
649 }
650
651 /* Prepend GRE header to outgoing frame */
652 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
653 if (m == NULL) {
654 MGETHDR(m, M_NOWAIT, MT_DATA);
655 if (m == NULL) {
656 priv->stats.memoryFailures++;
657 ERROUT(ENOBUFS);
658 }
659 m->m_len = m->m_pkthdr.len = grelen;
660 m->m_pkthdr.rcvif = NULL;
661 } else {
662 M_PREPEND(m, grelen, M_NOWAIT);
663 if (m == NULL || (m->m_len < grelen
664 && (m = m_pullup(m, grelen)) == NULL)) {
665 priv->stats.memoryFailures++;
666 ERROUT(ENOBUFS);
667 }
668 }
669 bcopy(gre, mtod(m, u_char *), grelen);
670
671 /* Update stats */
672 priv->stats.xmitPackets++;
673 priv->stats.xmitOctets += m->m_pkthdr.len;
674
675 /*
676 * XXX: we should reset timer only after an item has been sent
677 * successfully.
678 */
679 if (hpriv->conf.enableWindowing &&
680 gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
681 ng_pptpgre_start_recv_ack_timer(hpriv);
682
683 mtx_unlock(&hpriv->mtx);
684
685 /* Deliver packet */
686 if (item) {
687 NG_FWD_NEW_DATA(error, item, priv->lower, m);
688 } else {
689 NG_SEND_DATA_ONLY(error, priv->lower, m);
690 }
691
692 return (error);
693
694 done:
695 mtx_unlock(&hpriv->mtx);
696 NG_FREE_M(m);
697 if (item)
698 NG_FREE_ITEM(item);
699 return (error);
700 }
701
702 static void
ng_pptpgre_ack(const hpriv_p hpriv)703 ng_pptpgre_ack(const hpriv_p hpriv)
704 {
705 mtx_assert(&hpriv->mtx, MA_OWNED);
706 if (!(callout_pending(&hpriv->sackTimer))) {
707 /* If delayed ACK is disabled, send it now */
708 if (!hpriv->conf.enableDelayedAck) { /* ack now */
709 ng_pptpgre_xmit(hpriv, NULL);
710 /* ng_pptpgre_xmit() drops the mutex */
711 return;
712 }
713 /* ack later */
714 ng_pptpgre_start_send_ack_timer(hpriv);
715 mtx_unlock(&hpriv->mtx);
716 return;
717 }
718 mtx_unlock(&hpriv->mtx);
719 }
720
721 /*
722 * Delivers packets from the queue "q" to upper layers. Frees delivered
723 * entries with the exception of one equal to "st" that is allocated
724 * on caller's stack and not on the heap.
725 */
726 static int
ng_pptpgre_sendq(const hpriv_p hpriv,roqh * q,const struct ng_pptpgre_roq * st)727 ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
728 {
729 struct ng_pptpgre_roq *np;
730 struct mbuf *m;
731 int error = 0;
732
733 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
734 while (!SLIST_EMPTY(q)) {
735 np = SLIST_FIRST(q);
736 SLIST_REMOVE_HEAD(q, next);
737 NGI_GET_M(np->item, m);
738 NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
739 if (np != st)
740 free(np, M_NETGRAPH_PPTP);
741 }
742 return (error);
743 }
744
745 /*
746 * Handle an incoming packet. The packet includes the IP header.
747 */
748 static int
ng_pptpgre_rcvdata_lower(hook_p hook,item_p item)749 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
750 {
751 hpriv_p hpriv;
752 node_p node = NG_HOOK_NODE(hook);
753 const priv_p priv = NG_NODE_PRIVATE(node);
754 int iphlen, grelen, extralen;
755 const struct greheader *gre;
756 const struct ip *ip;
757 int error = 0;
758 struct mbuf *m;
759
760 roqh sendq = SLIST_HEAD_INITIALIZER(sendq); /* send queue on stack */
761 struct ng_pptpgre_roq *last = NULL; /* last packet in the sendq */
762 struct ng_pptpgre_roq *np, *prev;
763 struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
764 long diff;
765 u_int32_t seq;
766
767 m = NGI_M(item);
768 /* Update stats */
769 priv->stats.recvPackets++;
770 priv->stats.recvOctets += m->m_pkthdr.len;
771
772 /* Sanity check packet length */
773 if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
774 priv->stats.recvRunts++;
775 ERROUT(EINVAL);
776 }
777
778 /* Safely pull up the complete IP+GRE headers */
779 if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
780 if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
781 priv->stats.memoryFailures++;
782 _NGI_M(item) = NULL;
783 ERROUT(ENOBUFS);
784 }
785 _NGI_M(item) = m;
786 }
787 ip = mtod(m, const struct ip *);
788 iphlen = ip->ip_hl << 2;
789 if (m->m_len < iphlen + sizeof(*gre)) {
790 if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
791 priv->stats.memoryFailures++;
792 _NGI_M(item) = NULL;
793 ERROUT(ENOBUFS);
794 }
795 _NGI_M(item) = m;
796 ip = mtod(m, const struct ip *);
797 }
798 gre = (const struct greheader *)((const u_char *)ip + iphlen);
799 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
800 if (m->m_pkthdr.len < iphlen + grelen) {
801 priv->stats.recvRunts++;
802 ERROUT(EINVAL);
803 }
804 if (m->m_len < iphlen + grelen) {
805 if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
806 priv->stats.memoryFailures++;
807 _NGI_M(item) = NULL;
808 ERROUT(ENOBUFS);
809 }
810 _NGI_M(item) = m;
811 ip = mtod(m, const struct ip *);
812 gre = (const struct greheader *)((const u_char *)ip + iphlen);
813 }
814
815 /* Sanity check packet length and GRE header bits */
816 extralen = m->m_pkthdr.len
817 - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
818 if (extralen < 0) {
819 priv->stats.recvBadGRE++;
820 ERROUT(EINVAL);
821 }
822 if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
823 priv->stats.recvBadGRE++;
824 ERROUT(EINVAL);
825 }
826
827 hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
828 if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
829 priv->stats.recvBadCID++;
830 ERROUT(EINVAL);
831 }
832 mtx_lock(&hpriv->mtx);
833
834 /* Look for peer ack */
835 if (gre->hasAck) {
836 const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]);
837 const int index = ack - hpriv->recvAck - 1;
838 long sample;
839
840 /* Sanity check ack value */
841 if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
842 priv->stats.recvBadAcks++;
843 goto badAck; /* we never sent it! */
844 }
845 if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
846 goto badAck; /* ack already timed out */
847 hpriv->recvAck = ack;
848
849 /* Update adaptive timeout stuff */
850 if (hpriv->conf.enableWindowing) {
851 sample = ng_pptpgre_time() - hpriv->timeSent[index];
852 diff = sample - hpriv->rtt;
853 hpriv->rtt += PPTP_ACK_ALPHA(diff);
854 if (diff < 0)
855 diff = -diff;
856 hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
857 /* +2 to compensate low precision of int math */
858 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
859 if (hpriv->ato > PPTP_MAX_TIMEOUT)
860 hpriv->ato = PPTP_MAX_TIMEOUT;
861 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
862 hpriv->ato = PPTP_MIN_TIMEOUT;
863
864 /* Shift packet transmit times in our transmit window */
865 bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
866 sizeof(*hpriv->timeSent)
867 * (PPTP_XMIT_WIN - (index + 1)));
868
869 /* If we sent an entire window, increase window size */
870 if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
871 && hpriv->xmitWin < PPTP_XMIT_WIN) {
872 hpriv->xmitWin++;
873 hpriv->winAck = ack + hpriv->xmitWin;
874 }
875
876 /* Stop/(re)start receive ACK timer as necessary */
877 ng_uncallout(&hpriv->rackTimer, hpriv->node);
878 if (hpriv->recvAck != hpriv->xmitSeq)
879 ng_pptpgre_start_recv_ack_timer(hpriv);
880 }
881 }
882 badAck:
883
884 /* See if frame contains any data */
885 if (!gre->hasSeq) { /* no data to deliver */
886 priv->stats.recvLoneAcks++;
887 mtx_unlock(&hpriv->mtx);
888 ERROUT(0);
889 }
890
891 seq = be32dec(&gre->data[0]);
892
893 diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
894 if (diff <= 0) { /* late or duplicate packet */
895 if (diff < 0 && reorder_max == 0) /* reorder disabled */
896 priv->stats.recvOutOfOrder++; /* late */
897 else
898 priv->stats.recvDuplicates++; /* duplicate */
899 mtx_unlock(&hpriv->mtx);
900 ERROUT(EINVAL);
901 }
902
903 /* Trim mbuf down to internal payload */
904 m_adj(m, iphlen + grelen);
905 if (extralen > 0)
906 m_adj(m, -extralen);
907
908 #define INIT_SENDQ(t) do { \
909 t.item = item; \
910 t.seq = seq; \
911 SLIST_INSERT_HEAD(&sendq, &t, next); \
912 last = &t; \
913 hpriv->recvSeq = seq; \
914 goto deliver; \
915 } while(0)
916
917 if (diff == 1)
918 /* the packet came in order, place it at the start of sendq */
919 INIT_SENDQ(temp);
920
921 /* The packet came too early, try to enqueue it.
922 *
923 * Check for duplicate in the queue. After this loop, "prev" will be
924 * NULL if the packet should become new head of the queue,
925 * or else it should be inserted after the "prev".
926 */
927 prev = SLIST_FIRST(&hpriv->roq);
928 SLIST_FOREACH(np, &hpriv->roq, next) {
929 diff = PPTP_SEQ_DIFF(np->seq, seq);
930 if (diff == 0) { /* do not add duplicate, drop it */
931 priv->stats.recvDuplicates++;
932 mtx_unlock(&hpriv->mtx);
933 ERROUT(EINVAL);
934 }
935 if (diff > 0) { /* we found newer packet */
936 if (np == prev) /* that is the head of the queue */
937 prev = NULL; /* put current packet to the head */
938 break;
939 }
940 prev = np;
941 }
942
943 priv->stats.recvOutOfOrder++; /* duplicate not found */
944 if (hpriv->roq_len < reorder_max)
945 goto enqueue; /* reorder enabled and there is a room */
946
947 /*
948 * There is no room in the queue or reorder disabled.
949 *
950 * It the latter case, we may still have non-empty reorder queue
951 * if reorder was disabled in process of reordering.
952 * Then we correctly deliver the queue without growing it.
953 *
954 * In both cases, no malloc()'s until the queue is shortened.
955 */
956 priv->stats.recvReorderOverflow++;
957 if (prev == NULL) { /* new packet goes before the head */
958 INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
959 }
960 #undef INIT_SENDQ
961
962 /*
963 * Current packet goes after the head of reorder queue.
964 * Move the head to sendq to make room for current packet.
965 */
966 np = SLIST_FIRST(&hpriv->roq);
967 if (prev == np)
968 prev = NULL;
969 SLIST_REMOVE_HEAD(&hpriv->roq, next);
970 hpriv->roq_len--; /* we are allowed to use malloc() now */
971 SLIST_INSERT_HEAD(&sendq, np, next);
972 last = np;
973 hpriv->recvSeq = np->seq;
974
975 enqueue:
976 np = malloc(sizeof(*np), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
977 if (np == NULL) {
978 priv->stats.memoryFailures++;
979 /*
980 * Emergency: we cannot save new data.
981 * Flush the queue delivering all queued packets preceeding
982 * current one despite of gaps.
983 */
984 while (!SLIST_EMPTY(&hpriv->roq)) {
985 np = SLIST_FIRST(&hpriv->roq);
986 if (np->seq > seq)
987 break;
988 SLIST_REMOVE_HEAD(&hpriv->roq, next);
989 hpriv->roq_len--;
990 if (last == NULL)
991 SLIST_INSERT_HEAD(&sendq, np, next);
992 else
993 SLIST_INSERT_AFTER(last, np, next);
994 last = np;
995 }
996
997 /*
998 * Pretend we got all packets till the current one
999 * and acknowledge it.
1000 */
1001 hpriv->recvSeq = seq;
1002 ng_pptpgre_ack(hpriv); /* drops lock */
1003 ng_pptpgre_sendq(hpriv, &sendq, &temp);
1004 NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1005 ERROUT(ENOMEM);
1006 }
1007
1008 /* Add current (early) packet to the reorder queue. */
1009 np->item = item;
1010 np->seq = seq;
1011 if (prev == NULL)
1012 SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1013 else
1014 SLIST_INSERT_AFTER(prev, np, next);
1015 hpriv->roq_len++;
1016
1017 deliver:
1018 /* Look if we have some packets in sequence after sendq. */
1019 while (!SLIST_EMPTY(&hpriv->roq)) {
1020 np = SLIST_FIRST(&hpriv->roq);
1021 if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1022 break; /* the gap in the sequence */
1023
1024 /* "np" is in sequence, move it to the sendq. */
1025 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1026 hpriv->roq_len--;
1027 hpriv->recvSeq = np->seq;
1028
1029 if (last == NULL)
1030 SLIST_INSERT_HEAD(&sendq, np, next);
1031 else
1032 SLIST_INSERT_AFTER(last, np, next);
1033 last = np;
1034 }
1035
1036 if (SLIST_EMPTY(&hpriv->roq)) {
1037 if (callout_pending(&hpriv->reorderTimer))
1038 ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1039 } else {
1040 if (!callout_pending(&hpriv->reorderTimer))
1041 ng_pptpgre_start_reorder_timer(hpriv);
1042 }
1043
1044 if (SLIST_EMPTY(&sendq)) {
1045 /* Current packet has been queued, nothing to free/deliver. */
1046 mtx_unlock(&hpriv->mtx);
1047 return (error);
1048 }
1049
1050 /* We need to acknowledge last packet; do it soon... */
1051 ng_pptpgre_ack(hpriv); /* drops lock */
1052 ng_pptpgre_sendq(hpriv, &sendq, &temp);
1053 return (error);
1054
1055 done:
1056 NG_FREE_ITEM(item);
1057 return (error);
1058 }
1059
1060 /*************************************************************************
1061 TIMER RELATED FUNCTIONS
1062 *************************************************************************/
1063
1064 /*
1065 * Start a timer for the peer's acknowledging our oldest unacknowledged
1066 * sequence number. If we get an ack for this sequence number before
1067 * the timer goes off, we cancel the timer. Resets currently running
1068 * recv ack timer, if any.
1069 */
1070 static void
ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)1071 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1072 {
1073 int remain, ticks;
1074
1075 /* Compute how long until oldest unack'd packet times out,
1076 and reset the timer to that time. */
1077 remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1078 if (remain < 0)
1079 remain = 0;
1080
1081 /* Be conservative: timeout can happen up to 1 tick early */
1082 ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1083 ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1084 ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
1085 }
1086
1087 /*
1088 * The peer has failed to acknowledge the oldest unacknowledged sequence
1089 * number within the time allotted. Update our adaptive timeout parameters
1090 * and reset/restart the recv ack timer.
1091 */
1092 static void
ng_pptpgre_recv_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1093 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1094 {
1095 const priv_p priv = NG_NODE_PRIVATE(node);
1096 const hpriv_p hpriv = arg1;
1097
1098 /* Update adaptive timeout stuff */
1099 priv->stats.recvAckTimeouts++;
1100 hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1101 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1102 if (hpriv->ato > PPTP_MAX_TIMEOUT)
1103 hpriv->ato = PPTP_MAX_TIMEOUT;
1104 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1105 hpriv->ato = PPTP_MIN_TIMEOUT;
1106
1107 /* Reset ack and sliding window */
1108 hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */
1109 hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */
1110 hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */
1111 }
1112
1113 /*
1114 * Start the send ack timer. This assumes the timer is not
1115 * already running.
1116 */
1117 static void
ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)1118 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
1119 {
1120 int ackTimeout, ticks;
1121
1122 /* Take 1/4 of the estimated round trip time */
1123 ackTimeout = (hpriv->rtt >> 2);
1124 if (ackTimeout < PPTP_MIN_ACK_DELAY)
1125 ackTimeout = PPTP_MIN_ACK_DELAY;
1126 else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1127 ackTimeout = PPTP_MAX_ACK_DELAY;
1128
1129 /* Be conservative: timeout can happen up to 1 tick early */
1130 ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1131 ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1132 ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
1133 }
1134
1135 /*
1136 * We've waited as long as we're willing to wait before sending an
1137 * acknowledgement to the peer for received frames. We had hoped to
1138 * be able to piggy back our acknowledgement on an outgoing data frame,
1139 * but apparently there haven't been any since. So send the ack now.
1140 */
1141 static void
ng_pptpgre_send_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1142 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1143 {
1144 const hpriv_p hpriv = arg1;
1145
1146 mtx_lock(&hpriv->mtx);
1147 /* Send a frame with an ack but no payload */
1148 ng_pptpgre_xmit(hpriv, NULL);
1149 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1150 }
1151
1152 /*
1153 * Start a timer for the reorder queue. This assumes the timer is not
1154 * already running.
1155 */
1156 static void
ng_pptpgre_start_reorder_timer(hpriv_p hpriv)1157 ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1158 {
1159 int ticks;
1160
1161 /* Be conservative: timeout can happen up to 1 tick early */
1162 ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1163 ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1164 ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1165 }
1166
1167 /*
1168 * The oldest packet spent too much time in the reorder queue.
1169 * Deliver it and next packets in sequence, if any.
1170 */
1171 static void
ng_pptpgre_reorder_timeout(node_p node,hook_p hook,void * arg1,int arg2)1172 ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1173 {
1174 const priv_p priv = NG_NODE_PRIVATE(node);
1175 const hpriv_p hpriv = arg1;
1176 roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1177 struct ng_pptpgre_roq *np, *last = NULL;
1178
1179 priv->stats.recvReorderTimeouts++;
1180 mtx_lock(&hpriv->mtx);
1181 if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1182 mtx_unlock(&hpriv->mtx);
1183 return;
1184 }
1185
1186 last = np = SLIST_FIRST(&hpriv->roq);
1187 hpriv->roq_len--;
1188 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1189 SLIST_INSERT_HEAD(&sendq, np, next);
1190
1191 /* Look if we have more packets in sequence */
1192 while (!SLIST_EMPTY(&hpriv->roq)) {
1193 np = SLIST_FIRST(&hpriv->roq);
1194 if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1195 break; /* the gap in the sequence */
1196
1197 /* Next packet is in sequence, move it to the sendq. */
1198 hpriv->roq_len--;
1199 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1200 SLIST_INSERT_AFTER(last, np, next);
1201 last = np;
1202 }
1203
1204 hpriv->recvSeq = last->seq;
1205 if (!SLIST_EMPTY(&hpriv->roq))
1206 ng_pptpgre_start_reorder_timer(hpriv);
1207
1208 /* We need to acknowledge last packet; do it soon... */
1209 ng_pptpgre_ack(hpriv); /* drops lock */
1210 ng_pptpgre_sendq(hpriv, &sendq, NULL);
1211 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1212 }
1213
1214 /*************************************************************************
1215 MISC FUNCTIONS
1216 *************************************************************************/
1217
1218 /*
1219 * Find the hook with a given session ID.
1220 */
1221 static hpriv_p
ng_pptpgre_find_session(priv_p privp,u_int16_t cid)1222 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1223 {
1224 uint16_t hash = SESSHASH(cid);
1225 hpriv_p hpriv = NULL;
1226
1227 LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1228 if (hpriv->conf.cid == cid)
1229 break;
1230 }
1231
1232 return (hpriv);
1233 }
1234
1235 /*
1236 * Reset state (must be called with lock held or from writer)
1237 */
1238 static void
ng_pptpgre_reset(hpriv_p hpriv)1239 ng_pptpgre_reset(hpriv_p hpriv)
1240 {
1241 struct ng_pptpgre_roq *np;
1242
1243 /* Reset adaptive timeout state */
1244 hpriv->ato = PPTP_MAX_TIMEOUT;
1245 hpriv->rtt = PPTP_TIME_SCALE / 10;
1246 if (hpriv->conf.peerPpd > 1) /* ppd = 0 treat as = 1 */
1247 hpriv->rtt *= hpriv->conf.peerPpd;
1248 hpriv->dev = 0;
1249 hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1250 if (hpriv->xmitWin < 2) /* often the first packet is lost */
1251 hpriv->xmitWin = 2; /* because the peer isn't ready */
1252 else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1253 hpriv->xmitWin = PPTP_XMIT_WIN;
1254 hpriv->winAck = hpriv->xmitWin;
1255
1256 /* Reset sequence numbers */
1257 hpriv->recvSeq = ~0;
1258 hpriv->recvAck = ~0;
1259 hpriv->xmitSeq = ~0;
1260 hpriv->xmitAck = ~0;
1261
1262 /* Stop timers */
1263 ng_uncallout(&hpriv->sackTimer, hpriv->node);
1264 ng_uncallout(&hpriv->rackTimer, hpriv->node);
1265 ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1266
1267 /* Clear reorder queue */
1268 while (!SLIST_EMPTY(&hpriv->roq)) {
1269 np = SLIST_FIRST(&hpriv->roq);
1270 SLIST_REMOVE_HEAD(&hpriv->roq, next);
1271 NG_FREE_ITEM(np->item);
1272 free(np, M_NETGRAPH_PPTP);
1273 }
1274 hpriv->roq_len = 0;
1275 }
1276
1277 /*
1278 * Return the current time scaled & translated to our internally used format.
1279 */
1280 static pptptime_t
ng_pptpgre_time(void)1281 ng_pptpgre_time(void)
1282 {
1283 struct timeval tv;
1284 pptptime_t t;
1285
1286 microuptime(&tv);
1287 t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1288 t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1289 return(t);
1290 }
1291