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 */ 119add85a1dSArchie Cobbs typedef u_int32_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 */ 124e962a823SArchie Cobbs #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 100) /* 10 milliseconds */ 125add85a1dSArchie Cobbs #define PPTP_MAX_TIMEOUT (10 * PPTP_TIME_SCALE) /* 10 seconds */ 126add85a1dSArchie Cobbs 127e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */ 128add85a1dSArchie Cobbs #define PPTP_ACK_ALPHA(x) ((x) >> 3) /* alpha = 0.125 */ 129add85a1dSArchie Cobbs #define PPTP_ACK_BETA(x) ((x) >> 2) /* beta = 0.25 */ 130add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */ 131add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */ 132add85a1dSArchie Cobbs 1339bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y)) 1349bee7adfSArchie Cobbs 135add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */ 136add85a1dSArchie Cobbs struct ng_pptpgre_ackp { 137add85a1dSArchie Cobbs int32_t ato; /* adaptive time-out value */ 138add85a1dSArchie Cobbs int32_t rtt; /* round trip time estimate */ 139add85a1dSArchie Cobbs int32_t dev; /* deviation estimate */ 140add85a1dSArchie Cobbs u_int16_t xmitWin; /* size of xmit window */ 141add85a1dSArchie Cobbs struct callout_handle sackTimer; /* send ack timer */ 142add85a1dSArchie Cobbs struct callout_handle rackTimer; /* recv ack timer */ 1433cd7db22SArchie Cobbs node_p *sackTimerPtr; /* send ack timer pointer */ 1443cd7db22SArchie Cobbs node_p *rackTimerPtr; /* recv ack timer pointer */ 1453cd7db22SArchie Cobbs u_int32_t winAck; /* seq when xmitWin will grow */ 146add85a1dSArchie Cobbs pptptime_t timeSent[PPTP_XMIT_WIN]; 147add85a1dSArchie Cobbs }; 148add85a1dSArchie Cobbs 149add85a1dSArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet 150add85a1dSArchie Cobbs we can piggy-back the ACK off of. These parameters determine the mimimum 151add85a1dSArchie Cobbs and maxmimum length of time we're willing to wait in order to do that. */ 152add85a1dSArchie Cobbs #define PPTP_MAX_ACK_DELAY ((int) (0.25 * PPTP_TIME_SCALE)) 153add85a1dSArchie Cobbs 154add85a1dSArchie Cobbs /* Node private data */ 155add85a1dSArchie Cobbs struct ng_pptpgre_private { 156add85a1dSArchie Cobbs hook_p upper; /* hook to upper layers */ 157add85a1dSArchie Cobbs hook_p lower; /* hook to lower layers */ 158add85a1dSArchie Cobbs struct ng_pptpgre_conf conf; /* configuration info */ 159add85a1dSArchie Cobbs struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ 160add85a1dSArchie Cobbs u_int32_t recvSeq; /* last seq # we rcv'd */ 161add85a1dSArchie Cobbs u_int32_t xmitSeq; /* last seq # we sent */ 162add85a1dSArchie Cobbs u_int32_t recvAck; /* last seq # peer ack'd */ 163add85a1dSArchie Cobbs u_int32_t xmitAck; /* last seq # we ack'd */ 164add85a1dSArchie Cobbs struct timeval startTime; /* time node was created */ 1659bee7adfSArchie Cobbs struct ng_pptpgre_stats stats; /* node statistics */ 166add85a1dSArchie Cobbs }; 167add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p; 168add85a1dSArchie Cobbs 169add85a1dSArchie Cobbs /* Netgraph node methods */ 170add85a1dSArchie Cobbs static ng_constructor_t ng_pptpgre_constructor; 171add85a1dSArchie Cobbs static ng_rcvmsg_t ng_pptpgre_rcvmsg; 172add85a1dSArchie Cobbs static ng_shutdown_t ng_pptpgre_rmnode; 173add85a1dSArchie Cobbs static ng_newhook_t ng_pptpgre_newhook; 174add85a1dSArchie Cobbs static ng_rcvdata_t ng_pptpgre_rcvdata; 175add85a1dSArchie Cobbs static ng_disconnect_t ng_pptpgre_disconnect; 176add85a1dSArchie Cobbs 177add85a1dSArchie Cobbs /* Helper functions */ 178add85a1dSArchie Cobbs static int ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta); 179add85a1dSArchie Cobbs static int ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta); 1809bee7adfSArchie Cobbs static void ng_pptpgre_start_send_ack_timer(node_p node, long ackTimeout); 181add85a1dSArchie Cobbs static void ng_pptpgre_start_recv_ack_timer(node_p node); 182add85a1dSArchie Cobbs static void ng_pptpgre_recv_ack_timeout(void *arg); 183add85a1dSArchie Cobbs static void ng_pptpgre_send_ack_timeout(void *arg); 184add85a1dSArchie Cobbs static void ng_pptpgre_reset(node_p node); 185add85a1dSArchie Cobbs static pptptime_t ng_pptpgre_time(node_p node); 186add85a1dSArchie Cobbs 187add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */ 188add85a1dSArchie Cobbs static const struct ng_parse_struct_info 189add85a1dSArchie Cobbs ng_pptpgre_conf_type_info = NG_PPTPGRE_CONF_TYPE_INFO; 190add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = { 191add85a1dSArchie Cobbs &ng_parse_struct_type, 192add85a1dSArchie Cobbs &ng_pptpgre_conf_type_info, 193add85a1dSArchie Cobbs }; 194add85a1dSArchie Cobbs 1959bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */ 1969bee7adfSArchie Cobbs static const struct ng_parse_struct_info 1979bee7adfSArchie Cobbs ng_pptpgre_stats_type_info = NG_PPTPGRE_STATS_TYPE_INFO; 1989bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = { 1999bee7adfSArchie Cobbs &ng_parse_struct_type, 2009bee7adfSArchie Cobbs &ng_pptpgre_stats_type_info 2019bee7adfSArchie Cobbs }; 2029bee7adfSArchie Cobbs 203add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 204add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { 205add85a1dSArchie Cobbs { 206add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 207add85a1dSArchie Cobbs NGM_PPTPGRE_SET_CONFIG, 208add85a1dSArchie Cobbs "setconfig", 209add85a1dSArchie Cobbs &ng_pptpgre_conf_type, 210add85a1dSArchie Cobbs NULL 211add85a1dSArchie Cobbs }, 212add85a1dSArchie Cobbs { 213add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 214add85a1dSArchie Cobbs NGM_PPTPGRE_GET_CONFIG, 215add85a1dSArchie Cobbs "getconfig", 216add85a1dSArchie Cobbs NULL, 217add85a1dSArchie Cobbs &ng_pptpgre_conf_type 218add85a1dSArchie Cobbs }, 2199bee7adfSArchie Cobbs { 2209bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2219bee7adfSArchie Cobbs NGM_PPTPGRE_GET_STATS, 2229bee7adfSArchie Cobbs "getstats", 2239bee7adfSArchie Cobbs NULL, 2249bee7adfSArchie Cobbs &ng_pptp_stats_type 2259bee7adfSArchie Cobbs }, 2269bee7adfSArchie Cobbs { 2279bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2289bee7adfSArchie Cobbs NGM_PPTPGRE_CLR_STATS, 2299bee7adfSArchie Cobbs "clrstats", 2309bee7adfSArchie Cobbs NULL, 2319bee7adfSArchie Cobbs NULL 2329bee7adfSArchie Cobbs }, 2339bee7adfSArchie Cobbs { 2349bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2359bee7adfSArchie Cobbs NGM_PPTPGRE_GETCLR_STATS, 2369bee7adfSArchie Cobbs "getclrstats", 2379bee7adfSArchie Cobbs NULL, 2389bee7adfSArchie Cobbs &ng_pptp_stats_type 2399bee7adfSArchie Cobbs }, 240add85a1dSArchie Cobbs { 0 } 241add85a1dSArchie Cobbs }; 242add85a1dSArchie Cobbs 243add85a1dSArchie Cobbs /* Node type descriptor */ 244add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = { 245add85a1dSArchie Cobbs NG_VERSION, 246add85a1dSArchie Cobbs NG_PPTPGRE_NODE_TYPE, 247add85a1dSArchie Cobbs NULL, 248add85a1dSArchie Cobbs ng_pptpgre_constructor, 249add85a1dSArchie Cobbs ng_pptpgre_rcvmsg, 250add85a1dSArchie Cobbs ng_pptpgre_rmnode, 251add85a1dSArchie Cobbs ng_pptpgre_newhook, 252add85a1dSArchie Cobbs NULL, 253add85a1dSArchie Cobbs NULL, 254add85a1dSArchie Cobbs ng_pptpgre_rcvdata, 255add85a1dSArchie Cobbs ng_pptpgre_rcvdata, 256add85a1dSArchie Cobbs ng_pptpgre_disconnect, 257add85a1dSArchie Cobbs ng_pptpgre_cmdlist 258add85a1dSArchie Cobbs }; 259add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); 260add85a1dSArchie Cobbs 261add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 262add85a1dSArchie Cobbs 263add85a1dSArchie Cobbs /************************************************************************ 264add85a1dSArchie Cobbs NETGRAPH NODE STUFF 265add85a1dSArchie Cobbs ************************************************************************/ 266add85a1dSArchie Cobbs 267add85a1dSArchie Cobbs /* 268add85a1dSArchie Cobbs * Node type constructor 269add85a1dSArchie Cobbs */ 270add85a1dSArchie Cobbs static int 271add85a1dSArchie Cobbs ng_pptpgre_constructor(node_p *nodep) 272add85a1dSArchie Cobbs { 273add85a1dSArchie Cobbs priv_p priv; 274add85a1dSArchie Cobbs int error; 275add85a1dSArchie Cobbs 276add85a1dSArchie Cobbs /* Allocate private structure */ 277add85a1dSArchie Cobbs MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); 278add85a1dSArchie Cobbs if (priv == NULL) 279add85a1dSArchie Cobbs return (ENOMEM); 280add85a1dSArchie Cobbs bzero(priv, sizeof(*priv)); 281add85a1dSArchie Cobbs 282add85a1dSArchie Cobbs /* Call generic node constructor */ 283add85a1dSArchie Cobbs if ((error = ng_make_node_common(&ng_pptpgre_typestruct, nodep))) { 284add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 285add85a1dSArchie Cobbs return (error); 286add85a1dSArchie Cobbs } 287add85a1dSArchie Cobbs (*nodep)->private = priv; 288add85a1dSArchie Cobbs 289add85a1dSArchie Cobbs /* Initialize state */ 290add85a1dSArchie Cobbs callout_handle_init(&priv->ackp.sackTimer); 291add85a1dSArchie Cobbs callout_handle_init(&priv->ackp.rackTimer); 292add85a1dSArchie Cobbs 293add85a1dSArchie Cobbs /* Done */ 294add85a1dSArchie Cobbs return (0); 295add85a1dSArchie Cobbs } 296add85a1dSArchie Cobbs 297add85a1dSArchie Cobbs /* 298add85a1dSArchie Cobbs * Give our OK for a hook to be added. 299add85a1dSArchie Cobbs */ 300add85a1dSArchie Cobbs static int 301add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 302add85a1dSArchie Cobbs { 303add85a1dSArchie Cobbs const priv_p priv = node->private; 304add85a1dSArchie Cobbs hook_p *hookPtr; 305add85a1dSArchie Cobbs 306add85a1dSArchie Cobbs /* Check hook name */ 307add85a1dSArchie Cobbs if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) 308add85a1dSArchie Cobbs hookPtr = &priv->upper; 309add85a1dSArchie Cobbs else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) 310add85a1dSArchie Cobbs hookPtr = &priv->lower; 311add85a1dSArchie Cobbs else 312add85a1dSArchie Cobbs return (EINVAL); 313add85a1dSArchie Cobbs 314add85a1dSArchie Cobbs /* See if already connected */ 315add85a1dSArchie Cobbs if (*hookPtr != NULL) 316add85a1dSArchie Cobbs return (EISCONN); 317add85a1dSArchie Cobbs 318add85a1dSArchie Cobbs /* OK */ 319add85a1dSArchie Cobbs *hookPtr = hook; 320add85a1dSArchie Cobbs return (0); 321add85a1dSArchie Cobbs } 322add85a1dSArchie Cobbs 323add85a1dSArchie Cobbs /* 324add85a1dSArchie Cobbs * Receive a control message. 325add85a1dSArchie Cobbs */ 326add85a1dSArchie Cobbs static int 327add85a1dSArchie Cobbs ng_pptpgre_rcvmsg(node_p node, struct ng_mesg *msg, 328a4ec03cfSJulian Elischer const char *raddr, struct ng_mesg **rptr, hook_p lasthook) 329add85a1dSArchie Cobbs { 330add85a1dSArchie Cobbs const priv_p priv = node->private; 331add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 332add85a1dSArchie Cobbs int error = 0; 333add85a1dSArchie Cobbs 334add85a1dSArchie Cobbs switch (msg->header.typecookie) { 335add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 336add85a1dSArchie Cobbs switch (msg->header.cmd) { 337add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 338add85a1dSArchie Cobbs { 339add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 340add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 341add85a1dSArchie Cobbs 342add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 343add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 344add85a1dSArchie Cobbs ERROUT(EINVAL); 345add85a1dSArchie Cobbs ng_pptpgre_reset(node); /* reset on configure */ 346add85a1dSArchie Cobbs priv->conf = *newConf; 347add85a1dSArchie Cobbs break; 348add85a1dSArchie Cobbs } 349add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 350add85a1dSArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); 351add85a1dSArchie Cobbs if (resp == NULL) 352add85a1dSArchie Cobbs ERROUT(ENOMEM); 353add85a1dSArchie Cobbs bcopy(&priv->conf, resp->data, sizeof(priv->conf)); 354add85a1dSArchie Cobbs break; 3559bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 3569bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 3579bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 3589bee7adfSArchie Cobbs { 3599bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 3609bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 3619bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 3629bee7adfSArchie Cobbs if (resp == NULL) 3639bee7adfSArchie Cobbs ERROUT(ENOMEM); 3649bee7adfSArchie Cobbs bcopy(&priv->stats, 3659bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 3669bee7adfSArchie Cobbs } 3679bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 3689bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 3699bee7adfSArchie Cobbs break; 3709bee7adfSArchie Cobbs } 371add85a1dSArchie Cobbs default: 372add85a1dSArchie Cobbs error = EINVAL; 373add85a1dSArchie Cobbs break; 374add85a1dSArchie Cobbs } 375add85a1dSArchie Cobbs break; 376add85a1dSArchie Cobbs default: 377add85a1dSArchie Cobbs error = EINVAL; 378add85a1dSArchie Cobbs break; 379add85a1dSArchie Cobbs } 380add85a1dSArchie Cobbs if (rptr) 381add85a1dSArchie Cobbs *rptr = resp; 382add85a1dSArchie Cobbs else if (resp) 383add85a1dSArchie Cobbs FREE(resp, M_NETGRAPH); 384add85a1dSArchie Cobbs 385add85a1dSArchie Cobbs done: 386add85a1dSArchie Cobbs FREE(msg, M_NETGRAPH); 387add85a1dSArchie Cobbs return (error); 388add85a1dSArchie Cobbs } 389add85a1dSArchie Cobbs 390add85a1dSArchie Cobbs /* 391add85a1dSArchie Cobbs * Receive incoming data on a hook. 392add85a1dSArchie Cobbs */ 393add85a1dSArchie Cobbs static int 394a4ec03cfSJulian Elischer ng_pptpgre_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, 395a4ec03cfSJulian Elischer struct mbuf **ret_m, meta_p *ret_meta) 396add85a1dSArchie Cobbs { 397add85a1dSArchie Cobbs const node_p node = hook->node; 398add85a1dSArchie Cobbs const priv_p priv = node->private; 399add85a1dSArchie Cobbs 400add85a1dSArchie Cobbs /* If not configured, reject */ 401add85a1dSArchie Cobbs if (!priv->conf.enabled) { 402add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 403add85a1dSArchie Cobbs return (ENXIO); 404add85a1dSArchie Cobbs } 405add85a1dSArchie Cobbs 406add85a1dSArchie Cobbs /* Treat as xmit or recv data */ 407add85a1dSArchie Cobbs if (hook == priv->upper) 408add85a1dSArchie Cobbs return ng_pptpgre_xmit(node, m, meta); 409add85a1dSArchie Cobbs if (hook == priv->lower) 410add85a1dSArchie Cobbs return ng_pptpgre_recv(node, m, meta); 411add85a1dSArchie Cobbs panic("%s: weird hook", __FUNCTION__); 412add85a1dSArchie Cobbs } 413add85a1dSArchie Cobbs 414add85a1dSArchie Cobbs /* 415add85a1dSArchie Cobbs * Destroy node 416add85a1dSArchie Cobbs */ 417add85a1dSArchie Cobbs static int 418add85a1dSArchie Cobbs ng_pptpgre_rmnode(node_p node) 419add85a1dSArchie Cobbs { 420add85a1dSArchie Cobbs const priv_p priv = node->private; 421add85a1dSArchie Cobbs 4223cd7db22SArchie Cobbs /* Reset node */ 423add85a1dSArchie Cobbs ng_pptpgre_reset(node); 424add85a1dSArchie Cobbs 425add85a1dSArchie Cobbs /* Take down netgraph node */ 426add85a1dSArchie Cobbs node->flags |= NG_INVALID; 427add85a1dSArchie Cobbs ng_cutlinks(node); 428add85a1dSArchie Cobbs ng_unname(node); 429add85a1dSArchie Cobbs bzero(priv, sizeof(*priv)); 430add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 431add85a1dSArchie Cobbs node->private = NULL; 432add85a1dSArchie Cobbs ng_unref(node); 433add85a1dSArchie Cobbs return (0); 434add85a1dSArchie Cobbs } 435add85a1dSArchie Cobbs 436add85a1dSArchie Cobbs /* 437add85a1dSArchie Cobbs * Hook disconnection 438add85a1dSArchie Cobbs */ 439add85a1dSArchie Cobbs static int 440add85a1dSArchie Cobbs ng_pptpgre_disconnect(hook_p hook) 441add85a1dSArchie Cobbs { 442add85a1dSArchie Cobbs const node_p node = hook->node; 443add85a1dSArchie Cobbs const priv_p priv = node->private; 444add85a1dSArchie Cobbs 445add85a1dSArchie Cobbs /* Zero out hook pointer */ 446add85a1dSArchie Cobbs if (hook == priv->upper) 447add85a1dSArchie Cobbs priv->upper = NULL; 448add85a1dSArchie Cobbs else if (hook == priv->lower) 449add85a1dSArchie Cobbs priv->lower = NULL; 450add85a1dSArchie Cobbs else 451add85a1dSArchie Cobbs panic("%s: unknown hook", __FUNCTION__); 452add85a1dSArchie Cobbs 453add85a1dSArchie Cobbs /* Go away if no longer connected to anything */ 454add85a1dSArchie Cobbs if (node->numhooks == 0) 455add85a1dSArchie Cobbs ng_rmnode(node); 456add85a1dSArchie Cobbs return (0); 457add85a1dSArchie Cobbs } 458add85a1dSArchie Cobbs 459add85a1dSArchie Cobbs /************************************************************************* 460add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 461add85a1dSArchie Cobbs *************************************************************************/ 462add85a1dSArchie Cobbs 463add85a1dSArchie Cobbs /* 464add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 465add85a1dSArchie Cobbs */ 466add85a1dSArchie Cobbs static int 467add85a1dSArchie Cobbs ng_pptpgre_xmit(node_p node, struct mbuf *m, meta_p meta) 468add85a1dSArchie Cobbs { 469add85a1dSArchie Cobbs const priv_p priv = node->private; 470add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 471add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 472add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 473add85a1dSArchie Cobbs int grelen, error; 474add85a1dSArchie Cobbs 4759bee7adfSArchie Cobbs /* Check if there's data */ 4769bee7adfSArchie Cobbs if (m != NULL) { 4779bee7adfSArchie Cobbs 478add85a1dSArchie Cobbs /* Is our transmit window full? */ 4799bee7adfSArchie Cobbs if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, priv->recvAck) 4809bee7adfSArchie Cobbs >= a->xmitWin) { 4819bee7adfSArchie Cobbs priv->stats.xmitDrops++; 482add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 483add85a1dSArchie Cobbs return (ENOBUFS); 484add85a1dSArchie Cobbs } 485add85a1dSArchie Cobbs 486add85a1dSArchie Cobbs /* Sanity check frame length */ 487add85a1dSArchie Cobbs if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 4889bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 489add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 490add85a1dSArchie Cobbs return (EMSGSIZE); 491add85a1dSArchie Cobbs } 4929bee7adfSArchie Cobbs } else 4939bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 494add85a1dSArchie Cobbs 495add85a1dSArchie Cobbs /* Build GRE header */ 496add85a1dSArchie Cobbs ((u_int32_t *) gre)[0] = htonl(PPTP_INIT_VALUE); 497add85a1dSArchie Cobbs gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; 498add85a1dSArchie Cobbs gre->cid = htons(priv->conf.peerCid); 499add85a1dSArchie Cobbs 500add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 501add85a1dSArchie Cobbs if (m != NULL) { 502add85a1dSArchie Cobbs gre->hasSeq = 1; 503add85a1dSArchie Cobbs a->timeSent[priv->xmitSeq - priv->recvAck] 504add85a1dSArchie Cobbs = ng_pptpgre_time(node); 505add85a1dSArchie Cobbs priv->xmitSeq++; 506add85a1dSArchie Cobbs gre->data[0] = htonl(priv->xmitSeq); 507add85a1dSArchie Cobbs if (priv->xmitSeq == priv->recvAck + 1) 508add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 509add85a1dSArchie Cobbs } 510add85a1dSArchie Cobbs 511add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 5129bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(priv->xmitAck, priv->recvSeq) < 0) { 513add85a1dSArchie Cobbs gre->hasAck = 1; 514add85a1dSArchie Cobbs priv->xmitAck = priv->recvSeq; 515add85a1dSArchie Cobbs gre->data[gre->hasSeq] = htonl(priv->xmitAck); 5163cd7db22SArchie Cobbs a->sackTimerPtr = NULL; 517add85a1dSArchie Cobbs } 518add85a1dSArchie Cobbs 519add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 520add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 521add85a1dSArchie Cobbs if (m == NULL) { 522add85a1dSArchie Cobbs MGETHDR(m, M_DONTWAIT, MT_DATA); 523add85a1dSArchie Cobbs if (m == NULL) { 524add85a1dSArchie Cobbs NG_FREE_META(meta); 525add85a1dSArchie Cobbs return (ENOBUFS); 526add85a1dSArchie Cobbs } 527add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 528add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 529add85a1dSArchie Cobbs } else { 530add85a1dSArchie Cobbs M_PREPEND(m, grelen, M_NOWAIT); 531add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 532add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 533add85a1dSArchie Cobbs NG_FREE_META(meta); 534add85a1dSArchie Cobbs return (ENOBUFS); 535add85a1dSArchie Cobbs } 536add85a1dSArchie Cobbs } 537add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 538add85a1dSArchie Cobbs 5399bee7adfSArchie Cobbs /* Update stats */ 5409bee7adfSArchie Cobbs priv->stats.xmitPackets++; 5419bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 5429bee7adfSArchie Cobbs 543add85a1dSArchie Cobbs /* Deliver packet */ 544add85a1dSArchie Cobbs NG_SEND_DATA(error, priv->lower, m, meta); 545add85a1dSArchie Cobbs return (error); 546add85a1dSArchie Cobbs } 547add85a1dSArchie Cobbs 548add85a1dSArchie Cobbs /* 549add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 550add85a1dSArchie Cobbs */ 551add85a1dSArchie Cobbs static int 552add85a1dSArchie Cobbs ng_pptpgre_recv(node_p node, struct mbuf *m, meta_p meta) 553add85a1dSArchie Cobbs { 554add85a1dSArchie Cobbs const priv_p priv = node->private; 555add85a1dSArchie Cobbs int iphlen, grelen, extralen; 556add85a1dSArchie Cobbs struct greheader *gre; 557add85a1dSArchie Cobbs struct ip *ip; 558add85a1dSArchie Cobbs int error = 0; 559add85a1dSArchie Cobbs 5609bee7adfSArchie Cobbs /* Update stats */ 5619bee7adfSArchie Cobbs priv->stats.recvPackets++; 5629bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 5639bee7adfSArchie Cobbs 564add85a1dSArchie Cobbs /* Sanity check packet length */ 565add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { 5669bee7adfSArchie Cobbs priv->stats.recvRunts++; 567add85a1dSArchie Cobbs bad: 568add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); 569add85a1dSArchie Cobbs return (EINVAL); 570add85a1dSArchie Cobbs } 571add85a1dSArchie Cobbs 572add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 573add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 574add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 575add85a1dSArchie Cobbs NG_FREE_META(meta); 576add85a1dSArchie Cobbs return (ENOBUFS); 577add85a1dSArchie Cobbs } 578add85a1dSArchie Cobbs ip = mtod(m, struct ip *); 579add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 580add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 581add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 582add85a1dSArchie Cobbs NG_FREE_META(meta); 583add85a1dSArchie Cobbs return (ENOBUFS); 584add85a1dSArchie Cobbs } 585add85a1dSArchie Cobbs ip = mtod(m, struct ip *); 586add85a1dSArchie Cobbs } 587add85a1dSArchie Cobbs gre = (struct greheader *)((u_char *)ip + iphlen); 588add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 5899bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 5909bee7adfSArchie Cobbs priv->stats.recvRunts++; 591add85a1dSArchie Cobbs goto bad; 5929bee7adfSArchie Cobbs } 593add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 594add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 595add85a1dSArchie Cobbs NG_FREE_META(meta); 596add85a1dSArchie Cobbs return (ENOBUFS); 597add85a1dSArchie Cobbs } 598add85a1dSArchie Cobbs ip = mtod(m, struct ip *); 599add85a1dSArchie Cobbs gre = (struct greheader *)((u_char *)ip + iphlen); 600add85a1dSArchie Cobbs } 601add85a1dSArchie Cobbs 602add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 603add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 604add85a1dSArchie Cobbs - (iphlen + grelen + (u_int16_t)ntohs(gre->length)); 6059bee7adfSArchie Cobbs if (extralen < 0) { 6069bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 607add85a1dSArchie Cobbs goto bad; 6089bee7adfSArchie Cobbs } 6099bee7adfSArchie Cobbs if ((ntohl(*((u_int32_t *)gre)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) { 6109bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 611add85a1dSArchie Cobbs goto bad; 6129bee7adfSArchie Cobbs } 6139bee7adfSArchie Cobbs if (ntohs(gre->cid) != priv->conf.cid) { 6149bee7adfSArchie Cobbs priv->stats.recvBadCID++; 615add85a1dSArchie Cobbs goto bad; 6169bee7adfSArchie Cobbs } 617add85a1dSArchie Cobbs 618add85a1dSArchie Cobbs /* Look for peer ack */ 619add85a1dSArchie Cobbs if (gre->hasAck) { 620add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 621add85a1dSArchie Cobbs const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); 622add85a1dSArchie Cobbs const int index = ack - priv->recvAck - 1; 623add85a1dSArchie Cobbs const long sample = ng_pptpgre_time(node) - a->timeSent[index]; 624add85a1dSArchie Cobbs long diff; 625add85a1dSArchie Cobbs 626add85a1dSArchie Cobbs /* Sanity check ack value */ 6279bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { 6289bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 6299bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 6309bee7adfSArchie Cobbs } 6319bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) 6329bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 633add85a1dSArchie Cobbs priv->recvAck = ack; 634add85a1dSArchie Cobbs 635add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 636add85a1dSArchie Cobbs diff = sample - a->rtt; 637add85a1dSArchie Cobbs a->rtt += PPTP_ACK_ALPHA(diff); 638add85a1dSArchie Cobbs if (diff < 0) 639add85a1dSArchie Cobbs diff = -diff; 640add85a1dSArchie Cobbs a->dev += PPTP_ACK_BETA(diff - a->dev); 641e962a823SArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 642add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 643add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 644e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 645e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 646e962a823SArchie Cobbs 647e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 648add85a1dSArchie Cobbs ovbcopy(a->timeSent + index + 1, a->timeSent, 649add85a1dSArchie Cobbs sizeof(*a->timeSent) * (PPTP_XMIT_WIN - (index + 1))); 650e962a823SArchie Cobbs 651e962a823SArchie Cobbs /* If we sent an entire window, increase window size by one */ 6529bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 6539bee7adfSArchie Cobbs && a->xmitWin < PPTP_XMIT_WIN) { 654add85a1dSArchie Cobbs a->xmitWin++; 655add85a1dSArchie Cobbs a->winAck = ack + a->xmitWin; 656add85a1dSArchie Cobbs } 657add85a1dSArchie Cobbs 6589bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 659add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node); 660add85a1dSArchie Cobbs } 6619bee7adfSArchie Cobbs badAck: 662add85a1dSArchie Cobbs 663add85a1dSArchie Cobbs /* See if frame contains any data */ 664add85a1dSArchie Cobbs if (gre->hasSeq) { 665add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 666add85a1dSArchie Cobbs const u_int32_t seq = ntohl(gre->data[0]); 667add85a1dSArchie Cobbs 668add85a1dSArchie Cobbs /* Sanity check sequence number */ 6699bee7adfSArchie Cobbs if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { 6709bee7adfSArchie Cobbs if (seq == priv->recvSeq) 6719bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 6729bee7adfSArchie Cobbs else 6739bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 6749bee7adfSArchie Cobbs goto bad; /* out-of-order or dup */ 6759bee7adfSArchie Cobbs } 676add85a1dSArchie Cobbs priv->recvSeq = seq; 677add85a1dSArchie Cobbs 678add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 6793cd7db22SArchie Cobbs if (a->sackTimerPtr == NULL) { 6803cd7db22SArchie Cobbs long maxWait; 681add85a1dSArchie Cobbs 682add85a1dSArchie Cobbs /* Take half of the estimated round trip time */ 6833cd7db22SArchie Cobbs maxWait = (a->rtt >> 1); 684add85a1dSArchie Cobbs 685add85a1dSArchie Cobbs /* If too soon, just send one right now */ 686add85a1dSArchie Cobbs if (!priv->conf.enableDelayedAck) 687add85a1dSArchie Cobbs ng_pptpgre_xmit(node, NULL, NULL); 688add85a1dSArchie Cobbs else { /* send the ack later */ 6893cd7db22SArchie Cobbs if (maxWait > PPTP_MAX_ACK_DELAY) 6903cd7db22SArchie Cobbs maxWait = PPTP_MAX_ACK_DELAY; 6913cd7db22SArchie Cobbs ng_pptpgre_start_send_ack_timer(node, maxWait); 692add85a1dSArchie Cobbs } 693add85a1dSArchie Cobbs } 694add85a1dSArchie Cobbs 695add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 696add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 697add85a1dSArchie Cobbs if (extralen > 0) 698add85a1dSArchie Cobbs m_adj(m, -extralen); 699add85a1dSArchie Cobbs 700add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 701add85a1dSArchie Cobbs NG_SEND_DATA(error, priv->upper, m, meta); 7029bee7adfSArchie Cobbs } else { 7039bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 704add85a1dSArchie Cobbs NG_FREE_DATA(m, meta); /* no data to deliver */ 7059bee7adfSArchie Cobbs } 706add85a1dSArchie Cobbs return (error); 707add85a1dSArchie Cobbs } 708add85a1dSArchie Cobbs 709add85a1dSArchie Cobbs /************************************************************************* 710add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 711add85a1dSArchie Cobbs *************************************************************************/ 712add85a1dSArchie Cobbs 713add85a1dSArchie Cobbs /* 7149bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 715add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 716add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 717add85a1dSArchie Cobbs * recv ack timer, if any. 718add85a1dSArchie Cobbs */ 719add85a1dSArchie Cobbs static void 720add85a1dSArchie Cobbs ng_pptpgre_start_recv_ack_timer(node_p node) 721add85a1dSArchie Cobbs { 722add85a1dSArchie Cobbs const priv_p priv = node->private; 723add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 724add85a1dSArchie Cobbs int remain; 725add85a1dSArchie Cobbs 7263cd7db22SArchie Cobbs /* "Stop" current recv ack timer, if any */ 7273cd7db22SArchie Cobbs a->rackTimerPtr = NULL; 7289bee7adfSArchie Cobbs 7299bee7adfSArchie Cobbs /* Are we waiting for an acknowlegement? */ 730add85a1dSArchie Cobbs if (priv->recvAck == priv->xmitSeq) 731add85a1dSArchie Cobbs return; 732add85a1dSArchie Cobbs 733add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 734add85a1dSArchie Cobbs and reset the timer to that time. */ 735add85a1dSArchie Cobbs remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); 736add85a1dSArchie Cobbs if (remain < 0) 737add85a1dSArchie Cobbs remain = 0; 7389bee7adfSArchie Cobbs 7393cd7db22SArchie Cobbs /* Start new timer */ 7403cd7db22SArchie Cobbs MALLOC(a->rackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); 7413cd7db22SArchie Cobbs if (a->rackTimerPtr == NULL) 7423cd7db22SArchie Cobbs return; /* XXX potential hang here */ 7433cd7db22SArchie Cobbs *a->rackTimerPtr = node; /* insures the correct timeout event */ 7449bee7adfSArchie Cobbs node->refs++; 7453cd7db22SArchie Cobbs a->rackTimer = timeout(ng_pptpgre_recv_ack_timeout, 7463cd7db22SArchie Cobbs a->rackTimerPtr, remain * hz / PPTP_TIME_SCALE); 747add85a1dSArchie Cobbs } 748add85a1dSArchie Cobbs 749add85a1dSArchie Cobbs /* 750add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 751add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 752add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 753add85a1dSArchie Cobbs */ 754add85a1dSArchie Cobbs static void 755add85a1dSArchie Cobbs ng_pptpgre_recv_ack_timeout(void *arg) 756add85a1dSArchie Cobbs { 7574164c447SArchie Cobbs int s = splnet(); 7583cd7db22SArchie Cobbs const node_p node = *((node_p *)arg); 759add85a1dSArchie Cobbs const priv_p priv = node->private; 760add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 761add85a1dSArchie Cobbs 7623cd7db22SArchie Cobbs /* This complicated stuff is needed to avoid race conditions */ 7633cd7db22SArchie Cobbs FREE(arg, M_NETGRAPH); 7643cd7db22SArchie Cobbs KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__)); 7653cd7db22SArchie Cobbs if ((node->flags & NG_INVALID) != 0) { /* shutdown race condition */ 7669bee7adfSArchie Cobbs ng_unref(node); 7679bee7adfSArchie Cobbs splx(s); 7689bee7adfSArchie Cobbs return; 7699bee7adfSArchie Cobbs } 7709bee7adfSArchie Cobbs ng_unref(node); 7713cd7db22SArchie Cobbs if (arg != a->rackTimerPtr) { /* timer stopped race condition */ 7723cd7db22SArchie Cobbs splx(s); 7733cd7db22SArchie Cobbs return; 7743cd7db22SArchie Cobbs } 7753cd7db22SArchie Cobbs a->rackTimerPtr = NULL; 7769bee7adfSArchie Cobbs 777add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 7789bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 779add85a1dSArchie Cobbs a->rtt = PPTP_ACK_DELTA(a->rtt); 780add85a1dSArchie Cobbs a->ato = a->rtt + PPTP_ACK_CHI(a->dev); 781add85a1dSArchie Cobbs if (a->ato > PPTP_MAX_TIMEOUT) 782add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 783e962a823SArchie Cobbs if (a->ato < PPTP_MIN_TIMEOUT) 784e962a823SArchie Cobbs a->ato = PPTP_MIN_TIMEOUT; 785add85a1dSArchie Cobbs 786e962a823SArchie Cobbs /* Reset ack and sliding window */ 787e962a823SArchie Cobbs priv->recvAck = priv->xmitSeq; /* pretend we got the ack */ 788e962a823SArchie Cobbs a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ 789e962a823SArchie Cobbs a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ 7904164c447SArchie Cobbs splx(s); 791add85a1dSArchie Cobbs } 792add85a1dSArchie Cobbs 793add85a1dSArchie Cobbs /* 7949bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 7959bee7adfSArchie Cobbs * already running. 7969bee7adfSArchie Cobbs */ 7979bee7adfSArchie Cobbs static void 7989bee7adfSArchie Cobbs ng_pptpgre_start_send_ack_timer(node_p node, long ackTimeout) 7999bee7adfSArchie Cobbs { 8009bee7adfSArchie Cobbs const priv_p priv = node->private; 8019bee7adfSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 8029bee7adfSArchie Cobbs 8033cd7db22SArchie Cobbs /* Start new timer */ 8043cd7db22SArchie Cobbs KASSERT(a->sackTimerPtr == NULL, ("%s: sackTimer", __FUNCTION__)); 8053cd7db22SArchie Cobbs MALLOC(a->sackTimerPtr, node_p *, sizeof(node_p), M_NETGRAPH, M_NOWAIT); 8063cd7db22SArchie Cobbs if (a->sackTimerPtr == NULL) 8073cd7db22SArchie Cobbs return; /* XXX potential hang here */ 8083cd7db22SArchie Cobbs *a->sackTimerPtr = node; 8099bee7adfSArchie Cobbs node->refs++; 8103cd7db22SArchie Cobbs a->sackTimer = timeout(ng_pptpgre_send_ack_timeout, 8113cd7db22SArchie Cobbs a->sackTimerPtr, ackTimeout * hz / PPTP_TIME_SCALE); 8129bee7adfSArchie Cobbs } 8139bee7adfSArchie Cobbs 8149bee7adfSArchie Cobbs /* 815add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 816add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 817add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 818add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 819add85a1dSArchie Cobbs */ 820add85a1dSArchie Cobbs static void 821add85a1dSArchie Cobbs ng_pptpgre_send_ack_timeout(void *arg) 822add85a1dSArchie Cobbs { 8234164c447SArchie Cobbs int s = splnet(); 8243cd7db22SArchie Cobbs const node_p node = *((node_p *)arg); 825add85a1dSArchie Cobbs const priv_p priv = node->private; 826add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 827add85a1dSArchie Cobbs 8283cd7db22SArchie Cobbs /* This complicated stuff is needed to avoid race conditions */ 8293cd7db22SArchie Cobbs FREE(arg, M_NETGRAPH); 8303cd7db22SArchie Cobbs KASSERT(node->refs > 0, ("%s: no refs", __FUNCTION__)); 8313cd7db22SArchie Cobbs if ((node->flags & NG_INVALID) != 0) { /* shutdown race condition */ 8329bee7adfSArchie Cobbs ng_unref(node); 8339bee7adfSArchie Cobbs splx(s); 8349bee7adfSArchie Cobbs return; 8359bee7adfSArchie Cobbs } 8369bee7adfSArchie Cobbs ng_unref(node); 8373cd7db22SArchie Cobbs if (a->sackTimerPtr != arg) { /* timer stopped race condition */ 8383cd7db22SArchie Cobbs splx(s); 8393cd7db22SArchie Cobbs return; 8403cd7db22SArchie Cobbs } 8413cd7db22SArchie Cobbs a->sackTimerPtr = NULL; 8429bee7adfSArchie Cobbs 8439bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 844add85a1dSArchie Cobbs ng_pptpgre_xmit(node, NULL, NULL); 8454164c447SArchie Cobbs splx(s); 846add85a1dSArchie Cobbs } 847add85a1dSArchie Cobbs 848add85a1dSArchie Cobbs /************************************************************************* 849add85a1dSArchie Cobbs MISC FUNCTIONS 850add85a1dSArchie Cobbs *************************************************************************/ 851add85a1dSArchie Cobbs 852add85a1dSArchie Cobbs /* 853add85a1dSArchie Cobbs * Reset state 854add85a1dSArchie Cobbs */ 855add85a1dSArchie Cobbs static void 856add85a1dSArchie Cobbs ng_pptpgre_reset(node_p node) 857add85a1dSArchie Cobbs { 858add85a1dSArchie Cobbs const priv_p priv = node->private; 859add85a1dSArchie Cobbs struct ng_pptpgre_ackp *const a = &priv->ackp; 860add85a1dSArchie Cobbs 861add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 862add85a1dSArchie Cobbs a->ato = PPTP_MAX_TIMEOUT; 863add85a1dSArchie Cobbs a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ 864add85a1dSArchie Cobbs if (a->rtt < PPTP_MIN_RTT) 865add85a1dSArchie Cobbs a->rtt = PPTP_MIN_RTT; 866add85a1dSArchie Cobbs a->dev = 0; 867add85a1dSArchie Cobbs a->xmitWin = (priv->conf.recvWin + 1) / 2; 868e962a823SArchie Cobbs if (a->xmitWin < 2) /* often the first packet is lost */ 869e962a823SArchie Cobbs a->xmitWin = 2; /* because the peer isn't ready */ 870add85a1dSArchie Cobbs if (a->xmitWin > PPTP_XMIT_WIN) 871add85a1dSArchie Cobbs a->xmitWin = PPTP_XMIT_WIN; 872add85a1dSArchie Cobbs a->winAck = a->xmitWin; 873add85a1dSArchie Cobbs 874add85a1dSArchie Cobbs /* Reset sequence numbers */ 8753cd7db22SArchie Cobbs priv->recvSeq = ~0; 8763cd7db22SArchie Cobbs priv->recvAck = ~0; 8773cd7db22SArchie Cobbs priv->xmitSeq = ~0; 8783cd7db22SArchie Cobbs priv->xmitAck = ~0; 879add85a1dSArchie Cobbs 880add85a1dSArchie Cobbs /* Reset start time */ 8819bee7adfSArchie Cobbs getmicrouptime(&priv->startTime); 8829bee7adfSArchie Cobbs 8839bee7adfSArchie Cobbs /* Reset stats */ 8849bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 885add85a1dSArchie Cobbs 8863cd7db22SArchie Cobbs /* "Stop" timers */ 8873cd7db22SArchie Cobbs a->sackTimerPtr = NULL; 8883cd7db22SArchie Cobbs a->rackTimerPtr = NULL; 889add85a1dSArchie Cobbs } 890add85a1dSArchie Cobbs 891add85a1dSArchie Cobbs /* 892add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 893add85a1dSArchie Cobbs */ 894add85a1dSArchie Cobbs static pptptime_t 895add85a1dSArchie Cobbs ng_pptpgre_time(node_p node) 896add85a1dSArchie Cobbs { 897add85a1dSArchie Cobbs const priv_p priv = node->private; 898add85a1dSArchie Cobbs struct timeval tv; 899add85a1dSArchie Cobbs 9009bee7adfSArchie Cobbs getmicrouptime(&tv); 901add85a1dSArchie Cobbs if (tv.tv_sec < priv->startTime.tv_sec 902add85a1dSArchie Cobbs || (tv.tv_sec == priv->startTime.tv_sec 903add85a1dSArchie Cobbs && tv.tv_usec < priv->startTime.tv_usec)) 904add85a1dSArchie Cobbs return (0); 905add85a1dSArchie Cobbs timevalsub(&tv, &priv->startTime); 906add85a1dSArchie Cobbs tv.tv_sec *= PPTP_TIME_SCALE; 907add85a1dSArchie Cobbs tv.tv_usec /= 1000000 / PPTP_TIME_SCALE; 908add85a1dSArchie Cobbs return(tv.tv_sec + tv.tv_usec); 909add85a1dSArchie Cobbs } 910add85a1dSArchie Cobbs 911