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 | CTLFLAG_MPSAFE, 0,
  149     "PPTPGRE");
  150 
  151 /*
  152  * Reorder queue maximum length. Zero disables reorder.
  153  *
  154  * The node may keep reorder_max queue entries per session
  155  * if reorder is enabled, plus allocate one more for short time.
  156  *
  157  * Be conservative in memory consumption by default.
  158  * Lots of sessions with large queues can overflow M_NETGRAPH zone.
  159  */
  160 static int reorder_max = 1; /* reorder up to two swapped packets in a row */
  161 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
  162         &reorder_max, 0, "Reorder queue maximum length");
  163 
  164 static int reorder_timeout = PPTP_REORDER_TIMEOUT;
  165 SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
  166         &reorder_timeout, 0, "Reorder timeout is milliseconds");
  167 
  168 /* Packet reorder FIFO queue */
  169 struct ng_pptpgre_roq {
  170         SLIST_ENTRY(ng_pptpgre_roq)  next;      /* next entry of the queue */
  171         item_p                  item;           /* netgraph item */
  172         u_int32_t               seq;            /* packet sequence number */
  173 };
  174 SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
  175 typedef struct ng_pptpgre_roq_head roqh;
  176 
  177 /* We keep packet retransmit and acknowlegement state in this struct */
  178 struct ng_pptpgre_sess {
  179         node_p                  node;           /* this node pointer */
  180         hook_p                  hook;           /* hook to upper layers */
  181         struct ng_pptpgre_conf  conf;           /* configuration info */
  182         struct mtx              mtx;            /* session mutex */
  183         u_int32_t               recvSeq;        /* last seq # we rcv'd */
  184         u_int32_t               xmitSeq;        /* last seq # we sent */
  185         u_int32_t               recvAck;        /* last seq # peer ack'd */
  186         u_int32_t               xmitAck;        /* last seq # we ack'd */
  187         int32_t                 ato;            /* adaptive time-out value */
  188         int32_t                 rtt;            /* round trip time estimate */
  189         int32_t                 dev;            /* deviation estimate */
  190         u_int16_t               xmitWin;        /* size of xmit window */
  191         struct callout          sackTimer;      /* send ack timer */
  192         struct callout          rackTimer;      /* recv ack timer */
  193         u_int32_t               winAck;         /* seq when xmitWin will grow */
  194         pptptime_t              timeSent[PPTP_XMIT_WIN];
  195         LIST_ENTRY(ng_pptpgre_sess) sessions;
  196         roqh                    roq;            /* reorder queue head */
  197         u_int8_t                roq_len;        /* reorder queue length */
  198         struct callout          reorderTimer;   /* reorder timeout handler */
  199 };
  200 typedef struct ng_pptpgre_sess *hpriv_p;
  201 
  202 /* Node private data */
  203 struct ng_pptpgre_private {
  204         hook_p                  upper;          /* hook to upper layers */
  205         hook_p                  lower;          /* hook to lower layers */
  206         struct ng_pptpgre_sess  uppersess;      /* default session for compat */
  207         LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
  208         struct ng_pptpgre_stats stats;          /* node statistics */
  209 };
  210 typedef struct ng_pptpgre_private *priv_p;
  211 
  212 /* Netgraph node methods */
  213 static ng_constructor_t ng_pptpgre_constructor;
  214 static ng_rcvmsg_t      ng_pptpgre_rcvmsg;
  215 static ng_shutdown_t    ng_pptpgre_shutdown;
  216 static ng_newhook_t     ng_pptpgre_newhook;
  217 static ng_rcvdata_t     ng_pptpgre_rcvdata;
  218 static ng_rcvdata_t     ng_pptpgre_rcvdata_lower;
  219 static ng_disconnect_t  ng_pptpgre_disconnect;
  220 
  221 /* Helper functions */
  222 static int      ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
  223 static void     ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
  224 static void     ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
  225 static void     ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
  226 static void     ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
  227                     void *arg1, int arg2);
  228 static void     ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
  229                     void *arg1, int arg2);
  230 static void     ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
  231                     void *arg1, int arg2);
  232 static hpriv_p  ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
  233 static void     ng_pptpgre_reset(hpriv_p hpriv);
  234 static pptptime_t ng_pptpgre_time(void);
  235 static void     ng_pptpgre_ack(const hpriv_p hpriv);
  236 static int      ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
  237                     const struct ng_pptpgre_roq *st);
  238 
  239 /* Parse type for struct ng_pptpgre_conf */
  240 static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
  241         = NG_PPTPGRE_CONF_TYPE_INFO;
  242 static const struct ng_parse_type ng_pptpgre_conf_type = {
  243         &ng_parse_struct_type,
  244         &ng_pptpgre_conf_type_fields,
  245 };
  246 
  247 /* Parse type for struct ng_pptpgre_stats */
  248 static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
  249         = NG_PPTPGRE_STATS_TYPE_INFO;
  250 static const struct ng_parse_type ng_pptp_stats_type = {
  251         &ng_parse_struct_type,
  252         &ng_pptpgre_stats_type_fields
  253 };
  254 
  255 /* List of commands and how to convert arguments to/from ASCII */
  256 static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
  257         {
  258           NGM_PPTPGRE_COOKIE,
  259           NGM_PPTPGRE_SET_CONFIG,
  260           "setconfig",
  261           &ng_pptpgre_conf_type,
  262           NULL
  263         },
  264         {
  265           NGM_PPTPGRE_COOKIE,
  266           NGM_PPTPGRE_GET_CONFIG,
  267           "getconfig",
  268           &ng_parse_hint16_type,
  269           &ng_pptpgre_conf_type
  270         },
  271         {
  272           NGM_PPTPGRE_COOKIE,
  273           NGM_PPTPGRE_GET_STATS,
  274           "getstats",
  275           NULL,
  276           &ng_pptp_stats_type
  277         },
  278         {
  279           NGM_PPTPGRE_COOKIE,
  280           NGM_PPTPGRE_CLR_STATS,
  281           "clrstats",
  282           NULL,
  283           NULL
  284         },
  285         {
  286           NGM_PPTPGRE_COOKIE,
  287           NGM_PPTPGRE_GETCLR_STATS,
  288           "getclrstats",
  289           NULL,
  290           &ng_pptp_stats_type
  291         },
  292         { 0 }
  293 };
  294 
  295 /* Node type descriptor */
  296 static struct ng_type ng_pptpgre_typestruct = {
  297         .version =      NG_ABI_VERSION,
  298         .name =         NG_PPTPGRE_NODE_TYPE,
  299         .constructor =  ng_pptpgre_constructor,
  300         .rcvmsg =       ng_pptpgre_rcvmsg,
  301         .shutdown =     ng_pptpgre_shutdown,
  302         .newhook =      ng_pptpgre_newhook,
  303         .rcvdata =      ng_pptpgre_rcvdata,
  304         .disconnect =   ng_pptpgre_disconnect,
  305         .cmdlist =      ng_pptpgre_cmdlist,
  306 };
  307 NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
  308 
  309 #define ERROUT(x)       do { error = (x); goto done; } while (0)
  310 
  311 /************************************************************************
  312                         NETGRAPH NODE STUFF
  313  ************************************************************************/
  314 
  315 /*
  316  * Node type constructor
  317  */
  318 static int
  319 ng_pptpgre_constructor(node_p node)
  320 {
  321         priv_p priv;
  322         int i;
  323 
  324         /* Allocate private structure */
  325         priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
  326 
  327         NG_NODE_SET_PRIVATE(node, priv);
  328 
  329         /* Initialize state */
  330         mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
  331         ng_callout_init(&priv->uppersess.sackTimer);
  332         ng_callout_init(&priv->uppersess.rackTimer);
  333         priv->uppersess.node = node;
  334 
  335         SLIST_INIT(&priv->uppersess.roq);
  336         priv->uppersess.roq_len = 0;
  337         ng_callout_init(&priv->uppersess.reorderTimer);
  338 
  339         for (i = 0; i < SESSHASHSIZE; i++)
  340             LIST_INIT(&priv->sesshash[i]);
  341 
  342         LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
  343 
  344         /* Done */
  345         return (0);
  346 }
  347 
  348 /*
  349  * Give our OK for a hook to be added.
  350  */
  351 static int
  352 ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
  353 {
  354         const priv_p priv = NG_NODE_PRIVATE(node);
  355 
  356         /* Check hook name */
  357         if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
  358                 priv->upper = hook;
  359                 priv->uppersess.hook = hook;
  360                 NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
  361         } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
  362                 priv->lower = hook;
  363                 NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
  364         } else {
  365                 static const char hexdig[16] = "0123456789abcdef";
  366                 const char *hex;
  367                 hpriv_p hpriv;
  368                 int i, j;
  369                 uint16_t cid, hash;
  370 
  371                 /* Parse hook name to get session ID */
  372                 if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
  373                     sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
  374                         return (EINVAL);
  375                 hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
  376                 for (cid = i = 0; i < 4; i++) {
  377                         for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
  378                         if (j == 16)
  379                                 return (EINVAL);
  380                         cid = (cid << 4) | j;
  381                 }
  382                 if (hex[i] != '\0')
  383                         return (EINVAL);
  384 
  385                 hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO);
  386                 if (hpriv == NULL)
  387                         return (ENOMEM);
  388 
  389                 /* Initialize state */
  390                 mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
  391                 ng_callout_init(&hpriv->sackTimer);
  392                 ng_callout_init(&hpriv->rackTimer);
  393                 hpriv->conf.cid = cid;
  394                 hpriv->node = node;
  395                 hpriv->hook = hook;
  396 
  397                 SLIST_INIT(&hpriv->roq);
  398                 hpriv->roq_len = 0;
  399                 ng_callout_init(&hpriv->reorderTimer);
  400 
  401                 NG_HOOK_SET_PRIVATE(hook, hpriv);
  402 
  403                 hash = SESSHASH(cid);
  404                 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
  405         }
  406 
  407         return (0);
  408 }
  409 
  410 /*
  411  * Receive a control message.
  412  */
  413 static int
  414 ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
  415 {
  416         const priv_p priv = NG_NODE_PRIVATE(node);
  417         struct ng_mesg *resp = NULL;
  418         int error = 0;
  419         struct ng_mesg *msg;
  420 
  421         NGI_GET_MSG(item, msg);
  422         switch (msg->header.typecookie) {
  423         case NGM_PPTPGRE_COOKIE:
  424                 switch (msg->header.cmd) {
  425                 case NGM_PPTPGRE_SET_CONFIG:
  426                     {
  427                         struct ng_pptpgre_conf *const newConf =
  428                                 (struct ng_pptpgre_conf *) msg->data;
  429                         hpriv_p hpriv;
  430                         uint16_t hash;
  431 
  432                         /* Check for invalid or illegal config */
  433                         if (msg->header.arglen != sizeof(*newConf))
  434                                 ERROUT(EINVAL);
  435                         /* Try to find session by cid. */
  436                         hpriv = ng_pptpgre_find_session(priv, newConf->cid);
  437                         /* If not present - use upper. */
  438                         if (hpriv == NULL) {
  439                                 hpriv = &priv->uppersess;
  440                                 LIST_REMOVE(hpriv, sessions);
  441                                 hash = SESSHASH(newConf->cid);
  442                                 LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
  443                                     sessions);
  444                         }
  445                         ng_pptpgre_reset(hpriv);        /* reset on configure */
  446                         hpriv->conf = *newConf;
  447                         break;
  448                     }
  449                 case NGM_PPTPGRE_GET_CONFIG:
  450                     {
  451                         hpriv_p hpriv;
  452 
  453                         if (msg->header.arglen == 2) {
  454                                 /* Try to find session by cid. */
  455                                 hpriv = ng_pptpgre_find_session(priv,
  456                                     *((uint16_t *)msg->data));
  457                                 if (hpriv == NULL)
  458                                         ERROUT(EINVAL);
  459                         } else if (msg->header.arglen == 0) {
  460                                 /* Use upper. */
  461                                 hpriv = &priv->uppersess;
  462                         } else
  463                                 ERROUT(EINVAL);
  464                         NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
  465                         if (resp == NULL)
  466                                 ERROUT(ENOMEM);
  467                         bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
  468                         break;
  469                     }
  470                 case NGM_PPTPGRE_GET_STATS:
  471                 case NGM_PPTPGRE_CLR_STATS:
  472                 case NGM_PPTPGRE_GETCLR_STATS:
  473                     {
  474                         if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
  475                                 NG_MKRESPONSE(resp, msg,
  476                                     sizeof(priv->stats), M_NOWAIT);
  477                                 if (resp == NULL)
  478                                         ERROUT(ENOMEM);
  479                                 bcopy(&priv->stats,
  480                                     resp->data, sizeof(priv->stats));
  481                         }
  482                         if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
  483                                 bzero(&priv->stats, sizeof(priv->stats));
  484                         break;
  485                     }
  486                 default:
  487                         error = EINVAL;
  488                         break;
  489                 }
  490                 break;
  491         default:
  492                 error = EINVAL;
  493                 break;
  494         }
  495 done:
  496         NG_RESPOND_MSG(error, node, item, resp);
  497         NG_FREE_MSG(msg);
  498         return (error);
  499 }
  500 
  501 /*
  502  * Receive incoming data on a hook.
  503  */
  504 static int
  505 ng_pptpgre_rcvdata(hook_p hook, item_p item)
  506 {
  507         const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
  508         int rval;
  509 
  510         /* If not configured, reject */
  511         if (!hpriv->conf.enabled) {
  512                 NG_FREE_ITEM(item);
  513                 return (ENXIO);
  514         }
  515 
  516         mtx_lock(&hpriv->mtx);
  517 
  518         rval = ng_pptpgre_xmit(hpriv, item);
  519 
  520         mtx_assert(&hpriv->mtx, MA_NOTOWNED);
  521 
  522         return (rval);
  523 }
  524 
  525 /*
  526  * Hook disconnection
  527  */
  528 static int
  529 ng_pptpgre_disconnect(hook_p hook)
  530 {
  531         const node_p node = NG_HOOK_NODE(hook);
  532         const priv_p priv = NG_NODE_PRIVATE(node);
  533         const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
  534 
  535         /* Zero out hook pointer */
  536         if (hook == priv->upper) {
  537                 priv->upper = NULL;
  538                 priv->uppersess.hook = NULL;
  539         } else if (hook == priv->lower) {
  540                 priv->lower = NULL;
  541         } else {
  542                 /* Reset node (stops timers) */
  543                 ng_pptpgre_reset(hpriv);
  544 
  545                 LIST_REMOVE(hpriv, sessions);
  546                 mtx_destroy(&hpriv->mtx);
  547                 free(hpriv, M_NETGRAPH);
  548         }
  549 
  550         /* Go away if no longer connected to anything */
  551         if ((NG_NODE_NUMHOOKS(node) == 0)
  552         && (NG_NODE_IS_VALID(node)))
  553                 ng_rmnode_self(node);
  554         return (0);
  555 }
  556 
  557 /*
  558  * Destroy node
  559  */
  560 static int
  561 ng_pptpgre_shutdown(node_p node)
  562 {
  563         const priv_p priv = NG_NODE_PRIVATE(node);
  564 
  565         /* Reset node (stops timers) */
  566         ng_pptpgre_reset(&priv->uppersess);
  567 
  568         LIST_REMOVE(&priv->uppersess, sessions);
  569         mtx_destroy(&priv->uppersess.mtx);
  570 
  571         free(priv, M_NETGRAPH);
  572 
  573         /* Decrement ref count */
  574         NG_NODE_UNREF(node);
  575         return (0);
  576 }
  577 
  578 /*************************************************************************
  579                     TRANSMIT AND RECEIVE FUNCTIONS
  580 *************************************************************************/
  581 
  582 /*
  583  * Transmit an outgoing frame, or just an ack if m is NULL.
  584  */
  585 static int
  586 ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
  587 {
  588         const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
  589         u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
  590         struct greheader *const gre = (struct greheader *)buf;
  591         int grelen, error;
  592         struct mbuf *m;
  593 
  594         mtx_assert(&hpriv->mtx, MA_OWNED);
  595 
  596         if (item) {
  597                 NGI_GET_M(item, m);
  598         } else {
  599                 m = NULL;
  600         }
  601         /* Check if there's data */
  602         if (m != NULL) {
  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: 76fc31e44b2ce22e3d2f2005b416bab1


[ 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.