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 * 37add85a1dSArchie Cobbs * Author: Archie Cobbs <archie@whistle.com> 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 #include <sys/socket.h> 64add85a1dSArchie Cobbs #include <sys/syslog.h> 65add85a1dSArchie Cobbs #include <sys/ctype.h> 66add85a1dSArchie Cobbs 67add85a1dSArchie Cobbs #include <netinet/in.h> 68add85a1dSArchie Cobbs #include <netinet/in_systm.h> 69add85a1dSArchie Cobbs #include <netinet/ip.h> 70add85a1dSArchie Cobbs 71add85a1dSArchie Cobbs #include <netgraph/ng_message.h> 72add85a1dSArchie Cobbs #include <netgraph/netgraph.h> 73add85a1dSArchie Cobbs #include <netgraph/ng_parse.h> 74add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h> 75add85a1dSArchie Cobbs 76add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */ 77add85a1dSArchie Cobbs struct greheader { 78add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN 79add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 80add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 81add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 82add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 83add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 84add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 85add85a1dSArchie Cobbs u_char vers:3; /* version */ 86add85a1dSArchie Cobbs u_char flags:4; /* flags */ 87add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 88add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN 89add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 90add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 91add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 92add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 93add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 94add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 95add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 96add85a1dSArchie Cobbs u_char flags:4; /* flags */ 97add85a1dSArchie Cobbs u_char vers:3; /* version */ 98add85a1dSArchie Cobbs #else 99add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly 100add85a1dSArchie Cobbs #endif 101add85a1dSArchie Cobbs u_int16_t proto; /* protocol (ethertype) */ 102add85a1dSArchie Cobbs u_int16_t length; /* payload length */ 103add85a1dSArchie Cobbs u_int16_t cid; /* call id */ 104add85a1dSArchie Cobbs u_int32_t data[0]; /* opt. seq, ack, then data */ 105add85a1dSArchie Cobbs }; 106add85a1dSArchie Cobbs 107add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */ 108add85a1dSArchie Cobbs #define PPTP_GRE_PROTO 0x880b 109add85a1dSArchie Cobbs 110add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */ 111add85a1dSArchie Cobbs #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) 112add85a1dSArchie Cobbs #define PPTP_INIT_MASK 0xef7fffff 113add85a1dSArchie Cobbs 114add85a1dSArchie Cobbs /* Min and max packet length */ 115add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8) 116add85a1dSArchie Cobbs 117add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */ 118e962a823SArchie Cobbs #define PPTP_TIME_SCALE 1000 /* milliseconds */ 119678f9e33SArchie Cobbs typedef u_int64_t pptptime_t; 120add85a1dSArchie Cobbs 121add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */ 122e962a823SArchie Cobbs #define PPTP_XMIT_WIN 16 /* max xmit window */ 123e962a823SArchie Cobbs #define PPTP_MIN_RTT (PPTP_TIME_SCALE / 10) /* 100 milliseconds */ 124da010626SArchie Cobbs #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 500) /* 2 milliseconds */ 125add85a1dSArchie Cobbs #define PPTP_MAX_TIMEOUT (10 * PPTP_TIME_SCALE) /* 10 seconds */ 126add85a1dSArchie Cobbs 127da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet 128da010626SArchie Cobbs we can piggy-back the ACK off of. These parameters determine the mimimum 129da010626SArchie Cobbs and maxmimum length of time we're willing to wait in order to do that. 130da010626SArchie Cobbs These have no effect unless "enableDelayedAck" is turned on. */ 131da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */ 132da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */ 133da010626SArchie Cobbs 134e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */ 135add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x) ((x) >> 3) /* alpha = 0.125 */ 136add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x) ((x) >> 2) /* beta = 0.25 */ 137add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */ 138add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */ 139add85a1dSArchie Cobbs 1409bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y)) 1419bee7adfSArchie Cobbs 142add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */ 143add85a1dSArchie Cobbs struct ng_pptpgre_ackp { 144add85a1dSArchie Cobbs int32_t ato; /* adaptive time-out value */ 145add85a1dSArchie Cobbs int32_t rtt; /* round trip time estimate */ 146add85a1dSArchie Cobbs int32_t dev; /* deviation estimate */ 147add85a1dSArchie Cobbs u_int16_t xmitWin; /* size of xmit window */ 148add85a1dSArchie Cobbs struct callout_handle sackTimer; /* send ack timer */ 149add85a1dSArchie Cobbs struct callout_handle rackTimer; /* recv ack timer */ 1503cd7db22SArchie Cobbs node_p *sackTimerPtr; /* send ack timer pointer */ 1513cd7db22SArchie Cobbs node_p *rackTimerPtr; /* recv ack timer pointer */ 1523cd7db22SArchie Cobbs u_int32_t winAck; /* seq when xmitWin will grow */ 153add85a1dSArchie Cobbs pptptime_t timeSent[PPTP_XMIT_WIN]; 154678f9e33SArchie Cobbs #ifdef DEBUG_RAT 155da010626SArchie Cobbs pptptime_t timerStart; /* when rackTimer started */ 156da010626SArchie Cobbs pptptime_t timerLength; /* rackTimer duration */ 157678f9e33SArchie Cobbs #endif 158add85a1dSArchie Cobbs }; 159add85a1dSArchie Cobbs 160add85a1dSArchie Cobbs /* Node private data */ 161add85a1dSArchie Cobbs struct ng_pptpgre_private { 162add85a1dSArchie Cobbs hook_p upper; /* hook to upper layers */ 163add85a1dSArchie Cobbs hook_p lower; /* hook to lower layers */ 164add85a1dSArchie Cobbs struct ng_pptpgre_conf conf; /* configuration info */ 165add85a1dSArchie Cobbs struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ 166add85a1dSArchie Cobbs u_int32_t recvSeq; /* last seq # we rcv'd */ 167add85a1dSArchie Cobbs u_int32_t xmitSeq; /* last seq # we sent */ 168add85a1dSArchie Cobbs u_int32_t recvAck; /* last seq # peer ack'd */ 169add85a1dSArchie Cobbs u_int32_t xmitAck; /* last seq # we ack'd */ 170add85a1dSArchie Cobbs struct timeval startTime; /* time node was created */ 1719bee7adfSArchie Cobbs struct ng_pptpgre_stats stats; /* node statistics */ 172add85a1dSArchie Cobbs }; 173add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p; 174add85a1dSArchie Cobbs 175add85a1dSArchie Cobbs /* Netgraph node methods */ 176add85a1dSArchie Cobbs static ng_constructor_t ng_pptpgre_constructor; 177add85a1dSArchie Cobbs static ng_rcvmsg_t ng_pptpgre_rcvmsg; 178add85a1dSArchie Cobbs static ng_shutdown_t ng_pptpgre_rmnode; 179add85a1dSArchie Cobbs static ng_newhook_t ng_pptpgre_newhook; 180add85a1dSArchie Cobbs static ng_rcvdata_t ng_pptpgre_rcvdata; 181add85a1dSArchie Cobbs static ng_disconnect_t ng_pptpgre_disconnect; 182add85a1dSArchie Cobbs 183add85a1dSArchie Cobbs /* Helper functions */ 184add85a1dSArchie Cobbs static int ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta); 185add85a1dSArchie Cobbs static int ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta); 186da010626SArchie Cobbs static void ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout); 187add85a1dSArchie Cobbs static void ng_pptpgre_start_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 */ 194add85a1dSArchie Cobbs static const struct ng_parse_struct_info 195add85a1dSArchie Cobbs ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO; 196add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = { 197add85a1dSArchie Cobbs &ng_parse_struct_type, 198add85a1dSArchie Cobbs &ng_pptpgre_conf_type_info, 199add85a1dSArchie Cobbs }; 200add85a1dSArchie Cobbs 2019bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */ 2029bee7adfSArchie Cobbs static const struct ng_parse_struct_info 2039bee7adfSArchie Cobbs ng_pptpgre_stats_type_info = NG_PPTPGRE_STATS_TYPE_INFO; 2049bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = { 2059bee7adfSArchie Cobbs &ng_parse_struct_type, 2069bee7adfSArchie Cobbs &ng_pptpgre_stats_type_info 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 = { 251add85a1dSArchie Cobbs NG_VERSION, 252add85a1dSArchie Cobbs NG_PPTPGRE_NODE_TYPE, 253add85a1dSArchie Cobbs NULL, 254add85a1dSArchie Cobbs ng_pptpgre_constructor, 255add85a1dSArchie Cobbs ng_pptpgre_rcvmsg, 256add85a1dSArchie Cobbs ng_pptpgre_rmnode, 257add85a1dSArchie Cobbs ng_pptpgre_newhook, 258add85a1dSArchie Cobbs NULL, 259add85a1dSArchie Cobbs NULL, 260add85a1dSArchie Cobbs ng_pptpgre_rcvdata, 261add85a1dSArchie Cobbs ng_pptpgre_rcvdata, 262add85a1dSArchie Cobbs ng_pptpgre_disconnect, 263add85a1dSArchie Cobbs ng_pptpgre_cmdlist 264add85a1dSArchie Cobbs }; 265add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); 266add85a1dSArchie Cobbs 267add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 268add85a1dSArchie Cobbs 269add85a1dSArchie Cobbs /************************************************************************ 270add85a1dSArchie Cobbs NETGRAPH NODE STUFF 271add85a1dSArchie Cobbs ************************************************************************/ 272add85a1dSArchie Cobbs 273add85a1dSArchie Cobbs /* 274add85a1dSArchie Cobbs * Node type constructor 275add85a1dSArchie Cobbs */ 276add85a1dSArchie Cobbs static int 277add85a1dSArchie Cobbs ng_pptpgre_constructor(node_p *nodep) 278add85a1dSArchie Cobbs { 279add85a1dSArchie Cobbs priv_p priv; 280add85a1dSArchie Cobbs int error; 281add85a1dSArchie Cobbs 282add85a1dSArchie Cobbs /* Allocate private structure */ 283add85a1dSArchie Cobbs MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); 284add85a1dSArchie Cobbs if (priv == NULL) 285add85a1dSArchie Cobbs return (ENOMEM); 286add85a1dSArchie Cobbs bzero(priv, sizeof(*priv)); 287add85a1dSArchie Cobbs 288add85a1dSArchie Cobbs /* Call generic node constructor */ 289add85a1dSArchie Cobbs if ((error = ng_make_node_common(&ng_pptpgre_typestruct, nodep))) { 290add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 291add85a1dSArchie Cobbs return (error); 292add85a1dSArchie Cobbs } 293add85a1dSArchie Cobbs (*nodep)->private = priv; 294add85a1dSArchie Cobbs 295add85a1dSArchie Cobbs /* Initialize state */ 296add85a1dSArchie Cobbs callout_handle_init(&priv->ackp.sackTimer); 297add85a1dSArchie Cobbs callout_handle_init(&priv->ackp.rackTimer); 298add85a1dSArchie Cobbs 299add85a1dSArchie Cobbs /* Done */ 300add85a1dSArchie Cobbs return (0); 301add85a1dSArchie Cobbs } 302add85a1dSArchie Cobbs 303add85a1dSArchie Cobbs /* 304add85a1dSArchie Cobbs * Give our OK for a hook to be added. 305add85a1dSArchie Cobbs */ 306add85a1dSArchie Cobbs static int 307add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 308add85a1dSArchie Cobbs { 309add85a1dSArchie Cobbs const priv_p priv = node->private; 310add85a1dSArchie Cobbs hook_p *hookPtr; 311add85a1dSArchie Cobbs 312add85a1dSArchie Cobbs /* Check hook name */ 313add85a1dSArchie Cobbs if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) 314add85a1dSArchie Cobbs hookPtr = &priv->upper; 315add85a1dSArchie Cobbs else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) 316add85a1dSArchie Cobbs hookPtr = &priv->lower; 317add85a1dSArchie Cobbs else 318add85a1dSArchie Cobbs return (EINVAL); 319add85a1dSArchie Cobbs 320add85a1dSArchie Cobbs /* See if already connected */ 321add85a1dSArchie Cobbs if (*hookPtr != NULL) 322add85a1dSArchie Cobbs return (EISCONN); 323add85a1dSArchie Cobbs 324add85a1dSArchie Cobbs /* OK */ 325add85a1dSArchie Cobbs *hookPtr = hook; 326add85a1dSArchie Cobbs return (0); 327add85a1dSArchie Cobbs } 328add85a1dSArchie Cobbs 329add85a1dSArchie Cobbs /* 330add85a1dSArchie Cobbs * Receive a control message. 331add85a1dSArchie Cobbs */ 332add85a1dSArchie Cobbs static int 333add85a1dSArchie Cobbs ng_pptpgre_rcvmsg(node_p node, struct ng_mesg *msg, 334a4ec03cfSJulian Elischer const char *raddr, struct ng_mesg **rptr, hook_p lasthook) 335add85a1dSArchie Cobbs { 336add85a1dSArchie Cobbs const priv_p priv = node->private; 337add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 338add85a1dSArchie Cobbs int error = 0; 339add85a1dSArchie Cobbs 340add85a1dSArchie Cobbs switch (msg->header.typecookie) { 341add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 342add85a1dSArchie Cobbs switch (msg->header.cmd) { 343add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 344add85a1dSArchie Cobbs { 345add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 346add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 347add85a1dSArchie Cobbs 348add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 349add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 350add85a1dSArchie Cobbs ERROUT(EINVAL); 351add85a1dSArchie Cobbs ng_pptpgre_reset(node); /* reset on configure */ 352add85a1dSArchie Cobbs priv->conf = *newConf; 353add85a1dSArchie Cobbs break; 354add85a1dSArchie Cobbs } 355add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 356add85a1dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); 357add85a1dSArchie Cobbs if (resp == NULL) 358add85a1dSArchie Cobbs ERROUT(ENOMEM); 359add85a1dSArchie Cobbs bcopy(&priv->conf, resp->data, sizeof(priv->conf)); 360add85a1dSArchie Cobbs break; 3619bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 3629bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 3639bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 3649bee7adfSArchie Cobbs { 3659bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 3669bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 3679bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 3689bee7adfSArchie Cobbs if (resp == NULL) 3699bee7adfSArchie Cobbs ERROUT(ENOMEM); 3709bee7adfSArchie Cobbs bcopy(&priv->stats, 3719bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 3729bee7adfSArchie Cobbs } 3739bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 3749bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 3759bee7adfSArchie Cobbs break; 3769bee7adfSArchie Cobbs } 377add85a1dSArchie Cobbs default: 378add85a1dSArchie Cobbs error = EINVAL; 379add85a1dSArchie Cobbs break; 380add85a1dSArchie Cobbs } 381add85a1dSArchie Cobbs break; 382add85a1dSArchie Cobbs default: 383add85a1dSArchie Cobbs error = EINVAL; 384add85a1dSArchie Cobbs break; 385add85a1dSArchie Cobbs } 386add85a1dSArchie Cobbs if (rptr) 387add85a1dSArchie Cobbs *rptr = resp; 388add85a1dSArchie Cobbs else if (resp) 389add85a1dSArchie Cobbs FREE(resp, M_NETGRAPH); 390add85a1dSArchie Cobbs 391add85a1dSArchie Cobbs done: 392add85a1dSArchie Cobbs FREE(msg, M_NETGRAPH); 393add85a1dSArchie Cobbs return (error); 394add85a1dSArchie Cobbs } 395add85a1dSArchie Cobbs 396add85a1dSArchie Cobbs /* 397add85a1dSArchie Cobbs * Receive incoming data on a hook. 398add85a1dSArchie Cobbs */ 399add85a1dSArchie Cobbs static int 400a4ec03cfSJulian Elischer ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, 401a4ec03cfSJulian Elischer struct mbuf **ret_m, meta_p *ret_meta) 402add85a1dSArchie Cobbs { 403add85a1dSArchie Cobbs const node_p node = hook->node; 404add85a1dSArchie Cobbs const priv_p priv = node->private; 405add85a1dSArchie Cobbs 406add85a1dSArchie Cobbs /* If not configured, reject */ 407add85a1dSArchie Cobbs if (!priv->conf.enabled) { 408add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 409add85a1dSArchie Cobbs return (ENXIO); 410add85a1dSArchie Cobbs } 411add85a1dSArchie Cobbs 412add85a1dSArchie Cobbs /* Treat as xmit or recv data */ 413add85a1dSArchie Cobbs if (hook == priv->upper) 414add85a1dSArchie Cobbs return ng_pptpgre_xmit(node, m, meta); 415add85a1dSArchie Cobbs if (hook == priv->lower) 416add85a1dSArchie Cobbs return ng_pptpgre_recv(node, m, meta); 417add85a1dSArchie Cobbs panic("%s: weird hook", __FUNCTION__); 418add85a1dSArchie Cobbs } 419add85a1dSArchie Cobbs 420add85a1dSArchie Cobbs /* 421add85a1dSArchie Cobbs * Destroy node 422add85a1dSArchie Cobbs */ 423add85a1dSArchie Cobbs static int 424add85a1dSArchie Cobbs ng_pptpgre_rmnode(node_p node) 425add85a1dSArchie Cobbs { 426add85a1dSArchie Cobbs const priv_p priv = node->private; 427add85a1dSArchie Cobbs 4283cd7db22SArchie Cobbs /* Reset node */ 429add85a1dSArchie Cobbs ng_pptpgre_reset(node); 430add85a1dSArchie Cobbs 431add85a1dSArchie Cobbs /* Take down netgraph node */ 432add85a1dSArchie Cobbs node->flags |= NG_INVALID; 433add85a1dSArchie Cobbs ng_cutlinks(node); 434add85a1dSArchie Cobbs ng_unname(node); 435add85a1dSArchie Cobbs bzero(priv, sizeof(*priv)); 436add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 437add85a1dSArchie Cobbs node->private = NULL; 438add85a1dSArchie Cobbs ng_unref(node); 439add85a1dSArchie Cobbs return (0); 440add85a1dSArchie Cobbs } 441add85a1dSArchie Cobbs 442add85a1dSArchie Cobbs /* 443add85a1dSArchie Cobbs * Hook disconnection 444add85a1dSArchie Cobbs */ 445add85a1dSArchie Cobbs static int 446add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook) 447add85a1dSArchie Cobbs { 448add85a1dSArchie Cobbs const node_p node = hook->node; 449add85a1dSArchie Cobbs const priv_p priv = node->private; 450add85a1dSArchie Cobbs 451add85a1dSArchie Cobbs /* Zero out hook pointer */ 452add85a1dSArchie Cobbs if (hook == priv->upper) 453add85a1dSArchie Cobbs priv->upper = NULL; 454add85a1dSArchie Cobbs else if (hook == priv->lower) 455add85a1dSArchie Cobbs priv->lower = NULL; 456add85a1dSArchie Cobbs else 457add85a1dSArchie Cobbs panic("%s: unknown hook", __FUNCTION__); 458add85a1dSArchie Cobbs 459add85a1dSArchie Cobbs /* Go away if no longer connected to anything */ 460add85a1dSArchie Cobbs if (node->numhooks == 0) 461add85a1dSArchie Cobbs ng_rmnode(node); 462add85a1dSArchie Cobbs return (0); 463add85a1dSArchie Cobbs } 464add85a1dSArchie Cobbs 465add85a1dSArchie Cobbs /************************************************************************* 466add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 467add85a1dSArchie Cobbs *************************************************************************/ 468add85a1dSArchie Cobbs 469add85a1dSArchie Cobbs /* 470add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 471add85a1dSArchie Cobbs */ 472add85a1dSArchie Cobbs static int 473add85a1dSArchie Cobbs ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta) 474add85a1dSArchie Cobbs { 475add85a1dSArchie Cobbs const priv_p priv = node->private; 476add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 477add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 478add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 479add85a1dSArchie Cobbs int grelen, error; 480add85a1dSArchie Cobbs 4819bee7adfSArchie Cobbs /* Check if there's data */ 4829bee7adfSArchie Cobbs if (m != NULL) { 4839bee7adfSArchie Cobbs 484add85a1dSArchie Cobbs /* Is our transmit window full? */ 4859bee7adfSArchie Cobbs if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck) 4869bee7adfSArchie Cobbs >= a->xmitWin) { 4879bee7adfSArchie Cobbs priv->stats.xmitDrops++; 488add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 489add85a1dSArchie Cobbs return (ENOBUFS); 490add85a1dSArchie Cobbs } 491add85a1dSArchie Cobbs 492add85a1dSArchie Cobbs /* Sanity check frame length */ 493add85a1dSArchie Cobbs if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 4949bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 495add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 496add85a1dSArchie Cobbs return (EMSGSIZE); 497add85a1dSArchie Cobbs } 4989bee7adfSArchie Cobbs } else 4999bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 500add85a1dSArchie Cobbs 501add85a1dSArchie Cobbs /* Build GRE header */ 502add85a1dSArchie Cobbs ((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE); 503add85a1dSArchie Cobbs gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; 504add85a1dSArchie Cobbs gre->cid = htons(priv->conf.peerCid); 505add85a1dSArchie Cobbs 506add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 507add85a1dSArchie Cobbs if (m != NULL) { 508add85a1dSArchie Cobbs gre->hasSeq = 1; 509add85a1dSArchie Cobbs a->timeSent[priv->xmitSeq - priv->recvAck] 510add85a1dSArchie Cobbs = ng_pptpgre_time(node); 511add85a1dSArchie Cobbs priv->xmitSeq++; 512add85a1dSArchie Cobbs gre->data[0] = htonl(priv->xmitSeq); 513add85a1dSArchie Cobbs } 514add85a1dSArchie Cobbs 515add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 516678f9e33SArchie Cobbs if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) { 517add85a1dSArchie Cobbs gre->hasAck = 1; 518678f9e33SArchie Cobbs gre->data[gre->hasSeq] = htonl(priv->recvSeq); 519add85a1dSArchie Cobbs priv->xmitAck = priv->recvSeq; 520da010626SArchie Cobbs if (a->sackTimerPtr != NULL) { 521da010626SArchie Cobbs untimeout(ng_pptpgre_send_ack_timeout, 522da010626SArchie Cobbs a->sackTimerPtr, a->sackTimer); 5233cd7db22SArchie Cobbs a->sackTimerPtr = NULL; 524add85a1dSArchie Cobbs } 525da010626SArchie Cobbs } 526add85a1dSArchie Cobbs 527add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 528add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 529add85a1dSArchie Cobbs if (m == NULL) { 530add85a1dSArchie Cobbs MGETHDR(m, M_DONTWAIT, MT_DATA); 531add85a1dSArchie Cobbs if (m == NULL) { 532678f9e33SArchie Cobbs priv->stats.memoryFailures++; 533add85a1dSArchie Cobbs NG_FREE_META(meta); 534add85a1dSArchie Cobbs return (ENOBUFS); 535add85a1dSArchie Cobbs } 536add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 537add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 538add85a1dSArchie Cobbs } else { 539add85a1dSArchie Cobbs M_PREPEND(m, grelen, M_NOWAIT); 540add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 541add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 542678f9e33SArchie Cobbs priv->stats.memoryFailures++; 543add85a1dSArchie Cobbs NG_FREE_META(meta); 544add85a1dSArchie Cobbs return (ENOBUFS); 545add85a1dSArchie Cobbs } 546add85a1dSArchie Cobbs } 547add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 548add85a1dSArchie Cobbs 5499bee7adfSArchie Cobbs /* Update stats */ 5509bee7adfSArchie Cobbs priv->stats.xmitPackets++; 5519bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 5529bee7adfSArchie Cobbs 553add85a1dSArchie Cobbs /* Deliver packet */ 554add85a1dSArchie Cobbs NG_SEND_DATA(error, priv->lower, m, meta); 555678f9e33SArchie Cobbs 556da010626SArchie Cobbs /* Start receive ACK timer if data was sent and not already running */ 557678f9e33SArchie Cobbs if (error == 0 && gre->hasSeq && priv->xmitSeq == priv->recvAck + 1) 558678f9e33SArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 559add85a1dSArchie Cobbs return (error); 560add85a1dSArchie Cobbs } 561add85a1dSArchie Cobbs 562add85a1dSArchie Cobbs /* 563add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 564add85a1dSArchie Cobbs */ 565add85a1dSArchie Cobbs static int 566add85a1dSArchie Cobbs ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta) 567add85a1dSArchie Cobbs { 568add85a1dSArchie Cobbs const priv_p priv = node->private; 569add85a1dSArchie Cobbs int iphlen, grelen, extralen; 570add85a1dSArchie Cobbs struct greheader *gre; 571add85a1dSArchie Cobbs struct ip *ip; 572add85a1dSArchie Cobbs int error = 0; 573add85a1dSArchie Cobbs 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: 582add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 583add85a1dSArchie Cobbs return (EINVAL); 584add85a1dSArchie Cobbs } 585add85a1dSArchie Cobbs 586add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 587add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 588add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 589678f9e33SArchie Cobbs priv->stats.memoryFailures++; 590add85a1dSArchie Cobbs NG_FREE_META(meta); 591add85a1dSArchie Cobbs return (ENOBUFS); 592add85a1dSArchie Cobbs } 593add85a1dSArchie Cobbs ip = mtod(m, struct ip *); 594add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 595add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 596add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 597678f9e33SArchie Cobbs priv->stats.memoryFailures++; 598add85a1dSArchie Cobbs NG_FREE_META(meta); 599add85a1dSArchie Cobbs return (ENOBUFS); 600add85a1dSArchie Cobbs } 601add85a1dSArchie Cobbs ip = mtod(m, struct ip *); 602add85a1dSArchie Cobbs } 603add85a1dSArchie Cobbs gre = (struct greheader *)((u_char *)ip + iphlen); 604add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 6059bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 6069bee7adfSArchie Cobbs priv->stats.recvRunts++; 607add85a1dSArchie Cobbs goto bad; 6089bee7adfSArchie Cobbs } 609add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 610add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 611678f9e33SArchie Cobbs priv->stats.memoryFailures++; 612add85a1dSArchie Cobbs NG_FREE_META(meta); 613add85a1dSArchie Cobbs return (ENOBUFS); 614add85a1dSArchie Cobbs } 615add85a1dSArchie Cobbs ip = mtod(m, struct ip *); 616add85a1dSArchie Cobbs gre = (struct greheader *)((u_char *)ip + iphlen); 617add85a1dSArchie Cobbs } 618add85a1dSArchie Cobbs 619add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 620add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 621add85a1dSArchie Cobbs - (iphlen + grelen + (u_int16_t)ntohs(gre->length)); 6229bee7adfSArchie Cobbs if (extralen < 0) { 6239bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 624add85a1dSArchie Cobbs goto bad; 6259bee7adfSArchie Cobbs } 6269bee7adfSArchie Cobbs if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) { 6279bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 628add85a1dSArchie Cobbs goto bad; 6299bee7adfSArchie Cobbs } 6309bee7adfSArchie Cobbs if (ntohs(gre->cid) != priv->conf.cid) { 6319bee7adfSArchie Cobbs priv->stats.recvBadCID++; 632add85a1dSArchie Cobbs goto bad; 6339bee7adfSArchie Cobbs } 634add85a1dSArchie Cobbs 635add85a1dSArchie Cobbs /* Look for peer ack */ 636add85a1dSArchie Cobbs if (gre->hasAck) { 637add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 638add85a1dSArchie Cobbs const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); 639add85a1dSArchie Cobbs const int index = ack - priv->recvAck - 1; 640add85a1dSArchie Cobbs const long sample = ng_pptpgre_time(node) - a->timeSent[index]; 641add85a1dSArchie Cobbs long diff; 642add85a1dSArchie Cobbs 643add85a1dSArchie Cobbs /* Sanity check ack value */ 6449bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { 6459bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 6469bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 6479bee7adfSArchie Cobbs } 6489bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) 6499bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 650add85a1dSArchie Cobbs priv->recvAck = ack; 651add85a1dSArchie Cobbs 652add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 653add85a1dSArchie Cobbs diff = sample - a->rtt; 654add85a1dSArchie Cobbs a->rtt += PPTP_ACK_ALPHA(diff); 655add85a1dSArchie Cobbs if (diff < 0) 656add85a1dSArchie Cobbs diff = -diff; 657add85a1dSArchie Cobbs a->dev += PPTP_ACK_BETA(diff - a->dev); 658e962a823SArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 659add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 660add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 661e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 662e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 663e962a823SArchie Cobbs 664e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 665add85a1dSArchie Cobbs ovbcopy(a->timeSent + index + 1, a->timeSent, 666add85a1dSArchie Cobbs sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1))); 667e962a823SArchie Cobbs 668e962a823SArchie Cobbs /* If we sent an entire window, increase window size by one */ 6699bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 6709bee7adfSArchie Cobbs && a->xmitWin < PPTP_XMIT_WIN) { 671add85a1dSArchie Cobbs a->xmitWin++; 672add85a1dSArchie Cobbs a->winAck = ack + a->xmitWin; 673add85a1dSArchie Cobbs } 674add85a1dSArchie Cobbs 6759bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 676da010626SArchie Cobbs if (a->rackTimerPtr != NULL) { 677da010626SArchie Cobbs untimeout(ng_pptpgre_recv_ack_timeout, 678da010626SArchie Cobbs a->rackTimerPtr, a->rackTimer); 679678f9e33SArchie Cobbs a->rackTimerPtr = NULL; 680da010626SArchie Cobbs } 681678f9e33SArchie Cobbs if (priv->recvAck != priv->xmitSeq) 682add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 683add85a1dSArchie Cobbs } 6849bee7adfSArchie Cobbs badAck: 685add85a1dSArchie Cobbs 686add85a1dSArchie Cobbs /* See if frame contains any data */ 687add85a1dSArchie Cobbs if (gre->hasSeq) { 688add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 689add85a1dSArchie Cobbs const u_int32_t seq = ntohl(gre->data[0]); 690add85a1dSArchie Cobbs 691add85a1dSArchie Cobbs /* Sanity check sequence number */ 6929bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { 6939bee7adfSArchie Cobbs if (seq == priv->recvSeq) 6949bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 6959bee7adfSArchie Cobbs else 6969bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 6979bee7adfSArchie Cobbs goto bad; /* out-of-order or dup */ 6989bee7adfSArchie Cobbs } 699add85a1dSArchie Cobbs priv->recvSeq = seq; 700add85a1dSArchie Cobbs 701add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 7023cd7db22SArchie Cobbs if (a->sackTimerPtr == NULL) { 703da010626SArchie Cobbs int maxWait; 704add85a1dSArchie Cobbs 705da010626SArchie Cobbs /* Take 1/4 of the estimated round trip time */ 706da010626SArchie Cobbs maxWait = (a->rtt >> 2); 707add85a1dSArchie Cobbs 708678f9e33SArchie Cobbs /* If delayed ACK is disabled, send it now */ 709da010626SArchie Cobbs if (!priv->conf.enableDelayedAck 710da010626SArchie Cobbs || maxWait < PPTP_MIN_ACK_DELAY) 711add85a1dSArchie Cobbs ng_pptpgre_xmit(node, NULL, NULL); 712add85a1dSArchie Cobbs else { /* send the ack later */ 7133cd7db22SArchie Cobbs if (maxWait > PPTP_MAX_ACK_DELAY) 7143cd7db22SArchie Cobbs maxWait = PPTP_MAX_ACK_DELAY; 7153cd7db22SArchie Cobbs ng_pptpgre_start_send_ack_timer(node, maxWait); 716add85a1dSArchie Cobbs } 717add85a1dSArchie Cobbs } 718add85a1dSArchie Cobbs 719add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 720add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 721add85a1dSArchie Cobbs if (extralen > 0) 722add85a1dSArchie Cobbs m_adj(m, -extralen); 723add85a1dSArchie Cobbs 724add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 725add85a1dSArchie Cobbs NG_SEND_DATA(error, priv->upper, m, meta); 7269bee7adfSArchie Cobbs } else { 7279bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 728add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); /* no data to deliver */ 7299bee7adfSArchie Cobbs } 730add85a1dSArchie Cobbs return (error); 731add85a1dSArchie Cobbs } 732add85a1dSArchie Cobbs 733add85a1dSArchie Cobbs /************************************************************************* 734add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 735add85a1dSArchie Cobbs *************************************************************************/ 736add85a1dSArchie Cobbs 737add85a1dSArchie Cobbs /* 7389bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 739add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 740add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 741add85a1dSArchie Cobbs * recv ack timer, if any. 742add85a1dSArchie Cobbs */ 743add85a1dSArchie Cobbs static void 744add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node) 745add85a1dSArchie Cobbs { 746add85a1dSArchie Cobbs const priv_p priv = node->private; 747add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 748678f9e33SArchie Cobbs int remain, ticks; 749add85a1dSArchie Cobbs 750add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 751add85a1dSArchie Cobbs and reset the timer to that time. */ 752da010626SArchie Cobbs KASSERT(a->rackTimerPtr == NULL, ("%s: rackTimer", __FUNCTION__)); 753add85a1dSArchie Cobbs remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); 754add85a1dSArchie Cobbs if (remain < 0) 755add85a1dSArchie Cobbs remain = 0; 756678f9e33SArchie Cobbs #ifdef DEBUG_RAT 757678f9e33SArchie Cobbs a->timerLength = remain; 758678f9e33SArchie Cobbs a->timerStart = ng_pptpgre_time(node); 759678f9e33SArchie Cobbs #endif 7609bee7adfSArchie Cobbs 7613cd7db22SArchie Cobbs /* Start new timer */ 7623cd7db22SArchie Cobbs MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); 763678f9e33SArchie Cobbs if (a->rackTimerPtr == NULL) { 764678f9e33SArchie Cobbs priv->stats.memoryFailures++; 7653cd7db22SArchie Cobbs return; /* XXX potential hang here */ 766678f9e33SArchie Cobbs } 7673cd7db22SArchie Cobbs *a->rackTimerPtr = node; /* insures the correct timeout event */ 7689bee7adfSArchie Cobbs node->refs++; 769678f9e33SArchie Cobbs 770678f9e33SArchie Cobbs /* Be conservative: timeout() can return up to 1 tick early */ 771678f9e33SArchie Cobbs ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; 7723cd7db22SArchie Cobbs a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout, 773678f9e33SArchie Cobbs a->rackTimerPtr, ticks); 774add85a1dSArchie Cobbs } 775add85a1dSArchie Cobbs 776add85a1dSArchie Cobbs /* 777add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 778add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 779add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 780add85a1dSArchie Cobbs */ 781add85a1dSArchie Cobbs static void 782add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg) 783add85a1dSArchie Cobbs { 7844164c447SArchie Cobbs int s = splnet(); 7853cd7db22SArchie Cobbs const node_p node = *((node_p *)arg); 786add85a1dSArchie Cobbs const priv_p priv = node->private; 787add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 788add85a1dSArchie Cobbs 7893cd7db22SArchie Cobbs /* This complicated stuff is needed to avoid race conditions */ 7903cd7db22SArchie Cobbs FREE(arg, M_NETGRAPH); 7913cd7db22SArchie Cobbs KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__)); 7923cd7db22SArchie Cobbs if ((node->flags & NG_INVALID) != 0) { /* shutdown race condition */ 7939bee7adfSArchie Cobbs ng_unref(node); 7949bee7adfSArchie Cobbs splx(s); 7959bee7adfSArchie Cobbs return; 7969bee7adfSArchie Cobbs } 7979bee7adfSArchie Cobbs ng_unref(node); 7983cd7db22SArchie Cobbs if (arg != a->rackTimerPtr) { /* timer stopped race condition */ 7993cd7db22SArchie Cobbs splx(s); 8003cd7db22SArchie Cobbs return; 8013cd7db22SArchie Cobbs } 8023cd7db22SArchie Cobbs a->rackTimerPtr = NULL; 8039bee7adfSArchie Cobbs 804add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 8059bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 806add85a1dSArchie Cobbs a->rtt = PPTP_ACK_DELTA(a->rtt); 807add85a1dSArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 808add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 809add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 810e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 811e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 812add85a1dSArchie Cobbs 813678f9e33SArchie Cobbs #ifdef DEBUG_RAT 814678f9e33SArchie Cobbs log(LOG_DEBUG, 815678f9e33SArchie Cobbs "RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n", 816678f9e33SArchie Cobbs (int)ng_pptpgre_time(node), priv->recvAck + 1, 817678f9e33SArchie Cobbs (int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato); 818678f9e33SArchie Cobbs #endif 819678f9e33SArchie Cobbs 820e962a823SArchie Cobbs /* Reset ack and sliding window */ 821e962a823SArchie Cobbs priv->recvAck = priv->xmitSeq; /* pretend we got the ack */ 822e962a823SArchie Cobbs a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ 823e962a823SArchie Cobbs a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ 8244164c447SArchie Cobbs splx(s); 825add85a1dSArchie Cobbs } 826add85a1dSArchie Cobbs 827add85a1dSArchie Cobbs /* 8289bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 8299bee7adfSArchie Cobbs * already running. 8309bee7adfSArchie Cobbs */ 8319bee7adfSArchie Cobbs static void 832da010626SArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout) 8339bee7adfSArchie Cobbs { 8349bee7adfSArchie Cobbs const priv_p priv = node->private; 8359bee7adfSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 836678f9e33SArchie Cobbs int ticks; 8379bee7adfSArchie Cobbs 8383cd7db22SArchie Cobbs /* Start new timer */ 8393cd7db22SArchie Cobbs KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __FUNCTION__)); 8403cd7db22SArchie Cobbs MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); 841678f9e33SArchie Cobbs if (a->sackTimerPtr == NULL) { 842678f9e33SArchie Cobbs priv->stats.memoryFailures++; 8433cd7db22SArchie Cobbs return; /* XXX potential hang here */ 844678f9e33SArchie Cobbs } 8453cd7db22SArchie Cobbs *a->sackTimerPtr = node; 8469bee7adfSArchie Cobbs node->refs++; 847678f9e33SArchie Cobbs 848678f9e33SArchie Cobbs /* Be conservative: timeout() can return up to 1 tick early */ 849678f9e33SArchie Cobbs ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); 8503cd7db22SArchie Cobbs a->sackTimer = timeout(ng_pptpgre_send_ack_timeout, 851678f9e33SArchie Cobbs a->sackTimerPtr, ticks); 8529bee7adfSArchie Cobbs } 8539bee7adfSArchie Cobbs 8549bee7adfSArchie Cobbs /* 855add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 856add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 857add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 858add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 859add85a1dSArchie Cobbs */ 860add85a1dSArchie Cobbs static void 861add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg) 862add85a1dSArchie Cobbs { 8634164c447SArchie Cobbs int s = splnet(); 8643cd7db22SArchie Cobbs const node_p node = *((node_p *)arg); 865add85a1dSArchie Cobbs const priv_p priv = node->private; 866add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 867add85a1dSArchie Cobbs 8683cd7db22SArchie Cobbs /* This complicated stuff is needed to avoid race conditions */ 8693cd7db22SArchie Cobbs FREE(arg, M_NETGRAPH); 8703cd7db22SArchie Cobbs KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__)); 8713cd7db22SArchie Cobbs if ((node->flags & NG_INVALID) != 0) { /* shutdown race condition */ 8729bee7adfSArchie Cobbs ng_unref(node); 8739bee7adfSArchie Cobbs splx(s); 8749bee7adfSArchie Cobbs return; 8759bee7adfSArchie Cobbs } 8769bee7adfSArchie Cobbs ng_unref(node); 8773cd7db22SArchie Cobbs if (a->sackTimerPtr != arg) { /* timer stopped race condition */ 8783cd7db22SArchie Cobbs splx(s); 8793cd7db22SArchie Cobbs return; 8803cd7db22SArchie Cobbs } 8813cd7db22SArchie Cobbs a->sackTimerPtr = NULL; 8829bee7adfSArchie Cobbs 8839bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 884add85a1dSArchie Cobbs ng_pptpgre_xmit(node, NULL, NULL); 8854164c447SArchie Cobbs splx(s); 886add85a1dSArchie Cobbs } 887add85a1dSArchie Cobbs 888add85a1dSArchie Cobbs /************************************************************************* 889add85a1dSArchie Cobbs MISC FUNCTIONS 890add85a1dSArchie Cobbs *************************************************************************/ 891add85a1dSArchie Cobbs 892add85a1dSArchie Cobbs /* 893add85a1dSArchie Cobbs * Reset state 894add85a1dSArchie Cobbs */ 895add85a1dSArchie Cobbs static void 896add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node) 897add85a1dSArchie Cobbs { 898add85a1dSArchie Cobbs const priv_p priv = node->private; 899add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 900add85a1dSArchie Cobbs 901add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 902add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 903add85a1dSArchie Cobbs a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ 904add85a1dSArchie Cobbs if (a->rtt < PPTP_MIN_RTT) 905add85a1dSArchie Cobbs a->rtt = PPTP_MIN_RTT; 906add85a1dSArchie Cobbs a->dev = 0; 907add85a1dSArchie Cobbs a->xmitWin = (priv->conf.recvWin + 1) / 2; 908e962a823SArchie Cobbs if (a->xmitWin < 2) /* often the first packet is lost */ 909e962a823SArchie Cobbs a->xmitWin = 2; /* because the peer isn't ready */ 910add85a1dSArchie Cobbs if (a->xmitWin > PPTP_XMIT_WIN) 911add85a1dSArchie Cobbs a->xmitWin = PPTP_XMIT_WIN; 912add85a1dSArchie Cobbs a->winAck = a->xmitWin; 913add85a1dSArchie Cobbs 914add85a1dSArchie Cobbs /* Reset sequence numbers */ 9153cd7db22SArchie Cobbs priv->recvSeq = ~0; 9163cd7db22SArchie Cobbs priv->recvAck = ~0; 9173cd7db22SArchie Cobbs priv->xmitSeq = ~0; 9183cd7db22SArchie Cobbs priv->xmitAck = ~0; 919add85a1dSArchie Cobbs 920add85a1dSArchie Cobbs /* Reset start time */ 9219bee7adfSArchie Cobbs getmicrouptime(&priv->startTime); 9229bee7adfSArchie Cobbs 9239bee7adfSArchie Cobbs /* Reset stats */ 9249bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 925add85a1dSArchie Cobbs 926da010626SArchie Cobbs /* Stop timers */ 927da010626SArchie Cobbs if (a->sackTimerPtr != NULL) { 928da010626SArchie Cobbs untimeout(ng_pptpgre_send_ack_timeout, 929da010626SArchie Cobbs a->sackTimerPtr, a->sackTimer); 9303cd7db22SArchie Cobbs a->sackTimerPtr = NULL; 931da010626SArchie Cobbs } 932da010626SArchie Cobbs if (a->rackTimerPtr != NULL) { 933da010626SArchie Cobbs untimeout(ng_pptpgre_recv_ack_timeout, 934da010626SArchie Cobbs a->rackTimerPtr, a->rackTimer); 9353cd7db22SArchie Cobbs a->rackTimerPtr = NULL; 936add85a1dSArchie Cobbs } 937da010626SArchie Cobbs } 938add85a1dSArchie Cobbs 939add85a1dSArchie Cobbs /* 940add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 941add85a1dSArchie Cobbs */ 942add85a1dSArchie Cobbs static pptptime_t 943add85a1dSArchie Cobbs ng_pptpgre_time(node_p node) 944add85a1dSArchie Cobbs { 945add85a1dSArchie Cobbs const priv_p priv = node->private; 946add85a1dSArchie Cobbs struct timeval tv; 947678f9e33SArchie Cobbs pptptime_t t; 948add85a1dSArchie Cobbs 949678f9e33SArchie Cobbs microuptime(&tv); 950add85a1dSArchie Cobbs if (tv.tv_sec < priv->startTime.tv_sec 951add85a1dSArchie Cobbs || (tv.tv_sec == priv->startTime.tv_sec 952add85a1dSArchie Cobbs && tv.tv_usec < priv->startTime.tv_usec)) 953add85a1dSArchie Cobbs return (0); 954add85a1dSArchie Cobbs timevalsub(&tv, &priv->startTime); 955678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; 956678f9e33SArchie Cobbs t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE); 957678f9e33SArchie Cobbs return(t); 958add85a1dSArchie Cobbs } 959add85a1dSArchie Cobbs 960