The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netgraph/ng_pptpgre.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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$
   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 #include <sys/sysctl.h>
   68 
   69 #include <netinet/in.h>
   70 #include <netinet/in_systm.h>
   71 #include <netinet/ip.h>
   72 
   73 #include <netgraph/ng_message.h>
   74 #include <netgraph/netgraph.h>
   75 #include <netgraph/ng_parse.h>
   76 #include <netgraph/ng_pptpgre.h>
   77 
   78 /* GRE packet format, as used by PPTP */
   79 struct greheader {
   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 */
  100 #else
  101 #error BYTE_ORDER is not defined properly
  102 #endif
  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 */
  107 };
  108 
  109 /* The PPTP protocol ID used in the GRE 'proto' field */
  110 #define PPTP_GRE_PROTO          0x880b
  111 
  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
  115 
  116 /* Min and max packet length */
  117 #define PPTP_MAX_PAYLOAD        (0xffff - sizeof(struct greheader) - 8)
  118 
  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;
  122 
  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 */
  127 
  128 #define PPTP_REORDER_TIMEOUT    1
  129 
  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 minimum
  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 */
  136 
  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 */
  142 
  143 #define PPTP_SEQ_DIFF(x,y)      ((int32_t)(x) - (int32_t)(y))
  144 
  145 #define SESSHASHSIZE            0x0020
  146 #define SESSHASH(x)             (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
  147 
  148 SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW, 0, "PPTPGRE");
  149 
  150 /*
  151  * Reorder queue maximum length. Zero disables reorder.
  152  *
  153  * The node may keep reorder_max queue entries per session
  154  * if reorder is enabled, plus allocate one more for short time.
  155  *
  156  * Be conservative in memory consumption by default.
  157  * Lots of sessions with large queues can overflow M_NETGRAPH zone.
  158  */
  159 static int reorder_max = 1; /* reorder up to two swapped packets in a row */
  160 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
  161         &reorder_max, 0, "Reorder queue maximum length");
  162 
  163 static int reorder_timeout = PPTP_REORDER_TIMEOUT;
  164 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
  165         &reorder_timeout, 0, "Reorder timeout is milliseconds");
  166 
  167 /* Packet reorder FIFO queue */
  168 struct ng_pptpgre_roq {
  169         SLIST_ENTRY(ng_pptpgre_roq)  next;      /* next entry of the queue */
  170         item_p                  item;           /* netgraph item */
  171         u_int32_t               seq;            /* packet sequence number */
  172 };
  173 SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
  174 typedef struct ng_pptpgre_roq_head roqh;
  175 
  176 /* We keep packet retransmit and acknowlegement state in this struct */
  177 struct ng_pptpgre_sess {
  178         node_p                  node;           /* this node pointer */
  179         hook_p                  hook;           /* hook to upper layers */
  180         struct ng_pptpgre_conf  conf;           /* configuration info */
  181         struct mtx              mtx;            /* session mutex */
  182         u_int32_t               recvSeq;        /* last seq # we rcv'd */
  183         u_int32_t               xmitSeq;        /* last seq # we sent */
  184         u_int32_t               recvAck;        /* last seq # peer ack'd */
  185         u_int32_t               xmitAck;        /* last seq # we ack'd */
  186         int32_t                 ato;            /* adaptive time-out value */
  187         int32_t                 rtt;            /* round trip time estimate */
  188         int32_t                 dev;            /* deviation estimate */
  189         u_int16_t               xmitWin;        /* size of xmit window */
  190         struct callout          sackTimer;      /* send ack timer */
  191         struct callout          rackTimer;      /* recv ack timer */
  192         u_int32_t               winAck;         /* seq when xmitWin will grow */
  193         pptptime_t              timeSent[PPTP_XMIT_WIN];
  194         LIST_ENTRY(ng_pptpgre_sess) sessions;
  195         roqh                    roq;            /* reorder queue head */
  196         u_int8_t                roq_len;        /* reorder queue length */
  197         struct callout          reorderTimer;   /* reorder timeout handler */
  198 };
  199 typedef struct ng_pptpgre_sess *hpriv_p;
  200 
  201 /* Node private data */
  202 struct ng_pptpgre_private {
  203         hook_p                  upper;          /* hook to upper layers */
  204         hook_p                  lower;          /* hook to lower layers */
  205         struct ng_pptpgre_sess  uppersess;      /* default session for compat */
  206         LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
  207         struct ng_pptpgre_stats stats;          /* node statistics */
  208 };
  209 typedef struct ng_pptpgre_private *priv_p;
  210 
  211 /* Netgraph node methods */
  212 static ng_constructor_t ng_pptpgre_constructor;
  213 static ng_rcvmsg_t      ng_pptpgre_rcvmsg;
  214 static ng_shutdown_t    ng_pptpgre_shutdown;
  215 static ng_newhook_t     ng_pptpgre_newhook;
  216 static ng_rcvdata_t     ng_pptpgre_rcvdata;
  217 static ng_rcvdata_t     ng_pptpgre_rcvdata_lower;
  218 static ng_disconnect_t  ng_pptpgre_disconnect;
  219 
  220 /* Helper functions */
  221 static int      ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
  222 static void     ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
  223 static void     ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
  224 static void     ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
  225 static void     ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
  226                     void *arg1, int arg2);
  227 static void     ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
  228                     void *arg1, int arg2);
  229 static void     ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
  230                     void *arg1, int arg2);
  231 static hpriv_p  ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
  232 static void     ng_pptpgre_reset(hpriv_p hpriv);
  233 static pptptime_t ng_pptpgre_time(void);
  234 static void     ng_pptpgre_ack(const hpriv_p hpriv);
  235 static int      ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
  236                     const struct ng_pptpgre_roq *st);
  237 
  238 /* Parse type for struct ng_pptpgre_conf */
  239 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
  240         = NG_PPTPGRE_CONF_TYPE_INFO;
  241 static const struct ng_parse_type ng_pptpgre_conf_type = {
  242         &ng_parse_struct_type,
  243         &ng_pptpgre_conf_type_fields,
  244 };
  245 
  246 /* Parse type for struct ng_pptpgre_stats */
  247 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
  248         = NG_PPTPGRE_STATS_TYPE_INFO;
  249 static const struct ng_parse_type ng_pptp_stats_type = {
  250         &ng_parse_struct_type,
  251         &ng_pptpgre_stats_type_fields
  252 };
  253 
  254 /* List of commands and how to convert arguments to/from ASCII */
  255 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
  256         {
  257           NGM_PPTPGRE_COOKIE,
  258           NGM_PPTPGRE_SET_CONFIG,
  259           "setconfig",
  260           &ng_pptpgre_conf_type,
  261           NULL
  262         },
  263         {
  264           NGM_PPTPGRE_COOKIE,
  265           NGM_PPTPGRE_GET_CONFIG,
  266           "getconfig",
  267           &ng_parse_hint16_type,
  268           &ng_pptpgre_conf_type
  269         },
  270         {
  271           NGM_PPTPGRE_COOKIE,
  272           NGM_PPTPGRE_GET_STATS,
  273           "getstats",
  274           NULL,
  275           &ng_pptp_stats_type
  276         },
  277         {
  278           NGM_PPTPGRE_COOKIE,
  279           NGM_PPTPGRE_CLR_STATS,
  280           "clrstats",
  281           NULL,
  282           NULL
  283         },
  284         {
  285           NGM_PPTPGRE_COOKIE,
  286           NGM_PPTPGRE_GETCLR_STATS,
  287           "getclrstats",
  288           NULL,
  289           &ng_pptp_stats_type
  290         },
  291         { 0 }
  292 };
  293 
  294 /* Node type descriptor */
  295 static struct ng_type ng_pptpgre_typestruct = {
  296         .version =      NG_ABI_VERSION,
  297         .name =         NG_PPTPGRE_NODE_TYPE,
  298         .constructor =  ng_pptpgre_constructor,
  299         .rcvmsg =       ng_pptpgre_rcvmsg,
  300         .shutdown =     ng_pptpgre_shutdown,
  301         .newhook =      ng_pptpgre_newhook,
  302         .rcvdata =      ng_pptpgre_rcvdata,
  303         .disconnect =   ng_pptpgre_disconnect,
  304         .cmdlist =      ng_pptpgre_cmdlist,
  305 };
  306 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
  307 
  308 #define ERROUT(x)       do { error = (x); goto done; } while (0)
  309 
  310 /************************************************************************
  311                         NETGRAPH NODE STUFF
  312  ************************************************************************/
  313 
  314 /*
  315  * Node type constructor
  316  */
  317 static int
  318 ng_pptpgre_constructor(node_p node)
  319 {
  320         priv_p priv;
  321         int i;
  322 
  323         /* Allocate private structure */
  324         priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
  325 
  326         NG_NODE_SET_PRIVATE(node, priv);
  327 
  328         /* Initialize state */
  329         mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
  330         ng_callout_init(&priv->uppersess.sackTimer);
  331         ng_callout_init(&priv->uppersess.rackTimer);
  332         priv->uppersess.node = node;
  333 
  334         SLIST_INIT(&priv->uppersess.roq);
  335         priv->uppersess.roq_len = 0;
  336         ng_callout_init(&priv->uppersess.reorderTimer);
  337 
  338         for (i = 0; i < SESSHASHSIZE; i++)
  339             LIST_INIT(&priv->sesshash[i]);
  340 
  341         LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
  342 
  343         /* Done */
  344         return (0);
  345 }
  346 
  347 /*
  348  * Give our OK for a hook to be added.
  349  */
  350 static int
  351 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
  352 {
  353         const priv_p priv = NG_NODE_PRIVATE(node);
  354 
  355         /* Check hook name */
  356         if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
  357                 priv->upper = hook;
  358                 priv->uppersess.hook = hook;
  359                 NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
  360         } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
  361                 priv->lower = hook;
  362                 NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
  363         } else {
  364                 static const char hexdig[16] = "0123456789abcdef";
  365                 const char *hex;
  366                 hpriv_p hpriv;
  367                 int i, j;
  368                 uint16_t cid, hash;
  369 
  370                 /* Parse hook name to get session ID */
  371                 if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
  372                     sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
  373                         return (EINVAL);
  374                 hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
  375                 for (cid = i = 0; i < 4; i++) {
  376                         for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
  377                         if (j == 16)
  378                                 return (EINVAL);
  379                         cid = (cid << 4) | j;
  380                 }
  381                 if (hex[i] != '\0')
  382                         return (EINVAL);
  383 
  384                 hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
  385                 if (hpriv == NULL)
  386                         return (ENOMEM);
  387         
  388                 /* Initialize state */
  389                 mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
  390                 ng_callout_init(&hpriv->sackTimer);
  391                 ng_callout_init(&hpriv->rackTimer);
  392                 hpriv->conf.cid = cid;
  393                 hpriv->node = node;
  394                 hpriv->hook = hook;
  395 
  396                 SLIST_INIT(&hpriv->roq);
  397                 hpriv->roq_len = 0;
  398                 ng_callout_init(&hpriv->reorderTimer);
  399 
  400                 NG_HOOK_SET_PRIVATE(hook, hpriv);
  401 
  402                 hash = SESSHASH(cid);
  403                 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
  404         }
  405 
  406         return (0);
  407 }
  408 
  409 /*
  410  * Receive a control message.
  411  */
  412 static int
  413 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
  414 {
  415         const priv_p priv = NG_NODE_PRIVATE(node);
  416         struct ng_mesg *resp = NULL;
  417         int error = 0;
  418         struct ng_mesg *msg;
  419 
  420         NGI_GET_MSG(item, msg);
  421         switch (msg->header.typecookie) {
  422         case NGM_PPTPGRE_COOKIE:
  423                 switch (msg->header.cmd) {
  424                 case NGM_PPTPGRE_SET_CONFIG:
  425                     {
  426                         struct ng_pptpgre_conf *const newConf =
  427                                 (struct ng_pptpgre_conf *) msg->data;
  428                         hpriv_p hpriv;
  429                         uint16_t hash;
  430 
  431                         /* Check for invalid or illegal config */
  432                         if (msg->header.arglen != sizeof(*newConf))
  433                                 ERROUT(EINVAL);
  434                         /* Try to find session by cid. */
  435                         hpriv = ng_pptpgre_find_session(priv, newConf->cid);
  436                         /* If not present - use upper. */
  437                         if (hpriv == NULL) {
  438                                 hpriv = &priv->uppersess;
  439                                 LIST_REMOVE(hpriv, sessions);
  440                                 hash = SESSHASH(newConf->cid);
  441                                 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
  442                                     sessions);
  443                         }
  444                         ng_pptpgre_reset(hpriv);        /* reset on configure */
  445                         hpriv->conf = *newConf;
  446                         break;
  447                     }
  448                 case NGM_PPTPGRE_GET_CONFIG:
  449                     {
  450                         hpriv_p hpriv;
  451 
  452                         if (msg->header.arglen == 2) {
  453                                 /* Try to find session by cid. */
  454                                 hpriv = ng_pptpgre_find_session(priv,
  455                                     *((uint16_t *)msg->data));
  456                                 if (hpriv == NULL)
  457                                         ERROUT(EINVAL);
  458                         } else if (msg->header.arglen == 0) {
  459                                 /* Use upper. */
  460                                 hpriv = &priv->uppersess;
  461                         } else
  462                                 ERROUT(EINVAL);
  463                         NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
  464                         if (resp == NULL)
  465                                 ERROUT(ENOMEM);
  466                         bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
  467                         break;
  468                     }
  469                 case NGM_PPTPGRE_GET_STATS:
  470                 case NGM_PPTPGRE_CLR_STATS:
  471                 case NGM_PPTPGRE_GETCLR_STATS:
  472                     {
  473                         if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
  474                                 NG_MKRESPONSE(resp, msg,
  475                                     sizeof(priv->stats), M_NOWAIT);
  476                                 if (resp == NULL)
  477                                         ERROUT(ENOMEM);
  478                                 bcopy(&priv->stats,
  479                                     resp->data, sizeof(priv->stats));
  480                         }
  481                         if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
  482                                 bzero(&priv->stats, sizeof(priv->stats));
  483                         break;
  484                     }
  485                 default:
  486                         error = EINVAL;
  487                         break;
  488                 }
  489                 break;
  490         default:
  491                 error = EINVAL;
  492                 break;
  493         }
  494 done:
  495         NG_RESPOND_MSG(error, node, item, resp);
  496         NG_FREE_MSG(msg);
  497         return (error);
  498 }
  499 
  500 /*
  501  * Receive incoming data on a hook.
  502  */
  503 static int
  504 ng_pptpgre_rcvdata(hook_p hook, item_p item)
  505 {
  506         const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
  507         int rval;
  508 
  509         /* If not configured, reject */
  510         if (!hpriv->conf.enabled) {
  511                 NG_FREE_ITEM(item);
  512                 return (ENXIO);
  513         }
  514 
  515         mtx_lock(&hpriv->mtx);
  516 
  517         rval = ng_pptpgre_xmit(hpriv, item);
  518 
  519         mtx_assert(&hpriv->mtx, MA_NOTOWNED);
  520 
  521         return (rval);
  522 }
  523 
  524 /*
  525  * Hook disconnection
  526  */
  527 static int
  528 ng_pptpgre_disconnect(hook_p hook)
  529 {
  530         const node_p node = NG_HOOK_NODE(hook);
  531         const priv_p priv = NG_NODE_PRIVATE(node);
  532         const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
  533 
  534         /* Zero out hook pointer */
  535         if (hook == priv->upper) {
  536                 priv->upper = NULL;
  537                 priv->uppersess.hook = NULL;
  538         } else if (hook == priv->lower) {
  539                 priv->lower = NULL;
  540         } else {
  541                 /* Reset node (stops timers) */
  542                 ng_pptpgre_reset(hpriv);
  543 
  544                 LIST_REMOVE(hpriv, sessions);
  545                 mtx_destroy(&hpriv->mtx);
  546                 free(hpriv, M_NETGRAPH);
  547         }
  548 
  549         /* Go away if no longer connected to anything */
  550         if ((NG_NODE_NUMHOOKS(node) == 0)
  551         && (NG_NODE_IS_VALID(node)))
  552                 ng_rmnode_self(node);
  553         return (0);
  554 }
  555 
  556 /*
  557  * Destroy node
  558  */
  559 static int
  560 ng_pptpgre_shutdown(node_p node)
  561 {
  562         const priv_p priv = NG_NODE_PRIVATE(node);
  563 
  564         /* Reset node (stops timers) */
  565         ng_pptpgre_reset(&priv->uppersess);
  566 
  567         LIST_REMOVE(&priv->uppersess, sessions);
  568         mtx_destroy(&priv->uppersess.mtx);
  569 
  570         free(priv, M_NETGRAPH);
  571 
  572         /* Decrement ref count */
  573         NG_NODE_UNREF(node);
  574         return (0);
  575 }
  576 
  577 /*************************************************************************
  578                     TRANSMIT AND RECEIVE FUNCTIONS
  579 *************************************************************************/
  580 
  581 /*
  582  * Transmit an outgoing frame, or just an ack if m is NULL.
  583  */
  584 static int
  585 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
  586 {
  587         const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
  588         u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
  589         struct greheader *const gre = (struct greheader *)buf;
  590         int grelen, error;
  591         struct mbuf *m;
  592 
  593         mtx_assert(&hpriv->mtx, MA_OWNED);
  594 
  595         if (item) {
  596                 NGI_GET_M(item, m);
  597         } else {
  598                 m = NULL;
  599         }
  600         /* Check if there's data */
  601         if (m != NULL) {
  602 
  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++;
  609                                 ERROUT(ENOBUFS);
  610                         }
  611                 }
  612 
  613                 /* Sanity check frame length */
  614                 if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
  615                         priv->stats.xmitTooBig++;
  616                         ERROUT(EMSGSIZE);
  617                 }
  618         } else {
  619                 priv->stats.xmitLoneAcks++;
  620         }
  621 
  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);
  626 
  627         /* Include sequence number if packet contains any data */
  628         if (m != NULL) {
  629                 gre->hasSeq = 1;
  630                 if (hpriv->conf.enableWindowing) {
  631                         hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
  632                             = ng_pptpgre_time();
  633                 }
  634                 hpriv->xmitSeq++;
  635                 be32enc(&gre->data[0], hpriv->xmitSeq);
  636         }
  637 
  638         /* Include acknowledgement (and stop send ack timer) if needed */
  639         if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
  640                 gre->hasAck = 1;
  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);
  645         }
  646 
  647         /* Prepend GRE header to outgoing frame */
  648         grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
  649         if (m == NULL) {
  650                 MGETHDR(m, M_NOWAIT, MT_DATA);
  651                 if (m == NULL) {
  652                         priv->stats.memoryFailures++;
  653                         ERROUT(ENOBUFS);
  654                 }
  655                 m->m_len = m->m_pkthdr.len = grelen;
  656                 m->m_pkthdr.rcvif = NULL;
  657         } else {
  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++;
  662                         ERROUT(ENOBUFS);
  663                 }
  664         }
  665         bcopy(gre, mtod(m, u_char *), grelen);
  666 
  667         /* Update stats */
  668         priv->stats.xmitPackets++;
  669         priv->stats.xmitOctets += m->m_pkthdr.len;
  670 
  671         /*
  672          * XXX: we should reset timer only after an item has been sent
  673          * successfully.
  674          */
  675         if (hpriv->conf.enableWindowing &&
  676             gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
  677                 ng_pptpgre_start_recv_ack_timer(hpriv);
  678 
  679         mtx_unlock(&hpriv->mtx);
  680 
  681         /* Deliver packet */
  682         if (item) {
  683                 NG_FWD_NEW_DATA(error, item, priv->lower, m);
  684         } else {
  685                 NG_SEND_DATA_ONLY(error, priv->lower, m);
  686         }
  687 
  688         return (error);
  689 
  690 done:
  691         mtx_unlock(&hpriv->mtx);
  692         NG_FREE_M(m);
  693         if (item)
  694                 NG_FREE_ITEM(item);
  695         return (error);
  696 }
  697 
  698 static void
  699 ng_pptpgre_ack(const hpriv_p hpriv)
  700 {
  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 */
  707                         return;
  708                 }
  709                 /* ack later */
  710                 ng_pptpgre_start_send_ack_timer(hpriv);
  711                 mtx_unlock(&hpriv->mtx);
  712                 return;
  713         }
  714         mtx_unlock(&hpriv->mtx);
  715 }
  716 
  717 /*
  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.
  721  */
  722 static int
  723 ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
  724 {
  725         struct ng_pptpgre_roq *np;
  726         struct mbuf *m;
  727         int error = 0;
  728 
  729         mtx_assert(&hpriv->mtx, MA_NOTOWNED);
  730         while (!SLIST_EMPTY(q)) {
  731                 np = SLIST_FIRST(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);
  735                 if (np != st)
  736                         free(np, M_NETGRAPH);
  737         }
  738         return (error);
  739 }
  740 
  741 /*
  742  * Handle an incoming packet.  The packet includes the IP header.
  743  */
  744 static int
  745 ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
  746 {
  747         hpriv_p hpriv;
  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;
  752         const struct ip *ip;
  753         int error = 0;
  754         struct mbuf *m;
  755 
  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 };
  760         long diff;
  761         u_int32_t seq;
  762 
  763         m = NGI_M(item);
  764         /* Update stats */
  765         priv->stats.recvPackets++;
  766         priv->stats.recvOctets += m->m_pkthdr.len;
  767 
  768         /* Sanity check packet length */
  769         if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
  770                 priv->stats.recvRunts++;
  771                 ERROUT(EINVAL);
  772         }
  773 
  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++;
  778                         _NGI_M(item) = NULL;
  779                         ERROUT(ENOBUFS);
  780                 }
  781                 _NGI_M(item) = m;
  782         }
  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++;
  788                         _NGI_M(item) = NULL;
  789                         ERROUT(ENOBUFS);
  790                 }
  791                 _NGI_M(item) = m;
  792                 ip = mtod(m, const struct ip *);
  793         }
  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++;
  798                 ERROUT(EINVAL);
  799         }
  800         if (m->m_len < iphlen + grelen) {
  801                 if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
  802                         priv->stats.memoryFailures++;
  803                         _NGI_M(item) = NULL;
  804                         ERROUT(ENOBUFS);
  805                 }
  806                 _NGI_M(item) = m;
  807                 ip = mtod(m, const struct ip *);
  808                 gre = (const struct greheader *)((const u_char *)ip + iphlen);
  809         }
  810 
  811         /* Sanity check packet length and GRE header bits */
  812         extralen = m->m_pkthdr.len
  813             - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
  814         if (extralen < 0) {
  815                 priv->stats.recvBadGRE++;
  816                 ERROUT(EINVAL);
  817         }
  818         if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
  819                 priv->stats.recvBadGRE++;
  820                 ERROUT(EINVAL);
  821         }
  822 
  823         hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
  824         if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
  825                 priv->stats.recvBadCID++;
  826                 ERROUT(EINVAL);
  827         }
  828         mtx_lock(&hpriv->mtx);
  829 
  830         /* Look for peer ack */
  831         if (gre->hasAck) {
  832                 const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]);
  833                 const int index = ack - hpriv->recvAck - 1;
  834                 long sample;
  835 
  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! */
  840                 }
  841                 if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
  842                         goto badAck;            /* ack already timed out */
  843                 hpriv->recvAck = ack;
  844 
  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);
  850                         if (diff < 0)
  851                                 diff = -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;
  859 
  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)));
  864 
  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) {
  868                                 hpriv->xmitWin++;
  869                                 hpriv->winAck = ack + hpriv->xmitWin;
  870                         }
  871 
  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);
  876                 }
  877         }
  878 badAck:
  879 
  880         /* See if frame contains any data */
  881         if (!gre->hasSeq) {             /* no data to deliver */
  882                 priv->stats.recvLoneAcks++;
  883                 mtx_unlock(&hpriv->mtx);
  884                 ERROUT(0);
  885         }
  886 
  887         seq = be32dec(&gre->data[0]);
  888 
  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 */
  893                 else
  894                         priv->stats.recvDuplicates++;   /* duplicate */
  895                 mtx_unlock(&hpriv->mtx);
  896                 ERROUT(EINVAL);
  897         }
  898 
  899         /* Trim mbuf down to internal payload */
  900         m_adj(m, iphlen + grelen);
  901         if (extralen > 0)
  902                 m_adj(m, -extralen);
  903 
  904 #define INIT_SENDQ(t) do {                              \
  905                 t.item = item;                          \
  906                 t.seq = seq;                            \
  907                 SLIST_INSERT_HEAD(&sendq, &t, next);    \
  908                 last = &t;                              \
  909                 hpriv->recvSeq = seq;                   \
  910                 goto deliver;                           \
  911         } while(0)
  912 
  913         if (diff == 1)
  914                 /* the packet came in order, place it at the start of sendq */
  915                 INIT_SENDQ(temp);
  916 
  917         /* The packet came too early, try to enqueue it.
  918          *
  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".
  922          */
  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);
  929                         ERROUT(EINVAL);
  930                 }
  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 */
  934                         break;
  935                 }
  936                 prev = np;
  937         }
  938 
  939         priv->stats.recvOutOfOrder++;   /* duplicate not found */
  940         if (hpriv->roq_len < reorder_max)
  941                 goto enqueue;   /* reorder enabled and there is a room */
  942 
  943         /*
  944          * There is no room in the queue or reorder disabled.
  945          *
  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.
  949          *
  950          * In both cases, no malloc()'s until the queue is shortened.
  951          */
  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 */
  955         }
  956 #undef INIT_SENDQ
  957 
  958         /*
  959          * Current packet goes after the head of reorder queue.
  960          * Move the head to sendq to make room for current packet.
  961          */
  962         np = SLIST_FIRST(&hpriv->roq);
  963         if (prev == np)
  964                 prev = NULL;
  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);
  968         last = np;
  969         hpriv->recvSeq = np->seq;
  970 
  971 enqueue:
  972         np = malloc(sizeof(*np), M_NETGRAPH, M_NOWAIT | M_ZERO);
  973         if (np == NULL) {
  974                 priv->stats.memoryFailures++;
  975                 /*
  976                  * Emergency: we cannot save new data.
  977                  * Flush the queue delivering all queued packets preceeding
  978                  * current one despite of gaps.
  979                  */
  980                 while (!SLIST_EMPTY(&hpriv->roq)) {
  981                         np = SLIST_FIRST(&hpriv->roq);
  982                         if (np->seq > seq)
  983                                 break;
  984                         SLIST_REMOVE_HEAD(&hpriv->roq, next);
  985                         hpriv->roq_len--;
  986                         if (last == NULL)
  987                                 SLIST_INSERT_HEAD(&sendq, np, next);
  988                         else
  989                                 SLIST_INSERT_AFTER(last, np, next);
  990                         last = np;
  991                 }
  992 
  993                 /*
  994                  * Pretend we got all packets till the current one
  995                  * and acknowledge it.
  996                  */
  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);
 1001                 ERROUT(ENOMEM);
 1002         }
 1003 
 1004         /* Add current (early) packet to the reorder queue. */
 1005         np->item = item;
 1006         np->seq = seq;
 1007         if (prev == NULL)
 1008                 SLIST_INSERT_HEAD(&hpriv->roq, np, next);
 1009         else
 1010                 SLIST_INSERT_AFTER(prev, np, next);
 1011         hpriv->roq_len++;
 1012 
 1013 deliver:
 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 */
 1019 
 1020                 /* "np" is in sequence, move it to the sendq. */
 1021                 SLIST_REMOVE_HEAD(&hpriv->roq, next);
 1022                 hpriv->roq_len--;
 1023                 hpriv->recvSeq = np->seq;
 1024 
 1025                 if (last == NULL)
 1026                         SLIST_INSERT_HEAD(&sendq, np, next);
 1027                 else
 1028                         SLIST_INSERT_AFTER(last, np, next);
 1029                 last = np;
 1030         }
 1031 
 1032         if (SLIST_EMPTY(&hpriv->roq)) {
 1033                 if (callout_pending(&hpriv->reorderTimer))
 1034                         ng_uncallout(&hpriv->reorderTimer, hpriv->node);
 1035         } else {
 1036                 if (!callout_pending(&hpriv->reorderTimer))
 1037                         ng_pptpgre_start_reorder_timer(hpriv);
 1038         }
 1039 
 1040         if (SLIST_EMPTY(&sendq)) {
 1041                 /* Current packet has been queued, nothing to free/deliver. */
 1042                 mtx_unlock(&hpriv->mtx);
 1043                 return (error);
 1044         }
 1045 
 1046         /* We need to acknowledge last packet; do it soon... */
 1047         ng_pptpgre_ack(hpriv);          /* drops lock */
 1048         ng_pptpgre_sendq(hpriv, &sendq, &temp);
 1049         return (error);
 1050 
 1051 done:
 1052         NG_FREE_ITEM(item);
 1053         return (error);
 1054 }
 1055 
 1056 /*************************************************************************
 1057                     TIMER RELATED FUNCTIONS
 1058 *************************************************************************/
 1059 
 1060 /*
 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.
 1065  */
 1066 static void
 1067 ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
 1068 {
 1069         int remain, ticks;
 1070 
 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();
 1074         if (remain < 0)
 1075                 remain = 0;
 1076 
 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);
 1081 }
 1082 
 1083 /*
 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.
 1087  */
 1088 static void
 1089 ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
 1090 {
 1091         const priv_p priv = NG_NODE_PRIVATE(node);
 1092         const hpriv_p hpriv = arg1;
 1093 
 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;
 1102 
 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 */
 1107 }
 1108 
 1109 /*
 1110  * Start the send ack timer. This assumes the timer is not
 1111  * already running.
 1112  */
 1113 static void
 1114 ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
 1115 {
 1116         int ackTimeout, ticks;
 1117 
 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;
 1124 
 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);
 1129 }
 1130 
 1131 /*
 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.
 1136  */
 1137 static void
 1138 ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
 1139 {
 1140         const hpriv_p hpriv = arg1;
 1141 
 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);
 1146 }
 1147 
 1148 /*
 1149  * Start a timer for the reorder queue. This assumes the timer is not
 1150  * already running.
 1151  */
 1152 static void
 1153 ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
 1154 {
 1155         int ticks;
 1156 
 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);
 1161 }
 1162 
 1163 /*
 1164  * The oldest packet spent too much time in the reorder queue.
 1165  * Deliver it and next packets in sequence, if any.
 1166  */
 1167 static void
 1168 ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
 1169 {
 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;
 1174 
 1175         priv->stats.recvReorderTimeouts++;
 1176         mtx_lock(&hpriv->mtx);
 1177         if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
 1178                 mtx_unlock(&hpriv->mtx);
 1179                 return;
 1180         }
 1181 
 1182         last = np = SLIST_FIRST(&hpriv->roq);
 1183         hpriv->roq_len--;
 1184         SLIST_REMOVE_HEAD(&hpriv->roq, next);
 1185         SLIST_INSERT_HEAD(&sendq, np, next);
 1186 
 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 */
 1192 
 1193                 /* Next packet is in sequence, move it to the sendq. */
 1194                 hpriv->roq_len--;
 1195                 SLIST_REMOVE_HEAD(&hpriv->roq, next);
 1196                 SLIST_INSERT_AFTER(last, np, next);
 1197                 last = np;
 1198         }
 1199 
 1200         hpriv->recvSeq = last->seq;
 1201         if (!SLIST_EMPTY(&hpriv->roq))
 1202                 ng_pptpgre_start_reorder_timer(hpriv);
 1203 
 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);
 1208 }
 1209 
 1210 /*************************************************************************
 1211                     MISC FUNCTIONS
 1212 *************************************************************************/
 1213 
 1214 /*
 1215  * Find the hook with a given session ID.
 1216  */
 1217 static hpriv_p
 1218 ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
 1219 {
 1220         uint16_t        hash = SESSHASH(cid);
 1221         hpriv_p hpriv = NULL;
 1222 
 1223         LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
 1224                 if (hpriv->conf.cid == cid)
 1225                         break;
 1226         }
 1227 
 1228         return (hpriv);
 1229 }
 1230 
 1231 /*
 1232  * Reset state (must be called with lock held or from writer)
 1233  */
 1234 static void
 1235 ng_pptpgre_reset(hpriv_p hpriv)
 1236 {
 1237         struct ng_pptpgre_roq *np;
 1238 
 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;
 1244         hpriv->dev = 0;
 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;
 1251 
 1252         /* Reset sequence numbers */
 1253         hpriv->recvSeq = ~0;
 1254         hpriv->recvAck = ~0;
 1255         hpriv->xmitSeq = ~0;
 1256         hpriv->xmitAck = ~0;
 1257 
 1258         /* Stop timers */
 1259         ng_uncallout(&hpriv->sackTimer, hpriv->node);
 1260         ng_uncallout(&hpriv->rackTimer, hpriv->node);
 1261         ng_uncallout(&hpriv->reorderTimer, hpriv->node);
 1262 
 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);
 1269         }
 1270         hpriv->roq_len = 0;
 1271 }
 1272 
 1273 /*
 1274  * Return the current time scaled & translated to our internally used format.
 1275  */
 1276 static pptptime_t
 1277 ng_pptpgre_time(void)
 1278 {
 1279         struct timeval tv;
 1280         pptptime_t t;
 1281 
 1282         microuptime(&tv);
 1283         t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
 1284         t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
 1285         return(t);
 1286 }

Cache object: d9fd16a74cc326ca0e8993f75120b03c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.