1add85a1dSArchie Cobbs /* 2add85a1dSArchie Cobbs * ng_pptpgre.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 6add85a1dSArchie Cobbs * Copyright (c) 1996-1999 Whistle Communications, Inc. 7add85a1dSArchie Cobbs * All rights reserved. 8add85a1dSArchie Cobbs * 9add85a1dSArchie Cobbs * Subject to the following obligations and disclaimer of warranty, use and 10add85a1dSArchie Cobbs * redistribution of this software, in source or object code forms, with or 11add85a1dSArchie Cobbs * without modifications are expressly permitted by Whistle Communications; 12add85a1dSArchie Cobbs * provided, however, that: 13add85a1dSArchie Cobbs * 1. Any and all reproductions of the source or object code must include the 14add85a1dSArchie Cobbs * copyright notice above and the following disclaimer of warranties; and 15add85a1dSArchie Cobbs * 2. No rights are granted, in any manner or form, to use Whistle 16add85a1dSArchie Cobbs * Communications, Inc. trademarks, including the mark "WHISTLE 17add85a1dSArchie Cobbs * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 18add85a1dSArchie Cobbs * such appears in the above copyright notice or in the software. 19add85a1dSArchie Cobbs * 20add85a1dSArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 21add85a1dSArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 22add85a1dSArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 23add85a1dSArchie Cobbs * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 24add85a1dSArchie Cobbs * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 25add85a1dSArchie Cobbs * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 26add85a1dSArchie Cobbs * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 27add85a1dSArchie Cobbs * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 28add85a1dSArchie Cobbs * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 29add85a1dSArchie Cobbs * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 30add85a1dSArchie Cobbs * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31add85a1dSArchie Cobbs * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32add85a1dSArchie Cobbs * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33add85a1dSArchie Cobbs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34add85a1dSArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35add85a1dSArchie Cobbs * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 36add85a1dSArchie Cobbs * OF SUCH DAMAGE. 37add85a1dSArchie Cobbs * 38cc3bbd68SJulian Elischer * Author: Archie Cobbs <archie@freebsd.org> 39add85a1dSArchie Cobbs * 40add85a1dSArchie Cobbs * $FreeBSD$ 41add85a1dSArchie Cobbs * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $ 42add85a1dSArchie Cobbs */ 43add85a1dSArchie Cobbs 44add85a1dSArchie Cobbs /* 45add85a1dSArchie Cobbs * PPTP/GRE netgraph node type. 46add85a1dSArchie Cobbs * 47add85a1dSArchie Cobbs * This node type does the GRE encapsulation as specified for the PPTP 48add85a1dSArchie Cobbs * protocol (RFC 2637, section 4). This includes sequencing and 49add85a1dSArchie Cobbs * retransmission of frames, but not the actual packet delivery nor 50add85a1dSArchie Cobbs * any of the TCP control stream protocol. 51add85a1dSArchie Cobbs * 52add85a1dSArchie Cobbs * The "upper" hook of this node is suitable for attaching to a "ppp" 53add85a1dSArchie Cobbs * node link hook. The "lower" hook of this node is suitable for attaching 54add85a1dSArchie Cobbs * to a "ksocket" node on hook "inet/raw/gre". 55add85a1dSArchie Cobbs */ 56add85a1dSArchie Cobbs 57add85a1dSArchie Cobbs #include <sys/param.h> 58add85a1dSArchie Cobbs #include <sys/systm.h> 59add85a1dSArchie Cobbs #include <sys/kernel.h> 60add85a1dSArchie Cobbs #include <sys/time.h> 61f2ba84d7SGleb Smirnoff #include <sys/lock.h> 62add85a1dSArchie Cobbs #include <sys/malloc.h> 63f2ba84d7SGleb Smirnoff #include <sys/mbuf.h> 64f2ba84d7SGleb Smirnoff #include <sys/mutex.h> 6538f2d636SAlexander Motin #include <sys/endian.h> 66add85a1dSArchie Cobbs #include <sys/errno.h> 67add85a1dSArchie Cobbs 68add85a1dSArchie Cobbs #include <netinet/in.h> 69add85a1dSArchie Cobbs #include <netinet/in_systm.h> 70add85a1dSArchie Cobbs #include <netinet/ip.h> 71add85a1dSArchie Cobbs 72add85a1dSArchie Cobbs #include <netgraph/ng_message.h> 73add85a1dSArchie Cobbs #include <netgraph/netgraph.h> 74add85a1dSArchie Cobbs #include <netgraph/ng_parse.h> 75add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h> 76add85a1dSArchie Cobbs 77add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */ 78add85a1dSArchie Cobbs struct greheader { 79add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN 80add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 81add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 82add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 83add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 84add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 85add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 86add85a1dSArchie Cobbs u_char vers:3; /* version */ 87add85a1dSArchie Cobbs u_char flags:4; /* flags */ 88add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 89add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN 90add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 91add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 92add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 93add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 94add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 95add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 96add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 97add85a1dSArchie Cobbs u_char flags:4; /* flags */ 98add85a1dSArchie Cobbs u_char vers:3; /* version */ 99add85a1dSArchie Cobbs #else 100add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly 101add85a1dSArchie Cobbs #endif 102add85a1dSArchie Cobbs u_int16_t proto; /* protocol (ethertype) */ 103add85a1dSArchie Cobbs u_int16_t length; /* payload length */ 104add85a1dSArchie Cobbs u_int16_t cid; /* call id */ 105add85a1dSArchie Cobbs u_int32_t data[0]; /* opt. seq, ack, then data */ 106add85a1dSArchie Cobbs }; 107add85a1dSArchie Cobbs 108add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */ 109add85a1dSArchie Cobbs #define PPTP_GRE_PROTO 0x880b 110add85a1dSArchie Cobbs 111add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */ 112add85a1dSArchie Cobbs #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) 113add85a1dSArchie Cobbs #define PPTP_INIT_MASK 0xef7fffff 114add85a1dSArchie Cobbs 115add85a1dSArchie Cobbs /* Min and max packet length */ 116add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8) 117add85a1dSArchie Cobbs 118add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */ 119714f558bSAlexander Motin #define PPTP_TIME_SCALE 1024 /* milliseconds */ 120678f9e33SArchie Cobbs typedef u_int64_t pptptime_t; 121add85a1dSArchie Cobbs 122add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */ 123e962a823SArchie Cobbs #define PPTP_XMIT_WIN 16 /* max xmit window */ 1244a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 83) /* 12 milliseconds */ 1250306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT (3 * PPTP_TIME_SCALE) /* 3 seconds */ 126add85a1dSArchie Cobbs 127da010626SArchie Cobbs /* When we recieve a packet, we wait to see if there's an outgoing packet 128da010626SArchie Cobbs we can piggy-back the ACK off of. These parameters determine the mimimum 129da010626SArchie Cobbs and maxmimum length of time we're willing to wait in order to do that. 130da010626SArchie Cobbs These have no effect unless "enableDelayedAck" is turned on. */ 131da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */ 132da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */ 133da010626SArchie Cobbs 134e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */ 135f8e159d6SGleb Smirnoff #define PPTP_ACK_ALPHA(x) (((x) + 4) >> 3) /* alpha = 0.125 */ 136f8e159d6SGleb Smirnoff #define PPTP_ACK_BETA(x) (((x) + 2) >> 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 142489290e9SAlexander Motin #define SESSHASHSIZE 0x0020 143489290e9SAlexander Motin #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1)) 144489290e9SAlexander Motin 145add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */ 146489290e9SAlexander Motin struct ng_pptpgre_sess { 147489290e9SAlexander Motin node_p node; /* this node pointer */ 148489290e9SAlexander Motin hook_p hook; /* hook to upper layers */ 149489290e9SAlexander Motin struct ng_pptpgre_conf conf; /* configuration info */ 150489290e9SAlexander Motin struct mtx mtx; /* session mutex */ 151489290e9SAlexander Motin u_int32_t recvSeq; /* last seq # we rcv'd */ 152489290e9SAlexander Motin u_int32_t xmitSeq; /* last seq # we sent */ 153489290e9SAlexander Motin u_int32_t recvAck; /* last seq # peer ack'd */ 154489290e9SAlexander Motin u_int32_t xmitAck; /* last seq # we ack'd */ 155add85a1dSArchie Cobbs int32_t ato; /* adaptive time-out value */ 156add85a1dSArchie Cobbs int32_t rtt; /* round trip time estimate */ 157add85a1dSArchie Cobbs int32_t dev; /* deviation estimate */ 158add85a1dSArchie Cobbs u_int16_t xmitWin; /* size of xmit window */ 1594a48abb2SArchie Cobbs struct callout sackTimer; /* send ack timer */ 1604a48abb2SArchie Cobbs struct callout rackTimer; /* recv ack timer */ 1613cd7db22SArchie Cobbs u_int32_t winAck; /* seq when xmitWin will grow */ 162add85a1dSArchie Cobbs pptptime_t timeSent[PPTP_XMIT_WIN]; 163489290e9SAlexander Motin LIST_ENTRY(ng_pptpgre_sess) sessions; 164add85a1dSArchie Cobbs }; 165489290e9SAlexander Motin typedef struct ng_pptpgre_sess *hpriv_p; 166add85a1dSArchie Cobbs 167add85a1dSArchie Cobbs /* Node private data */ 168add85a1dSArchie Cobbs struct ng_pptpgre_private { 169add85a1dSArchie Cobbs hook_p upper; /* hook to upper layers */ 170add85a1dSArchie Cobbs hook_p lower; /* hook to lower layers */ 171489290e9SAlexander Motin struct ng_pptpgre_sess uppersess; /* default session for compat */ 172489290e9SAlexander Motin LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE]; 1739bee7adfSArchie Cobbs struct ng_pptpgre_stats stats; /* node statistics */ 174add85a1dSArchie Cobbs }; 175add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p; 176add85a1dSArchie Cobbs 177add85a1dSArchie Cobbs /* Netgraph node methods */ 178add85a1dSArchie Cobbs static ng_constructor_t ng_pptpgre_constructor; 179add85a1dSArchie Cobbs static ng_rcvmsg_t ng_pptpgre_rcvmsg; 180069154d5SJulian Elischer static ng_shutdown_t ng_pptpgre_shutdown; 181add85a1dSArchie Cobbs static ng_newhook_t ng_pptpgre_newhook; 182add85a1dSArchie Cobbs static ng_rcvdata_t ng_pptpgre_rcvdata; 183489290e9SAlexander Motin static ng_rcvdata_t ng_pptpgre_rcvdata_lower; 184add85a1dSArchie Cobbs static ng_disconnect_t ng_pptpgre_disconnect; 185add85a1dSArchie Cobbs 186add85a1dSArchie Cobbs /* Helper functions */ 187489290e9SAlexander Motin static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item); 188714f558bSAlexander Motin static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv); 189489290e9SAlexander Motin static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv); 190089323f3SGleb Smirnoff static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, 191089323f3SGleb Smirnoff void *arg1, int arg2); 192089323f3SGleb Smirnoff static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, 193089323f3SGleb Smirnoff void *arg1, int arg2); 194489290e9SAlexander Motin static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid); 195489290e9SAlexander Motin static void ng_pptpgre_reset(hpriv_p hpriv); 196489290e9SAlexander Motin static pptptime_t ng_pptpgre_time(void); 197add85a1dSArchie Cobbs 198add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */ 199f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[] 200f0184ff8SArchie Cobbs = NG_PPTPGRE_CONF_TYPE_INFO; 201add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = { 202add85a1dSArchie Cobbs &ng_parse_struct_type, 203f0184ff8SArchie Cobbs &ng_pptpgre_conf_type_fields, 204add85a1dSArchie Cobbs }; 205add85a1dSArchie Cobbs 2069bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */ 207f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[] 208f0184ff8SArchie Cobbs = NG_PPTPGRE_STATS_TYPE_INFO; 2099bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = { 2109bee7adfSArchie Cobbs &ng_parse_struct_type, 211f0184ff8SArchie Cobbs &ng_pptpgre_stats_type_fields 2129bee7adfSArchie Cobbs }; 2139bee7adfSArchie Cobbs 214add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 215add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { 216add85a1dSArchie Cobbs { 217add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 218add85a1dSArchie Cobbs NGM_PPTPGRE_SET_CONFIG, 219add85a1dSArchie Cobbs "setconfig", 220add85a1dSArchie Cobbs &ng_pptpgre_conf_type, 221add85a1dSArchie Cobbs NULL 222add85a1dSArchie Cobbs }, 223add85a1dSArchie Cobbs { 224add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 225add85a1dSArchie Cobbs NGM_PPTPGRE_GET_CONFIG, 226add85a1dSArchie Cobbs "getconfig", 227489290e9SAlexander Motin &ng_parse_hint16_type, 228add85a1dSArchie Cobbs &ng_pptpgre_conf_type 229add85a1dSArchie Cobbs }, 2309bee7adfSArchie Cobbs { 2319bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2329bee7adfSArchie Cobbs NGM_PPTPGRE_GET_STATS, 2339bee7adfSArchie Cobbs "getstats", 2349bee7adfSArchie Cobbs NULL, 2359bee7adfSArchie Cobbs &ng_pptp_stats_type 2369bee7adfSArchie Cobbs }, 2379bee7adfSArchie Cobbs { 2389bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2399bee7adfSArchie Cobbs NGM_PPTPGRE_CLR_STATS, 2409bee7adfSArchie Cobbs "clrstats", 2419bee7adfSArchie Cobbs NULL, 2429bee7adfSArchie Cobbs NULL 2439bee7adfSArchie Cobbs }, 2449bee7adfSArchie Cobbs { 2459bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2469bee7adfSArchie Cobbs NGM_PPTPGRE_GETCLR_STATS, 2479bee7adfSArchie Cobbs "getclrstats", 2489bee7adfSArchie Cobbs NULL, 2499bee7adfSArchie Cobbs &ng_pptp_stats_type 2509bee7adfSArchie Cobbs }, 251add85a1dSArchie Cobbs { 0 } 252add85a1dSArchie Cobbs }; 253add85a1dSArchie Cobbs 254add85a1dSArchie Cobbs /* Node type descriptor */ 255add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = { 256f8aae777SJulian Elischer .version = NG_ABI_VERSION, 257f8aae777SJulian Elischer .name = NG_PPTPGRE_NODE_TYPE, 258f8aae777SJulian Elischer .constructor = ng_pptpgre_constructor, 259f8aae777SJulian Elischer .rcvmsg = ng_pptpgre_rcvmsg, 260f8aae777SJulian Elischer .shutdown = ng_pptpgre_shutdown, 261f8aae777SJulian Elischer .newhook = ng_pptpgre_newhook, 262f8aae777SJulian Elischer .rcvdata = ng_pptpgre_rcvdata, 263f8aae777SJulian Elischer .disconnect = ng_pptpgre_disconnect, 264f8aae777SJulian Elischer .cmdlist = ng_pptpgre_cmdlist, 265add85a1dSArchie Cobbs }; 266add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); 267add85a1dSArchie Cobbs 268add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 269add85a1dSArchie Cobbs 270add85a1dSArchie Cobbs /************************************************************************ 271add85a1dSArchie Cobbs NETGRAPH NODE STUFF 272add85a1dSArchie Cobbs ************************************************************************/ 273add85a1dSArchie Cobbs 274add85a1dSArchie Cobbs /* 275add85a1dSArchie Cobbs * Node type constructor 276add85a1dSArchie Cobbs */ 277add85a1dSArchie Cobbs static int 278069154d5SJulian Elischer ng_pptpgre_constructor(node_p node) 279add85a1dSArchie Cobbs { 280add85a1dSArchie Cobbs priv_p priv; 281489290e9SAlexander Motin int i; 282add85a1dSArchie Cobbs 283add85a1dSArchie Cobbs /* Allocate private structure */ 2841ede983cSDag-Erling Smørgrav priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 285add85a1dSArchie Cobbs if (priv == NULL) 286add85a1dSArchie Cobbs return (ENOMEM); 287add85a1dSArchie Cobbs 28830400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 289add85a1dSArchie Cobbs 290add85a1dSArchie Cobbs /* Initialize state */ 291489290e9SAlexander Motin mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF); 292489290e9SAlexander Motin ng_callout_init(&priv->uppersess.sackTimer); 293489290e9SAlexander Motin ng_callout_init(&priv->uppersess.rackTimer); 294489290e9SAlexander Motin priv->uppersess.node = node; 295489290e9SAlexander Motin 296489290e9SAlexander Motin for (i = 0; i < SESSHASHSIZE; i++) 297489290e9SAlexander Motin LIST_INIT(&priv->sesshash[i]); 298489290e9SAlexander Motin 299489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions); 300add85a1dSArchie Cobbs 301add85a1dSArchie Cobbs /* Done */ 302add85a1dSArchie Cobbs return (0); 303add85a1dSArchie Cobbs } 304add85a1dSArchie Cobbs 305add85a1dSArchie Cobbs /* 306add85a1dSArchie Cobbs * Give our OK for a hook to be added. 307add85a1dSArchie Cobbs */ 308add85a1dSArchie Cobbs static int 309add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 310add85a1dSArchie Cobbs { 31130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 312add85a1dSArchie Cobbs 313add85a1dSArchie Cobbs /* Check hook name */ 314489290e9SAlexander Motin if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) { 315489290e9SAlexander Motin priv->upper = hook; 316489290e9SAlexander Motin priv->uppersess.hook = hook; 317489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, &priv->uppersess); 318489290e9SAlexander Motin } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) { 319489290e9SAlexander Motin priv->lower = hook; 320489290e9SAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower); 321489290e9SAlexander Motin } else { 322489290e9SAlexander Motin static const char hexdig[16] = "0123456789abcdef"; 323489290e9SAlexander Motin const char *hex; 324489290e9SAlexander Motin hpriv_p hpriv; 325489290e9SAlexander Motin int i, j; 326489290e9SAlexander Motin uint16_t cid, hash; 327489290e9SAlexander Motin 328489290e9SAlexander Motin /* Parse hook name to get session ID */ 329489290e9SAlexander Motin if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P, 330489290e9SAlexander Motin sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0) 331489290e9SAlexander Motin return (EINVAL); 332489290e9SAlexander Motin hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1; 333489290e9SAlexander Motin for (cid = i = 0; i < 4; i++) { 334489290e9SAlexander Motin for (j = 0; j < 16 && hex[i] != hexdig[j]; j++); 335489290e9SAlexander Motin if (j == 16) 336489290e9SAlexander Motin return (EINVAL); 337489290e9SAlexander Motin cid = (cid << 4) | j; 338489290e9SAlexander Motin } 339489290e9SAlexander Motin if (hex[i] != '\0') 340add85a1dSArchie Cobbs return (EINVAL); 341add85a1dSArchie Cobbs 342489290e9SAlexander Motin hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO); 343489290e9SAlexander Motin if (hpriv == NULL) 344489290e9SAlexander Motin return (ENOMEM); 345add85a1dSArchie Cobbs 346489290e9SAlexander Motin /* Initialize state */ 347489290e9SAlexander Motin mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF); 348489290e9SAlexander Motin ng_callout_init(&hpriv->sackTimer); 349489290e9SAlexander Motin ng_callout_init(&hpriv->rackTimer); 350489290e9SAlexander Motin hpriv->conf.cid = cid; 351489290e9SAlexander Motin hpriv->node = node; 352489290e9SAlexander Motin hpriv->hook = hook; 353489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, hpriv); 354489290e9SAlexander Motin 355489290e9SAlexander Motin hash = SESSHASH(cid); 356489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions); 357489290e9SAlexander Motin } 358489290e9SAlexander Motin 359add85a1dSArchie Cobbs return (0); 360add85a1dSArchie Cobbs } 361add85a1dSArchie Cobbs 362add85a1dSArchie Cobbs /* 363add85a1dSArchie Cobbs * Receive a control message. 364add85a1dSArchie Cobbs */ 365add85a1dSArchie Cobbs static int 366069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook) 367add85a1dSArchie Cobbs { 36830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 369add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 370add85a1dSArchie Cobbs int error = 0; 371069154d5SJulian Elischer struct ng_mesg *msg; 372add85a1dSArchie Cobbs 373069154d5SJulian Elischer NGI_GET_MSG(item, msg); 374add85a1dSArchie Cobbs switch (msg->header.typecookie) { 375add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 376add85a1dSArchie Cobbs switch (msg->header.cmd) { 377add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 378add85a1dSArchie Cobbs { 379add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 380add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 381489290e9SAlexander Motin hpriv_p hpriv; 382489290e9SAlexander Motin uint16_t hash; 383add85a1dSArchie Cobbs 384add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 385add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 386add85a1dSArchie Cobbs ERROUT(EINVAL); 387489290e9SAlexander Motin /* Try to find session by cid. */ 388489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, newConf->cid); 389489290e9SAlexander Motin /* If not present - use upper. */ 390489290e9SAlexander Motin if (hpriv == NULL) { 391489290e9SAlexander Motin hpriv = &priv->uppersess; 392489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions); 393489290e9SAlexander Motin hash = SESSHASH(newConf->cid); 394489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, 395489290e9SAlexander Motin sessions); 396489290e9SAlexander Motin } 397489290e9SAlexander Motin ng_pptpgre_reset(hpriv); /* reset on configure */ 398489290e9SAlexander Motin hpriv->conf = *newConf; 399add85a1dSArchie Cobbs break; 400add85a1dSArchie Cobbs } 401add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 402489290e9SAlexander Motin { 403489290e9SAlexander Motin hpriv_p hpriv; 404489290e9SAlexander Motin 405489290e9SAlexander Motin if (msg->header.arglen == 2) { 406489290e9SAlexander Motin /* Try to find session by cid. */ 407489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, 408489290e9SAlexander Motin *((uint16_t *)msg->data)); 409489290e9SAlexander Motin if (hpriv == NULL) 410489290e9SAlexander Motin ERROUT(EINVAL); 411489290e9SAlexander Motin } else if (msg->header.arglen == 0) { 412489290e9SAlexander Motin /* Use upper. */ 413489290e9SAlexander Motin hpriv = &priv->uppersess; 414489290e9SAlexander Motin } else 415489290e9SAlexander Motin ERROUT(EINVAL); 416489290e9SAlexander Motin NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT); 417add85a1dSArchie Cobbs if (resp == NULL) 418add85a1dSArchie Cobbs ERROUT(ENOMEM); 419489290e9SAlexander Motin bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf)); 420add85a1dSArchie Cobbs break; 421489290e9SAlexander Motin } 4229bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 4239bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 4249bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 4259bee7adfSArchie Cobbs { 4269bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 4279bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 4289bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 4299bee7adfSArchie Cobbs if (resp == NULL) 4309bee7adfSArchie Cobbs ERROUT(ENOMEM); 4319bee7adfSArchie Cobbs bcopy(&priv->stats, 4329bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 4339bee7adfSArchie Cobbs } 4349bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 4359bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 4369bee7adfSArchie Cobbs break; 4379bee7adfSArchie Cobbs } 438add85a1dSArchie Cobbs default: 439add85a1dSArchie Cobbs error = EINVAL; 440add85a1dSArchie Cobbs break; 441add85a1dSArchie Cobbs } 442add85a1dSArchie Cobbs break; 443add85a1dSArchie Cobbs default: 444add85a1dSArchie Cobbs error = EINVAL; 445add85a1dSArchie Cobbs break; 446add85a1dSArchie Cobbs } 447589f6ed8SJulian Elischer done: 448069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 449069154d5SJulian Elischer NG_FREE_MSG(msg); 450add85a1dSArchie Cobbs return (error); 451add85a1dSArchie Cobbs } 452add85a1dSArchie Cobbs 453add85a1dSArchie Cobbs /* 454add85a1dSArchie Cobbs * Receive incoming data on a hook. 455add85a1dSArchie Cobbs */ 456add85a1dSArchie Cobbs static int 457069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item) 458add85a1dSArchie Cobbs { 459489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 460f2ba84d7SGleb Smirnoff int rval; 461add85a1dSArchie Cobbs 462add85a1dSArchie Cobbs /* If not configured, reject */ 463489290e9SAlexander Motin if (!hpriv->conf.enabled) { 464069154d5SJulian Elischer NG_FREE_ITEM(item); 465add85a1dSArchie Cobbs return (ENXIO); 466add85a1dSArchie Cobbs } 467add85a1dSArchie Cobbs 468489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 469f2ba84d7SGleb Smirnoff 470489290e9SAlexander Motin rval = ng_pptpgre_xmit(hpriv, item); 471f2ba84d7SGleb Smirnoff 472489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 473f2ba84d7SGleb Smirnoff 474f2ba84d7SGleb Smirnoff return (rval); 475add85a1dSArchie Cobbs } 476add85a1dSArchie Cobbs 477add85a1dSArchie Cobbs /* 478489290e9SAlexander Motin * Hook disconnection 479489290e9SAlexander Motin */ 480489290e9SAlexander Motin static int 481489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook) 482489290e9SAlexander Motin { 483489290e9SAlexander Motin const node_p node = NG_HOOK_NODE(hook); 484489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(node); 485489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 486489290e9SAlexander Motin 487489290e9SAlexander Motin /* Zero out hook pointer */ 488489290e9SAlexander Motin if (hook == priv->upper) { 489489290e9SAlexander Motin priv->upper = NULL; 490489290e9SAlexander Motin priv->uppersess.hook = NULL; 491489290e9SAlexander Motin } else if (hook == priv->lower) { 492489290e9SAlexander Motin priv->lower = NULL; 493489290e9SAlexander Motin } else { 494489290e9SAlexander Motin /* Reset node (stops timers) */ 495489290e9SAlexander Motin ng_pptpgre_reset(hpriv); 496489290e9SAlexander Motin 497489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions); 498489290e9SAlexander Motin mtx_destroy(&hpriv->mtx); 499489290e9SAlexander Motin free(hpriv, M_NETGRAPH); 500489290e9SAlexander Motin } 501489290e9SAlexander Motin 502489290e9SAlexander Motin /* Go away if no longer connected to anything */ 503489290e9SAlexander Motin if ((NG_NODE_NUMHOOKS(node) == 0) 504489290e9SAlexander Motin && (NG_NODE_IS_VALID(node))) 505489290e9SAlexander Motin ng_rmnode_self(node); 506489290e9SAlexander Motin return (0); 507489290e9SAlexander Motin } 508489290e9SAlexander Motin 509489290e9SAlexander Motin /* 510add85a1dSArchie Cobbs * Destroy node 511add85a1dSArchie Cobbs */ 512add85a1dSArchie Cobbs static int 513069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node) 514add85a1dSArchie Cobbs { 51530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 516add85a1dSArchie Cobbs 517089323f3SGleb Smirnoff /* Reset node (stops timers) */ 518489290e9SAlexander Motin ng_pptpgre_reset(&priv->uppersess); 519add85a1dSArchie Cobbs 520489290e9SAlexander Motin LIST_REMOVE(&priv->uppersess, sessions); 521489290e9SAlexander Motin mtx_destroy(&priv->uppersess.mtx); 522f2ba84d7SGleb Smirnoff 5231ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH); 5244a48abb2SArchie Cobbs 5254a48abb2SArchie Cobbs /* Decrement ref count */ 52630400f03SJulian Elischer NG_NODE_UNREF(node); 527add85a1dSArchie Cobbs return (0); 528add85a1dSArchie Cobbs } 529add85a1dSArchie Cobbs 530add85a1dSArchie Cobbs /************************************************************************* 531add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 532add85a1dSArchie Cobbs *************************************************************************/ 533add85a1dSArchie Cobbs 534add85a1dSArchie Cobbs /* 535add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 536add85a1dSArchie Cobbs */ 537add85a1dSArchie Cobbs static int 538489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item) 539add85a1dSArchie Cobbs { 540489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(hpriv->node); 541add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 542add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 543add85a1dSArchie Cobbs int grelen, error; 544069154d5SJulian Elischer struct mbuf *m; 545add85a1dSArchie Cobbs 546489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_OWNED); 54783beeed9SGleb Smirnoff 548069154d5SJulian Elischer if (item) { 549069154d5SJulian Elischer NGI_GET_M(item, m); 550069154d5SJulian Elischer } else { 551069154d5SJulian Elischer m = NULL; 552069154d5SJulian Elischer } 5539bee7adfSArchie Cobbs /* Check if there's data */ 5549bee7adfSArchie Cobbs if (m != NULL) { 5559bee7adfSArchie Cobbs 556922ee196SArchie Cobbs /* Check if windowing is enabled */ 557489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 558add85a1dSArchie Cobbs /* Is our transmit window full? */ 559489290e9SAlexander Motin if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq, 560489290e9SAlexander Motin hpriv->recvAck) >= hpriv->xmitWin) { 5619bee7adfSArchie Cobbs priv->stats.xmitDrops++; 56283beeed9SGleb Smirnoff ERROUT(ENOBUFS); 563add85a1dSArchie Cobbs } 564922ee196SArchie Cobbs } 565add85a1dSArchie Cobbs 566add85a1dSArchie Cobbs /* Sanity check frame length */ 567add85a1dSArchie Cobbs if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 5689bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 56983beeed9SGleb Smirnoff ERROUT(EMSGSIZE); 570add85a1dSArchie Cobbs } 571069154d5SJulian Elischer } else { 5729bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 573069154d5SJulian Elischer } 574add85a1dSArchie Cobbs 575add85a1dSArchie Cobbs /* Build GRE header */ 57638f2d636SAlexander Motin be32enc(gre, PPTP_INIT_VALUE); 57738f2d636SAlexander Motin be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0); 57838f2d636SAlexander Motin be16enc(&gre->cid, hpriv->conf.peerCid); 579add85a1dSArchie Cobbs 580add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 581add85a1dSArchie Cobbs if (m != NULL) { 582add85a1dSArchie Cobbs gre->hasSeq = 1; 583489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 584489290e9SAlexander Motin hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck] 585489290e9SAlexander Motin = ng_pptpgre_time(); 586922ee196SArchie Cobbs } 587489290e9SAlexander Motin hpriv->xmitSeq++; 58838f2d636SAlexander Motin be32enc(&gre->data[0], hpriv->xmitSeq); 589add85a1dSArchie Cobbs } 590add85a1dSArchie Cobbs 591add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 592489290e9SAlexander Motin if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) { 593add85a1dSArchie Cobbs gre->hasAck = 1; 59438f2d636SAlexander Motin be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq); 595489290e9SAlexander Motin hpriv->xmitAck = hpriv->recvSeq; 596714f558bSAlexander Motin if (hpriv->conf.enableDelayedAck) 597714f558bSAlexander Motin ng_uncallout(&hpriv->sackTimer, hpriv->node); 598da010626SArchie Cobbs } 599add85a1dSArchie Cobbs 600add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 601add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 602add85a1dSArchie Cobbs if (m == NULL) { 603a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 604add85a1dSArchie Cobbs if (m == NULL) { 605678f9e33SArchie Cobbs priv->stats.memoryFailures++; 60683beeed9SGleb Smirnoff ERROUT(ENOBUFS); 607add85a1dSArchie Cobbs } 608add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 609add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 610add85a1dSArchie Cobbs } else { 611a163d034SWarner Losh M_PREPEND(m, grelen, M_DONTWAIT); 612add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 613add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 614678f9e33SArchie Cobbs priv->stats.memoryFailures++; 61583beeed9SGleb Smirnoff ERROUT(ENOBUFS); 616add85a1dSArchie Cobbs } 617add85a1dSArchie Cobbs } 618add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 619add85a1dSArchie Cobbs 6209bee7adfSArchie Cobbs /* Update stats */ 6219bee7adfSArchie Cobbs priv->stats.xmitPackets++; 6229bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 6239bee7adfSArchie Cobbs 62483beeed9SGleb Smirnoff /* 62583beeed9SGleb Smirnoff * XXX: we should reset timer only after an item has been sent 62683beeed9SGleb Smirnoff * successfully. 62783beeed9SGleb Smirnoff */ 628489290e9SAlexander Motin if (hpriv->conf.enableWindowing && 629489290e9SAlexander Motin gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1) 630489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv); 63183beeed9SGleb Smirnoff 632489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 63383beeed9SGleb Smirnoff 634add85a1dSArchie Cobbs /* Deliver packet */ 635069154d5SJulian Elischer if (item) { 636069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->lower, m); 637069154d5SJulian Elischer } else { 638069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m); 639069154d5SJulian Elischer } 640069154d5SJulian Elischer 64183beeed9SGleb Smirnoff return (error); 642678f9e33SArchie Cobbs 64383beeed9SGleb Smirnoff done: 644489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 64583beeed9SGleb Smirnoff NG_FREE_M(m); 64683beeed9SGleb Smirnoff if (item) 64783beeed9SGleb Smirnoff NG_FREE_ITEM(item); 648add85a1dSArchie Cobbs return (error); 649add85a1dSArchie Cobbs } 650add85a1dSArchie Cobbs 651add85a1dSArchie Cobbs /* 652add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 653add85a1dSArchie Cobbs */ 654add85a1dSArchie Cobbs static int 655489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item) 656add85a1dSArchie Cobbs { 657489290e9SAlexander Motin hpriv_p hpriv; 658489290e9SAlexander Motin node_p node = NG_HOOK_NODE(hook); 65930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 660add85a1dSArchie Cobbs int iphlen, grelen, extralen; 661816b834fSArchie Cobbs const struct greheader *gre; 662816b834fSArchie Cobbs const struct ip *ip; 663add85a1dSArchie Cobbs int error = 0; 664069154d5SJulian Elischer struct mbuf *m; 665add85a1dSArchie Cobbs 666069154d5SJulian Elischer NGI_GET_M(item, m); 6679bee7adfSArchie Cobbs /* Update stats */ 6689bee7adfSArchie Cobbs priv->stats.recvPackets++; 6699bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 6709bee7adfSArchie Cobbs 671add85a1dSArchie Cobbs /* Sanity check packet length */ 672add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { 6739bee7adfSArchie Cobbs priv->stats.recvRunts++; 67483beeed9SGleb Smirnoff ERROUT(EINVAL); 675add85a1dSArchie Cobbs } 676add85a1dSArchie Cobbs 677add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 678add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 679add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 680678f9e33SArchie Cobbs priv->stats.memoryFailures++; 68183beeed9SGleb Smirnoff ERROUT(ENOBUFS); 682add85a1dSArchie Cobbs } 683816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 684add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 685add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 686add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 687678f9e33SArchie Cobbs priv->stats.memoryFailures++; 68883beeed9SGleb Smirnoff ERROUT(ENOBUFS); 689add85a1dSArchie Cobbs } 690816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 691add85a1dSArchie Cobbs } 692816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 693add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 6949bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 6959bee7adfSArchie Cobbs priv->stats.recvRunts++; 69683beeed9SGleb Smirnoff ERROUT(EINVAL); 6979bee7adfSArchie Cobbs } 698add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 699add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 700678f9e33SArchie Cobbs priv->stats.memoryFailures++; 70183beeed9SGleb Smirnoff ERROUT(ENOBUFS); 702add85a1dSArchie Cobbs } 703816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 704816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 705add85a1dSArchie Cobbs } 706add85a1dSArchie Cobbs 707add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 708add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 70938f2d636SAlexander Motin - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length)); 7109bee7adfSArchie Cobbs if (extralen < 0) { 7119bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 71283beeed9SGleb Smirnoff ERROUT(EINVAL); 7139bee7adfSArchie Cobbs } 71438f2d636SAlexander Motin if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) { 7159bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 71683beeed9SGleb Smirnoff ERROUT(EINVAL); 7179bee7adfSArchie Cobbs } 718489290e9SAlexander Motin 71938f2d636SAlexander Motin hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid)); 720489290e9SAlexander Motin if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) { 7219bee7adfSArchie Cobbs priv->stats.recvBadCID++; 72283beeed9SGleb Smirnoff ERROUT(EINVAL); 7239bee7adfSArchie Cobbs } 724489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 725add85a1dSArchie Cobbs 726add85a1dSArchie Cobbs /* Look for peer ack */ 727add85a1dSArchie Cobbs if (gre->hasAck) { 72838f2d636SAlexander Motin const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]); 729489290e9SAlexander Motin const int index = ack - hpriv->recvAck - 1; 73022dfb9bdSArchie Cobbs long sample; 731add85a1dSArchie Cobbs long diff; 732add85a1dSArchie Cobbs 733add85a1dSArchie Cobbs /* Sanity check ack value */ 734489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) { 7359bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 7369bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 7379bee7adfSArchie Cobbs } 738489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0) 7399bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 740489290e9SAlexander Motin hpriv->recvAck = ack; 741add85a1dSArchie Cobbs 742add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 743489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 744489290e9SAlexander Motin sample = ng_pptpgre_time() - hpriv->timeSent[index]; 745489290e9SAlexander Motin diff = sample - hpriv->rtt; 746489290e9SAlexander Motin hpriv->rtt += PPTP_ACK_ALPHA(diff); 747add85a1dSArchie Cobbs if (diff < 0) 748add85a1dSArchie Cobbs diff = -diff; 749489290e9SAlexander Motin hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev); 750f8e159d6SGleb Smirnoff /* +2 to compensate low precision of int math */ 751489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2); 752489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT) 753489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 754489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT) 755489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT; 756e962a823SArchie Cobbs 757e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 758489290e9SAlexander Motin bcopy(hpriv->timeSent + index + 1, hpriv->timeSent, 759489290e9SAlexander Motin sizeof(*hpriv->timeSent) 760922ee196SArchie Cobbs * (PPTP_XMIT_WIN - (index + 1))); 761e962a823SArchie Cobbs 762922ee196SArchie Cobbs /* If we sent an entire window, increase window size */ 763489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0 764489290e9SAlexander Motin && hpriv->xmitWin < PPTP_XMIT_WIN) { 765489290e9SAlexander Motin hpriv->xmitWin++; 766489290e9SAlexander Motin hpriv->winAck = ack + hpriv->xmitWin; 767add85a1dSArchie Cobbs } 768add85a1dSArchie Cobbs 7699bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 770714f558bSAlexander Motin ng_uncallout(&hpriv->rackTimer, hpriv->node); 771489290e9SAlexander Motin if (hpriv->recvAck != hpriv->xmitSeq) 772489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv); 773add85a1dSArchie Cobbs } 774922ee196SArchie Cobbs } 7759bee7adfSArchie Cobbs badAck: 776add85a1dSArchie Cobbs 777add85a1dSArchie Cobbs /* See if frame contains any data */ 778add85a1dSArchie Cobbs if (gre->hasSeq) { 77938f2d636SAlexander Motin const u_int32_t seq = be32dec(&gre->data[0]); 780add85a1dSArchie Cobbs 781add85a1dSArchie Cobbs /* Sanity check sequence number */ 782489290e9SAlexander Motin if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) { 783489290e9SAlexander Motin if (seq == hpriv->recvSeq) 7849bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 7859bee7adfSArchie Cobbs else 7869bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 787489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 78883beeed9SGleb Smirnoff ERROUT(EINVAL); 7899bee7adfSArchie Cobbs } 790489290e9SAlexander Motin hpriv->recvSeq = seq; 791add85a1dSArchie Cobbs 792add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 793489290e9SAlexander Motin if (!(callout_pending(&hpriv->sackTimer))) { 794678f9e33SArchie Cobbs /* If delayed ACK is disabled, send it now */ 795489290e9SAlexander Motin if (!hpriv->conf.enableDelayedAck) { /* ack now */ 796489290e9SAlexander Motin ng_pptpgre_xmit(hpriv, NULL); 797489290e9SAlexander Motin /* ng_pptpgre_xmit() drops the mutex */ 79883beeed9SGleb Smirnoff } else { /* ack later */ 799714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv); 800489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 801add85a1dSArchie Cobbs } 802489290e9SAlexander Motin } else 803489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 804add85a1dSArchie Cobbs 805add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 806add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 807add85a1dSArchie Cobbs if (extralen > 0) 808add85a1dSArchie Cobbs m_adj(m, -extralen); 809add85a1dSArchie Cobbs 810489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 811489290e9SAlexander Motin 812add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 813489290e9SAlexander Motin NG_FWD_NEW_DATA(error, item, hpriv->hook, m); 8149bee7adfSArchie Cobbs } else { 8159bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 816489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 817069154d5SJulian Elischer NG_FREE_ITEM(item); 818069154d5SJulian Elischer NG_FREE_M(m); /* no data to deliver */ 8199bee7adfSArchie Cobbs } 82083beeed9SGleb Smirnoff 82183beeed9SGleb Smirnoff return (error); 82283beeed9SGleb Smirnoff 82383beeed9SGleb Smirnoff done: 82483beeed9SGleb Smirnoff NG_FREE_ITEM(item); 82583beeed9SGleb Smirnoff NG_FREE_M(m); 826add85a1dSArchie Cobbs return (error); 827add85a1dSArchie Cobbs } 828add85a1dSArchie Cobbs 829add85a1dSArchie Cobbs /************************************************************************* 830add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 831add85a1dSArchie Cobbs *************************************************************************/ 832add85a1dSArchie Cobbs 833add85a1dSArchie Cobbs /* 8349bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 835add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 836add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 837add85a1dSArchie Cobbs * recv ack timer, if any. 838add85a1dSArchie Cobbs */ 839add85a1dSArchie Cobbs static void 840489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv) 841add85a1dSArchie Cobbs { 842678f9e33SArchie Cobbs int remain, ticks; 843add85a1dSArchie Cobbs 844add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 845add85a1dSArchie Cobbs and reset the timer to that time. */ 846489290e9SAlexander Motin remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time(); 847add85a1dSArchie Cobbs if (remain < 0) 848add85a1dSArchie Cobbs remain = 0; 8499bee7adfSArchie Cobbs 8504a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 851678f9e33SArchie Cobbs ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; 852489290e9SAlexander Motin ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook, 853489290e9SAlexander Motin ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0); 8544a48abb2SArchie Cobbs } 8554a48abb2SArchie Cobbs 8564a48abb2SArchie Cobbs /* 857add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 858add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 859add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 860add85a1dSArchie Cobbs */ 861add85a1dSArchie Cobbs static void 862089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 863add85a1dSArchie Cobbs { 86430400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 865489290e9SAlexander Motin const hpriv_p hpriv = arg1; 8669bee7adfSArchie Cobbs 867add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 8689bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 869489290e9SAlexander Motin hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */ 870489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev); 871489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT) 872489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 873489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT) 874489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT; 875678f9e33SArchie Cobbs 876e962a823SArchie Cobbs /* Reset ack and sliding window */ 877489290e9SAlexander Motin hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */ 878489290e9SAlexander Motin hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */ 879489290e9SAlexander Motin hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */ 880add85a1dSArchie Cobbs } 881add85a1dSArchie Cobbs 882add85a1dSArchie Cobbs /* 8839bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 8849bee7adfSArchie Cobbs * already running. 8859bee7adfSArchie Cobbs */ 8869bee7adfSArchie Cobbs static void 887714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv) 8889bee7adfSArchie Cobbs { 889714f558bSAlexander Motin int ackTimeout, ticks; 890714f558bSAlexander Motin 891714f558bSAlexander Motin /* Take 1/4 of the estimated round trip time */ 892714f558bSAlexander Motin ackTimeout = (hpriv->rtt >> 2); 893714f558bSAlexander Motin if (ackTimeout < PPTP_MIN_ACK_DELAY) 894714f558bSAlexander Motin ackTimeout = PPTP_MIN_ACK_DELAY; 895714f558bSAlexander Motin else if (ackTimeout > PPTP_MAX_ACK_DELAY) 896714f558bSAlexander Motin ackTimeout = PPTP_MAX_ACK_DELAY; 8979bee7adfSArchie Cobbs 8984a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 899678f9e33SArchie Cobbs ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); 900489290e9SAlexander Motin ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook, 901489290e9SAlexander Motin ticks, ng_pptpgre_send_ack_timeout, hpriv, 0); 9024a48abb2SArchie Cobbs } 9034a48abb2SArchie Cobbs 9044a48abb2SArchie Cobbs /* 905add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 906add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 907add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 908add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 909add85a1dSArchie Cobbs */ 910add85a1dSArchie Cobbs static void 911089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 912add85a1dSArchie Cobbs { 913489290e9SAlexander Motin const hpriv_p hpriv = arg1; 91483beeed9SGleb Smirnoff 915489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 9169bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 917489290e9SAlexander Motin ng_pptpgre_xmit(hpriv, NULL); 918489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 919add85a1dSArchie Cobbs } 920add85a1dSArchie Cobbs 921add85a1dSArchie Cobbs /************************************************************************* 922add85a1dSArchie Cobbs MISC FUNCTIONS 923add85a1dSArchie Cobbs *************************************************************************/ 924add85a1dSArchie Cobbs 925add85a1dSArchie Cobbs /* 926489290e9SAlexander Motin * Find the hook with a given session ID. 927489290e9SAlexander Motin */ 928489290e9SAlexander Motin static hpriv_p 929489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid) 930489290e9SAlexander Motin { 931489290e9SAlexander Motin uint16_t hash = SESSHASH(cid); 932489290e9SAlexander Motin hpriv_p hpriv = NULL; 933489290e9SAlexander Motin 934489290e9SAlexander Motin LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) { 935489290e9SAlexander Motin if (hpriv->conf.cid == cid) 936489290e9SAlexander Motin break; 937489290e9SAlexander Motin } 938489290e9SAlexander Motin 939489290e9SAlexander Motin return (hpriv); 940489290e9SAlexander Motin } 941489290e9SAlexander Motin 942489290e9SAlexander Motin /* 943489290e9SAlexander Motin * Reset state (must be called with lock held or from writer) 944add85a1dSArchie Cobbs */ 945add85a1dSArchie Cobbs static void 946489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv) 947add85a1dSArchie Cobbs { 948add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 949489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 950714f558bSAlexander Motin hpriv->rtt = PPTP_TIME_SCALE / 10; 951714f558bSAlexander Motin if (hpriv->conf.peerPpd > 1) /* ppd = 0 treat as = 1 */ 952714f558bSAlexander Motin hpriv->rtt *= hpriv->conf.peerPpd; 953489290e9SAlexander Motin hpriv->dev = 0; 954489290e9SAlexander Motin hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2; 955489290e9SAlexander Motin if (hpriv->xmitWin < 2) /* often the first packet is lost */ 956489290e9SAlexander Motin hpriv->xmitWin = 2; /* because the peer isn't ready */ 957489290e9SAlexander Motin else if (hpriv->xmitWin > PPTP_XMIT_WIN) 958489290e9SAlexander Motin hpriv->xmitWin = PPTP_XMIT_WIN; 959489290e9SAlexander Motin hpriv->winAck = hpriv->xmitWin; 960add85a1dSArchie Cobbs 961add85a1dSArchie Cobbs /* Reset sequence numbers */ 962489290e9SAlexander Motin hpriv->recvSeq = ~0; 963489290e9SAlexander Motin hpriv->recvAck = ~0; 964489290e9SAlexander Motin hpriv->xmitSeq = ~0; 965489290e9SAlexander Motin hpriv->xmitAck = ~0; 966add85a1dSArchie Cobbs 9674a48abb2SArchie Cobbs /* Stop timers */ 968714f558bSAlexander Motin ng_uncallout(&hpriv->sackTimer, hpriv->node); 969714f558bSAlexander Motin ng_uncallout(&hpriv->rackTimer, hpriv->node); 970add85a1dSArchie Cobbs } 971add85a1dSArchie Cobbs 972add85a1dSArchie Cobbs /* 973add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 974add85a1dSArchie Cobbs */ 975add85a1dSArchie Cobbs static pptptime_t 976489290e9SAlexander Motin ng_pptpgre_time(void) 977add85a1dSArchie Cobbs { 978add85a1dSArchie Cobbs struct timeval tv; 979678f9e33SArchie Cobbs pptptime_t t; 980add85a1dSArchie Cobbs 981678f9e33SArchie Cobbs microuptime(&tv); 982678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; 983714f558bSAlexander Motin t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE); 984678f9e33SArchie Cobbs return(t); 985add85a1dSArchie Cobbs } 986