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 */ 122f6a19065SArchie Cobbs #define PPTP_MAX_TIMEOUT (1 * PPTP_TIME_SCALE) /* 1 second */ 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 node_p *sackTimerPtr; /* send ack timer pointer */ 1483cd7db22SArchie Cobbs node_p *rackTimerPtr; /* recv ack timer pointer */ 1493cd7db22SArchie Cobbs u_int32_t winAck; /* seq when xmitWin will grow */ 150add85a1dSArchie Cobbs pptptime_t timeSent[PPTP_XMIT_WIN]; 151678f9e33SArchie Cobbs #ifdef DEBUG_RAT 152da010626SArchie Cobbs pptptime_t timerStart; /* when rackTimer started */ 153da010626SArchie Cobbs pptptime_t timerLength; /* rackTimer duration */ 154678f9e33SArchie Cobbs #endif 155add85a1dSArchie Cobbs }; 156add85a1dSArchie Cobbs 157add85a1dSArchie Cobbs /* Node private data */ 158add85a1dSArchie Cobbs struct ng_pptpgre_private { 159add85a1dSArchie Cobbs hook_p upper; /* hook to upper layers */ 160add85a1dSArchie Cobbs hook_p lower; /* hook to lower layers */ 161add85a1dSArchie Cobbs struct ng_pptpgre_conf conf; /* configuration info */ 162add85a1dSArchie Cobbs struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ 163add85a1dSArchie Cobbs u_int32_t recvSeq; /* last seq # we rcv'd */ 164add85a1dSArchie Cobbs u_int32_t xmitSeq; /* last seq # we sent */ 165add85a1dSArchie Cobbs u_int32_t recvAck; /* last seq # peer ack'd */ 166add85a1dSArchie Cobbs u_int32_t xmitAck; /* last seq # we ack'd */ 1674a48abb2SArchie Cobbs u_int timers; /* number of pending timers */ 168add85a1dSArchie Cobbs struct timeval startTime; /* time node was created */ 1699bee7adfSArchie Cobbs struct ng_pptpgre_stats stats; /* node statistics */ 170add85a1dSArchie Cobbs }; 171add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p; 172add85a1dSArchie Cobbs 173add85a1dSArchie Cobbs /* Netgraph node methods */ 174add85a1dSArchie Cobbs static ng_constructor_t ng_pptpgre_constructor; 175add85a1dSArchie Cobbs static ng_rcvmsg_t ng_pptpgre_rcvmsg; 176069154d5SJulian Elischer static ng_shutdown_t ng_pptpgre_shutdown; 177add85a1dSArchie Cobbs static ng_newhook_t ng_pptpgre_newhook; 178add85a1dSArchie Cobbs static ng_rcvdata_t ng_pptpgre_rcvdata; 179add85a1dSArchie Cobbs static ng_disconnect_t ng_pptpgre_disconnect; 180add85a1dSArchie Cobbs 181add85a1dSArchie Cobbs /* Helper functions */ 182069154d5SJulian Elischer static int ng_pptpgre_xmit(node_p node, item_p item); 183069154d5SJulian Elischer static int ng_pptpgre_recv(node_p node, item_p item); 184da010626SArchie Cobbs static void ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout); 1854a48abb2SArchie Cobbs static void ng_pptpgre_stop_send_ack_timer(node_p node); 186add85a1dSArchie Cobbs static void ng_pptpgre_start_recv_ack_timer(node_p node); 1874a48abb2SArchie Cobbs static void ng_pptpgre_stop_recv_ack_timer(node_p node); 188add85a1dSArchie Cobbs static void ng_pptpgre_recv_ack_timeout(void *arg); 189add85a1dSArchie Cobbs static void ng_pptpgre_send_ack_timeout(void *arg); 190add85a1dSArchie Cobbs static void ng_pptpgre_reset(node_p node); 191add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node); 192add85a1dSArchie Cobbs 193add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */ 194f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[] 195f0184ff8SArchie Cobbs = NG_PPTPGRE_CONF_TYPE_INFO; 196add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = { 197add85a1dSArchie Cobbs &ng_parse_struct_type, 198f0184ff8SArchie Cobbs &ng_pptpgre_conf_type_fields, 199add85a1dSArchie Cobbs }; 200add85a1dSArchie Cobbs 2019bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */ 202f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[] 203f0184ff8SArchie Cobbs = NG_PPTPGRE_STATS_TYPE_INFO; 2049bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = { 2059bee7adfSArchie Cobbs &ng_parse_struct_type, 206f0184ff8SArchie Cobbs &ng_pptpgre_stats_type_fields 2079bee7adfSArchie Cobbs }; 2089bee7adfSArchie Cobbs 209add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 210add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { 211add85a1dSArchie Cobbs { 212add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 213add85a1dSArchie Cobbs NGM_PPTPGRE_SET_CONFIG, 214add85a1dSArchie Cobbs "setconfig", 215add85a1dSArchie Cobbs &ng_pptpgre_conf_type, 216add85a1dSArchie Cobbs NULL 217add85a1dSArchie Cobbs }, 218add85a1dSArchie Cobbs { 219add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 220add85a1dSArchie Cobbs NGM_PPTPGRE_GET_CONFIG, 221add85a1dSArchie Cobbs "getconfig", 222add85a1dSArchie Cobbs NULL, 223add85a1dSArchie Cobbs &ng_pptpgre_conf_type 224add85a1dSArchie Cobbs }, 2259bee7adfSArchie Cobbs { 2269bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2279bee7adfSArchie Cobbs NGM_PPTPGRE_GET_STATS, 2289bee7adfSArchie Cobbs "getstats", 2299bee7adfSArchie Cobbs NULL, 2309bee7adfSArchie Cobbs &ng_pptp_stats_type 2319bee7adfSArchie Cobbs }, 2329bee7adfSArchie Cobbs { 2339bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2349bee7adfSArchie Cobbs NGM_PPTPGRE_CLR_STATS, 2359bee7adfSArchie Cobbs "clrstats", 2369bee7adfSArchie Cobbs NULL, 2379bee7adfSArchie Cobbs NULL 2389bee7adfSArchie Cobbs }, 2399bee7adfSArchie Cobbs { 2409bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2419bee7adfSArchie Cobbs NGM_PPTPGRE_GETCLR_STATS, 2429bee7adfSArchie Cobbs "getclrstats", 2439bee7adfSArchie Cobbs NULL, 2449bee7adfSArchie Cobbs &ng_pptp_stats_type 2459bee7adfSArchie Cobbs }, 246add85a1dSArchie Cobbs { 0 } 247add85a1dSArchie Cobbs }; 248add85a1dSArchie Cobbs 249add85a1dSArchie Cobbs /* Node type descriptor */ 250add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = { 251f8aae777SJulian Elischer .version = NG_ABI_VERSION, 252f8aae777SJulian Elischer .name = NG_PPTPGRE_NODE_TYPE, 253f8aae777SJulian Elischer .constructor = ng_pptpgre_constructor, 254f8aae777SJulian Elischer .rcvmsg = ng_pptpgre_rcvmsg, 255f8aae777SJulian Elischer .shutdown = ng_pptpgre_shutdown, 256f8aae777SJulian Elischer .newhook = ng_pptpgre_newhook, 257f8aae777SJulian Elischer .rcvdata = ng_pptpgre_rcvdata, 258f8aae777SJulian Elischer .disconnect = ng_pptpgre_disconnect, 259f8aae777SJulian Elischer .cmdlist = ng_pptpgre_cmdlist, 260add85a1dSArchie Cobbs }; 261add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); 262add85a1dSArchie Cobbs 263add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 264add85a1dSArchie Cobbs 265add85a1dSArchie Cobbs /************************************************************************ 266add85a1dSArchie Cobbs NETGRAPH NODE STUFF 267add85a1dSArchie Cobbs ************************************************************************/ 268add85a1dSArchie Cobbs 269add85a1dSArchie Cobbs /* 270add85a1dSArchie Cobbs * Node type constructor 271add85a1dSArchie Cobbs */ 272add85a1dSArchie Cobbs static int 273069154d5SJulian Elischer ng_pptpgre_constructor(node_p node) 274add85a1dSArchie Cobbs { 275add85a1dSArchie Cobbs priv_p priv; 276add85a1dSArchie Cobbs 277add85a1dSArchie Cobbs /* Allocate private structure */ 27899cdf4ccSDavid Malone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 279add85a1dSArchie Cobbs if (priv == NULL) 280add85a1dSArchie Cobbs return (ENOMEM); 281add85a1dSArchie Cobbs 28230400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 283add85a1dSArchie Cobbs 284add85a1dSArchie Cobbs /* Initialize state */ 2854a48abb2SArchie Cobbs callout_init(&priv->ackp.sackTimer, 0); 2864a48abb2SArchie Cobbs callout_init(&priv->ackp.rackTimer, 0); 287add85a1dSArchie Cobbs 288add85a1dSArchie Cobbs /* Done */ 289add85a1dSArchie Cobbs return (0); 290add85a1dSArchie Cobbs } 291add85a1dSArchie Cobbs 292add85a1dSArchie Cobbs /* 293add85a1dSArchie Cobbs * Give our OK for a hook to be added. 294add85a1dSArchie Cobbs */ 295add85a1dSArchie Cobbs static int 296add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 297add85a1dSArchie Cobbs { 29830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 299add85a1dSArchie Cobbs hook_p *hookPtr; 300add85a1dSArchie Cobbs 301add85a1dSArchie Cobbs /* Check hook name */ 302add85a1dSArchie Cobbs if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) 303add85a1dSArchie Cobbs hookPtr = &priv->upper; 304add85a1dSArchie Cobbs else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) 305add85a1dSArchie Cobbs hookPtr = &priv->lower; 306add85a1dSArchie Cobbs else 307add85a1dSArchie Cobbs return (EINVAL); 308add85a1dSArchie Cobbs 309add85a1dSArchie Cobbs /* See if already connected */ 310add85a1dSArchie Cobbs if (*hookPtr != NULL) 311add85a1dSArchie Cobbs return (EISCONN); 312add85a1dSArchie Cobbs 313add85a1dSArchie Cobbs /* OK */ 314add85a1dSArchie Cobbs *hookPtr = hook; 315add85a1dSArchie Cobbs return (0); 316add85a1dSArchie Cobbs } 317add85a1dSArchie Cobbs 318add85a1dSArchie Cobbs /* 319add85a1dSArchie Cobbs * Receive a control message. 320add85a1dSArchie Cobbs */ 321add85a1dSArchie Cobbs static int 322069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook) 323add85a1dSArchie Cobbs { 32430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 325add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 326add85a1dSArchie Cobbs int error = 0; 327069154d5SJulian Elischer struct ng_mesg *msg; 328add85a1dSArchie Cobbs 329069154d5SJulian Elischer NGI_GET_MSG(item, msg); 330add85a1dSArchie Cobbs switch (msg->header.typecookie) { 331add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 332add85a1dSArchie Cobbs switch (msg->header.cmd) { 333add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 334add85a1dSArchie Cobbs { 335add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 336add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 337add85a1dSArchie Cobbs 338add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 339add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 340add85a1dSArchie Cobbs ERROUT(EINVAL); 341add85a1dSArchie Cobbs ng_pptpgre_reset(node); /* reset on configure */ 342add85a1dSArchie Cobbs priv->conf = *newConf; 343add85a1dSArchie Cobbs break; 344add85a1dSArchie Cobbs } 345add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 346add85a1dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); 347add85a1dSArchie Cobbs if (resp == NULL) 348add85a1dSArchie Cobbs ERROUT(ENOMEM); 349add85a1dSArchie Cobbs bcopy(&priv->conf, resp->data, sizeof(priv->conf)); 350add85a1dSArchie Cobbs break; 3519bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 3529bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 3539bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 3549bee7adfSArchie Cobbs { 3559bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 3569bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 3579bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 3589bee7adfSArchie Cobbs if (resp == NULL) 3599bee7adfSArchie Cobbs ERROUT(ENOMEM); 3609bee7adfSArchie Cobbs bcopy(&priv->stats, 3619bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 3629bee7adfSArchie Cobbs } 3639bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 3649bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 3659bee7adfSArchie Cobbs break; 3669bee7adfSArchie Cobbs } 367add85a1dSArchie Cobbs default: 368add85a1dSArchie Cobbs error = EINVAL; 369add85a1dSArchie Cobbs break; 370add85a1dSArchie Cobbs } 371add85a1dSArchie Cobbs break; 372add85a1dSArchie Cobbs default: 373add85a1dSArchie Cobbs error = EINVAL; 374add85a1dSArchie Cobbs break; 375add85a1dSArchie Cobbs } 376589f6ed8SJulian Elischer done: 377069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 378069154d5SJulian Elischer NG_FREE_MSG(msg); 379add85a1dSArchie Cobbs return (error); 380add85a1dSArchie Cobbs } 381add85a1dSArchie Cobbs 382add85a1dSArchie Cobbs /* 383add85a1dSArchie Cobbs * Receive incoming data on a hook. 384add85a1dSArchie Cobbs */ 385add85a1dSArchie Cobbs static int 386069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item) 387add85a1dSArchie Cobbs { 38830400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 38930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 390add85a1dSArchie Cobbs 391add85a1dSArchie Cobbs /* If not configured, reject */ 392add85a1dSArchie Cobbs if (!priv->conf.enabled) { 393069154d5SJulian Elischer NG_FREE_ITEM(item); 394add85a1dSArchie Cobbs return (ENXIO); 395add85a1dSArchie Cobbs } 396add85a1dSArchie Cobbs 397add85a1dSArchie Cobbs /* Treat as xmit or recv data */ 398add85a1dSArchie Cobbs if (hook == priv->upper) 399069154d5SJulian Elischer return ng_pptpgre_xmit(node, item); 400add85a1dSArchie Cobbs if (hook == priv->lower) 401069154d5SJulian Elischer return ng_pptpgre_recv(node, item); 4026e551fb6SDavid E. O'Brien panic("%s: weird hook", __func__); 403add85a1dSArchie Cobbs } 404add85a1dSArchie Cobbs 405add85a1dSArchie Cobbs /* 406add85a1dSArchie Cobbs * Destroy node 407add85a1dSArchie Cobbs */ 408add85a1dSArchie Cobbs static int 409069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node) 410add85a1dSArchie Cobbs { 41130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 412add85a1dSArchie Cobbs 4133cd7db22SArchie Cobbs /* Reset node */ 414add85a1dSArchie Cobbs ng_pptpgre_reset(node); 415add85a1dSArchie Cobbs 4164a48abb2SArchie Cobbs /* If no timers remain, free private info as well */ 4174a48abb2SArchie Cobbs if (priv->timers == 0) { 418add85a1dSArchie Cobbs bzero(priv, sizeof(*priv)); 419add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 42030400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 4214a48abb2SArchie Cobbs } 4224a48abb2SArchie Cobbs 4234a48abb2SArchie Cobbs /* Decrement ref count */ 42430400f03SJulian Elischer NG_NODE_UNREF(node); 425add85a1dSArchie Cobbs return (0); 426add85a1dSArchie Cobbs } 427add85a1dSArchie Cobbs 428add85a1dSArchie Cobbs /* 429add85a1dSArchie Cobbs * Hook disconnection 430add85a1dSArchie Cobbs */ 431add85a1dSArchie Cobbs static int 432add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook) 433add85a1dSArchie Cobbs { 43430400f03SJulian Elischer const node_p node = NG_HOOK_NODE(hook); 43530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 436add85a1dSArchie Cobbs 437add85a1dSArchie Cobbs /* Zero out hook pointer */ 438add85a1dSArchie Cobbs if (hook == priv->upper) 439add85a1dSArchie Cobbs priv->upper = NULL; 440add85a1dSArchie Cobbs else if (hook == priv->lower) 441add85a1dSArchie Cobbs priv->lower = NULL; 442add85a1dSArchie Cobbs else 4436e551fb6SDavid E. O'Brien panic("%s: unknown hook", __func__); 444add85a1dSArchie Cobbs 445add85a1dSArchie Cobbs /* Go away if no longer connected to anything */ 44630400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(node) == 0) 44730400f03SJulian Elischer && (NG_NODE_IS_VALID(node))) 448069154d5SJulian Elischer ng_rmnode_self(node); 449add85a1dSArchie Cobbs return (0); 450add85a1dSArchie Cobbs } 451add85a1dSArchie Cobbs 452add85a1dSArchie Cobbs /************************************************************************* 453add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 454add85a1dSArchie Cobbs *************************************************************************/ 455add85a1dSArchie Cobbs 456add85a1dSArchie Cobbs /* 457add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 458add85a1dSArchie Cobbs */ 459add85a1dSArchie Cobbs static int 460069154d5SJulian Elischer ng_pptpgre_xmit(node_p node, item_p item) 461add85a1dSArchie Cobbs { 46230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 463add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 464add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 465add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 466add85a1dSArchie Cobbs int grelen, error; 467069154d5SJulian Elischer struct mbuf *m; 468add85a1dSArchie Cobbs 469069154d5SJulian Elischer if (item) { 470069154d5SJulian Elischer NGI_GET_M(item, m); 471069154d5SJulian Elischer } else { 472069154d5SJulian Elischer m = NULL; 473069154d5SJulian Elischer } 4749bee7adfSArchie Cobbs /* Check if there's data */ 4759bee7adfSArchie Cobbs if (m != NULL) { 4769bee7adfSArchie Cobbs 477922ee196SArchie Cobbs /* Check if windowing is enabled */ 478922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 479add85a1dSArchie Cobbs /* Is our transmit window full? */ 480922ee196SArchie Cobbs if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, 481922ee196SArchie Cobbs priv->recvAck) >= a->xmitWin) { 4829bee7adfSArchie Cobbs priv->stats.xmitDrops++; 483069154d5SJulian Elischer NG_FREE_M(m); 484069154d5SJulian Elischer NG_FREE_ITEM(item); 485add85a1dSArchie Cobbs return (ENOBUFS); 486add85a1dSArchie Cobbs } 487922ee196SArchie Cobbs } 488add85a1dSArchie Cobbs 489add85a1dSArchie Cobbs /* Sanity check frame length */ 490add85a1dSArchie Cobbs if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 4919bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 492069154d5SJulian Elischer NG_FREE_M(m); 493069154d5SJulian Elischer NG_FREE_ITEM(item); 494add85a1dSArchie Cobbs return (EMSGSIZE); 495add85a1dSArchie Cobbs } 496069154d5SJulian Elischer } else { 4979bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 498069154d5SJulian Elischer } 499add85a1dSArchie Cobbs 500add85a1dSArchie Cobbs /* Build GRE header */ 501add85a1dSArchie Cobbs ((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE); 502add85a1dSArchie Cobbs gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; 503add85a1dSArchie Cobbs gre->cid = htons(priv->conf.peerCid); 504add85a1dSArchie Cobbs 505add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 506add85a1dSArchie Cobbs if (m != NULL) { 507add85a1dSArchie Cobbs gre->hasSeq = 1; 508922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 509add85a1dSArchie Cobbs a->timeSent[priv->xmitSeq - priv->recvAck] 510add85a1dSArchie Cobbs = ng_pptpgre_time(node); 511922ee196SArchie Cobbs } 512add85a1dSArchie Cobbs priv->xmitSeq++; 513add85a1dSArchie Cobbs gre->data[0] = htonl(priv->xmitSeq); 514add85a1dSArchie Cobbs } 515add85a1dSArchie Cobbs 516add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 517678f9e33SArchie Cobbs if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) { 518add85a1dSArchie Cobbs gre->hasAck = 1; 519678f9e33SArchie Cobbs gre->data[gre->hasSeq] = htonl(priv->recvSeq); 520add85a1dSArchie Cobbs priv->xmitAck = priv->recvSeq; 5214a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node); 522da010626SArchie Cobbs } 523add85a1dSArchie Cobbs 524add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 525add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 526add85a1dSArchie Cobbs if (m == NULL) { 527a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 528add85a1dSArchie Cobbs if (m == NULL) { 529678f9e33SArchie Cobbs priv->stats.memoryFailures++; 530069154d5SJulian Elischer if (item) 531069154d5SJulian Elischer NG_FREE_ITEM(item); 532add85a1dSArchie Cobbs return (ENOBUFS); 533add85a1dSArchie Cobbs } 534add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 535add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 536add85a1dSArchie Cobbs } else { 537a163d034SWarner Losh M_PREPEND(m, grelen, M_DONTWAIT); 538add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 539add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 540678f9e33SArchie Cobbs priv->stats.memoryFailures++; 541069154d5SJulian Elischer if (item) 542069154d5SJulian Elischer NG_FREE_ITEM(item); 543add85a1dSArchie Cobbs return (ENOBUFS); 544add85a1dSArchie Cobbs } 545add85a1dSArchie Cobbs } 546add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 547add85a1dSArchie Cobbs 5489bee7adfSArchie Cobbs /* Update stats */ 5499bee7adfSArchie Cobbs priv->stats.xmitPackets++; 5509bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 5519bee7adfSArchie Cobbs 552add85a1dSArchie Cobbs /* Deliver packet */ 553069154d5SJulian Elischer if (item) { 554069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->lower, m); 555069154d5SJulian Elischer } else { 556069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m); 557069154d5SJulian Elischer } 558069154d5SJulian Elischer 559678f9e33SArchie Cobbs 560da010626SArchie Cobbs /* Start receive ACK timer if data was sent and not already running */ 561678f9e33SArchie Cobbs if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1) 562678f9e33SArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 563add85a1dSArchie Cobbs return (error); 564add85a1dSArchie Cobbs } 565add85a1dSArchie Cobbs 566add85a1dSArchie Cobbs /* 567add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 568add85a1dSArchie Cobbs */ 569add85a1dSArchie Cobbs static int 570069154d5SJulian Elischer ng_pptpgre_recv(node_p node, item_p item) 571add85a1dSArchie Cobbs { 57230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 573add85a1dSArchie Cobbs int iphlen, grelen, extralen; 574816b834fSArchie Cobbs const struct greheader *gre; 575816b834fSArchie Cobbs const struct ip *ip; 576add85a1dSArchie Cobbs int error = 0; 577069154d5SJulian Elischer struct mbuf *m; 578add85a1dSArchie Cobbs 579069154d5SJulian Elischer NGI_GET_M(item, m); 5809bee7adfSArchie Cobbs /* Update stats */ 5819bee7adfSArchie Cobbs priv->stats.recvPackets++; 5829bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 5839bee7adfSArchie Cobbs 584add85a1dSArchie Cobbs /* Sanity check packet length */ 585add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { 5869bee7adfSArchie Cobbs priv->stats.recvRunts++; 587add85a1dSArchie Cobbs bad: 588069154d5SJulian Elischer NG_FREE_M(m); 589069154d5SJulian Elischer NG_FREE_ITEM(item); 590add85a1dSArchie Cobbs return (EINVAL); 591add85a1dSArchie Cobbs } 592add85a1dSArchie Cobbs 593add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 594add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 595add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 596678f9e33SArchie Cobbs priv->stats.memoryFailures++; 597069154d5SJulian Elischer NG_FREE_ITEM(item); 598add85a1dSArchie Cobbs return (ENOBUFS); 599add85a1dSArchie Cobbs } 600816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 601add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 602add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 603add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 604678f9e33SArchie Cobbs priv->stats.memoryFailures++; 605069154d5SJulian Elischer NG_FREE_ITEM(item); 606add85a1dSArchie Cobbs return (ENOBUFS); 607add85a1dSArchie Cobbs } 608816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 609add85a1dSArchie Cobbs } 610816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 611add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 6129bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 6139bee7adfSArchie Cobbs priv->stats.recvRunts++; 614add85a1dSArchie Cobbs goto bad; 6159bee7adfSArchie Cobbs } 616add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 617add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 618678f9e33SArchie Cobbs priv->stats.memoryFailures++; 619069154d5SJulian Elischer NG_FREE_ITEM(item); 620add85a1dSArchie Cobbs return (ENOBUFS); 621add85a1dSArchie Cobbs } 622816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 623816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 624add85a1dSArchie Cobbs } 625add85a1dSArchie Cobbs 626add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 627add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 628cc78c48aSArchie Cobbs - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length)); 6299bee7adfSArchie Cobbs if (extralen < 0) { 6309bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 631add85a1dSArchie Cobbs goto bad; 6329bee7adfSArchie Cobbs } 633816b834fSArchie Cobbs if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK) 634816b834fSArchie Cobbs != PPTP_INIT_VALUE) { 6359bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 636add85a1dSArchie Cobbs goto bad; 6379bee7adfSArchie Cobbs } 6389bee7adfSArchie Cobbs if (ntohs(gre->cid) != priv->conf.cid) { 6399bee7adfSArchie Cobbs priv->stats.recvBadCID++; 640add85a1dSArchie Cobbs goto bad; 6419bee7adfSArchie Cobbs } 642add85a1dSArchie Cobbs 643add85a1dSArchie Cobbs /* Look for peer ack */ 644add85a1dSArchie Cobbs if (gre->hasAck) { 645add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 646add85a1dSArchie Cobbs const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); 647add85a1dSArchie Cobbs const int index = ack - priv->recvAck - 1; 64822dfb9bdSArchie Cobbs long sample; 649add85a1dSArchie Cobbs long diff; 650add85a1dSArchie Cobbs 651add85a1dSArchie Cobbs /* Sanity check ack value */ 6529bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { 6539bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 6549bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 6559bee7adfSArchie Cobbs } 6569bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) 6579bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 658add85a1dSArchie Cobbs priv->recvAck = ack; 659add85a1dSArchie Cobbs 660add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 661922ee196SArchie Cobbs if (priv->conf.enableWindowing) { 66222dfb9bdSArchie Cobbs sample = ng_pptpgre_time(node) - a->timeSent[index]; 663add85a1dSArchie Cobbs diff = sample - a->rtt; 664add85a1dSArchie Cobbs a->rtt += PPTP_ACK_ALPHA(diff); 665add85a1dSArchie Cobbs if (diff < 0) 666add85a1dSArchie Cobbs diff = -diff; 667add85a1dSArchie Cobbs a->dev += PPTP_ACK_BETA(diff - a->dev); 668e962a823SArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 669add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 670add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 671e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 672e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 673e962a823SArchie Cobbs 674e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 675f7854568SDag-Erling Smørgrav bcopy(a->timeSent + index + 1, a->timeSent, 676922ee196SArchie Cobbs sizeof(*a->timeSent) 677922ee196SArchie Cobbs * (PPTP_XMIT_WIN - (index + 1))); 678e962a823SArchie Cobbs 679922ee196SArchie Cobbs /* If we sent an entire window, increase window size */ 6809bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 6819bee7adfSArchie Cobbs && a->xmitWin < PPTP_XMIT_WIN) { 682add85a1dSArchie Cobbs a->xmitWin++; 683add85a1dSArchie Cobbs a->winAck = ack + a->xmitWin; 684add85a1dSArchie Cobbs } 685add85a1dSArchie Cobbs 6869bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 6874a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node); 688678f9e33SArchie Cobbs if (priv->recvAck != priv->xmitSeq) 689add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 690add85a1dSArchie Cobbs } 691922ee196SArchie Cobbs } 6929bee7adfSArchie Cobbs badAck: 693add85a1dSArchie Cobbs 694add85a1dSArchie Cobbs /* See if frame contains any data */ 695add85a1dSArchie Cobbs if (gre->hasSeq) { 696add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 697add85a1dSArchie Cobbs const u_int32_t seq = ntohl(gre->data[0]); 698add85a1dSArchie Cobbs 699add85a1dSArchie Cobbs /* Sanity check sequence number */ 7009bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { 7019bee7adfSArchie Cobbs if (seq == priv->recvSeq) 7029bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 7039bee7adfSArchie Cobbs else 7049bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 7059bee7adfSArchie Cobbs goto bad; /* out-of-order or dup */ 7069bee7adfSArchie Cobbs } 707add85a1dSArchie Cobbs priv->recvSeq = seq; 708add85a1dSArchie Cobbs 709add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 7103cd7db22SArchie Cobbs if (a->sackTimerPtr == NULL) { 711da010626SArchie Cobbs int maxWait; 712add85a1dSArchie Cobbs 713da010626SArchie Cobbs /* Take 1/4 of the estimated round trip time */ 714da010626SArchie Cobbs maxWait = (a->rtt >> 2); 715add85a1dSArchie Cobbs 716678f9e33SArchie Cobbs /* If delayed ACK is disabled, send it now */ 7174a48abb2SArchie Cobbs if (!priv->conf.enableDelayedAck) /* ack now */ 718069154d5SJulian Elischer ng_pptpgre_xmit(node, NULL); 7194a48abb2SArchie Cobbs else { /* ack later */ 7204a48abb2SArchie Cobbs if (maxWait < PPTP_MIN_ACK_DELAY) 7214a48abb2SArchie Cobbs maxWait = PPTP_MIN_ACK_DELAY; 7223cd7db22SArchie Cobbs if (maxWait > PPTP_MAX_ACK_DELAY) 7233cd7db22SArchie Cobbs maxWait = PPTP_MAX_ACK_DELAY; 7243cd7db22SArchie Cobbs ng_pptpgre_start_send_ack_timer(node, maxWait); 725add85a1dSArchie Cobbs } 726add85a1dSArchie Cobbs } 727add85a1dSArchie Cobbs 728add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 729add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 730add85a1dSArchie Cobbs if (extralen > 0) 731add85a1dSArchie Cobbs m_adj(m, -extralen); 732add85a1dSArchie Cobbs 733add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 734069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->upper, m); 7359bee7adfSArchie Cobbs } else { 7369bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 737069154d5SJulian Elischer NG_FREE_ITEM(item); 738069154d5SJulian Elischer NG_FREE_M(m); /* no data to deliver */ 7399bee7adfSArchie Cobbs } 740add85a1dSArchie Cobbs return (error); 741add85a1dSArchie Cobbs } 742add85a1dSArchie Cobbs 743add85a1dSArchie Cobbs /************************************************************************* 744add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 745add85a1dSArchie Cobbs *************************************************************************/ 746add85a1dSArchie Cobbs 747add85a1dSArchie Cobbs /* 7489bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 749add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 750add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 751add85a1dSArchie Cobbs * recv ack timer, if any. 752add85a1dSArchie Cobbs */ 753add85a1dSArchie Cobbs static void 754add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node) 755add85a1dSArchie Cobbs { 75630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 757add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 758678f9e33SArchie Cobbs int remain, ticks; 759add85a1dSArchie Cobbs 760922ee196SArchie Cobbs if (!priv->conf.enableWindowing) 761922ee196SArchie Cobbs return; 762922ee196SArchie Cobbs 763add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 764add85a1dSArchie Cobbs and reset the timer to that time. */ 7656e551fb6SDavid E. O'Brien KASSERT(a->rackTimerPtr == NULL, ("%s: rackTimer", __func__)); 766add85a1dSArchie Cobbs remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); 767add85a1dSArchie Cobbs if (remain < 0) 768add85a1dSArchie Cobbs remain = 0; 769678f9e33SArchie Cobbs #ifdef DEBUG_RAT 770678f9e33SArchie Cobbs a->timerLength = remain; 771678f9e33SArchie Cobbs a->timerStart = ng_pptpgre_time(node); 772678f9e33SArchie Cobbs #endif 7739bee7adfSArchie Cobbs 7743cd7db22SArchie Cobbs /* Start new timer */ 7753cd7db22SArchie Cobbs MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); 776678f9e33SArchie Cobbs if (a->rackTimerPtr == NULL) { 777678f9e33SArchie Cobbs priv->stats.memoryFailures++; 7783cd7db22SArchie Cobbs return; /* XXX potential hang here */ 779678f9e33SArchie Cobbs } 7804a48abb2SArchie Cobbs *a->rackTimerPtr = node; /* ensures the correct timeout event */ 78130400f03SJulian Elischer NG_NODE_REF(node); 7824a48abb2SArchie Cobbs priv->timers++; 783678f9e33SArchie Cobbs 7844a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 785678f9e33SArchie Cobbs ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; 7864a48abb2SArchie Cobbs callout_reset(&a->rackTimer, ticks, 7874a48abb2SArchie Cobbs ng_pptpgre_recv_ack_timeout, a->rackTimerPtr); 7884a48abb2SArchie Cobbs } 7894a48abb2SArchie Cobbs 7904a48abb2SArchie Cobbs /* 7914a48abb2SArchie Cobbs * Stop receive ack timer. 7924a48abb2SArchie Cobbs */ 7934a48abb2SArchie Cobbs static void 7944a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node_p node) 7954a48abb2SArchie Cobbs { 7964a48abb2SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 7974a48abb2SArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 7984a48abb2SArchie Cobbs 799922ee196SArchie Cobbs if (!priv->conf.enableWindowing) 800922ee196SArchie Cobbs return; 801922ee196SArchie Cobbs 8024a48abb2SArchie Cobbs if (callout_stop(&a->rackTimer)) { 8034a48abb2SArchie Cobbs FREE(a->rackTimerPtr, M_NETGRAPH); 8044a48abb2SArchie Cobbs priv->timers--; 8054a48abb2SArchie Cobbs NG_NODE_UNREF(node); 8064a48abb2SArchie Cobbs } 8074a48abb2SArchie Cobbs a->rackTimerPtr = NULL; 808add85a1dSArchie Cobbs } 809add85a1dSArchie Cobbs 810add85a1dSArchie Cobbs /* 811add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 812add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 813add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 814add85a1dSArchie Cobbs */ 815add85a1dSArchie Cobbs static void 816add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg) 817add85a1dSArchie Cobbs { 8184164c447SArchie Cobbs int s = splnet(); 8193cd7db22SArchie Cobbs const node_p node = *((node_p *)arg); 82030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 821add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 822add85a1dSArchie Cobbs 8233cd7db22SArchie Cobbs /* This complicated stuff is needed to avoid race conditions */ 8243cd7db22SArchie Cobbs FREE(arg, M_NETGRAPH); 8256e551fb6SDavid E. O'Brien KASSERT(node->nd_refs > 0, ("%s: no nd_refs", __func__)); 8264a48abb2SArchie Cobbs KASSERT(priv != NULL, ("%s: priv=NULL", __func__)); 8274a48abb2SArchie Cobbs priv->timers--; 82830400f03SJulian Elischer if (NG_NODE_NOT_VALID(node)) { /* shutdown race condition */ 8294a48abb2SArchie Cobbs if (priv->timers == 0) { 8304a48abb2SArchie Cobbs FREE(priv, M_NETGRAPH); 8314a48abb2SArchie Cobbs NG_NODE_SET_PRIVATE(node, NULL); 8324a48abb2SArchie Cobbs } 83330400f03SJulian Elischer NG_NODE_UNREF(node); 8349bee7adfSArchie Cobbs splx(s); 8359bee7adfSArchie Cobbs return; 8369bee7adfSArchie Cobbs } 8373cd7db22SArchie Cobbs if (arg != a->rackTimerPtr) { /* timer stopped race condition */ 838422c7276SArchie Cobbs NG_NODE_UNREF(node); 8393cd7db22SArchie Cobbs splx(s); 8403cd7db22SArchie Cobbs return; 8413cd7db22SArchie Cobbs } 8423cd7db22SArchie Cobbs a->rackTimerPtr = NULL; 8439bee7adfSArchie Cobbs 844add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 8459bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 846add85a1dSArchie Cobbs a->rtt = PPTP_ACK_DELTA(a->rtt); 847add85a1dSArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 848add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 849add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 850e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 851e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 852add85a1dSArchie Cobbs 853678f9e33SArchie Cobbs #ifdef DEBUG_RAT 854678f9e33SArchie Cobbs log(LOG_DEBUG, 855678f9e33SArchie Cobbs "RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n", 856678f9e33SArchie Cobbs (int)ng_pptpgre_time(node), priv->recvAck + 1, 857678f9e33SArchie Cobbs (int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato); 858678f9e33SArchie Cobbs #endif 859678f9e33SArchie Cobbs 860e962a823SArchie Cobbs /* Reset ack and sliding window */ 861e962a823SArchie Cobbs priv->recvAck = priv->xmitSeq; /* pretend we got the ack */ 862e962a823SArchie Cobbs a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ 863e962a823SArchie Cobbs a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ 864422c7276SArchie Cobbs NG_NODE_UNREF(node); 8654164c447SArchie Cobbs splx(s); 866add85a1dSArchie Cobbs } 867add85a1dSArchie Cobbs 868add85a1dSArchie Cobbs /* 8699bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 8709bee7adfSArchie Cobbs * already running. 8719bee7adfSArchie Cobbs */ 8729bee7adfSArchie Cobbs static void 873da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout) 8749bee7adfSArchie Cobbs { 87530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 8769bee7adfSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 877678f9e33SArchie Cobbs int ticks; 8789bee7adfSArchie Cobbs 8793cd7db22SArchie Cobbs /* Start new timer */ 8806e551fb6SDavid E. O'Brien KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __func__)); 8813cd7db22SArchie Cobbs MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); 882678f9e33SArchie Cobbs if (a->sackTimerPtr == NULL) { 883678f9e33SArchie Cobbs priv->stats.memoryFailures++; 8843cd7db22SArchie Cobbs return; /* XXX potential hang here */ 885678f9e33SArchie Cobbs } 8864a48abb2SArchie Cobbs *a->sackTimerPtr = node; /* ensures the correct timeout event */ 88730400f03SJulian Elischer NG_NODE_REF(node); 8884a48abb2SArchie Cobbs priv->timers++; 889678f9e33SArchie Cobbs 8904a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 891678f9e33SArchie Cobbs ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); 8924a48abb2SArchie Cobbs callout_reset(&a->sackTimer, ticks, 8934a48abb2SArchie Cobbs ng_pptpgre_send_ack_timeout, a->sackTimerPtr); 8944a48abb2SArchie Cobbs } 8954a48abb2SArchie Cobbs 8964a48abb2SArchie Cobbs /* 8974a48abb2SArchie Cobbs * Stop send ack timer. 8984a48abb2SArchie Cobbs */ 8994a48abb2SArchie Cobbs static void 9004a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node_p node) 9014a48abb2SArchie Cobbs { 9024a48abb2SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 9034a48abb2SArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 9044a48abb2SArchie Cobbs 9054a48abb2SArchie Cobbs if (callout_stop(&a->sackTimer)) { 9064a48abb2SArchie Cobbs FREE(a->sackTimerPtr, M_NETGRAPH); 9074a48abb2SArchie Cobbs priv->timers--; 9084a48abb2SArchie Cobbs NG_NODE_UNREF(node); 9094a48abb2SArchie Cobbs } 9104a48abb2SArchie Cobbs a->sackTimerPtr = NULL; 9119bee7adfSArchie Cobbs } 9129bee7adfSArchie Cobbs 9139bee7adfSArchie Cobbs /* 914add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 915add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 916add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 917add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 918add85a1dSArchie Cobbs */ 919add85a1dSArchie Cobbs static void 920add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg) 921add85a1dSArchie Cobbs { 9224164c447SArchie Cobbs int s = splnet(); 9233cd7db22SArchie Cobbs const node_p node = *((node_p *)arg); 92430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 925add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 926add85a1dSArchie Cobbs 9273cd7db22SArchie Cobbs /* This complicated stuff is needed to avoid race conditions */ 9283cd7db22SArchie Cobbs FREE(arg, M_NETGRAPH); 9296e551fb6SDavid E. O'Brien KASSERT(node->nd_refs > 0, ("%s: no nd_refs", __func__)); 9304a48abb2SArchie Cobbs KASSERT(priv != NULL, ("%s: priv=NULL", __func__)); 9314a48abb2SArchie Cobbs priv->timers--; 93230400f03SJulian Elischer if (NG_NODE_NOT_VALID(node)) { /* shutdown race condition */ 9334a48abb2SArchie Cobbs if (priv->timers == 0) { 9344a48abb2SArchie Cobbs FREE(priv, M_NETGRAPH); 9354a48abb2SArchie Cobbs NG_NODE_SET_PRIVATE(node, NULL); 9364a48abb2SArchie Cobbs } 93730400f03SJulian Elischer NG_NODE_UNREF(node); 9389bee7adfSArchie Cobbs splx(s); 9399bee7adfSArchie Cobbs return; 9409bee7adfSArchie Cobbs } 9413cd7db22SArchie Cobbs if (a->sackTimerPtr != arg) { /* timer stopped race condition */ 9425951069aSJulian Elischer NG_NODE_UNREF(node); 9433cd7db22SArchie Cobbs splx(s); 9443cd7db22SArchie Cobbs return; 9453cd7db22SArchie Cobbs } 9463cd7db22SArchie Cobbs a->sackTimerPtr = NULL; 9479bee7adfSArchie Cobbs 9489bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 949069154d5SJulian Elischer ng_pptpgre_xmit(node, NULL); 9505951069aSJulian Elischer NG_NODE_UNREF(node); 9514164c447SArchie Cobbs splx(s); 952add85a1dSArchie Cobbs } 953add85a1dSArchie Cobbs 954add85a1dSArchie Cobbs /************************************************************************* 955add85a1dSArchie Cobbs MISC FUNCTIONS 956add85a1dSArchie Cobbs *************************************************************************/ 957add85a1dSArchie Cobbs 958add85a1dSArchie Cobbs /* 959add85a1dSArchie Cobbs * Reset state 960add85a1dSArchie Cobbs */ 961add85a1dSArchie Cobbs static void 962add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node) 963add85a1dSArchie Cobbs { 96430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 965add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 966add85a1dSArchie Cobbs 967add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 968add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 969add85a1dSArchie Cobbs a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ 970add85a1dSArchie Cobbs if (a->rtt < PPTP_MIN_RTT) 971add85a1dSArchie Cobbs a->rtt = PPTP_MIN_RTT; 972add85a1dSArchie Cobbs a->dev = 0; 973add85a1dSArchie Cobbs a->xmitWin = (priv->conf.recvWin + 1) / 2; 974e962a823SArchie Cobbs if (a->xmitWin < 2) /* often the first packet is lost */ 975e962a823SArchie Cobbs a->xmitWin = 2; /* because the peer isn't ready */ 976add85a1dSArchie Cobbs if (a->xmitWin > PPTP_XMIT_WIN) 977add85a1dSArchie Cobbs a->xmitWin = PPTP_XMIT_WIN; 978add85a1dSArchie Cobbs a->winAck = a->xmitWin; 979add85a1dSArchie Cobbs 980add85a1dSArchie Cobbs /* Reset sequence numbers */ 9813cd7db22SArchie Cobbs priv->recvSeq = ~0; 9823cd7db22SArchie Cobbs priv->recvAck = ~0; 9833cd7db22SArchie Cobbs priv->xmitSeq = ~0; 9843cd7db22SArchie Cobbs priv->xmitAck = ~0; 985add85a1dSArchie Cobbs 986add85a1dSArchie Cobbs /* Reset start time */ 9879bee7adfSArchie Cobbs getmicrouptime(&priv->startTime); 9889bee7adfSArchie Cobbs 9899bee7adfSArchie Cobbs /* Reset stats */ 9909bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 991add85a1dSArchie Cobbs 9924a48abb2SArchie Cobbs /* Stop timers */ 9934a48abb2SArchie Cobbs ng_pptpgre_stop_send_ack_timer(node); 9944a48abb2SArchie Cobbs ng_pptpgre_stop_recv_ack_timer(node); 995add85a1dSArchie Cobbs } 996add85a1dSArchie Cobbs 997add85a1dSArchie Cobbs /* 998add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 999add85a1dSArchie Cobbs */ 1000add85a1dSArchie Cobbs static pptptime_t 1001add85a1dSArchie Cobbs ng_pptpgre_time(node_p node) 1002add85a1dSArchie Cobbs { 100330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 1004add85a1dSArchie Cobbs struct timeval tv; 1005678f9e33SArchie Cobbs pptptime_t t; 1006add85a1dSArchie Cobbs 1007678f9e33SArchie Cobbs microuptime(&tv); 1008add85a1dSArchie Cobbs if (tv.tv_sec < priv->startTime.tv_sec 1009add85a1dSArchie Cobbs || (tv.tv_sec == priv->startTime.tv_sec 1010add85a1dSArchie Cobbs && tv.tv_usec < priv->startTime.tv_usec)) 1011add85a1dSArchie Cobbs return (0); 1012add85a1dSArchie Cobbs timevalsub(&tv, &priv->startTime); 1013678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; 1014678f9e33SArchie Cobbs t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE); 1015678f9e33SArchie Cobbs return(t); 1016add85a1dSArchie Cobbs } 1017add85a1dSArchie Cobbs 1018