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

Cache object: 25305ec17f24334fbb623ba4cbf1b216


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