1add85a1dSArchie Cobbs 2add85a1dSArchie Cobbs /* 3add85a1dSArchie Cobbs * ng_pptpgre.c 4add85a1dSArchie Cobbs * 5add85a1dSArchie Cobbs * Copyright (c) 1996-1999 Whistle Communications, Inc. 6add85a1dSArchie Cobbs * All rights reserved. 7add85a1dSArchie Cobbs * 8add85a1dSArchie Cobbs * Subject to the following obligations and disclaimer of warranty, use and 9add85a1dSArchie Cobbs * redistribution of this software, in source or object code forms, with or 10add85a1dSArchie Cobbs * without modifications are expressly permitted by Whistle Communications; 11add85a1dSArchie Cobbs * provided, however, that: 12add85a1dSArchie Cobbs * 1. Any and all reproductions of the source or object code must include the 13add85a1dSArchie Cobbs * copyright notice above and the following disclaimer of warranties; and 14add85a1dSArchie Cobbs * 2. No rights are granted, in any manner or form, to use Whistle 15add85a1dSArchie Cobbs * Communications, Inc. trademarks, including the mark "WHISTLE 16add85a1dSArchie Cobbs * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17add85a1dSArchie Cobbs * such appears in the above copyright notice or in the software. 18add85a1dSArchie Cobbs * 19add85a1dSArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20add85a1dSArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21add85a1dSArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22add85a1dSArchie Cobbs * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23add85a1dSArchie Cobbs * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24add85a1dSArchie Cobbs * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25add85a1dSArchie Cobbs * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26add85a1dSArchie Cobbs * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27add85a1dSArchie Cobbs * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28add85a1dSArchie Cobbs * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29add85a1dSArchie Cobbs * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30add85a1dSArchie Cobbs * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31add85a1dSArchie Cobbs * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32add85a1dSArchie Cobbs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33add85a1dSArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34add85a1dSArchie Cobbs * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35add85a1dSArchie Cobbs * OF SUCH DAMAGE. 36add85a1dSArchie Cobbs * 37cc3bbd68SJulian Elischer * Author: Archie Cobbs <archie@freebsd.org> 38add85a1dSArchie Cobbs * 39add85a1dSArchie Cobbs * $FreeBSD$ 40add85a1dSArchie Cobbs * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $ 41add85a1dSArchie Cobbs */ 42add85a1dSArchie Cobbs 43add85a1dSArchie Cobbs /* 44add85a1dSArchie Cobbs * PPTP/GRE netgraph node type. 45add85a1dSArchie Cobbs * 46add85a1dSArchie Cobbs * This node type does the GRE encapsulation as specified for the PPTP 47add85a1dSArchie Cobbs * protocol (RFC 2637, section 4). This includes sequencing and 48add85a1dSArchie Cobbs * retransmission of frames, but not the actual packet delivery nor 49add85a1dSArchie Cobbs * any of the TCP control stream protocol. 50add85a1dSArchie Cobbs * 51add85a1dSArchie Cobbs * The "upper" hook of this node is suitable for attaching to a "ppp" 52add85a1dSArchie Cobbs * node link hook. The "lower" hook of this node is suitable for attaching 53add85a1dSArchie Cobbs * to a "ksocket" node on hook "inet/raw/gre". 54add85a1dSArchie Cobbs */ 55add85a1dSArchie Cobbs 56add85a1dSArchie Cobbs #include <sys/param.h> 57add85a1dSArchie Cobbs #include <sys/systm.h> 58add85a1dSArchie Cobbs #include <sys/kernel.h> 59add85a1dSArchie Cobbs #include <sys/time.h> 60add85a1dSArchie Cobbs #include <sys/mbuf.h> 61add85a1dSArchie Cobbs #include <sys/malloc.h> 62add85a1dSArchie Cobbs #include <sys/errno.h> 63add85a1dSArchie Cobbs 64add85a1dSArchie Cobbs #include <netinet/in.h> 65add85a1dSArchie Cobbs #include <netinet/in_systm.h> 66add85a1dSArchie Cobbs #include <netinet/ip.h> 67add85a1dSArchie Cobbs 68add85a1dSArchie Cobbs #include <netgraph/ng_message.h> 69add85a1dSArchie Cobbs #include <netgraph/netgraph.h> 70add85a1dSArchie Cobbs #include <netgraph/ng_parse.h> 71add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h> 72add85a1dSArchie Cobbs 73add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */ 74add85a1dSArchie Cobbs struct greheader { 75add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN 76add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 77add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 78add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 79add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 80add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 81add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 82add85a1dSArchie Cobbs u_char vers:3; /* version */ 83add85a1dSArchie Cobbs u_char flags:4; /* flags */ 84add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 85add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN 86add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 87add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 88add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 89add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 90add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 91add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 92add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 93add85a1dSArchie Cobbs u_char flags:4; /* flags */ 94add85a1dSArchie Cobbs u_char vers:3; /* version */ 95add85a1dSArchie Cobbs #else 96add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly 97add85a1dSArchie Cobbs #endif 98add85a1dSArchie Cobbs u_int16_t proto; /* protocol (ethertype) */ 99add85a1dSArchie Cobbs u_int16_t length; /* payload length */ 100add85a1dSArchie Cobbs u_int16_t cid; /* call id */ 101add85a1dSArchie Cobbs u_int32_t data[0]; /* opt. seq, ack, then data */ 102add85a1dSArchie Cobbs }; 103add85a1dSArchie Cobbs 104add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */ 105add85a1dSArchie Cobbs #define PPTP_GRE_PROTO 0x880b 106add85a1dSArchie Cobbs 107add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */ 108add85a1dSArchie Cobbs #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) 109add85a1dSArchie Cobbs #define PPTP_INIT_MASK 0xef7fffff 110add85a1dSArchie Cobbs 111add85a1dSArchie Cobbs /* Min and max packet length */ 112add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8) 113add85a1dSArchie Cobbs 114add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */ 115e962a823SArchie Cobbs #define PPTP_TIME_SCALE 1000 /* milliseconds */ 116678f9e33SArchie Cobbs typedef u_int64_t pptptime_t; 117add85a1dSArchie Cobbs 118add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */ 119e962a823SArchie Cobbs #define PPTP_XMIT_WIN 16 /* max xmit window */ 120e962a823SArchie Cobbs #define PPTP_MIN_RTT (PPTP_TIME_SCALE / 10) /* 100 milliseconds */ 1214a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 83) /* 12 milliseconds */ 1220306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT (3 * PPTP_TIME_SCALE) /* 3 seconds */ 123add85a1dSArchie Cobbs 124da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet 125da010626SArchie Cobbs we can piggy-back the ACK off of. These parameters determine the mimimum 126da010626SArchie Cobbs and maxmimum length of time we're willing to wait in order to do that. 127da010626SArchie Cobbs These have no effect unless "enableDelayedAck" is turned on. */ 128da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */ 129da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */ 130da010626SArchie Cobbs 131e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */ 132add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x) ((x) >> 3) /* alpha = 0.125 */ 133add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x) ((x) >> 2) /* beta = 0.25 */ 134add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */ 135add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */ 136add85a1dSArchie Cobbs 1379bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y)) 1389bee7adfSArchie Cobbs 139add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */ 140add85a1dSArchie Cobbs struct ng_pptpgre_ackp { 141add85a1dSArchie Cobbs int32_t ato; /* adaptive time-out value */ 142add85a1dSArchie Cobbs int32_t rtt; /* round trip time estimate */ 143add85a1dSArchie Cobbs int32_t dev; /* deviation estimate */ 144add85a1dSArchie Cobbs u_int16_t xmitWin; /* size of xmit window */ 1454a48abb2SArchie Cobbs struct callout sackTimer; /* send ack timer */ 1464a48abb2SArchie Cobbs struct callout rackTimer; /* recv ack timer */ 1473cd7db22SArchie Cobbs u_int32_t winAck; /* seq when xmitWin will grow */ 148add85a1dSArchie Cobbs pptptime_t timeSent[PPTP_XMIT_WIN]; 149678f9e33SArchie Cobbs #ifdef DEBUG_RAT 150da010626SArchie Cobbs pptptime_t timerStart; /* when rackTimer started */ 151da010626SArchie Cobbs pptptime_t timerLength; /* rackTimer duration */ 152678f9e33SArchie Cobbs #endif 153add85a1dSArchie Cobbs }; 154add85a1dSArchie Cobbs 155add85a1dSArchie Cobbs /* Node private data */ 156add85a1dSArchie Cobbs struct ng_pptpgre_private { 157add85a1dSArchie Cobbs hook_p upper; /* hook to upper layers */ 158add85a1dSArchie Cobbs hook_p lower; /* hook to lower layers */ 159add85a1dSArchie Cobbs struct ng_pptpgre_conf conf; /* configuration info */ 160add85a1dSArchie Cobbs struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ 161add85a1dSArchie Cobbs u_int32_t recvSeq; /* last seq # we rcv'd */ 162add85a1dSArchie Cobbs u_int32_t xmitSeq; /* last seq # we sent */ 163add85a1dSArchie Cobbs u_int32_t recvAck; /* last seq # peer ack'd */ 164add85a1dSArchie Cobbs u_int32_t xmitAck; /* last seq # we ack'd */ 165add85a1dSArchie Cobbs struct timeval startTime; /* time node was created */ 1669bee7adfSArchie Cobbs struct ng_pptpgre_stats stats; /* node statistics */ 167add85a1dSArchie Cobbs }; 168add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p; 169add85a1dSArchie Cobbs 170add85a1dSArchie Cobbs /* Netgraph node methods */ 171add85a1dSArchie Cobbs static ng_constructor_t ng_pptpgre_constructor; 172add85a1dSArchie Cobbs static ng_rcvmsg_t ng_pptpgre_rcvmsg; 173069154d5SJulian Elischer static ng_shutdown_t ng_pptpgre_shutdown; 174add85a1dSArchie Cobbs static ng_newhook_t ng_pptpgre_newhook; 175add85a1dSArchie Cobbs static ng_rcvdata_t ng_pptpgre_rcvdata; 176add85a1dSArchie Cobbs static ng_disconnect_t ng_pptpgre_disconnect; 177add85a1dSArchie Cobbs 178add85a1dSArchie Cobbs /* Helper functions */ 179069154d5SJulian Elischer static int ng_pptpgre_xmit(node_p node, item_p item); 180069154d5SJulian Elischer static int ng_pptpgre_recv(node_p node, item_p item); 181da010626SArchie Cobbs static void ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout); 1824a48abb2SArchie Cobbs static void ng_pptpgre_stop_send_ack_timer(node_p node); 183add85a1dSArchie Cobbs static void ng_pptpgre_start_recv_ack_timer(node_p node); 1844a48abb2SArchie Cobbs static void ng_pptpgre_stop_recv_ack_timer(node_p node); 185089323f3SGleb Smirnoff static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, 186089323f3SGleb Smirnoff void *arg1, int arg2); 187089323f3SGleb Smirnoff static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, 188089323f3SGleb Smirnoff void *arg1, int arg2); 189add85a1dSArchie Cobbs static void ng_pptpgre_reset(node_p node); 190add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node); 191add85a1dSArchie Cobbs 192add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */ 193f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[] 194f0184ff8SArchie Cobbs = NG_PPTPGRE_CONF_TYPE_INFO; 195add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = { 196add85a1dSArchie Cobbs &ng_parse_struct_type, 197f0184ff8SArchie Cobbs &ng_pptpgre_conf_type_fields, 198add85a1dSArchie Cobbs }; 199add85a1dSArchie Cobbs 2009bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */ 201f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[] 202f0184ff8SArchie Cobbs = NG_PPTPGRE_STATS_TYPE_INFO; 2039bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = { 2049bee7adfSArchie Cobbs &ng_parse_struct_type, 205f0184ff8SArchie Cobbs &ng_pptpgre_stats_type_fields 2069bee7adfSArchie Cobbs }; 2079bee7adfSArchie Cobbs 208add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 209add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { 210add85a1dSArchie Cobbs { 211add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 212add85a1dSArchie Cobbs NGM_PPTPGRE_SET_CONFIG, 213add85a1dSArchie Cobbs "setconfig", 214add85a1dSArchie Cobbs &ng_pptpgre_conf_type, 215add85a1dSArchie Cobbs NULL 216add85a1dSArchie Cobbs }, 217add85a1dSArchie Cobbs { 218add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 219add85a1dSArchie Cobbs NGM_PPTPGRE_GET_CONFIG, 220add85a1dSArchie Cobbs "getconfig", 221add85a1dSArchie Cobbs NULL, 222add85a1dSArchie Cobbs &ng_pptpgre_conf_type 223add85a1dSArchie Cobbs }, 2249bee7adfSArchie Cobbs { 2259bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2269bee7adfSArchie Cobbs NGM_PPTPGRE_GET_STATS, 2279bee7adfSArchie Cobbs "getstats", 2289bee7adfSArchie Cobbs NULL, 2299bee7adfSArchie Cobbs &ng_pptp_stats_type 2309bee7adfSArchie Cobbs }, 2319bee7adfSArchie Cobbs { 2329bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2339bee7adfSArchie Cobbs NGM_PPTPGRE_CLR_STATS, 2349bee7adfSArchie Cobbs "clrstats", 2359bee7adfSArchie Cobbs NULL, 2369bee7adfSArchie Cobbs NULL 2379bee7adfSArchie Cobbs }, 2389bee7adfSArchie Cobbs { 2399bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2409bee7adfSArchie Cobbs NGM_PPTPGRE_GETCLR_STATS, 2419bee7adfSArchie Cobbs "getclrstats", 2429bee7adfSArchie Cobbs NULL, 2439bee7adfSArchie Cobbs &ng_pptp_stats_type 2449bee7adfSArchie Cobbs }, 245add85a1dSArchie Cobbs { 0 } 246add85a1dSArchie Cobbs }; 247add85a1dSArchie Cobbs 248add85a1dSArchie Cobbs /* Node type descriptor */ 249add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = { 250f8aae777SJulian Elischer .version = NG_ABI_VERSION, 251f8aae777SJulian Elischer .name = NG_PPTPGRE_NODE_TYPE, 252f8aae777SJulian Elischer .constructor = ng_pptpgre_constructor, 253f8aae777SJulian Elischer .rcvmsg = ng_pptpgre_rcvmsg, 254f8aae777SJulian Elischer .shutdown = ng_pptpgre_shutdown, 255f8aae777SJulian Elischer .newhook = ng_pptpgre_newhook, 256f8aae777SJulian Elischer .rcvdata = ng_pptpgre_rcvdata, 257f8aae777SJulian Elischer .disconnect = ng_pptpgre_disconnect, 258f8aae777SJulian Elischer .cmdlist = ng_pptpgre_cmdlist, 259add85a1dSArchie Cobbs }; 260add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); 261add85a1dSArchie Cobbs 262add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 263add85a1dSArchie Cobbs 264add85a1dSArchie Cobbs /************************************************************************ 265add85a1dSArchie Cobbs NETGRAPH NODE STUFF 266add85a1dSArchie Cobbs ************************************************************************/ 267add85a1dSArchie Cobbs 268add85a1dSArchie Cobbs /* 269add85a1dSArchie Cobbs * Node type constructor 270add85a1dSArchie Cobbs */ 271add85a1dSArchie Cobbs static int 272069154d5SJulian Elischer ng_pptpgre_constructor(node_p node) 273add85a1dSArchie Cobbs { 274add85a1dSArchie Cobbs priv_p priv; 275add85a1dSArchie Cobbs 276add85a1dSArchie Cobbs /* Allocate private structure */ 27799cdf4ccSDavid Malone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 278add85a1dSArchie Cobbs if (priv == NULL) 279add85a1dSArchie Cobbs return (ENOMEM); 280add85a1dSArchie Cobbs 28130400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 282add85a1dSArchie Cobbs 283add85a1dSArchie Cobbs /* Initialize state */ 284089323f3SGleb Smirnoff ng_callout_init(&priv->ackp.sackTimer); 285089323f3SGleb Smirnoff ng_callout_init(&priv->ackp.rackTimer); 286add85a1dSArchie Cobbs 287add85a1dSArchie Cobbs /* Done */ 288add85a1dSArchie Cobbs return (0); 289add85a1dSArchie Cobbs } 290add85a1dSArchie Cobbs 291add85a1dSArchie Cobbs /* 292add85a1dSArchie Cobbs * Give our OK for a hook to be added. 293add85a1dSArchie Cobbs */ 294add85a1dSArchie Cobbs static int 295add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 296add85a1dSArchie Cobbs { 29730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 298add85a1dSArchie Cobbs hook_p *hookPtr; 299add85a1dSArchie Cobbs 300add85a1dSArchie Cobbs /* Check hook name */ 301add85a1dSArchie Cobbs if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) 302add85a1dSArchie Cobbs hookPtr = &priv->upper; 303add85a1dSArchie Cobbs else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) 304add85a1dSArchie Cobbs hookPtr = &priv->lower; 305add85a1dSArchie Cobbs else 306add85a1dSArchie Cobbs return (EINVAL); 307add85a1dSArchie Cobbs 308add85a1dSArchie Cobbs /* See if already connected */ 309add85a1dSArchie Cobbs if (*hookPtr != NULL) 310add85a1dSArchie Cobbs return (EISCONN); 311add85a1dSArchie Cobbs 312add85a1dSArchie Cobbs /* OK */ 313add85a1dSArchie Cobbs *hookPtr = hook; 314add85a1dSArchie Cobbs return (0); 315add85a1dSArchie Cobbs } 316add85a1dSArchie Cobbs 317add85a1dSArchie Cobbs /* 318add85a1dSArchie Cobbs * Receive a control message. 319add85a1dSArchie Cobbs */ 320add85a1dSArchie Cobbs static int 321069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook) 322add85a1dSArchie Cobbs { 32330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 324add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 325add85a1dSArchie Cobbs int error = 0; 326069154d5SJulian Elischer struct ng_mesg *msg; 327add85a1dSArchie Cobbs 328069154d5SJulian Elischer NGI_GET_MSG(item, msg); 329add85a1dSArchie Cobbs switch (msg->header.typecookie) { 330add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 331add85a1dSArchie Cobbs switch (msg->header.cmd) { 332add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 333add85a1dSArchie Cobbs { 334add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 335add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 336add85a1dSArchie Cobbs 337add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 338add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 339add85a1dSArchie Cobbs ERROUT(EINVAL); 340add85a1dSArchie Cobbs ng_pptpgre_reset(node); /* reset on configure */ 341add85a1dSArchie Cobbs priv->conf = *newConf; 342add85a1dSArchie Cobbs break; 343add85a1dSArchie Cobbs } 344add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 345add85a1dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); 346add85a1dSArchie Cobbs if (resp == NULL) 347add85a1dSArchie Cobbs ERROUT(ENOMEM); 348add85a1dSArchie Cobbs bcopy(&priv->conf, resp->data, sizeof(priv->conf)); 349add85a1dSArchie Cobbs break; 3509bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 3519bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 3529bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 3539bee7adfSArchie Cobbs { 3549bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 3559bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 3569bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 3579bee7adfSArchie Cobbs if (resp == NULL) 3589bee7adfSArchie Cobbs ERROUT(ENOMEM); 3599bee7adfSArchie Cobbs bcopy(&priv->stats, 3609bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 3619bee7adfSArchie Cobbs } 3629bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 3639bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 3649bee7adfSArchie Cobbs break; 3659bee7adfSArchie Cobbs } 366add85a1dSArchie Cobbs default: 367add85a1dSArchie Cobbs error = EINVAL; 368add85a1dSArchie Cobbs break; 369add85a1dSArchie Cobbs } 370add85a1dSArchie Cobbs break; 371add85a1dSArchie Cobbs default: 372add85a1dSArchie Cobbs error = EINVAL; 373add85a1dSArchie Cobbs break; 374add85a1dSArchie Cobbs } 375589f6ed8SJulian Elischer done: 376069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 377069154d5SJulian Elischer NG_FREE_MSG(msg); 378add85a1dSArchie Cobbs return (error); 379add85a1dSArchie Cobbs } 380add85a1dSArchie Cobbs 381add85a1dSArchie Cobbs /* 382add85a1dSArchie Cobbs * Receive incoming data on a hook. 383add85a1dSArchie Cobbs */ 384add85a1dSArchie Cobbs static int 385069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item) 386add85a1dSArchie Cobbs { 38730400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 38830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 389add85a1dSArchie Cobbs 390add85a1dSArchie Cobbs /* If not configured, reject */ 391add85a1dSArchie Cobbs if (!priv->conf.enabled) { 392069154d5SJulian Elischer NG_FREE_ITEM(item); 393add85a1dSArchie Cobbs return (ENXIO); 394add85a1dSArchie Cobbs } 395add85a1dSArchie Cobbs 396add85a1dSArchie Cobbs /* Treat as xmit or recv data */ 397add85a1dSArchie Cobbs if (hook == priv->upper) 398069154d5SJulian Elischer return ng_pptpgre_xmit(node, item); 399add85a1dSArchie Cobbs if (hook == priv->lower) 400069154d5SJulian Elischer return ng_pptpgre_recv(node, item); 4016e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 402add85a1dSArchie Cobbs } 403add85a1dSArchie Cobbs 404add85a1dSArchie Cobbs /* 405add85a1dSArchie Cobbs * Destroy node 406add85a1dSArchie Cobbs */ 407add85a1dSArchie Cobbs static int 408069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node) 409add85a1dSArchie Cobbs { 41030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 411add85a1dSArchie Cobbs 412089323f3SGleb Smirnoff /* Reset node (stops timers) */ 413add85a1dSArchie Cobbs ng_pptpgre_reset(node); 414add85a1dSArchie Cobbs 415add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 4164a48abb2SArchie Cobbs 4174a48abb2SArchie Cobbs /* Decrement ref count */ 41830400f03SJulian Elischer NG_NODE_UNREF(node); 419add85a1dSArchie Cobbs return (0); 420add85a1dSArchie Cobbs } 421add85a1dSArchie Cobbs 422add85a1dSArchie Cobbs /* 423add85a1dSArchie Cobbs * Hook disconnection 424add85a1dSArchie Cobbs */ 425add85a1dSArchie Cobbs static int 426add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook) 427add85a1dSArchie Cobbs { 42830400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 42930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 430add85a1dSArchie Cobbs 431add85a1dSArchie Cobbs /* Zero out hook pointer */ 432add85a1dSArchie Cobbs if (hook == priv->upper) 433add85a1dSArchie Cobbs priv->upper = NULL; 434add85a1dSArchie Cobbs else if (hook == priv->lower) 435add85a1dSArchie Cobbs priv->lower = NULL; 436add85a1dSArchie Cobbs else 4376e551fb6SDavid E. O'Brien panic("%s: unknown hook", __func__); 438add85a1dSArchie Cobbs 439add85a1dSArchie Cobbs /* Go away if no longer connected to anything */ 44030400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(node) == 0) 44130400f03SJulian Elischer && (NG_NODE_IS_VALID(node))) 442069154d5SJulian Elischer ng_rmnode_self(node); 443add85a1dSArchie Cobbs return (0); 444add85a1dSArchie Cobbs } 445add85a1dSArchie Cobbs 446add85a1dSArchie Cobbs /************************************************************************* 447add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 448add85a1dSArchie Cobbs *************************************************************************/ 449add85a1dSArchie Cobbs 450add85a1dSArchie Cobbs /* 451add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 452add85a1dSArchie Cobbs */ 453add85a1dSArchie Cobbs static int 454069154d5SJulian Elischer ng_pptpgre_xmit(node_p node, item_p item) 455add85a1dSArchie Cobbs { 45630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 457add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 458add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 459add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 460add85a1dSArchie Cobbs int grelen, error; 461069154d5SJulian Elischer struct mbuf *m; 462add85a1dSArchie Cobbs 463069154d5SJulian Elischer if (item) { 464069154d5SJulian Elischer NGI_GET_M(item, m); 465069154d5SJulian Elischer } else { 466069154d5SJulian Elischer m = NULL; 467069154d5SJulian Elischer } 4689bee7adfSArchie Cobbs /* Check if there's data */ 4699bee7adfSArchie Cobbs if (m != NULL) { 4709bee7adfSArchie Cobbs 471922ee196SArchie Cobbs /* Check if windowing is enabled */ 472922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 473add85a1dSArchie Cobbs /* Is our transmit window full? */ 474922ee196SArchie Cobbs if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, 475922ee196SArchie Cobbs priv->recvAck) >= a->xmitWin) { 4769bee7adfSArchie Cobbs priv->stats.xmitDrops++; 477069154d5SJulian Elischer NG_FREE_M(m); 478069154d5SJulian Elischer NG_FREE_ITEM(item); 479add85a1dSArchie Cobbs return (ENOBUFS); 480add85a1dSArchie Cobbs } 481922ee196SArchie Cobbs } 482add85a1dSArchie Cobbs 483add85a1dSArchie Cobbs /* Sanity check frame length */ 484add85a1dSArchie Cobbs if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 4859bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 486069154d5SJulian Elischer NG_FREE_M(m); 487069154d5SJulian Elischer NG_FREE_ITEM(item); 488add85a1dSArchie Cobbs return (EMSGSIZE); 489add85a1dSArchie Cobbs } 490069154d5SJulian Elischer } else { 4919bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 492069154d5SJulian Elischer } 493add85a1dSArchie Cobbs 494add85a1dSArchie Cobbs /* Build GRE header */ 495add85a1dSArchie Cobbs ((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE); 496add85a1dSArchie Cobbs gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; 497add85a1dSArchie Cobbs gre->cid = htons(priv->conf.peerCid); 498add85a1dSArchie Cobbs 499add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 500add85a1dSArchie Cobbs if (m != NULL) { 501add85a1dSArchie Cobbs gre->hasSeq = 1; 502922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 503add85a1dSArchie Cobbs a->timeSent[priv->xmitSeq - priv->recvAck] 504add85a1dSArchie Cobbs = ng_pptpgre_time(node); 505922ee196SArchie Cobbs } 506add85a1dSArchie Cobbs priv->xmitSeq++; 507add85a1dSArchie Cobbs gre->data[0] = htonl(priv->xmitSeq); 508add85a1dSArchie Cobbs } 509add85a1dSArchie Cobbs 510add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 511678f9e33SArchie Cobbs if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) { 512add85a1dSArchie Cobbs gre->hasAck = 1; 513678f9e33SArchie Cobbs gre->data[gre->hasSeq] = htonl(priv->recvSeq); 514add85a1dSArchie Cobbs priv->xmitAck = priv->recvSeq; 5154a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node); 516da010626SArchie Cobbs } 517add85a1dSArchie Cobbs 518add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 519add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 520add85a1dSArchie Cobbs if (m == NULL) { 521a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 522add85a1dSArchie Cobbs if (m == NULL) { 523678f9e33SArchie Cobbs priv->stats.memoryFailures++; 524069154d5SJulian Elischer if (item) 525069154d5SJulian Elischer NG_FREE_ITEM(item); 526add85a1dSArchie Cobbs return (ENOBUFS); 527add85a1dSArchie Cobbs } 528add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 529add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 530add85a1dSArchie Cobbs } else { 531a163d034SWarner Losh M_PREPEND(m, grelen, M_DONTWAIT); 532add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 533add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 534678f9e33SArchie Cobbs priv->stats.memoryFailures++; 535069154d5SJulian Elischer if (item) 536069154d5SJulian Elischer NG_FREE_ITEM(item); 537add85a1dSArchie Cobbs return (ENOBUFS); 538add85a1dSArchie Cobbs } 539add85a1dSArchie Cobbs } 540add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 541add85a1dSArchie Cobbs 5429bee7adfSArchie Cobbs /* Update stats */ 5439bee7adfSArchie Cobbs priv->stats.xmitPackets++; 5449bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 5459bee7adfSArchie Cobbs 546add85a1dSArchie Cobbs /* Deliver packet */ 547069154d5SJulian Elischer if (item) { 548069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->lower, m); 549069154d5SJulian Elischer } else { 550069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m); 551069154d5SJulian Elischer } 552069154d5SJulian Elischer 553678f9e33SArchie Cobbs 554da010626SArchie Cobbs /* Start receive ACK timer if data was sent and not already running */ 555678f9e33SArchie Cobbs if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1) 556678f9e33SArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 557add85a1dSArchie Cobbs return (error); 558add85a1dSArchie Cobbs } 559add85a1dSArchie Cobbs 560add85a1dSArchie Cobbs /* 561add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 562add85a1dSArchie Cobbs */ 563add85a1dSArchie Cobbs static int 564069154d5SJulian Elischer ng_pptpgre_recv(node_p node, item_p item) 565add85a1dSArchie Cobbs { 56630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 567add85a1dSArchie Cobbs int iphlen, grelen, extralen; 568816b834fSArchie Cobbs const struct greheader *gre; 569816b834fSArchie Cobbs const struct ip *ip; 570add85a1dSArchie Cobbs int error = 0; 571069154d5SJulian Elischer struct mbuf *m; 572add85a1dSArchie Cobbs 573069154d5SJulian Elischer NGI_GET_M(item, m); 5749bee7adfSArchie Cobbs /* Update stats */ 5759bee7adfSArchie Cobbs priv->stats.recvPackets++; 5769bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 5779bee7adfSArchie Cobbs 578add85a1dSArchie Cobbs /* Sanity check packet length */ 579add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { 5809bee7adfSArchie Cobbs priv->stats.recvRunts++; 581add85a1dSArchie Cobbs bad: 582069154d5SJulian Elischer NG_FREE_M(m); 583069154d5SJulian Elischer NG_FREE_ITEM(item); 584add85a1dSArchie Cobbs return (EINVAL); 585add85a1dSArchie Cobbs } 586add85a1dSArchie Cobbs 587add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 588add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 589add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 590678f9e33SArchie Cobbs priv->stats.memoryFailures++; 591069154d5SJulian Elischer NG_FREE_ITEM(item); 592add85a1dSArchie Cobbs return (ENOBUFS); 593add85a1dSArchie Cobbs } 594816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 595add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 596add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 597add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 598678f9e33SArchie Cobbs priv->stats.memoryFailures++; 599069154d5SJulian Elischer NG_FREE_ITEM(item); 600add85a1dSArchie Cobbs return (ENOBUFS); 601add85a1dSArchie Cobbs } 602816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 603add85a1dSArchie Cobbs } 604816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 605add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 6069bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 6079bee7adfSArchie Cobbs priv->stats.recvRunts++; 608add85a1dSArchie Cobbs goto bad; 6099bee7adfSArchie Cobbs } 610add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 611add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 612678f9e33SArchie Cobbs priv->stats.memoryFailures++; 613069154d5SJulian Elischer NG_FREE_ITEM(item); 614add85a1dSArchie Cobbs return (ENOBUFS); 615add85a1dSArchie Cobbs } 616816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 617816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 618add85a1dSArchie Cobbs } 619add85a1dSArchie Cobbs 620add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 621add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 622cc78c48aSArchie Cobbs - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length)); 6239bee7adfSArchie Cobbs if (extralen < 0) { 6249bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 625add85a1dSArchie Cobbs goto bad; 6269bee7adfSArchie Cobbs } 627816b834fSArchie Cobbs if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK) 628816b834fSArchie Cobbs != PPTP_INIT_VALUE) { 6299bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 630add85a1dSArchie Cobbs goto bad; 6319bee7adfSArchie Cobbs } 6329bee7adfSArchie Cobbs if (ntohs(gre->cid) != priv->conf.cid) { 6339bee7adfSArchie Cobbs priv->stats.recvBadCID++; 634add85a1dSArchie Cobbs goto bad; 6359bee7adfSArchie Cobbs } 636add85a1dSArchie Cobbs 637add85a1dSArchie Cobbs /* Look for peer ack */ 638add85a1dSArchie Cobbs if (gre->hasAck) { 639add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 640add85a1dSArchie Cobbs const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); 641add85a1dSArchie Cobbs const int index = ack - priv->recvAck - 1; 64222dfb9bdSArchie Cobbs long sample; 643add85a1dSArchie Cobbs long diff; 644add85a1dSArchie Cobbs 645add85a1dSArchie Cobbs /* Sanity check ack value */ 6469bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { 6479bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 6489bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 6499bee7adfSArchie Cobbs } 6509bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) 6519bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 652add85a1dSArchie Cobbs priv->recvAck = ack; 653add85a1dSArchie Cobbs 654add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 655922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 65622dfb9bdSArchie Cobbs sample = ng_pptpgre_time(node) - a->timeSent[index]; 657add85a1dSArchie Cobbs diff = sample - a->rtt; 658add85a1dSArchie Cobbs a->rtt += PPTP_ACK_ALPHA(diff); 659add85a1dSArchie Cobbs if (diff < 0) 660add85a1dSArchie Cobbs diff = -diff; 661add85a1dSArchie Cobbs a->dev += PPTP_ACK_BETA(diff - a->dev); 662e962a823SArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 663add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 664add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 665e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 666e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 667e962a823SArchie Cobbs 668e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 669f7854568SDag-Erling Smørgrav bcopy(a->timeSent + index + 1, a->timeSent, 670922ee196SArchie Cobbs sizeof(*a->timeSent) 671922ee196SArchie Cobbs * (PPTP_XMIT_WIN - (index + 1))); 672e962a823SArchie Cobbs 673922ee196SArchie Cobbs /* If we sent an entire window, increase window size */ 6749bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 6759bee7adfSArchie Cobbs && a->xmitWin < PPTP_XMIT_WIN) { 676add85a1dSArchie Cobbs a->xmitWin++; 677add85a1dSArchie Cobbs a->winAck = ack + a->xmitWin; 678add85a1dSArchie Cobbs } 679add85a1dSArchie Cobbs 6809bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 6814a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node); 682678f9e33SArchie Cobbs if (priv->recvAck != priv->xmitSeq) 683add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 684add85a1dSArchie Cobbs } 685922ee196SArchie Cobbs } 6869bee7adfSArchie Cobbs badAck: 687add85a1dSArchie Cobbs 688add85a1dSArchie Cobbs /* See if frame contains any data */ 689add85a1dSArchie Cobbs if (gre->hasSeq) { 690add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 691add85a1dSArchie Cobbs const u_int32_t seq = ntohl(gre->data[0]); 692add85a1dSArchie Cobbs 693add85a1dSArchie Cobbs /* Sanity check sequence number */ 6949bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { 6959bee7adfSArchie Cobbs if (seq == priv->recvSeq) 6969bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 6979bee7adfSArchie Cobbs else 6989bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 6999bee7adfSArchie Cobbs goto bad; /* out-of-order or dup */ 7009bee7adfSArchie Cobbs } 701add85a1dSArchie Cobbs priv->recvSeq = seq; 702add85a1dSArchie Cobbs 703add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 704089323f3SGleb Smirnoff if (!(a->sackTimer.c_flags & CALLOUT_PENDING)) { 705da010626SArchie Cobbs int maxWait; 706add85a1dSArchie Cobbs 707da010626SArchie Cobbs /* Take 1/4 of the estimated round trip time */ 708da010626SArchie Cobbs maxWait = (a->rtt >> 2); 709add85a1dSArchie Cobbs 710678f9e33SArchie Cobbs /* If delayed ACK is disabled, send it now */ 7114a48abb2SArchie Cobbs if (!priv->conf.enableDelayedAck) /* ack now */ 712069154d5SJulian Elischer ng_pptpgre_xmit(node, NULL); 7134a48abb2SArchie Cobbs else { /* ack later */ 7144a48abb2SArchie Cobbs if (maxWait < PPTP_MIN_ACK_DELAY) 7154a48abb2SArchie Cobbs maxWait = PPTP_MIN_ACK_DELAY; 7163cd7db22SArchie Cobbs if (maxWait > PPTP_MAX_ACK_DELAY) 7173cd7db22SArchie Cobbs maxWait = PPTP_MAX_ACK_DELAY; 7183cd7db22SArchie Cobbs ng_pptpgre_start_send_ack_timer(node, maxWait); 719add85a1dSArchie Cobbs } 720add85a1dSArchie Cobbs } 721add85a1dSArchie Cobbs 722add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 723add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 724add85a1dSArchie Cobbs if (extralen > 0) 725add85a1dSArchie Cobbs m_adj(m, -extralen); 726add85a1dSArchie Cobbs 727add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 728069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->upper, m); 7299bee7adfSArchie Cobbs } else { 7309bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 731069154d5SJulian Elischer NG_FREE_ITEM(item); 732069154d5SJulian Elischer NG_FREE_M(m); /* no data to deliver */ 7339bee7adfSArchie Cobbs } 734add85a1dSArchie Cobbs return (error); 735add85a1dSArchie Cobbs } 736add85a1dSArchie Cobbs 737add85a1dSArchie Cobbs /************************************************************************* 738add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 739add85a1dSArchie Cobbs *************************************************************************/ 740add85a1dSArchie Cobbs 741add85a1dSArchie Cobbs /* 7429bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 743add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 744add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 745add85a1dSArchie Cobbs * recv ack timer, if any. 746add85a1dSArchie Cobbs */ 747add85a1dSArchie Cobbs static void 748add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node) 749add85a1dSArchie Cobbs { 75030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 751add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 752678f9e33SArchie Cobbs int remain, ticks; 753add85a1dSArchie Cobbs 754922ee196SArchie Cobbs if (!priv->conf.enableWindowing) 755922ee196SArchie Cobbs return; 756922ee196SArchie Cobbs 757add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 758add85a1dSArchie Cobbs and reset the timer to that time. */ 759add85a1dSArchie Cobbs remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); 760add85a1dSArchie Cobbs if (remain < 0) 761add85a1dSArchie Cobbs remain = 0; 762678f9e33SArchie Cobbs #ifdef DEBUG_RAT 763678f9e33SArchie Cobbs a->timerLength = remain; 764678f9e33SArchie Cobbs a->timerStart = ng_pptpgre_time(node); 765678f9e33SArchie Cobbs #endif 7669bee7adfSArchie Cobbs 7674a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 768678f9e33SArchie Cobbs ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; 769089323f3SGleb Smirnoff ng_callout(&a->rackTimer, node, NULL, ticks, 770089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout, NULL, 0); 7714a48abb2SArchie Cobbs } 7724a48abb2SArchie Cobbs 7734a48abb2SArchie Cobbs /* 7744a48abb2SArchie Cobbs * Stop receive ack timer. 7754a48abb2SArchie Cobbs */ 7764a48abb2SArchie Cobbs static void 7774a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node_p node) 7784a48abb2SArchie Cobbs { 7794a48abb2SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 7804a48abb2SArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 7814a48abb2SArchie Cobbs 782922ee196SArchie Cobbs if (!priv->conf.enableWindowing) 783922ee196SArchie Cobbs return; 784922ee196SArchie Cobbs 785089323f3SGleb Smirnoff ng_uncallout(&a->rackTimer, node); 786add85a1dSArchie Cobbs } 787add85a1dSArchie Cobbs 788add85a1dSArchie Cobbs /* 789add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 790add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 791add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 792add85a1dSArchie Cobbs */ 793add85a1dSArchie Cobbs static void 794089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 795add85a1dSArchie Cobbs { 79630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 797add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 798add85a1dSArchie Cobbs 7999bee7adfSArchie Cobbs 800add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 8019bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 802add85a1dSArchie Cobbs a->rtt = PPTP_ACK_DELTA(a->rtt); 803add85a1dSArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 804add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 805add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 806e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 807e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 808add85a1dSArchie Cobbs 809678f9e33SArchie Cobbs #ifdef DEBUG_RAT 810678f9e33SArchie Cobbs log(LOG_DEBUG, 811678f9e33SArchie Cobbs "RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n", 812678f9e33SArchie Cobbs (int)ng_pptpgre_time(node), priv->recvAck + 1, 813678f9e33SArchie Cobbs (int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato); 814678f9e33SArchie Cobbs #endif 815678f9e33SArchie Cobbs 816e962a823SArchie Cobbs /* Reset ack and sliding window */ 817e962a823SArchie Cobbs priv->recvAck = priv->xmitSeq; /* pretend we got the ack */ 818e962a823SArchie Cobbs a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ 819e962a823SArchie Cobbs a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ 820add85a1dSArchie Cobbs } 821add85a1dSArchie Cobbs 822add85a1dSArchie Cobbs /* 8239bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 8249bee7adfSArchie Cobbs * already running. 8259bee7adfSArchie Cobbs */ 8269bee7adfSArchie Cobbs static void 827da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout) 8289bee7adfSArchie Cobbs { 82930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 8309bee7adfSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 831678f9e33SArchie Cobbs int ticks; 8329bee7adfSArchie Cobbs 8334a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 834678f9e33SArchie Cobbs ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); 835089323f3SGleb Smirnoff ng_callout(&a->sackTimer, node, NULL, ticks, 836089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout, NULL, 0); 8374a48abb2SArchie Cobbs } 8384a48abb2SArchie Cobbs 8394a48abb2SArchie Cobbs /* 8404a48abb2SArchie Cobbs * Stop send ack timer. 8414a48abb2SArchie Cobbs */ 8424a48abb2SArchie Cobbs static void 8434a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node_p node) 8444a48abb2SArchie Cobbs { 8454a48abb2SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 8464a48abb2SArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 8474a48abb2SArchie Cobbs 848089323f3SGleb Smirnoff ng_uncallout(&a->sackTimer, node); 8499bee7adfSArchie Cobbs } 8509bee7adfSArchie Cobbs 8519bee7adfSArchie Cobbs /* 852add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 853add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 854add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 855add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 856add85a1dSArchie Cobbs */ 857add85a1dSArchie Cobbs static void 858089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 859add85a1dSArchie Cobbs { 8609bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 861069154d5SJulian Elischer ng_pptpgre_xmit(node, NULL); 862add85a1dSArchie Cobbs } 863add85a1dSArchie Cobbs 864add85a1dSArchie Cobbs /************************************************************************* 865add85a1dSArchie Cobbs MISC FUNCTIONS 866add85a1dSArchie Cobbs *************************************************************************/ 867add85a1dSArchie Cobbs 868add85a1dSArchie Cobbs /* 869add85a1dSArchie Cobbs * Reset state 870add85a1dSArchie Cobbs */ 871add85a1dSArchie Cobbs static void 872add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node) 873add85a1dSArchie Cobbs { 87430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 875add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 876add85a1dSArchie Cobbs 877add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 878add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 879add85a1dSArchie Cobbs a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ 880add85a1dSArchie Cobbs if (a->rtt < PPTP_MIN_RTT) 881add85a1dSArchie Cobbs a->rtt = PPTP_MIN_RTT; 882add85a1dSArchie Cobbs a->dev = 0; 883add85a1dSArchie Cobbs a->xmitWin = (priv->conf.recvWin + 1) / 2; 884e962a823SArchie Cobbs if (a->xmitWin < 2) /* often the first packet is lost */ 885e962a823SArchie Cobbs a->xmitWin = 2; /* because the peer isn't ready */ 886add85a1dSArchie Cobbs if (a->xmitWin > PPTP_XMIT_WIN) 887add85a1dSArchie Cobbs a->xmitWin = PPTP_XMIT_WIN; 888add85a1dSArchie Cobbs a->winAck = a->xmitWin; 889add85a1dSArchie Cobbs 890add85a1dSArchie Cobbs /* Reset sequence numbers */ 8913cd7db22SArchie Cobbs priv->recvSeq = ~0; 8923cd7db22SArchie Cobbs priv->recvAck = ~0; 8933cd7db22SArchie Cobbs priv->xmitSeq = ~0; 8943cd7db22SArchie Cobbs priv->xmitAck = ~0; 895add85a1dSArchie Cobbs 896add85a1dSArchie Cobbs /* Reset start time */ 8979bee7adfSArchie Cobbs getmicrouptime(&priv->startTime); 8989bee7adfSArchie Cobbs 8999bee7adfSArchie Cobbs /* Reset stats */ 9009bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 901add85a1dSArchie Cobbs 9024a48abb2SArchie Cobbs /* Stop timers */ 9034a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node); 9044a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node); 905add85a1dSArchie Cobbs } 906add85a1dSArchie Cobbs 907add85a1dSArchie Cobbs /* 908add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 909add85a1dSArchie Cobbs */ 910add85a1dSArchie Cobbs static pptptime_t 911add85a1dSArchie Cobbs ng_pptpgre_time(node_p node) 912add85a1dSArchie Cobbs { 91330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 914add85a1dSArchie Cobbs struct timeval tv; 915678f9e33SArchie Cobbs pptptime_t t; 916add85a1dSArchie Cobbs 917678f9e33SArchie Cobbs microuptime(&tv); 918add85a1dSArchie Cobbs if (tv.tv_sec < priv->startTime.tv_sec 919add85a1dSArchie Cobbs || (tv.tv_sec == priv->startTime.tv_sec 920add85a1dSArchie Cobbs && tv.tv_usec < priv->startTime.tv_usec)) 921add85a1dSArchie Cobbs return (0); 922add85a1dSArchie Cobbs timevalsub(&tv, &priv->startTime); 923678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; 924678f9e33SArchie Cobbs t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE); 925678f9e33SArchie Cobbs return(t); 926add85a1dSArchie Cobbs } 927