1add85a1dSArchie Cobbs /* 2add85a1dSArchie Cobbs * ng_pptpgre.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 6add85a1dSArchie Cobbs * Copyright (c) 1996-1999 Whistle Communications, Inc. 7add85a1dSArchie Cobbs * All rights reserved. 8add85a1dSArchie Cobbs * 9add85a1dSArchie Cobbs * Subject to the following obligations and disclaimer of warranty, use and 10add85a1dSArchie Cobbs * redistribution of this software, in source or object code forms, with or 11add85a1dSArchie Cobbs * without modifications are expressly permitted by Whistle Communications; 12add85a1dSArchie Cobbs * provided, however, that: 13add85a1dSArchie Cobbs * 1. Any and all reproductions of the source or object code must include the 14add85a1dSArchie Cobbs * copyright notice above and the following disclaimer of warranties; and 15add85a1dSArchie Cobbs * 2. No rights are granted, in any manner or form, to use Whistle 16add85a1dSArchie Cobbs * Communications, Inc. trademarks, including the mark "WHISTLE 17add85a1dSArchie Cobbs * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 18add85a1dSArchie Cobbs * such appears in the above copyright notice or in the software. 19add85a1dSArchie Cobbs * 20add85a1dSArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 21add85a1dSArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 22add85a1dSArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 23add85a1dSArchie Cobbs * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 24add85a1dSArchie Cobbs * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 25add85a1dSArchie Cobbs * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 26add85a1dSArchie Cobbs * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 27add85a1dSArchie Cobbs * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 28add85a1dSArchie Cobbs * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 29add85a1dSArchie Cobbs * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 30add85a1dSArchie Cobbs * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31add85a1dSArchie Cobbs * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32add85a1dSArchie Cobbs * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33add85a1dSArchie Cobbs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34add85a1dSArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35add85a1dSArchie Cobbs * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 36add85a1dSArchie Cobbs * OF SUCH DAMAGE. 37add85a1dSArchie Cobbs * 38cc3bbd68SJulian Elischer * Author: Archie Cobbs <archie@freebsd.org> 39add85a1dSArchie Cobbs * 40add85a1dSArchie Cobbs * $FreeBSD$ 41add85a1dSArchie Cobbs * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $ 42add85a1dSArchie Cobbs */ 43add85a1dSArchie Cobbs 44add85a1dSArchie Cobbs /* 45add85a1dSArchie Cobbs * PPTP/GRE netgraph node type. 46add85a1dSArchie Cobbs * 47add85a1dSArchie Cobbs * This node type does the GRE encapsulation as specified for the PPTP 48add85a1dSArchie Cobbs * protocol (RFC 2637, section 4). This includes sequencing and 49add85a1dSArchie Cobbs * retransmission of frames, but not the actual packet delivery nor 50add85a1dSArchie Cobbs * any of the TCP control stream protocol. 51add85a1dSArchie Cobbs * 52add85a1dSArchie Cobbs * The "upper" hook of this node is suitable for attaching to a "ppp" 53add85a1dSArchie Cobbs * node link hook. The "lower" hook of this node is suitable for attaching 54add85a1dSArchie Cobbs * to a "ksocket" node on hook "inet/raw/gre". 55add85a1dSArchie Cobbs */ 56add85a1dSArchie Cobbs 57add85a1dSArchie Cobbs #include <sys/param.h> 58add85a1dSArchie Cobbs #include <sys/systm.h> 59add85a1dSArchie Cobbs #include <sys/kernel.h> 60add85a1dSArchie Cobbs #include <sys/time.h> 61f2ba84d7SGleb Smirnoff #include <sys/lock.h> 62add85a1dSArchie Cobbs #include <sys/malloc.h> 63f2ba84d7SGleb Smirnoff #include <sys/mbuf.h> 64f2ba84d7SGleb Smirnoff #include <sys/mutex.h> 65add85a1dSArchie Cobbs #include <sys/errno.h> 66add85a1dSArchie Cobbs 67add85a1dSArchie Cobbs #include <netinet/in.h> 68add85a1dSArchie Cobbs #include <netinet/in_systm.h> 69add85a1dSArchie Cobbs #include <netinet/ip.h> 70add85a1dSArchie Cobbs 71add85a1dSArchie Cobbs #include <netgraph/ng_message.h> 72add85a1dSArchie Cobbs #include <netgraph/netgraph.h> 73add85a1dSArchie Cobbs #include <netgraph/ng_parse.h> 74add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h> 75add85a1dSArchie Cobbs 76add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */ 77add85a1dSArchie Cobbs struct greheader { 78add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN 79add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 80add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 81add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 82add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 83add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 84add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 85add85a1dSArchie Cobbs u_char vers:3; /* version */ 86add85a1dSArchie Cobbs u_char flags:4; /* flags */ 87add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 88add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN 89add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 90add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 91add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 92add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 93add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 94add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 95add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 96add85a1dSArchie Cobbs u_char flags:4; /* flags */ 97add85a1dSArchie Cobbs u_char vers:3; /* version */ 98add85a1dSArchie Cobbs #else 99add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly 100add85a1dSArchie Cobbs #endif 101add85a1dSArchie Cobbs u_int16_t proto; /* protocol (ethertype) */ 102add85a1dSArchie Cobbs u_int16_t length; /* payload length */ 103add85a1dSArchie Cobbs u_int16_t cid; /* call id */ 104add85a1dSArchie Cobbs u_int32_t data[0]; /* opt. seq, ack, then data */ 105add85a1dSArchie Cobbs }; 106add85a1dSArchie Cobbs 107add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */ 108add85a1dSArchie Cobbs #define PPTP_GRE_PROTO 0x880b 109add85a1dSArchie Cobbs 110add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */ 111add85a1dSArchie Cobbs #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) 112add85a1dSArchie Cobbs #define PPTP_INIT_MASK 0xef7fffff 113add85a1dSArchie Cobbs 114add85a1dSArchie Cobbs /* Min and max packet length */ 115add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8) 116add85a1dSArchie Cobbs 117add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */ 118e962a823SArchie Cobbs #define PPTP_TIME_SCALE 1000 /* milliseconds */ 119678f9e33SArchie Cobbs typedef u_int64_t pptptime_t; 120add85a1dSArchie Cobbs 121add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */ 122e962a823SArchie Cobbs #define PPTP_XMIT_WIN 16 /* max xmit window */ 123e962a823SArchie Cobbs #define PPTP_MIN_RTT (PPTP_TIME_SCALE / 10) /* 100 milliseconds */ 1244a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 83) /* 12 milliseconds */ 1250306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT (3 * PPTP_TIME_SCALE) /* 3 seconds */ 126add85a1dSArchie Cobbs 127da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet 128da010626SArchie Cobbs we can piggy-back the ACK off of. These parameters determine the mimimum 129da010626SArchie Cobbs and maxmimum length of time we're willing to wait in order to do that. 130da010626SArchie Cobbs These have no effect unless "enableDelayedAck" is turned on. */ 131da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */ 132da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */ 133da010626SArchie Cobbs 134e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */ 135add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x) ((x) >> 3) /* alpha = 0.125 */ 136add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x) ((x) >> 2) /* beta = 0.25 */ 137add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */ 138add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */ 139add85a1dSArchie Cobbs 1409bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y)) 1419bee7adfSArchie Cobbs 142add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */ 143add85a1dSArchie Cobbs struct ng_pptpgre_ackp { 144add85a1dSArchie Cobbs int32_t ato; /* adaptive time-out value */ 145add85a1dSArchie Cobbs int32_t rtt; /* round trip time estimate */ 146add85a1dSArchie Cobbs int32_t dev; /* deviation estimate */ 147add85a1dSArchie Cobbs u_int16_t xmitWin; /* size of xmit window */ 1484a48abb2SArchie Cobbs struct callout sackTimer; /* send ack timer */ 1494a48abb2SArchie Cobbs struct callout rackTimer; /* recv ack timer */ 1503cd7db22SArchie Cobbs u_int32_t winAck; /* seq when xmitWin will grow */ 151add85a1dSArchie Cobbs pptptime_t timeSent[PPTP_XMIT_WIN]; 152678f9e33SArchie Cobbs #ifdef DEBUG_RAT 153da010626SArchie Cobbs pptptime_t timerStart; /* when rackTimer started */ 154da010626SArchie Cobbs pptptime_t timerLength; /* rackTimer duration */ 155678f9e33SArchie Cobbs #endif 156add85a1dSArchie Cobbs }; 157add85a1dSArchie Cobbs 158add85a1dSArchie Cobbs /* Node private data */ 159add85a1dSArchie Cobbs struct ng_pptpgre_private { 160add85a1dSArchie Cobbs hook_p upper; /* hook to upper layers */ 161add85a1dSArchie Cobbs hook_p lower; /* hook to lower layers */ 162add85a1dSArchie Cobbs struct ng_pptpgre_conf conf; /* configuration info */ 163add85a1dSArchie Cobbs struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ 164add85a1dSArchie Cobbs u_int32_t recvSeq; /* last seq # we rcv'd */ 165add85a1dSArchie Cobbs u_int32_t xmitSeq; /* last seq # we sent */ 166add85a1dSArchie Cobbs u_int32_t recvAck; /* last seq # peer ack'd */ 167add85a1dSArchie Cobbs u_int32_t xmitAck; /* last seq # we ack'd */ 168add85a1dSArchie Cobbs struct timeval startTime; /* time node was created */ 1699bee7adfSArchie Cobbs struct ng_pptpgre_stats stats; /* node statistics */ 170f2ba84d7SGleb Smirnoff struct mtx mtx; /* node mutex */ 171add85a1dSArchie Cobbs }; 172add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p; 173add85a1dSArchie Cobbs 174add85a1dSArchie Cobbs /* Netgraph node methods */ 175add85a1dSArchie Cobbs static ng_constructor_t ng_pptpgre_constructor; 176add85a1dSArchie Cobbs static ng_rcvmsg_t ng_pptpgre_rcvmsg; 177069154d5SJulian Elischer static ng_shutdown_t ng_pptpgre_shutdown; 178add85a1dSArchie Cobbs static ng_newhook_t ng_pptpgre_newhook; 179add85a1dSArchie Cobbs static ng_rcvdata_t ng_pptpgre_rcvdata; 180add85a1dSArchie Cobbs static ng_disconnect_t ng_pptpgre_disconnect; 181add85a1dSArchie Cobbs 182add85a1dSArchie Cobbs /* Helper functions */ 183069154d5SJulian Elischer static int ng_pptpgre_xmit(node_p node, item_p item); 184069154d5SJulian Elischer static int ng_pptpgre_recv(node_p node, item_p item); 185da010626SArchie Cobbs static void ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout); 1864a48abb2SArchie Cobbs static void ng_pptpgre_stop_send_ack_timer(node_p node); 187add85a1dSArchie Cobbs static void ng_pptpgre_start_recv_ack_timer(node_p node); 1884a48abb2SArchie Cobbs static void ng_pptpgre_stop_recv_ack_timer(node_p node); 189089323f3SGleb Smirnoff static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, 190089323f3SGleb Smirnoff void *arg1, int arg2); 191089323f3SGleb Smirnoff static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, 192089323f3SGleb Smirnoff void *arg1, int arg2); 193add85a1dSArchie Cobbs static void ng_pptpgre_reset(node_p node); 194add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node); 195add85a1dSArchie Cobbs 196add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */ 197f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[] 198f0184ff8SArchie Cobbs = NG_PPTPGRE_CONF_TYPE_INFO; 199add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = { 200add85a1dSArchie Cobbs &ng_parse_struct_type, 201f0184ff8SArchie Cobbs &ng_pptpgre_conf_type_fields, 202add85a1dSArchie Cobbs }; 203add85a1dSArchie Cobbs 2049bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */ 205f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[] 206f0184ff8SArchie Cobbs = NG_PPTPGRE_STATS_TYPE_INFO; 2079bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = { 2089bee7adfSArchie Cobbs &ng_parse_struct_type, 209f0184ff8SArchie Cobbs &ng_pptpgre_stats_type_fields 2109bee7adfSArchie Cobbs }; 2119bee7adfSArchie Cobbs 212add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 213add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { 214add85a1dSArchie Cobbs { 215add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 216add85a1dSArchie Cobbs NGM_PPTPGRE_SET_CONFIG, 217add85a1dSArchie Cobbs "setconfig", 218add85a1dSArchie Cobbs &ng_pptpgre_conf_type, 219add85a1dSArchie Cobbs NULL 220add85a1dSArchie Cobbs }, 221add85a1dSArchie Cobbs { 222add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 223add85a1dSArchie Cobbs NGM_PPTPGRE_GET_CONFIG, 224add85a1dSArchie Cobbs "getconfig", 225add85a1dSArchie Cobbs NULL, 226add85a1dSArchie Cobbs &ng_pptpgre_conf_type 227add85a1dSArchie Cobbs }, 2289bee7adfSArchie Cobbs { 2299bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2309bee7adfSArchie Cobbs NGM_PPTPGRE_GET_STATS, 2319bee7adfSArchie Cobbs "getstats", 2329bee7adfSArchie Cobbs NULL, 2339bee7adfSArchie Cobbs &ng_pptp_stats_type 2349bee7adfSArchie Cobbs }, 2359bee7adfSArchie Cobbs { 2369bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2379bee7adfSArchie Cobbs NGM_PPTPGRE_CLR_STATS, 2389bee7adfSArchie Cobbs "clrstats", 2399bee7adfSArchie Cobbs NULL, 2409bee7adfSArchie Cobbs NULL 2419bee7adfSArchie Cobbs }, 2429bee7adfSArchie Cobbs { 2439bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2449bee7adfSArchie Cobbs NGM_PPTPGRE_GETCLR_STATS, 2459bee7adfSArchie Cobbs "getclrstats", 2469bee7adfSArchie Cobbs NULL, 2479bee7adfSArchie Cobbs &ng_pptp_stats_type 2489bee7adfSArchie Cobbs }, 249add85a1dSArchie Cobbs { 0 } 250add85a1dSArchie Cobbs }; 251add85a1dSArchie Cobbs 252add85a1dSArchie Cobbs /* Node type descriptor */ 253add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = { 254f8aae777SJulian Elischer .version = NG_ABI_VERSION, 255f8aae777SJulian Elischer .name = NG_PPTPGRE_NODE_TYPE, 256f8aae777SJulian Elischer .constructor = ng_pptpgre_constructor, 257f8aae777SJulian Elischer .rcvmsg = ng_pptpgre_rcvmsg, 258f8aae777SJulian Elischer .shutdown = ng_pptpgre_shutdown, 259f8aae777SJulian Elischer .newhook = ng_pptpgre_newhook, 260f8aae777SJulian Elischer .rcvdata = ng_pptpgre_rcvdata, 261f8aae777SJulian Elischer .disconnect = ng_pptpgre_disconnect, 262f8aae777SJulian Elischer .cmdlist = ng_pptpgre_cmdlist, 263add85a1dSArchie Cobbs }; 264add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); 265add85a1dSArchie Cobbs 266add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 267add85a1dSArchie Cobbs 268add85a1dSArchie Cobbs /************************************************************************ 269add85a1dSArchie Cobbs NETGRAPH NODE STUFF 270add85a1dSArchie Cobbs ************************************************************************/ 271add85a1dSArchie Cobbs 272add85a1dSArchie Cobbs /* 273add85a1dSArchie Cobbs * Node type constructor 274add85a1dSArchie Cobbs */ 275add85a1dSArchie Cobbs static int 276069154d5SJulian Elischer ng_pptpgre_constructor(node_p node) 277add85a1dSArchie Cobbs { 278add85a1dSArchie Cobbs priv_p priv; 279add85a1dSArchie Cobbs 280add85a1dSArchie Cobbs /* Allocate private structure */ 28199cdf4ccSDavid Malone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 282add85a1dSArchie Cobbs if (priv == NULL) 283add85a1dSArchie Cobbs return (ENOMEM); 284add85a1dSArchie Cobbs 28530400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 286add85a1dSArchie Cobbs 287add85a1dSArchie Cobbs /* Initialize state */ 288f2ba84d7SGleb Smirnoff mtx_init(&priv->mtx, "ng_pptp", NULL, MTX_DEF); 289f2ba84d7SGleb Smirnoff ng_callout_init_mtx(&priv->ackp.sackTimer, &priv->mtx); 290f2ba84d7SGleb Smirnoff ng_callout_init_mtx(&priv->ackp.rackTimer, &priv->mtx); 291add85a1dSArchie Cobbs 292add85a1dSArchie Cobbs /* Done */ 293add85a1dSArchie Cobbs return (0); 294add85a1dSArchie Cobbs } 295add85a1dSArchie Cobbs 296add85a1dSArchie Cobbs /* 297add85a1dSArchie Cobbs * Give our OK for a hook to be added. 298add85a1dSArchie Cobbs */ 299add85a1dSArchie Cobbs static int 300add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 301add85a1dSArchie Cobbs { 30230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 303add85a1dSArchie Cobbs hook_p *hookPtr; 304add85a1dSArchie Cobbs 305add85a1dSArchie Cobbs /* Check hook name */ 306add85a1dSArchie Cobbs if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) 307add85a1dSArchie Cobbs hookPtr = &priv->upper; 308add85a1dSArchie Cobbs else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) 309add85a1dSArchie Cobbs hookPtr = &priv->lower; 310add85a1dSArchie Cobbs else 311add85a1dSArchie Cobbs return (EINVAL); 312add85a1dSArchie Cobbs 313add85a1dSArchie Cobbs /* See if already connected */ 314add85a1dSArchie Cobbs if (*hookPtr != NULL) 315add85a1dSArchie Cobbs return (EISCONN); 316add85a1dSArchie Cobbs 317add85a1dSArchie Cobbs /* OK */ 318add85a1dSArchie Cobbs *hookPtr = hook; 319add85a1dSArchie Cobbs return (0); 320add85a1dSArchie Cobbs } 321add85a1dSArchie Cobbs 322add85a1dSArchie Cobbs /* 323add85a1dSArchie Cobbs * Receive a control message. 324add85a1dSArchie Cobbs */ 325add85a1dSArchie Cobbs static int 326069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook) 327add85a1dSArchie Cobbs { 32830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 329add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 330add85a1dSArchie Cobbs int error = 0; 331069154d5SJulian Elischer struct ng_mesg *msg; 332add85a1dSArchie Cobbs 333069154d5SJulian Elischer NGI_GET_MSG(item, msg); 334add85a1dSArchie Cobbs switch (msg->header.typecookie) { 335add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 336add85a1dSArchie Cobbs switch (msg->header.cmd) { 337add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 338add85a1dSArchie Cobbs { 339add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 340add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 341add85a1dSArchie Cobbs 342add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 343add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 344add85a1dSArchie Cobbs ERROUT(EINVAL); 345add85a1dSArchie Cobbs ng_pptpgre_reset(node); /* reset on configure */ 346add85a1dSArchie Cobbs priv->conf = *newConf; 347add85a1dSArchie Cobbs break; 348add85a1dSArchie Cobbs } 349add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 350add85a1dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); 351add85a1dSArchie Cobbs if (resp == NULL) 352add85a1dSArchie Cobbs ERROUT(ENOMEM); 353add85a1dSArchie Cobbs bcopy(&priv->conf, resp->data, sizeof(priv->conf)); 354add85a1dSArchie Cobbs break; 3559bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 3569bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 3579bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 3589bee7adfSArchie Cobbs { 3599bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 3609bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 3619bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 3629bee7adfSArchie Cobbs if (resp == NULL) 3639bee7adfSArchie Cobbs ERROUT(ENOMEM); 3649bee7adfSArchie Cobbs bcopy(&priv->stats, 3659bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 3669bee7adfSArchie Cobbs } 3679bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 3689bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 3699bee7adfSArchie Cobbs break; 3709bee7adfSArchie Cobbs } 371add85a1dSArchie Cobbs default: 372add85a1dSArchie Cobbs error = EINVAL; 373add85a1dSArchie Cobbs break; 374add85a1dSArchie Cobbs } 375add85a1dSArchie Cobbs break; 376add85a1dSArchie Cobbs default: 377add85a1dSArchie Cobbs error = EINVAL; 378add85a1dSArchie Cobbs break; 379add85a1dSArchie Cobbs } 380589f6ed8SJulian Elischer done: 381069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 382069154d5SJulian Elischer NG_FREE_MSG(msg); 383add85a1dSArchie Cobbs return (error); 384add85a1dSArchie Cobbs } 385add85a1dSArchie Cobbs 386add85a1dSArchie Cobbs /* 387add85a1dSArchie Cobbs * Receive incoming data on a hook. 388add85a1dSArchie Cobbs */ 389add85a1dSArchie Cobbs static int 390069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item) 391add85a1dSArchie Cobbs { 39230400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 39330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 394f2ba84d7SGleb Smirnoff int rval; 395add85a1dSArchie Cobbs 396add85a1dSArchie Cobbs /* If not configured, reject */ 397add85a1dSArchie Cobbs if (!priv->conf.enabled) { 398069154d5SJulian Elischer NG_FREE_ITEM(item); 399add85a1dSArchie Cobbs return (ENXIO); 400add85a1dSArchie Cobbs } 401add85a1dSArchie Cobbs 402f2ba84d7SGleb Smirnoff mtx_lock(&priv->mtx); 403f2ba84d7SGleb Smirnoff 404add85a1dSArchie Cobbs /* Treat as xmit or recv data */ 405add85a1dSArchie Cobbs if (hook == priv->upper) 406f2ba84d7SGleb Smirnoff rval = ng_pptpgre_xmit(node, item); 407f2ba84d7SGleb Smirnoff else if (hook == priv->lower) 408f2ba84d7SGleb Smirnoff rval = ng_pptpgre_recv(node, item); 409f2ba84d7SGleb Smirnoff else 4106e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 411f2ba84d7SGleb Smirnoff 412f2ba84d7SGleb Smirnoff mtx_unlock(&priv->mtx); 413f2ba84d7SGleb Smirnoff 414f2ba84d7SGleb Smirnoff return (rval); 415add85a1dSArchie Cobbs } 416add85a1dSArchie Cobbs 417add85a1dSArchie Cobbs /* 418add85a1dSArchie Cobbs * Destroy node 419add85a1dSArchie Cobbs */ 420add85a1dSArchie Cobbs static int 421069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node) 422add85a1dSArchie Cobbs { 42330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 424add85a1dSArchie Cobbs 425089323f3SGleb Smirnoff /* Reset node (stops timers) */ 426add85a1dSArchie Cobbs ng_pptpgre_reset(node); 427add85a1dSArchie Cobbs 428f2ba84d7SGleb Smirnoff mtx_destroy(&priv->mtx); 429f2ba84d7SGleb Smirnoff 430add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 4314a48abb2SArchie Cobbs 4324a48abb2SArchie Cobbs /* Decrement ref count */ 43330400f03SJulian Elischer NG_NODE_UNREF(node); 434add85a1dSArchie Cobbs return (0); 435add85a1dSArchie Cobbs } 436add85a1dSArchie Cobbs 437add85a1dSArchie Cobbs /* 438add85a1dSArchie Cobbs * Hook disconnection 439add85a1dSArchie Cobbs */ 440add85a1dSArchie Cobbs static int 441add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook) 442add85a1dSArchie Cobbs { 44330400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 44430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 445add85a1dSArchie Cobbs 446add85a1dSArchie Cobbs /* Zero out hook pointer */ 447add85a1dSArchie Cobbs if (hook == priv->upper) 448add85a1dSArchie Cobbs priv->upper = NULL; 449add85a1dSArchie Cobbs else if (hook == priv->lower) 450add85a1dSArchie Cobbs priv->lower = NULL; 451add85a1dSArchie Cobbs else 4526e551fb6SDavid E. O'Brien panic("%s: unknown hook", __func__); 453add85a1dSArchie Cobbs 454add85a1dSArchie Cobbs /* Go away if no longer connected to anything */ 45530400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(node) == 0) 45630400f03SJulian Elischer && (NG_NODE_IS_VALID(node))) 457069154d5SJulian Elischer ng_rmnode_self(node); 458add85a1dSArchie Cobbs return (0); 459add85a1dSArchie Cobbs } 460add85a1dSArchie Cobbs 461add85a1dSArchie Cobbs /************************************************************************* 462add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 463add85a1dSArchie Cobbs *************************************************************************/ 464add85a1dSArchie Cobbs 465add85a1dSArchie Cobbs /* 466add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 467add85a1dSArchie Cobbs */ 468add85a1dSArchie Cobbs static int 469069154d5SJulian Elischer ng_pptpgre_xmit(node_p node, item_p item) 470add85a1dSArchie Cobbs { 47130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 472add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 473add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 474add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 475add85a1dSArchie Cobbs int grelen, error; 476069154d5SJulian Elischer struct mbuf *m; 477add85a1dSArchie Cobbs 478069154d5SJulian Elischer if (item) { 479069154d5SJulian Elischer NGI_GET_M(item, m); 480069154d5SJulian Elischer } else { 481069154d5SJulian Elischer m = NULL; 482069154d5SJulian Elischer } 4839bee7adfSArchie Cobbs /* Check if there's data */ 4849bee7adfSArchie Cobbs if (m != NULL) { 4859bee7adfSArchie Cobbs 486922ee196SArchie Cobbs /* Check if windowing is enabled */ 487922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 488add85a1dSArchie Cobbs /* Is our transmit window full? */ 489922ee196SArchie Cobbs if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, 490922ee196SArchie Cobbs priv->recvAck) >= a->xmitWin) { 4919bee7adfSArchie Cobbs priv->stats.xmitDrops++; 492069154d5SJulian Elischer NG_FREE_M(m); 493069154d5SJulian Elischer NG_FREE_ITEM(item); 494add85a1dSArchie Cobbs return (ENOBUFS); 495add85a1dSArchie Cobbs } 496922ee196SArchie Cobbs } 497add85a1dSArchie Cobbs 498add85a1dSArchie Cobbs /* Sanity check frame length */ 499add85a1dSArchie Cobbs if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 5009bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 501069154d5SJulian Elischer NG_FREE_M(m); 502069154d5SJulian Elischer NG_FREE_ITEM(item); 503add85a1dSArchie Cobbs return (EMSGSIZE); 504add85a1dSArchie Cobbs } 505069154d5SJulian Elischer } else { 5069bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 507069154d5SJulian Elischer } 508add85a1dSArchie Cobbs 509add85a1dSArchie Cobbs /* Build GRE header */ 510add85a1dSArchie Cobbs ((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE); 511add85a1dSArchie Cobbs gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; 512add85a1dSArchie Cobbs gre->cid = htons(priv->conf.peerCid); 513add85a1dSArchie Cobbs 514add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 515add85a1dSArchie Cobbs if (m != NULL) { 516add85a1dSArchie Cobbs gre->hasSeq = 1; 517922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 518add85a1dSArchie Cobbs a->timeSent[priv->xmitSeq - priv->recvAck] 519add85a1dSArchie Cobbs = ng_pptpgre_time(node); 520922ee196SArchie Cobbs } 521add85a1dSArchie Cobbs priv->xmitSeq++; 522add85a1dSArchie Cobbs gre->data[0] = htonl(priv->xmitSeq); 523add85a1dSArchie Cobbs } 524add85a1dSArchie Cobbs 525add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 526678f9e33SArchie Cobbs if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) { 527add85a1dSArchie Cobbs gre->hasAck = 1; 528678f9e33SArchie Cobbs gre->data[gre->hasSeq] = htonl(priv->recvSeq); 529add85a1dSArchie Cobbs priv->xmitAck = priv->recvSeq; 5304a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node); 531da010626SArchie Cobbs } 532add85a1dSArchie Cobbs 533add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 534add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 535add85a1dSArchie Cobbs if (m == NULL) { 536a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 537add85a1dSArchie Cobbs if (m == NULL) { 538678f9e33SArchie Cobbs priv->stats.memoryFailures++; 539069154d5SJulian Elischer if (item) 540069154d5SJulian Elischer NG_FREE_ITEM(item); 541add85a1dSArchie Cobbs return (ENOBUFS); 542add85a1dSArchie Cobbs } 543add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 544add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 545add85a1dSArchie Cobbs } else { 546a163d034SWarner Losh M_PREPEND(m, grelen, M_DONTWAIT); 547add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 548add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 549678f9e33SArchie Cobbs priv->stats.memoryFailures++; 550069154d5SJulian Elischer if (item) 551069154d5SJulian Elischer NG_FREE_ITEM(item); 552add85a1dSArchie Cobbs return (ENOBUFS); 553add85a1dSArchie Cobbs } 554add85a1dSArchie Cobbs } 555add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 556add85a1dSArchie Cobbs 5579bee7adfSArchie Cobbs /* Update stats */ 5589bee7adfSArchie Cobbs priv->stats.xmitPackets++; 5599bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 5609bee7adfSArchie Cobbs 561add85a1dSArchie Cobbs /* Deliver packet */ 562069154d5SJulian Elischer if (item) { 563069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->lower, m); 564069154d5SJulian Elischer } else { 565069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m); 566069154d5SJulian Elischer } 567069154d5SJulian Elischer 568678f9e33SArchie Cobbs 569da010626SArchie Cobbs /* Start receive ACK timer if data was sent and not already running */ 570678f9e33SArchie Cobbs if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1) 571678f9e33SArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 572add85a1dSArchie Cobbs return (error); 573add85a1dSArchie Cobbs } 574add85a1dSArchie Cobbs 575add85a1dSArchie Cobbs /* 576add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 577add85a1dSArchie Cobbs */ 578add85a1dSArchie Cobbs static int 579069154d5SJulian Elischer ng_pptpgre_recv(node_p node, item_p item) 580add85a1dSArchie Cobbs { 58130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 582add85a1dSArchie Cobbs int iphlen, grelen, extralen; 583816b834fSArchie Cobbs const struct greheader *gre; 584816b834fSArchie Cobbs const struct ip *ip; 585add85a1dSArchie Cobbs int error = 0; 586069154d5SJulian Elischer struct mbuf *m; 587add85a1dSArchie Cobbs 588069154d5SJulian Elischer NGI_GET_M(item, m); 5899bee7adfSArchie Cobbs /* Update stats */ 5909bee7adfSArchie Cobbs priv->stats.recvPackets++; 5919bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 5929bee7adfSArchie Cobbs 593add85a1dSArchie Cobbs /* Sanity check packet length */ 594add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { 5959bee7adfSArchie Cobbs priv->stats.recvRunts++; 596add85a1dSArchie Cobbs bad: 597069154d5SJulian Elischer NG_FREE_M(m); 598069154d5SJulian Elischer NG_FREE_ITEM(item); 599add85a1dSArchie Cobbs return (EINVAL); 600add85a1dSArchie Cobbs } 601add85a1dSArchie Cobbs 602add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 603add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 604add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 605678f9e33SArchie Cobbs priv->stats.memoryFailures++; 606069154d5SJulian Elischer NG_FREE_ITEM(item); 607add85a1dSArchie Cobbs return (ENOBUFS); 608add85a1dSArchie Cobbs } 609816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 610add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 611add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 612add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 613678f9e33SArchie Cobbs priv->stats.memoryFailures++; 614069154d5SJulian Elischer NG_FREE_ITEM(item); 615add85a1dSArchie Cobbs return (ENOBUFS); 616add85a1dSArchie Cobbs } 617816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 618add85a1dSArchie Cobbs } 619816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 620add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 6219bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 6229bee7adfSArchie Cobbs priv->stats.recvRunts++; 623add85a1dSArchie Cobbs goto bad; 6249bee7adfSArchie Cobbs } 625add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 626add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 627678f9e33SArchie Cobbs priv->stats.memoryFailures++; 628069154d5SJulian Elischer NG_FREE_ITEM(item); 629add85a1dSArchie Cobbs return (ENOBUFS); 630add85a1dSArchie Cobbs } 631816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 632816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 633add85a1dSArchie Cobbs } 634add85a1dSArchie Cobbs 635add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 636add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 637cc78c48aSArchie Cobbs - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length)); 6389bee7adfSArchie Cobbs if (extralen < 0) { 6399bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 640add85a1dSArchie Cobbs goto bad; 6419bee7adfSArchie Cobbs } 642816b834fSArchie Cobbs if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK) 643816b834fSArchie Cobbs != PPTP_INIT_VALUE) { 6449bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 645add85a1dSArchie Cobbs goto bad; 6469bee7adfSArchie Cobbs } 6479bee7adfSArchie Cobbs if (ntohs(gre->cid) != priv->conf.cid) { 6489bee7adfSArchie Cobbs priv->stats.recvBadCID++; 649add85a1dSArchie Cobbs goto bad; 6509bee7adfSArchie Cobbs } 651add85a1dSArchie Cobbs 652add85a1dSArchie Cobbs /* Look for peer ack */ 653add85a1dSArchie Cobbs if (gre->hasAck) { 654add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 655add85a1dSArchie Cobbs const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); 656add85a1dSArchie Cobbs const int index = ack - priv->recvAck - 1; 65722dfb9bdSArchie Cobbs long sample; 658add85a1dSArchie Cobbs long diff; 659add85a1dSArchie Cobbs 660add85a1dSArchie Cobbs /* Sanity check ack value */ 6619bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { 6629bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 6639bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 6649bee7adfSArchie Cobbs } 6659bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) 6669bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 667add85a1dSArchie Cobbs priv->recvAck = ack; 668add85a1dSArchie Cobbs 669add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 670922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 67122dfb9bdSArchie Cobbs sample = ng_pptpgre_time(node) - a->timeSent[index]; 672add85a1dSArchie Cobbs diff = sample - a->rtt; 673add85a1dSArchie Cobbs a->rtt += PPTP_ACK_ALPHA(diff); 674add85a1dSArchie Cobbs if (diff < 0) 675add85a1dSArchie Cobbs diff = -diff; 676add85a1dSArchie Cobbs a->dev += PPTP_ACK_BETA(diff - a->dev); 677e962a823SArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 678add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 679add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 680e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 681e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 682e962a823SArchie Cobbs 683e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 684f7854568SDag-Erling Smørgrav bcopy(a->timeSent + index + 1, a->timeSent, 685922ee196SArchie Cobbs sizeof(*a->timeSent) 686922ee196SArchie Cobbs * (PPTP_XMIT_WIN - (index + 1))); 687e962a823SArchie Cobbs 688922ee196SArchie Cobbs /* If we sent an entire window, increase window size */ 6899bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 6909bee7adfSArchie Cobbs && a->xmitWin < PPTP_XMIT_WIN) { 691add85a1dSArchie Cobbs a->xmitWin++; 692add85a1dSArchie Cobbs a->winAck = ack + a->xmitWin; 693add85a1dSArchie Cobbs } 694add85a1dSArchie Cobbs 6959bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 6964a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node); 697678f9e33SArchie Cobbs if (priv->recvAck != priv->xmitSeq) 698add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 699add85a1dSArchie Cobbs } 700922ee196SArchie Cobbs } 7019bee7adfSArchie Cobbs badAck: 702add85a1dSArchie Cobbs 703add85a1dSArchie Cobbs /* See if frame contains any data */ 704add85a1dSArchie Cobbs if (gre->hasSeq) { 705add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 706add85a1dSArchie Cobbs const u_int32_t seq = ntohl(gre->data[0]); 707add85a1dSArchie Cobbs 708add85a1dSArchie Cobbs /* Sanity check sequence number */ 7099bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { 7109bee7adfSArchie Cobbs if (seq == priv->recvSeq) 7119bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 7129bee7adfSArchie Cobbs else 7139bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 7149bee7adfSArchie Cobbs goto bad; /* out-of-order or dup */ 7159bee7adfSArchie Cobbs } 716add85a1dSArchie Cobbs priv->recvSeq = seq; 717add85a1dSArchie Cobbs 718add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 719dc2f4d7fSGleb Smirnoff if (!(callout_pending(&a->sackTimer))) { 720da010626SArchie Cobbs int maxWait; 721add85a1dSArchie Cobbs 722da010626SArchie Cobbs /* Take 1/4 of the estimated round trip time */ 723da010626SArchie Cobbs maxWait = (a->rtt >> 2); 724add85a1dSArchie Cobbs 725678f9e33SArchie Cobbs /* If delayed ACK is disabled, send it now */ 7264a48abb2SArchie Cobbs if (!priv->conf.enableDelayedAck) /* ack now */ 727069154d5SJulian Elischer ng_pptpgre_xmit(node, NULL); 7284a48abb2SArchie Cobbs else { /* ack later */ 7294a48abb2SArchie Cobbs if (maxWait < PPTP_MIN_ACK_DELAY) 7304a48abb2SArchie Cobbs maxWait = PPTP_MIN_ACK_DELAY; 7313cd7db22SArchie Cobbs if (maxWait > PPTP_MAX_ACK_DELAY) 7323cd7db22SArchie Cobbs maxWait = PPTP_MAX_ACK_DELAY; 7333cd7db22SArchie Cobbs ng_pptpgre_start_send_ack_timer(node, maxWait); 734add85a1dSArchie Cobbs } 735add85a1dSArchie Cobbs } 736add85a1dSArchie Cobbs 737add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 738add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 739add85a1dSArchie Cobbs if (extralen > 0) 740add85a1dSArchie Cobbs m_adj(m, -extralen); 741add85a1dSArchie Cobbs 742add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 743069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->upper, m); 7449bee7adfSArchie Cobbs } else { 7459bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 746069154d5SJulian Elischer NG_FREE_ITEM(item); 747069154d5SJulian Elischer NG_FREE_M(m); /* no data to deliver */ 7489bee7adfSArchie Cobbs } 749add85a1dSArchie Cobbs return (error); 750add85a1dSArchie Cobbs } 751add85a1dSArchie Cobbs 752add85a1dSArchie Cobbs /************************************************************************* 753add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 754add85a1dSArchie Cobbs *************************************************************************/ 755add85a1dSArchie Cobbs 756add85a1dSArchie Cobbs /* 7579bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 758add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 759add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 760add85a1dSArchie Cobbs * recv ack timer, if any. 761add85a1dSArchie Cobbs */ 762add85a1dSArchie Cobbs static void 763add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node) 764add85a1dSArchie Cobbs { 76530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 766add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 767678f9e33SArchie Cobbs int remain, ticks; 768add85a1dSArchie Cobbs 769922ee196SArchie Cobbs if (!priv->conf.enableWindowing) 770922ee196SArchie Cobbs return; 771922ee196SArchie Cobbs 772add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 773add85a1dSArchie Cobbs and reset the timer to that time. */ 774add85a1dSArchie Cobbs remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); 775add85a1dSArchie Cobbs if (remain < 0) 776add85a1dSArchie Cobbs remain = 0; 777678f9e33SArchie Cobbs #ifdef DEBUG_RAT 778678f9e33SArchie Cobbs a->timerLength = remain; 779678f9e33SArchie Cobbs a->timerStart = ng_pptpgre_time(node); 780678f9e33SArchie Cobbs #endif 7819bee7adfSArchie Cobbs 7824a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 783678f9e33SArchie Cobbs ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; 784089323f3SGleb Smirnoff ng_callout(&a->rackTimer, node, NULL, ticks, 785089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout, NULL, 0); 7864a48abb2SArchie Cobbs } 7874a48abb2SArchie Cobbs 7884a48abb2SArchie Cobbs /* 7894a48abb2SArchie Cobbs * Stop receive ack timer. 7904a48abb2SArchie Cobbs */ 7914a48abb2SArchie Cobbs static void 7924a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node_p node) 7934a48abb2SArchie Cobbs { 7944a48abb2SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 7954a48abb2SArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 7964a48abb2SArchie Cobbs 797922ee196SArchie Cobbs if (!priv->conf.enableWindowing) 798922ee196SArchie Cobbs return; 799922ee196SArchie Cobbs 800089323f3SGleb Smirnoff ng_uncallout(&a->rackTimer, node); 801add85a1dSArchie Cobbs } 802add85a1dSArchie Cobbs 803add85a1dSArchie Cobbs /* 804add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 805add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 806add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 807add85a1dSArchie Cobbs */ 808add85a1dSArchie Cobbs static void 809089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 810add85a1dSArchie Cobbs { 81130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 812add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 813add85a1dSArchie Cobbs 8149bee7adfSArchie Cobbs 815add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 8169bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 817add85a1dSArchie Cobbs a->rtt = PPTP_ACK_DELTA(a->rtt); 818add85a1dSArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 819add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 820add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 821e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 822e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 823add85a1dSArchie Cobbs 824678f9e33SArchie Cobbs #ifdef DEBUG_RAT 825678f9e33SArchie Cobbs log(LOG_DEBUG, 826678f9e33SArchie Cobbs "RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n", 827678f9e33SArchie Cobbs (int)ng_pptpgre_time(node), priv->recvAck + 1, 828678f9e33SArchie Cobbs (int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato); 829678f9e33SArchie Cobbs #endif 830678f9e33SArchie Cobbs 831e962a823SArchie Cobbs /* Reset ack and sliding window */ 832e962a823SArchie Cobbs priv->recvAck = priv->xmitSeq; /* pretend we got the ack */ 833e962a823SArchie Cobbs a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ 834e962a823SArchie Cobbs a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ 835add85a1dSArchie Cobbs } 836add85a1dSArchie Cobbs 837add85a1dSArchie Cobbs /* 8389bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 8399bee7adfSArchie Cobbs * already running. 8409bee7adfSArchie Cobbs */ 8419bee7adfSArchie Cobbs static void 842da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout) 8439bee7adfSArchie Cobbs { 84430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 8459bee7adfSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 846678f9e33SArchie Cobbs int ticks; 8479bee7adfSArchie Cobbs 8484a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 849678f9e33SArchie Cobbs ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); 850089323f3SGleb Smirnoff ng_callout(&a->sackTimer, node, NULL, ticks, 851089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout, NULL, 0); 8524a48abb2SArchie Cobbs } 8534a48abb2SArchie Cobbs 8544a48abb2SArchie Cobbs /* 8554a48abb2SArchie Cobbs * Stop send ack timer. 8564a48abb2SArchie Cobbs */ 8574a48abb2SArchie Cobbs static void 8584a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node_p node) 8594a48abb2SArchie Cobbs { 8604a48abb2SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 8614a48abb2SArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 8624a48abb2SArchie Cobbs 863089323f3SGleb Smirnoff ng_uncallout(&a->sackTimer, node); 8649bee7adfSArchie Cobbs } 8659bee7adfSArchie Cobbs 8669bee7adfSArchie Cobbs /* 867add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 868add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 869add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 870add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 871add85a1dSArchie Cobbs */ 872add85a1dSArchie Cobbs static void 873089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 874add85a1dSArchie Cobbs { 8759bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 876069154d5SJulian Elischer ng_pptpgre_xmit(node, NULL); 877add85a1dSArchie Cobbs } 878add85a1dSArchie Cobbs 879add85a1dSArchie Cobbs /************************************************************************* 880add85a1dSArchie Cobbs MISC FUNCTIONS 881add85a1dSArchie Cobbs *************************************************************************/ 882add85a1dSArchie Cobbs 883add85a1dSArchie Cobbs /* 884add85a1dSArchie Cobbs * Reset state 885add85a1dSArchie Cobbs */ 886add85a1dSArchie Cobbs static void 887add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node) 888add85a1dSArchie Cobbs { 88930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 890add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 891add85a1dSArchie Cobbs 892f2ba84d7SGleb Smirnoff mtx_lock(&priv->mtx); 893f2ba84d7SGleb Smirnoff 894add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 895add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 896add85a1dSArchie Cobbs a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ 897add85a1dSArchie Cobbs if (a->rtt < PPTP_MIN_RTT) 898add85a1dSArchie Cobbs a->rtt = PPTP_MIN_RTT; 899add85a1dSArchie Cobbs a->dev = 0; 900add85a1dSArchie Cobbs a->xmitWin = (priv->conf.recvWin + 1) / 2; 901e962a823SArchie Cobbs if (a->xmitWin < 2) /* often the first packet is lost */ 902e962a823SArchie Cobbs a->xmitWin = 2; /* because the peer isn't ready */ 903add85a1dSArchie Cobbs if (a->xmitWin > PPTP_XMIT_WIN) 904add85a1dSArchie Cobbs a->xmitWin = PPTP_XMIT_WIN; 905add85a1dSArchie Cobbs a->winAck = a->xmitWin; 906add85a1dSArchie Cobbs 907add85a1dSArchie Cobbs /* Reset sequence numbers */ 9083cd7db22SArchie Cobbs priv->recvSeq = ~0; 9093cd7db22SArchie Cobbs priv->recvAck = ~0; 9103cd7db22SArchie Cobbs priv->xmitSeq = ~0; 9113cd7db22SArchie Cobbs priv->xmitAck = ~0; 912add85a1dSArchie Cobbs 913add85a1dSArchie Cobbs /* Reset start time */ 9149bee7adfSArchie Cobbs getmicrouptime(&priv->startTime); 9159bee7adfSArchie Cobbs 9169bee7adfSArchie Cobbs /* Reset stats */ 9179bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 918add85a1dSArchie Cobbs 9194a48abb2SArchie Cobbs /* Stop timers */ 9204a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node); 9214a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node); 922f2ba84d7SGleb Smirnoff 923f2ba84d7SGleb Smirnoff mtx_unlock(&priv->mtx); 924add85a1dSArchie Cobbs } 925add85a1dSArchie Cobbs 926add85a1dSArchie Cobbs /* 927add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 928add85a1dSArchie Cobbs */ 929add85a1dSArchie Cobbs static pptptime_t 930add85a1dSArchie Cobbs ng_pptpgre_time(node_p node) 931add85a1dSArchie Cobbs { 93230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 933add85a1dSArchie Cobbs struct timeval tv; 934678f9e33SArchie Cobbs pptptime_t t; 935add85a1dSArchie Cobbs 936678f9e33SArchie Cobbs microuptime(&tv); 937add85a1dSArchie Cobbs if (tv.tv_sec < priv->startTime.tv_sec 938add85a1dSArchie Cobbs || (tv.tv_sec == priv->startTime.tv_sec 939add85a1dSArchie Cobbs && tv.tv_usec < priv->startTime.tv_usec)) 940add85a1dSArchie Cobbs return (0); 941add85a1dSArchie Cobbs timevalsub(&tv, &priv->startTime); 942678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; 943678f9e33SArchie Cobbs t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE); 944678f9e33SArchie Cobbs return(t); 945add85a1dSArchie Cobbs } 946