1 /*
2 * ng_pptpgre.c
3 */
4
5 /*-
6 * Copyright (c) 1996-1999 Whistle Communications, Inc.
7 * All rights reserved.
8 *
9 * Subject to the following obligations and disclaimer of warranty, use and
10 * redistribution of this software, in source or object code forms, with or
11 * without modifications are expressly permitted by Whistle Communications;
12 * provided, however, that:
13 * 1. Any and all reproductions of the source or object code must include the
14 * copyright notice above and the following disclaimer of warranties; and
15 * 2. No rights are granted, in any manner or form, to use Whistle
16 * Communications, Inc. trademarks, including the mark "WHISTLE
17 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18 * such appears in the above copyright notice or in the software.
19 *
20 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36 * OF SUCH DAMAGE.
37 *
38 * Author: Archie Cobbs <archie@freebsd.org>
39 *
40 * $FreeBSD: releng/8.1/sys/netgraph/ng_pptpgre.c 206661 2010-04-15 12:46:16Z mav $
41 * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
42 */
43
44 /*
45 * PPTP/GRE netgraph node type.
46 *
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.
51 *
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".
55 */
56
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/kernel.h>
60 #include <sys/time.h>
61 #include <sys/lock.h>
62 #include <sys/malloc.h>
63 #include <sys/mbuf.h>
64 #include <sys/mutex.h>
65 #include <sys/endian.h>
66 #include <sys/errno.h>
67
68 #include <netinet/in.h>
69 #include <netinet/in_systm.h>
70 #include <netinet/ip.h>
71
72 #include <netgraph/ng_message.h>
73 #include <netgraph/netgraph.h>
74 #include <netgraph/ng_parse.h>
75 #include <netgraph/ng_pptpgre.h>
76
77 /* GRE packet format, as used by PPTP */
78 struct greheader {
79 #if BYTE_ORDER == LITTLE_ENDIAN
80 u_char recursion:3; /* recursion control */
81 u_char ssr:1; /* strict source route */
82 u_char hasSeq:1; /* sequence number present */
83 u_char hasKey:1; /* key present */
84 u_char hasRoute:1; /* routing present */
85 u_char hasSum:1; /* checksum present */
86 u_char vers:3; /* version */
87 u_char flags:4; /* flags */
88 u_char hasAck:1; /* acknowlege number present */
89 #elif BYTE_ORDER == BIG_ENDIAN
90 u_char hasSum:1; /* checksum present */
91 u_char hasRoute:1; /* routing present */
92 u_char hasKey:1; /* key present */
93 u_char hasSeq:1; /* sequence number present */
94 u_char ssr:1; /* strict source route */
95 u_char recursion:3; /* recursion control */
96 u_char hasAck:1; /* acknowlege number present */
97 u_char flags:4; /* flags */
98 u_char vers:3; /* version */
99 #else
100 #error BYTE_ORDER is not defined properly
101 #endif
102 u_int16_t proto; /* protocol (ethertype) */
103 u_int16_t length; /* payload length */
104 u_int16_t cid; /* call id */
105 u_int32_t data[0]; /* opt. seq, ack, then data */
106 };
107
108 /* The PPTP protocol ID used in the GRE 'proto' field */
109 #define PPTP_GRE_PROTO 0x880b
110
111 /* Bits that must be set a certain way in all PPTP/GRE packets */
112 #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO)
113 #define PPTP_INIT_MASK 0xef7fffff
114
115 /* Min and max packet length */
116 #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8)
117
118 /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
119 #define PPTP_TIME_SCALE 1024 /* milliseconds */
120 typedef u_int64_t pptptime_t;
121
122 /* Acknowledgment timeout parameters and functions */
123 #define PPTP_XMIT_WIN 16 /* max xmit window */
124 #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 83) /* 12 milliseconds */
125 #define PPTP_MAX_TIMEOUT (3 * PPTP_TIME_SCALE) /* 3 seconds */
126
127 /* When we recieve a packet, we wait to see if there's an outgoing packet
128 we can piggy-back the ACK off of. These parameters determine the mimimum
129 and maxmimum length of time we're willing to wait in order to do that.
130 These have no effect unless "enableDelayedAck" is turned on. */
131 #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */
132 #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */
133
134 /* See RFC 2637 section 4.4 */
135 #define PPTP_ACK_ALPHA(x) (((x) + 4) >> 3) /* alpha = 0.125 */
136 #define PPTP_ACK_BETA(x) (((x) + 2) >> 2) /* beta = 0.25 */
137 #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */
138 #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */
139
140 #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y))
141
142 #define SESSHASHSIZE 0x0020
143 #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
144
145 /* We keep packet retransmit and acknowlegement state in this struct */
146 struct ng_pptpgre_sess {
147 node_p node; /* this node pointer */
148 hook_p hook; /* hook to upper layers */
149 struct ng_pptpgre_conf conf; /* configuration info */
150 struct mtx mtx; /* session mutex */
151 u_int32_t recvSeq; /* last seq # we rcv'd */
152 u_int32_t xmitSeq; /* last seq # we sent */
153 u_int32_t recvAck; /* last seq # peer ack'd */
154 u_int32_t xmitAck; /* last seq # we ack'd */
155 int32_t ato; /* adaptive time-out value */
156 int32_t rtt; /* round trip time estimate */
157 int32_t dev; /* deviation estimate */
158 u_int16_t xmitWin; /* size of xmit window */
159 struct callout sackTimer; /* send ack timer */
160 struct callout rackTimer; /* recv ack timer */
161 u_int32_t winAck; /* seq when xmitWin will grow */
162 pptptime_t timeSent[PPTP_XMIT_WIN];
163 LIST_ENTRY(ng_pptpgre_sess) sessions;
164 };
165 typedef struct ng_pptpgre_sess *hpriv_p;
166
167 /* Node private data */
168 struct ng_pptpgre_private {
169 hook_p upper; /* hook to upper layers */
170 hook_p lower; /* hook to lower layers */
171 struct ng_pptpgre_sess uppersess; /* default session for compat */
172 LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
173 struct ng_pptpgre_stats stats; /* node statistics */
174 };
175 typedef struct ng_pptpgre_private *priv_p;
176
177 /* Netgraph node methods */
178 static ng_constructor_t ng_pptpgre_constructor;
179 static ng_rcvmsg_t ng_pptpgre_rcvmsg;
180 static ng_shutdown_t ng_pptpgre_shutdown;
181 static ng_newhook_t ng_pptpgre_newhook;
182 static ng_rcvdata_t ng_pptpgre_rcvdata;
183 static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
184 static ng_disconnect_t ng_pptpgre_disconnect;
185
186 /* Helper functions */
187 static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
188 static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
189 static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
190 static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
191 void *arg1, int arg2);
192 static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
193 void *arg1, int arg2);
194 static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
195 static void ng_pptpgre_reset(hpriv_p hpriv);
196 static pptptime_t ng_pptpgre_time(void);
197
198 /* Parse type for struct ng_pptpgre_conf */
199 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
200 = NG_PPTPGRE_CONF_TYPE_INFO;
201 static const struct ng_parse_type ng_pptpgre_conf_type = {
202 &ng_parse_struct_type,
203 &ng_pptpgre_conf_type_fields,
204 };
205
206 /* Parse type for struct ng_pptpgre_stats */
207 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
208 = NG_PPTPGRE_STATS_TYPE_INFO;
209 static const struct ng_parse_type ng_pptp_stats_type = {
210 &ng_parse_struct_type,
211 &ng_pptpgre_stats_type_fields
212 };
213
214 /* List of commands and how to convert arguments to/from ASCII */
215 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
216 {
217 NGM_PPTPGRE_COOKIE,
218 NGM_PPTPGRE_SET_CONFIG,
219 "setconfig",
220 &ng_pptpgre_conf_type,
221 NULL
222 },
223 {
224 NGM_PPTPGRE_COOKIE,
225 NGM_PPTPGRE_GET_CONFIG,
226 "getconfig",
227 &ng_parse_hint16_type,
228 &ng_pptpgre_conf_type
229 },
230 {
231 NGM_PPTPGRE_COOKIE,
232 NGM_PPTPGRE_GET_STATS,
233 "getstats",
234 NULL,
235 &ng_pptp_stats_type
236 },
237 {
238 NGM_PPTPGRE_COOKIE,
239 NGM_PPTPGRE_CLR_STATS,
240 "clrstats",
241 NULL,
242 NULL
243 },
244 {
245 NGM_PPTPGRE_COOKIE,
246 NGM_PPTPGRE_GETCLR_STATS,
247 "getclrstats",
248 NULL,
249 &ng_pptp_stats_type
250 },
251 { 0 }
252 };
253
254 /* Node type descriptor */
255 static struct ng_type ng_pptpgre_typestruct = {
256 .version = NG_ABI_VERSION,
257 .name = NG_PPTPGRE_NODE_TYPE,
258 .constructor = ng_pptpgre_constructor,
259 .rcvmsg = ng_pptpgre_rcvmsg,
260 .shutdown = ng_pptpgre_shutdown,
261 .newhook = ng_pptpgre_newhook,
262 .rcvdata = ng_pptpgre_rcvdata,
263 .disconnect = ng_pptpgre_disconnect,
264 .cmdlist = ng_pptpgre_cmdlist,
265 };
266 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
267
268 #define ERROUT(x) do { error = (x); goto done; } while (0)
269
270 /************************************************************************
271 NETGRAPH NODE STUFF
272 ************************************************************************/
273
274 /*
275 * Node type constructor
276 */
277 static int
278 ng_pptpgre_constructor(node_p node)
279 {
280 priv_p priv;
281 int i;
282
283 /* Allocate private structure */
284 priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
285 if (priv == NULL)
286 return (ENOMEM);
287
288 NG_NODE_SET_PRIVATE(node, priv);
289
290 /* Initialize state */
291 mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
292 ng_callout_init(&priv->uppersess.sackTimer);
293 ng_callout_init(&priv->uppersess.rackTimer);
294 priv->uppersess.node = node;
295
296 for (i = 0; i < SESSHASHSIZE; i++)
297 LIST_INIT(&priv->sesshash[i]);
298
299 LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
300
301 /* Done */
302 return (0);
303 }
304
305 /*
306 * Give our OK for a hook to be added.
307 */
308 static int
309 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
310 {
311 const priv_p priv = NG_NODE_PRIVATE(node);
312
313 /* Check hook name */
314 if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
315 priv->upper = hook;
316 priv->uppersess.hook = hook;
317 NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
318 } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
319 priv->lower = hook;
320 NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
321 } else {
322 static const char hexdig[16] = "0123456789abcdef";
323 const char *hex;
324 hpriv_p hpriv;
325 int i, j;
326 uint16_t cid, hash;
327
328 /* Parse hook name to get session ID */
329 if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
330 sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
331 return (EINVAL);
332 hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
333 for (cid = i = 0; i < 4; i++) {
334 for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
335 if (j == 16)
336 return (EINVAL);
337 cid = (cid << 4) | j;
338 }
339 if (hex[i] != '\0')
340 return (EINVAL);
341
342 hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
343 if (hpriv == NULL)
344 return (ENOMEM);
345
346 /* Initialize state */
347 mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
348 ng_callout_init(&hpriv->sackTimer);
349 ng_callout_init(&hpriv->rackTimer);
350 hpriv->conf.cid = cid;
351 hpriv->node = node;
352 hpriv->hook = hook;
353 NG_HOOK_SET_PRIVATE(hook, hpriv);
354
355 hash = SESSHASH(cid);
356 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
357 }
358
359 return (0);
360 }
361
362 /*
363 * Receive a control message.
364 */
365 static int
366 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
367 {
368 const priv_p priv = NG_NODE_PRIVATE(node);
369 struct ng_mesg *resp = NULL;
370 int error = 0;
371 struct ng_mesg *msg;
372
373 NGI_GET_MSG(item, msg);
374 switch (msg->header.typecookie) {
375 case NGM_PPTPGRE_COOKIE:
376 switch (msg->header.cmd) {
377 case NGM_PPTPGRE_SET_CONFIG:
378 {
379 struct ng_pptpgre_conf *const newConf =
380 (struct ng_pptpgre_conf *) msg->data;
381 hpriv_p hpriv;
382 uint16_t hash;
383
384 /* Check for invalid or illegal config */
385 if (msg->header.arglen != sizeof(*newConf))
386 ERROUT(EINVAL);
387 /* Try to find session by cid. */
388 hpriv = ng_pptpgre_find_session(priv, newConf->cid);
389 /* If not present - use upper. */
390 if (hpriv == NULL) {
391 hpriv = &priv->uppersess;
392 LIST_REMOVE(hpriv, sessions);
393 hash = SESSHASH(newConf->cid);
394 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
395 sessions);
396 }
397 ng_pptpgre_reset(hpriv); /* reset on configure */
398 hpriv->conf = *newConf;
399 break;
400 }
401 case NGM_PPTPGRE_GET_CONFIG:
402 {
403 hpriv_p hpriv;
404
405 if (msg->header.arglen == 2) {
406 /* Try to find session by cid. */
407 hpriv = ng_pptpgre_find_session(priv,
408 *((uint16_t *)msg->data));
409 if (hpriv == NULL)
410 ERROUT(EINVAL);
411 } else if (msg->header.arglen == 0) {
412 /* Use upper. */
413 hpriv = &priv->uppersess;
414 } else
415 ERROUT(EINVAL);
416 NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
417 if (resp == NULL)
418 ERROUT(ENOMEM);
419 bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
420 break;
421 }
422 case NGM_PPTPGRE_GET_STATS:
423 case NGM_PPTPGRE_CLR_STATS:
424 case NGM_PPTPGRE_GETCLR_STATS:
425 {
426 if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
427 NG_MKRESPONSE(resp, msg,
428 sizeof(priv->stats), M_NOWAIT);
429 if (resp == NULL)
430 ERROUT(ENOMEM);
431 bcopy(&priv->stats,
432 resp->data, sizeof(priv->stats));
433 }
434 if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
435 bzero(&priv->stats, sizeof(priv->stats));
436 break;
437 }
438 default:
439 error = EINVAL;
440 break;
441 }
442 break;
443 default:
444 error = EINVAL;
445 break;
446 }
447 done:
448 NG_RESPOND_MSG(error, node, item, resp);
449 NG_FREE_MSG(msg);
450 return (error);
451 }
452
453 /*
454 * Receive incoming data on a hook.
455 */
456 static int
457 ng_pptpgre_rcvdata(hook_p hook, item_p item)
458 {
459 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
460 int rval;
461
462 /* If not configured, reject */
463 if (!hpriv->conf.enabled) {
464 NG_FREE_ITEM(item);
465 return (ENXIO);
466 }
467
468 mtx_lock(&hpriv->mtx);
469
470 rval = ng_pptpgre_xmit(hpriv, item);
471
472 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
473
474 return (rval);
475 }
476
477 /*
478 * Hook disconnection
479 */
480 static int
481 ng_pptpgre_disconnect(hook_p hook)
482 {
483 const node_p node = NG_HOOK_NODE(hook);
484 const priv_p priv = NG_NODE_PRIVATE(node);
485 const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
486
487 /* Zero out hook pointer */
488 if (hook == priv->upper) {
489 priv->upper = NULL;
490 priv->uppersess.hook = NULL;
491 } else if (hook == priv->lower) {
492 priv->lower = NULL;
493 } else {
494 /* Reset node (stops timers) */
495 ng_pptpgre_reset(hpriv);
496
497 LIST_REMOVE(hpriv, sessions);
498 mtx_destroy(&hpriv->mtx);
499 free(hpriv, M_NETGRAPH);
500 }
501
502 /* Go away if no longer connected to anything */
503 if ((NG_NODE_NUMHOOKS(node) == 0)
504 && (NG_NODE_IS_VALID(node)))
505 ng_rmnode_self(node);
506 return (0);
507 }
508
509 /*
510 * Destroy node
511 */
512 static int
513 ng_pptpgre_shutdown(node_p node)
514 {
515 const priv_p priv = NG_NODE_PRIVATE(node);
516
517 /* Reset node (stops timers) */
518 ng_pptpgre_reset(&priv->uppersess);
519
520 LIST_REMOVE(&priv->uppersess, sessions);
521 mtx_destroy(&priv->uppersess.mtx);
522
523 free(priv, M_NETGRAPH);
524
525 /* Decrement ref count */
526 NG_NODE_UNREF(node);
527 return (0);
528 }
529
530 /*************************************************************************
531 TRANSMIT AND RECEIVE FUNCTIONS
532 *************************************************************************/
533
534 /*
535 * Transmit an outgoing frame, or just an ack if m is NULL.
536 */
537 static int
538 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
539 {
540 const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
541 u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
542 struct greheader *const gre = (struct greheader *)buf;
543 int grelen, error;
544 struct mbuf *m;
545
546 mtx_assert(&hpriv->mtx, MA_OWNED);
547
548 if (item) {
549 NGI_GET_M(item, m);
550 } else {
551 m = NULL;
552 }
553 /* Check if there's data */
554 if (m != NULL) {
555
556 /* Check if windowing is enabled */
557 if (hpriv->conf.enableWindowing) {
558 /* Is our transmit window full? */
559 if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
560 hpriv->recvAck) >= hpriv->xmitWin) {
561 priv->stats.xmitDrops++;
562 ERROUT(ENOBUFS);
563 }
564 }
565
566 /* Sanity check frame length */
567 if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
568 priv->stats.xmitTooBig++;
569 ERROUT(EMSGSIZE);
570 }
571 } else {
572 priv->stats.xmitLoneAcks++;
573 }
574
575 /* Build GRE header */
576 be32enc(gre, PPTP_INIT_VALUE);
577 be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
578 be16enc(&gre->cid, hpriv->conf.peerCid);
579
580 /* Include sequence number if packet contains any data */
581 if (m != NULL) {
582 gre->hasSeq = 1;
583 if (hpriv->conf.enableWindowing) {
584 hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
585 = ng_pptpgre_time();
586 }
587 hpriv->xmitSeq++;
588 be32enc(&gre->data[0], hpriv->xmitSeq);
589 }
590
591 /* Include acknowledgement (and stop send ack timer) if needed */
592 if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
593 gre->hasAck = 1;
594 be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
595 hpriv->xmitAck = hpriv->recvSeq;
596 if (hpriv->conf.enableDelayedAck)
597 ng_uncallout(&hpriv->sackTimer, hpriv->node);
598 }
599
600 /* Prepend GRE header to outgoing frame */
601 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
602 if (m == NULL) {
603 MGETHDR(m, M_DONTWAIT, MT_DATA);
604 if (m == NULL) {
605 priv->stats.memoryFailures++;
606 ERROUT(ENOBUFS);
607 }
608 m->m_len = m->m_pkthdr.len = grelen;
609 m->m_pkthdr.rcvif = NULL;
610 } else {
611 M_PREPEND(m, grelen, M_DONTWAIT);
612 if (m == NULL || (m->m_len < grelen
613 && (m = m_pullup(m, grelen)) == NULL)) {
614 priv->stats.memoryFailures++;
615 ERROUT(ENOBUFS);
616 }
617 }
618 bcopy(gre, mtod(m, u_char *), grelen);
619
620 /* Update stats */
621 priv->stats.xmitPackets++;
622 priv->stats.xmitOctets += m->m_pkthdr.len;
623
624 /*
625 * XXX: we should reset timer only after an item has been sent
626 * successfully.
627 */
628 if (hpriv->conf.enableWindowing &&
629 gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
630 ng_pptpgre_start_recv_ack_timer(hpriv);
631
632 mtx_unlock(&hpriv->mtx);
633
634 /* Deliver packet */
635 if (item) {
636 NG_FWD_NEW_DATA(error, item, priv->lower, m);
637 } else {
638 NG_SEND_DATA_ONLY(error, priv->lower, m);
639 }
640
641 return (error);
642
643 done:
644 mtx_unlock(&hpriv->mtx);
645 NG_FREE_M(m);
646 if (item)
647 NG_FREE_ITEM(item);
648 return (error);
649 }
650
651 /*
652 * Handle an incoming packet. The packet includes the IP header.
653 */
654 static int
655 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
656 {
657 hpriv_p hpriv;
658 node_p node = NG_HOOK_NODE(hook);
659 const priv_p priv = NG_NODE_PRIVATE(node);
660 int iphlen, grelen, extralen;
661 const struct greheader *gre;
662 const struct ip *ip;
663 int error = 0;
664 struct mbuf *m;
665
666 NGI_GET_M(item, m);
667 /* Update stats */
668 priv->stats.recvPackets++;
669 priv->stats.recvOctets += m->m_pkthdr.len;
670
671 /* Sanity check packet length */
672 if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
673 priv->stats.recvRunts++;
674 ERROUT(EINVAL);
675 }
676
677 /* Safely pull up the complete IP+GRE headers */
678 if (m->m_len < sizeof(*ip) + sizeof(*gre)
679 && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
680 priv->stats.memoryFailures++;
681 ERROUT(ENOBUFS);
682 }
683 ip = mtod(m, const struct ip *);
684 iphlen = ip->ip_hl << 2;
685 if (m->m_len < iphlen + sizeof(*gre)) {
686 if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
687 priv->stats.memoryFailures++;
688 ERROUT(ENOBUFS);
689 }
690 ip = mtod(m, const struct ip *);
691 }
692 gre = (const struct greheader *)((const u_char *)ip + iphlen);
693 grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
694 if (m->m_pkthdr.len < iphlen + grelen) {
695 priv->stats.recvRunts++;
696 ERROUT(EINVAL);
697 }
698 if (m->m_len < iphlen + grelen) {
699 if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
700 priv->stats.memoryFailures++;
701 ERROUT(ENOBUFS);
702 }
703 ip = mtod(m, const struct ip *);
704 gre = (const struct greheader *)((const u_char *)ip + iphlen);
705 }
706
707 /* Sanity check packet length and GRE header bits */
708 extralen = m->m_pkthdr.len
709 - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
710 if (extralen < 0) {
711 priv->stats.recvBadGRE++;
712 ERROUT(EINVAL);
713 }
714 if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
715 priv->stats.recvBadGRE++;
716 ERROUT(EINVAL);
717 }
718
719 hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
720 if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
721 priv->stats.recvBadCID++;
722 ERROUT(EINVAL);
723 }
724 mtx_lock(&hpriv->mtx);
725
726 /* Look for peer ack */
727 if (gre->hasAck) {
728 const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]);
729 const int index = ack - hpriv->recvAck - 1;
730 long sample;
731 long diff;
732
733 /* Sanity check ack value */
734 if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
735 priv->stats.recvBadAcks++;
736 goto badAck; /* we never sent it! */
737 }
738 if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
739 goto badAck; /* ack already timed out */
740 hpriv->recvAck = ack;
741
742 /* Update adaptive timeout stuff */
743 if (hpriv->conf.enableWindowing) {
744 sample = ng_pptpgre_time() - hpriv->timeSent[index];
745 diff = sample - hpriv->rtt;
746 hpriv->rtt += PPTP_ACK_ALPHA(diff);
747 if (diff < 0)
748 diff = -diff;
749 hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
750 /* +2 to compensate low precision of int math */
751 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
752 if (hpriv->ato > PPTP_MAX_TIMEOUT)
753 hpriv->ato = PPTP_MAX_TIMEOUT;
754 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
755 hpriv->ato = PPTP_MIN_TIMEOUT;
756
757 /* Shift packet transmit times in our transmit window */
758 bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
759 sizeof(*hpriv->timeSent)
760 * (PPTP_XMIT_WIN - (index + 1)));
761
762 /* If we sent an entire window, increase window size */
763 if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
764 && hpriv->xmitWin < PPTP_XMIT_WIN) {
765 hpriv->xmitWin++;
766 hpriv->winAck = ack + hpriv->xmitWin;
767 }
768
769 /* Stop/(re)start receive ACK timer as necessary */
770 ng_uncallout(&hpriv->rackTimer, hpriv->node);
771 if (hpriv->recvAck != hpriv->xmitSeq)
772 ng_pptpgre_start_recv_ack_timer(hpriv);
773 }
774 }
775 badAck:
776
777 /* See if frame contains any data */
778 if (gre->hasSeq) {
779 const u_int32_t seq = be32dec(&gre->data[0]);
780
781 /* Sanity check sequence number */
782 if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) {
783 if (seq == hpriv->recvSeq)
784 priv->stats.recvDuplicates++;
785 else
786 priv->stats.recvOutOfOrder++;
787 mtx_unlock(&hpriv->mtx);
788 ERROUT(EINVAL);
789 }
790 hpriv->recvSeq = seq;
791
792 /* We need to acknowledge this packet; do it soon... */
793 if (!(callout_pending(&hpriv->sackTimer))) {
794 /* If delayed ACK is disabled, send it now */
795 if (!hpriv->conf.enableDelayedAck) { /* ack now */
796 ng_pptpgre_xmit(hpriv, NULL);
797 /* ng_pptpgre_xmit() drops the mutex */
798 } else { /* ack later */
799 ng_pptpgre_start_send_ack_timer(hpriv);
800 mtx_unlock(&hpriv->mtx);
801 }
802 } else
803 mtx_unlock(&hpriv->mtx);
804
805 /* Trim mbuf down to internal payload */
806 m_adj(m, iphlen + grelen);
807 if (extralen > 0)
808 m_adj(m, -extralen);
809
810 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
811
812 /* Deliver frame to upper layers */
813 NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
814 } else {
815 priv->stats.recvLoneAcks++;
816 mtx_unlock(&hpriv->mtx);
817 NG_FREE_ITEM(item);
818 NG_FREE_M(m); /* no data to deliver */
819 }
820
821 return (error);
822
823 done:
824 NG_FREE_ITEM(item);
825 NG_FREE_M(m);
826 return (error);
827 }
828
829 /*************************************************************************
830 TIMER RELATED FUNCTIONS
831 *************************************************************************/
832
833 /*
834 * Start a timer for the peer's acknowledging our oldest unacknowledged
835 * sequence number. If we get an ack for this sequence number before
836 * the timer goes off, we cancel the timer. Resets currently running
837 * recv ack timer, if any.
838 */
839 static void
840 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
841 {
842 int remain, ticks;
843
844 /* Compute how long until oldest unack'd packet times out,
845 and reset the timer to that time. */
846 remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
847 if (remain < 0)
848 remain = 0;
849
850 /* Be conservative: timeout can happen up to 1 tick early */
851 ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1;
852 ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
853 ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
854 }
855
856 /*
857 * The peer has failed to acknowledge the oldest unacknowledged sequence
858 * number within the time allotted. Update our adaptive timeout parameters
859 * and reset/restart the recv ack timer.
860 */
861 static void
862 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
863 {
864 const priv_p priv = NG_NODE_PRIVATE(node);
865 const hpriv_p hpriv = arg1;
866
867 /* Update adaptive timeout stuff */
868 priv->stats.recvAckTimeouts++;
869 hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
870 hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
871 if (hpriv->ato > PPTP_MAX_TIMEOUT)
872 hpriv->ato = PPTP_MAX_TIMEOUT;
873 else if (hpriv->ato < PPTP_MIN_TIMEOUT)
874 hpriv->ato = PPTP_MIN_TIMEOUT;
875
876 /* Reset ack and sliding window */
877 hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */
878 hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */
879 hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */
880 }
881
882 /*
883 * Start the send ack timer. This assumes the timer is not
884 * already running.
885 */
886 static void
887 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
888 {
889 int ackTimeout, ticks;
890
891 /* Take 1/4 of the estimated round trip time */
892 ackTimeout = (hpriv->rtt >> 2);
893 if (ackTimeout < PPTP_MIN_ACK_DELAY)
894 ackTimeout = PPTP_MIN_ACK_DELAY;
895 else if (ackTimeout > PPTP_MAX_ACK_DELAY)
896 ackTimeout = PPTP_MAX_ACK_DELAY;
897
898 /* Be conservative: timeout can happen up to 1 tick early */
899 ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE);
900 ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
901 ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
902 }
903
904 /*
905 * We've waited as long as we're willing to wait before sending an
906 * acknowledgement to the peer for received frames. We had hoped to
907 * be able to piggy back our acknowledgement on an outgoing data frame,
908 * but apparently there haven't been any since. So send the ack now.
909 */
910 static void
911 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
912 {
913 const hpriv_p hpriv = arg1;
914
915 mtx_lock(&hpriv->mtx);
916 /* Send a frame with an ack but no payload */
917 ng_pptpgre_xmit(hpriv, NULL);
918 mtx_assert(&hpriv->mtx, MA_NOTOWNED);
919 }
920
921 /*************************************************************************
922 MISC FUNCTIONS
923 *************************************************************************/
924
925 /*
926 * Find the hook with a given session ID.
927 */
928 static hpriv_p
929 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
930 {
931 uint16_t hash = SESSHASH(cid);
932 hpriv_p hpriv = NULL;
933
934 LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
935 if (hpriv->conf.cid == cid)
936 break;
937 }
938
939 return (hpriv);
940 }
941
942 /*
943 * Reset state (must be called with lock held or from writer)
944 */
945 static void
946 ng_pptpgre_reset(hpriv_p hpriv)
947 {
948 /* Reset adaptive timeout state */
949 hpriv->ato = PPTP_MAX_TIMEOUT;
950 hpriv->rtt = PPTP_TIME_SCALE / 10;
951 if (hpriv->conf.peerPpd > 1) /* ppd = 0 treat as = 1 */
952 hpriv->rtt *= hpriv->conf.peerPpd;
953 hpriv->dev = 0;
954 hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
955 if (hpriv->xmitWin < 2) /* often the first packet is lost */
956 hpriv->xmitWin = 2; /* because the peer isn't ready */
957 else if (hpriv->xmitWin > PPTP_XMIT_WIN)
958 hpriv->xmitWin = PPTP_XMIT_WIN;
959 hpriv->winAck = hpriv->xmitWin;
960
961 /* Reset sequence numbers */
962 hpriv->recvSeq = ~0;
963 hpriv->recvAck = ~0;
964 hpriv->xmitSeq = ~0;
965 hpriv->xmitAck = ~0;
966
967 /* Stop timers */
968 ng_uncallout(&hpriv->sackTimer, hpriv->node);
969 ng_uncallout(&hpriv->rackTimer, hpriv->node);
970 }
971
972 /*
973 * Return the current time scaled & translated to our internally used format.
974 */
975 static pptptime_t
976 ng_pptpgre_time(void)
977 {
978 struct timeval tv;
979 pptptime_t t;
980
981 microuptime(&tv);
982 t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
983 t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
984 return(t);
985 }
Cache object: 67984a170671e93bfd40c4e2671ea186
|