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> 65add85a1dSArchie Cobbs #include <sys/errno.h> 66add85a1dSArchie Cobbs 67add85a1dSArchie Cobbs #include <netinet/in.h> 68add85a1dSArchie Cobbs #include <netinet/in_systm.h> 69add85a1dSArchie Cobbs #include <netinet/ip.h> 70add85a1dSArchie Cobbs 71add85a1dSArchie Cobbs #include <netgraph/ng_message.h> 72add85a1dSArchie Cobbs #include <netgraph/netgraph.h> 73add85a1dSArchie Cobbs #include <netgraph/ng_parse.h> 74add85a1dSArchie Cobbs #include <netgraph/ng_pptpgre.h> 75add85a1dSArchie Cobbs 76add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */ 77add85a1dSArchie Cobbs struct greheader { 78add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN 79add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 80add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 81add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 82add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 83add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 84add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 85add85a1dSArchie Cobbs u_char vers:3; /* version */ 86add85a1dSArchie Cobbs u_char flags:4; /* flags */ 87add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 88add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN 89add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */ 90add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */ 91add85a1dSArchie Cobbs u_char hasKey:1; /* key present */ 92add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */ 93add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */ 94add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */ 95add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */ 96add85a1dSArchie Cobbs u_char flags:4; /* flags */ 97add85a1dSArchie Cobbs u_char vers:3; /* version */ 98add85a1dSArchie Cobbs #else 99add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly 100add85a1dSArchie Cobbs #endif 101add85a1dSArchie Cobbs u_int16_t proto; /* protocol (ethertype) */ 102add85a1dSArchie Cobbs u_int16_t length; /* payload length */ 103add85a1dSArchie Cobbs u_int16_t cid; /* call id */ 104add85a1dSArchie Cobbs u_int32_t data[0]; /* opt. seq, ack, then data */ 105add85a1dSArchie Cobbs }; 106add85a1dSArchie Cobbs 107add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */ 108add85a1dSArchie Cobbs #define PPTP_GRE_PROTO 0x880b 109add85a1dSArchie Cobbs 110add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */ 111add85a1dSArchie Cobbs #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO) 112add85a1dSArchie Cobbs #define PPTP_INIT_MASK 0xef7fffff 113add85a1dSArchie Cobbs 114add85a1dSArchie Cobbs /* Min and max packet length */ 115add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8) 116add85a1dSArchie Cobbs 117add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */ 118e962a823SArchie Cobbs #define PPTP_TIME_SCALE 1000 /* milliseconds */ 119678f9e33SArchie Cobbs typedef u_int64_t pptptime_t; 120add85a1dSArchie Cobbs 121add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */ 122e962a823SArchie Cobbs #define PPTP_XMIT_WIN 16 /* max xmit window */ 123e962a823SArchie Cobbs #define PPTP_MIN_RTT (PPTP_TIME_SCALE / 10) /* 100 milliseconds */ 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); 188489290e9SAlexander Motin static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv, int ackTimeout); 189489290e9SAlexander Motin static void ng_pptpgre_stop_send_ack_timer(hpriv_p hpriv); 190489290e9SAlexander Motin static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv); 191489290e9SAlexander Motin static void ng_pptpgre_stop_recv_ack_timer(hpriv_p hpriv); 192089323f3SGleb Smirnoff static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, 193089323f3SGleb Smirnoff void *arg1, int arg2); 194089323f3SGleb Smirnoff static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, 195089323f3SGleb Smirnoff void *arg1, int arg2); 196489290e9SAlexander Motin static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid); 197489290e9SAlexander Motin static void ng_pptpgre_reset(hpriv_p hpriv); 198489290e9SAlexander Motin static pptptime_t ng_pptpgre_time(void); 199add85a1dSArchie Cobbs 200add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */ 201f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[] 202f0184ff8SArchie Cobbs = NG_PPTPGRE_CONF_TYPE_INFO; 203add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = { 204add85a1dSArchie Cobbs &ng_parse_struct_type, 205f0184ff8SArchie Cobbs &ng_pptpgre_conf_type_fields, 206add85a1dSArchie Cobbs }; 207add85a1dSArchie Cobbs 2089bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */ 209f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[] 210f0184ff8SArchie Cobbs = NG_PPTPGRE_STATS_TYPE_INFO; 2119bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = { 2129bee7adfSArchie Cobbs &ng_parse_struct_type, 213f0184ff8SArchie Cobbs &ng_pptpgre_stats_type_fields 2149bee7adfSArchie Cobbs }; 2159bee7adfSArchie Cobbs 216add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 217add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { 218add85a1dSArchie Cobbs { 219add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 220add85a1dSArchie Cobbs NGM_PPTPGRE_SET_CONFIG, 221add85a1dSArchie Cobbs "setconfig", 222add85a1dSArchie Cobbs &ng_pptpgre_conf_type, 223add85a1dSArchie Cobbs NULL 224add85a1dSArchie Cobbs }, 225add85a1dSArchie Cobbs { 226add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE, 227add85a1dSArchie Cobbs NGM_PPTPGRE_GET_CONFIG, 228add85a1dSArchie Cobbs "getconfig", 229489290e9SAlexander Motin &ng_parse_hint16_type, 230add85a1dSArchie Cobbs &ng_pptpgre_conf_type 231add85a1dSArchie Cobbs }, 2329bee7adfSArchie Cobbs { 2339bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2349bee7adfSArchie Cobbs NGM_PPTPGRE_GET_STATS, 2359bee7adfSArchie Cobbs "getstats", 2369bee7adfSArchie Cobbs NULL, 2379bee7adfSArchie Cobbs &ng_pptp_stats_type 2389bee7adfSArchie Cobbs }, 2399bee7adfSArchie Cobbs { 2409bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2419bee7adfSArchie Cobbs NGM_PPTPGRE_CLR_STATS, 2429bee7adfSArchie Cobbs "clrstats", 2439bee7adfSArchie Cobbs NULL, 2449bee7adfSArchie Cobbs NULL 2459bee7adfSArchie Cobbs }, 2469bee7adfSArchie Cobbs { 2479bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE, 2489bee7adfSArchie Cobbs NGM_PPTPGRE_GETCLR_STATS, 2499bee7adfSArchie Cobbs "getclrstats", 2509bee7adfSArchie Cobbs NULL, 2519bee7adfSArchie Cobbs &ng_pptp_stats_type 2529bee7adfSArchie Cobbs }, 253add85a1dSArchie Cobbs { 0 } 254add85a1dSArchie Cobbs }; 255add85a1dSArchie Cobbs 256add85a1dSArchie Cobbs /* Node type descriptor */ 257add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = { 258f8aae777SJulian Elischer .version = NG_ABI_VERSION, 259f8aae777SJulian Elischer .name = NG_PPTPGRE_NODE_TYPE, 260f8aae777SJulian Elischer .constructor = ng_pptpgre_constructor, 261f8aae777SJulian Elischer .rcvmsg = ng_pptpgre_rcvmsg, 262f8aae777SJulian Elischer .shutdown = ng_pptpgre_shutdown, 263f8aae777SJulian Elischer .newhook = ng_pptpgre_newhook, 264f8aae777SJulian Elischer .rcvdata = ng_pptpgre_rcvdata, 265f8aae777SJulian Elischer .disconnect = ng_pptpgre_disconnect, 266f8aae777SJulian Elischer .cmdlist = ng_pptpgre_cmdlist, 267add85a1dSArchie Cobbs }; 268add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct); 269add85a1dSArchie Cobbs 270add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0) 271add85a1dSArchie Cobbs 272add85a1dSArchie Cobbs /************************************************************************ 273add85a1dSArchie Cobbs NETGRAPH NODE STUFF 274add85a1dSArchie Cobbs ************************************************************************/ 275add85a1dSArchie Cobbs 276add85a1dSArchie Cobbs /* 277add85a1dSArchie Cobbs * Node type constructor 278add85a1dSArchie Cobbs */ 279add85a1dSArchie Cobbs static int 280069154d5SJulian Elischer ng_pptpgre_constructor(node_p node) 281add85a1dSArchie Cobbs { 282add85a1dSArchie Cobbs priv_p priv; 283489290e9SAlexander Motin int i; 284add85a1dSArchie Cobbs 285add85a1dSArchie Cobbs /* Allocate private structure */ 28699cdf4ccSDavid Malone MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 287add85a1dSArchie Cobbs if (priv == NULL) 288add85a1dSArchie Cobbs return (ENOMEM); 289add85a1dSArchie Cobbs 29030400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 291add85a1dSArchie Cobbs 292add85a1dSArchie Cobbs /* Initialize state */ 293489290e9SAlexander Motin mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF); 294489290e9SAlexander Motin ng_callout_init(&priv->uppersess.sackTimer); 295489290e9SAlexander Motin ng_callout_init(&priv->uppersess.rackTimer); 296489290e9SAlexander Motin priv->uppersess.node = node; 297489290e9SAlexander Motin 298489290e9SAlexander Motin for (i = 0; i < SESSHASHSIZE; i++) 299489290e9SAlexander Motin LIST_INIT(&priv->sesshash[i]); 300489290e9SAlexander Motin 301489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions); 302add85a1dSArchie Cobbs 303add85a1dSArchie Cobbs /* Done */ 304add85a1dSArchie Cobbs return (0); 305add85a1dSArchie Cobbs } 306add85a1dSArchie Cobbs 307add85a1dSArchie Cobbs /* 308add85a1dSArchie Cobbs * Give our OK for a hook to be added. 309add85a1dSArchie Cobbs */ 310add85a1dSArchie Cobbs static int 311add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 312add85a1dSArchie Cobbs { 31330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 314add85a1dSArchie Cobbs 315add85a1dSArchie Cobbs /* Check hook name */ 316489290e9SAlexander Motin if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) { 317489290e9SAlexander Motin priv->upper = hook; 318489290e9SAlexander Motin priv->uppersess.hook = hook; 319489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, &priv->uppersess); 320489290e9SAlexander Motin } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) { 321489290e9SAlexander Motin priv->lower = hook; 322489290e9SAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower); 323489290e9SAlexander Motin } else { 324489290e9SAlexander Motin static const char hexdig[16] = "0123456789abcdef"; 325489290e9SAlexander Motin const char *hex; 326489290e9SAlexander Motin hpriv_p hpriv; 327489290e9SAlexander Motin int i, j; 328489290e9SAlexander Motin uint16_t cid, hash; 329489290e9SAlexander Motin 330489290e9SAlexander Motin /* Parse hook name to get session ID */ 331489290e9SAlexander Motin if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P, 332489290e9SAlexander Motin sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0) 333489290e9SAlexander Motin return (EINVAL); 334489290e9SAlexander Motin hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1; 335489290e9SAlexander Motin for (cid = i = 0; i < 4; i++) { 336489290e9SAlexander Motin for (j = 0; j < 16 && hex[i] != hexdig[j]; j++); 337489290e9SAlexander Motin if (j == 16) 338489290e9SAlexander Motin return (EINVAL); 339489290e9SAlexander Motin cid = (cid << 4) | j; 340489290e9SAlexander Motin } 341489290e9SAlexander Motin if (hex[i] != '\0') 342add85a1dSArchie Cobbs return (EINVAL); 343add85a1dSArchie Cobbs 344489290e9SAlexander Motin hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO); 345489290e9SAlexander Motin if (hpriv == NULL) 346489290e9SAlexander Motin return (ENOMEM); 347add85a1dSArchie Cobbs 348489290e9SAlexander Motin /* Initialize state */ 349489290e9SAlexander Motin mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF); 350489290e9SAlexander Motin ng_callout_init(&hpriv->sackTimer); 351489290e9SAlexander Motin ng_callout_init(&hpriv->rackTimer); 352489290e9SAlexander Motin hpriv->conf.cid = cid; 353489290e9SAlexander Motin hpriv->node = node; 354489290e9SAlexander Motin hpriv->hook = hook; 355489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, hpriv); 356489290e9SAlexander Motin 357489290e9SAlexander Motin hash = SESSHASH(cid); 358489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions); 359489290e9SAlexander Motin } 360489290e9SAlexander Motin 361add85a1dSArchie Cobbs return (0); 362add85a1dSArchie Cobbs } 363add85a1dSArchie Cobbs 364add85a1dSArchie Cobbs /* 365add85a1dSArchie Cobbs * Receive a control message. 366add85a1dSArchie Cobbs */ 367add85a1dSArchie Cobbs static int 368069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook) 369add85a1dSArchie Cobbs { 37030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 371add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 372add85a1dSArchie Cobbs int error = 0; 373069154d5SJulian Elischer struct ng_mesg *msg; 374add85a1dSArchie Cobbs 375069154d5SJulian Elischer NGI_GET_MSG(item, msg); 376add85a1dSArchie Cobbs switch (msg->header.typecookie) { 377add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 378add85a1dSArchie Cobbs switch (msg->header.cmd) { 379add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 380add85a1dSArchie Cobbs { 381add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 382add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 383489290e9SAlexander Motin hpriv_p hpriv; 384489290e9SAlexander Motin uint16_t hash; 385add85a1dSArchie Cobbs 386add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 387add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 388add85a1dSArchie Cobbs ERROUT(EINVAL); 389489290e9SAlexander Motin /* Try to find session by cid. */ 390489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, newConf->cid); 391489290e9SAlexander Motin /* If not present - use upper. */ 392489290e9SAlexander Motin if (hpriv == NULL) { 393489290e9SAlexander Motin hpriv = &priv->uppersess; 394489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions); 395489290e9SAlexander Motin hash = SESSHASH(newConf->cid); 396489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, 397489290e9SAlexander Motin sessions); 398489290e9SAlexander Motin } 399489290e9SAlexander Motin ng_pptpgre_reset(hpriv); /* reset on configure */ 400489290e9SAlexander Motin hpriv->conf = *newConf; 401add85a1dSArchie Cobbs break; 402add85a1dSArchie Cobbs } 403add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 404489290e9SAlexander Motin { 405489290e9SAlexander Motin hpriv_p hpriv; 406489290e9SAlexander Motin 407489290e9SAlexander Motin if (msg->header.arglen == 2) { 408489290e9SAlexander Motin /* Try to find session by cid. */ 409489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, 410489290e9SAlexander Motin *((uint16_t *)msg->data)); 411489290e9SAlexander Motin if (hpriv == NULL) 412489290e9SAlexander Motin ERROUT(EINVAL); 413489290e9SAlexander Motin } else if (msg->header.arglen == 0) { 414489290e9SAlexander Motin /* Use upper. */ 415489290e9SAlexander Motin hpriv = &priv->uppersess; 416489290e9SAlexander Motin } else 417489290e9SAlexander Motin ERROUT(EINVAL); 418489290e9SAlexander Motin NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT); 419add85a1dSArchie Cobbs if (resp == NULL) 420add85a1dSArchie Cobbs ERROUT(ENOMEM); 421489290e9SAlexander Motin bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf)); 422add85a1dSArchie Cobbs break; 423489290e9SAlexander Motin } 4249bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 4259bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 4269bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 4279bee7adfSArchie Cobbs { 4289bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 4299bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 4309bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 4319bee7adfSArchie Cobbs if (resp == NULL) 4329bee7adfSArchie Cobbs ERROUT(ENOMEM); 4339bee7adfSArchie Cobbs bcopy(&priv->stats, 4349bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 4359bee7adfSArchie Cobbs } 4369bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 4379bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 4389bee7adfSArchie Cobbs break; 4399bee7adfSArchie Cobbs } 440add85a1dSArchie Cobbs default: 441add85a1dSArchie Cobbs error = EINVAL; 442add85a1dSArchie Cobbs break; 443add85a1dSArchie Cobbs } 444add85a1dSArchie Cobbs break; 445add85a1dSArchie Cobbs default: 446add85a1dSArchie Cobbs error = EINVAL; 447add85a1dSArchie Cobbs break; 448add85a1dSArchie Cobbs } 449589f6ed8SJulian Elischer done: 450069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 451069154d5SJulian Elischer NG_FREE_MSG(msg); 452add85a1dSArchie Cobbs return (error); 453add85a1dSArchie Cobbs } 454add85a1dSArchie Cobbs 455add85a1dSArchie Cobbs /* 456add85a1dSArchie Cobbs * Receive incoming data on a hook. 457add85a1dSArchie Cobbs */ 458add85a1dSArchie Cobbs static int 459069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item) 460add85a1dSArchie Cobbs { 461489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 462f2ba84d7SGleb Smirnoff int rval; 463add85a1dSArchie Cobbs 464add85a1dSArchie Cobbs /* If not configured, reject */ 465489290e9SAlexander Motin if (!hpriv->conf.enabled) { 466069154d5SJulian Elischer NG_FREE_ITEM(item); 467add85a1dSArchie Cobbs return (ENXIO); 468add85a1dSArchie Cobbs } 469add85a1dSArchie Cobbs 470489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 471f2ba84d7SGleb Smirnoff 472489290e9SAlexander Motin rval = ng_pptpgre_xmit(hpriv, item); 473f2ba84d7SGleb Smirnoff 474489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 475f2ba84d7SGleb Smirnoff 476f2ba84d7SGleb Smirnoff return (rval); 477add85a1dSArchie Cobbs } 478add85a1dSArchie Cobbs 479add85a1dSArchie Cobbs /* 480489290e9SAlexander Motin * Hook disconnection 481489290e9SAlexander Motin */ 482489290e9SAlexander Motin static int 483489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook) 484489290e9SAlexander Motin { 485489290e9SAlexander Motin const node_p node = NG_HOOK_NODE(hook); 486489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(node); 487489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 488489290e9SAlexander Motin 489489290e9SAlexander Motin /* Zero out hook pointer */ 490489290e9SAlexander Motin if (hook == priv->upper) { 491489290e9SAlexander Motin priv->upper = NULL; 492489290e9SAlexander Motin priv->uppersess.hook = NULL; 493489290e9SAlexander Motin } else if (hook == priv->lower) { 494489290e9SAlexander Motin priv->lower = NULL; 495489290e9SAlexander Motin } else { 496489290e9SAlexander Motin /* Reset node (stops timers) */ 497489290e9SAlexander Motin ng_pptpgre_reset(hpriv); 498489290e9SAlexander Motin 499489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions); 500489290e9SAlexander Motin mtx_destroy(&hpriv->mtx); 501489290e9SAlexander Motin free(hpriv, M_NETGRAPH); 502489290e9SAlexander Motin } 503489290e9SAlexander Motin 504489290e9SAlexander Motin /* Go away if no longer connected to anything */ 505489290e9SAlexander Motin if ((NG_NODE_NUMHOOKS(node) == 0) 506489290e9SAlexander Motin && (NG_NODE_IS_VALID(node))) 507489290e9SAlexander Motin ng_rmnode_self(node); 508489290e9SAlexander Motin return (0); 509489290e9SAlexander Motin } 510489290e9SAlexander Motin 511489290e9SAlexander Motin /* 512add85a1dSArchie Cobbs * Destroy node 513add85a1dSArchie Cobbs */ 514add85a1dSArchie Cobbs static int 515069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node) 516add85a1dSArchie Cobbs { 51730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 518add85a1dSArchie Cobbs 519089323f3SGleb Smirnoff /* Reset node (stops timers) */ 520489290e9SAlexander Motin ng_pptpgre_reset(&priv->uppersess); 521add85a1dSArchie Cobbs 522489290e9SAlexander Motin LIST_REMOVE(&priv->uppersess, sessions); 523489290e9SAlexander Motin mtx_destroy(&priv->uppersess.mtx); 524f2ba84d7SGleb Smirnoff 525add85a1dSArchie Cobbs FREE(priv, M_NETGRAPH); 5264a48abb2SArchie Cobbs 5274a48abb2SArchie Cobbs /* Decrement ref count */ 52830400f03SJulian Elischer NG_NODE_UNREF(node); 529add85a1dSArchie Cobbs return (0); 530add85a1dSArchie Cobbs } 531add85a1dSArchie Cobbs 532add85a1dSArchie Cobbs /************************************************************************* 533add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 534add85a1dSArchie Cobbs *************************************************************************/ 535add85a1dSArchie Cobbs 536add85a1dSArchie Cobbs /* 537add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 538add85a1dSArchie Cobbs */ 539add85a1dSArchie Cobbs static int 540489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item) 541add85a1dSArchie Cobbs { 542489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(hpriv->node); 543add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 544add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 545add85a1dSArchie Cobbs int grelen, error; 546069154d5SJulian Elischer struct mbuf *m; 547add85a1dSArchie Cobbs 548489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_OWNED); 54983beeed9SGleb Smirnoff 550069154d5SJulian Elischer if (item) { 551069154d5SJulian Elischer NGI_GET_M(item, m); 552069154d5SJulian Elischer } else { 553069154d5SJulian Elischer m = NULL; 554069154d5SJulian Elischer } 5559bee7adfSArchie Cobbs /* Check if there's data */ 5569bee7adfSArchie Cobbs if (m != NULL) { 5579bee7adfSArchie Cobbs 558922ee196SArchie Cobbs /* Check if windowing is enabled */ 559489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 560add85a1dSArchie Cobbs /* Is our transmit window full? */ 561489290e9SAlexander Motin if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq, 562489290e9SAlexander Motin hpriv->recvAck) >= hpriv->xmitWin) { 5639bee7adfSArchie Cobbs priv->stats.xmitDrops++; 56483beeed9SGleb Smirnoff ERROUT(ENOBUFS); 565add85a1dSArchie Cobbs } 566922ee196SArchie Cobbs } 567add85a1dSArchie Cobbs 568add85a1dSArchie Cobbs /* Sanity check frame length */ 569add85a1dSArchie Cobbs if (m != NULL && m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 5709bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 57183beeed9SGleb Smirnoff ERROUT(EMSGSIZE); 572add85a1dSArchie Cobbs } 573069154d5SJulian Elischer } else { 5749bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 575069154d5SJulian Elischer } 576add85a1dSArchie Cobbs 577add85a1dSArchie Cobbs /* Build GRE header */ 578add85a1dSArchie Cobbs ((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE); 579add85a1dSArchie Cobbs gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; 580489290e9SAlexander Motin gre->cid = htons(hpriv->conf.peerCid); 581add85a1dSArchie Cobbs 582add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 583add85a1dSArchie Cobbs if (m != NULL) { 584add85a1dSArchie Cobbs gre->hasSeq = 1; 585489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 586489290e9SAlexander Motin hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck] 587489290e9SAlexander Motin = ng_pptpgre_time(); 588922ee196SArchie Cobbs } 589489290e9SAlexander Motin hpriv->xmitSeq++; 590489290e9SAlexander Motin gre->data[0] = htonl(hpriv->xmitSeq); 591add85a1dSArchie Cobbs } 592add85a1dSArchie Cobbs 593add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 594489290e9SAlexander Motin if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) { 595add85a1dSArchie Cobbs gre->hasAck = 1; 596489290e9SAlexander Motin gre->data[gre->hasSeq] = htonl(hpriv->recvSeq); 597489290e9SAlexander Motin hpriv->xmitAck = hpriv->recvSeq; 598489290e9SAlexander Motin ng_pptpgre_stop_send_ack_timer(hpriv); 599da010626SArchie Cobbs } 600add85a1dSArchie Cobbs 601add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 602add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 603add85a1dSArchie Cobbs if (m == NULL) { 604a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 605add85a1dSArchie Cobbs if (m == NULL) { 606678f9e33SArchie Cobbs priv->stats.memoryFailures++; 60783beeed9SGleb Smirnoff ERROUT(ENOBUFS); 608add85a1dSArchie Cobbs } 609add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 610add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 611add85a1dSArchie Cobbs } else { 612a163d034SWarner Losh M_PREPEND(m, grelen, M_DONTWAIT); 613add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 614add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 615678f9e33SArchie Cobbs priv->stats.memoryFailures++; 61683beeed9SGleb Smirnoff ERROUT(ENOBUFS); 617add85a1dSArchie Cobbs } 618add85a1dSArchie Cobbs } 619add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 620add85a1dSArchie Cobbs 6219bee7adfSArchie Cobbs /* Update stats */ 6229bee7adfSArchie Cobbs priv->stats.xmitPackets++; 6239bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 6249bee7adfSArchie Cobbs 62583beeed9SGleb Smirnoff /* 62683beeed9SGleb Smirnoff * XXX: we should reset timer only after an item has been sent 62783beeed9SGleb Smirnoff * successfully. 62883beeed9SGleb Smirnoff */ 629489290e9SAlexander Motin if (hpriv->conf.enableWindowing && 630489290e9SAlexander Motin gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1) 631489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv); 63283beeed9SGleb Smirnoff 633489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 63483beeed9SGleb Smirnoff 635add85a1dSArchie Cobbs /* Deliver packet */ 636069154d5SJulian Elischer if (item) { 637069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->lower, m); 638069154d5SJulian Elischer } else { 639069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m); 640069154d5SJulian Elischer } 641069154d5SJulian Elischer 64283beeed9SGleb Smirnoff return (error); 643678f9e33SArchie Cobbs 64483beeed9SGleb Smirnoff done: 645489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 64683beeed9SGleb Smirnoff NG_FREE_M(m); 64783beeed9SGleb Smirnoff if (item) 64883beeed9SGleb Smirnoff NG_FREE_ITEM(item); 649add85a1dSArchie Cobbs return (error); 650add85a1dSArchie Cobbs } 651add85a1dSArchie Cobbs 652add85a1dSArchie Cobbs /* 653add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 654add85a1dSArchie Cobbs */ 655add85a1dSArchie Cobbs static int 656489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item) 657add85a1dSArchie Cobbs { 658489290e9SAlexander Motin hpriv_p hpriv; 659489290e9SAlexander Motin node_p node = NG_HOOK_NODE(hook); 66030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 661add85a1dSArchie Cobbs int iphlen, grelen, extralen; 662816b834fSArchie Cobbs const struct greheader *gre; 663816b834fSArchie Cobbs const struct ip *ip; 664add85a1dSArchie Cobbs int error = 0; 665069154d5SJulian Elischer struct mbuf *m; 666add85a1dSArchie Cobbs 667069154d5SJulian Elischer NGI_GET_M(item, m); 6689bee7adfSArchie Cobbs /* Update stats */ 6699bee7adfSArchie Cobbs priv->stats.recvPackets++; 6709bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 6719bee7adfSArchie Cobbs 672add85a1dSArchie Cobbs /* Sanity check packet length */ 673add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { 6749bee7adfSArchie Cobbs priv->stats.recvRunts++; 67583beeed9SGleb Smirnoff ERROUT(EINVAL); 676add85a1dSArchie Cobbs } 677add85a1dSArchie Cobbs 678add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 679add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 680add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 681678f9e33SArchie Cobbs priv->stats.memoryFailures++; 68283beeed9SGleb Smirnoff ERROUT(ENOBUFS); 683add85a1dSArchie Cobbs } 684816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 685add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 686add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 687add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 688678f9e33SArchie Cobbs priv->stats.memoryFailures++; 68983beeed9SGleb Smirnoff ERROUT(ENOBUFS); 690add85a1dSArchie Cobbs } 691816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 692add85a1dSArchie Cobbs } 693816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 694add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 6959bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 6969bee7adfSArchie Cobbs priv->stats.recvRunts++; 69783beeed9SGleb Smirnoff ERROUT(EINVAL); 6989bee7adfSArchie Cobbs } 699add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 700add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 701678f9e33SArchie Cobbs priv->stats.memoryFailures++; 70283beeed9SGleb Smirnoff ERROUT(ENOBUFS); 703add85a1dSArchie Cobbs } 704816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 705816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 706add85a1dSArchie Cobbs } 707add85a1dSArchie Cobbs 708add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 709add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 710cc78c48aSArchie Cobbs - (iphlen + grelen + gre->hasSeq * (u_int16_t)ntohs(gre->length)); 7119bee7adfSArchie Cobbs if (extralen < 0) { 7129bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 71383beeed9SGleb Smirnoff ERROUT(EINVAL); 7149bee7adfSArchie Cobbs } 715816b834fSArchie Cobbs if ((ntohl(*((const u_int32_t *)gre)) & PPTP_INIT_MASK) 716816b834fSArchie Cobbs != PPTP_INIT_VALUE) { 7179bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 71883beeed9SGleb Smirnoff ERROUT(EINVAL); 7199bee7adfSArchie Cobbs } 720489290e9SAlexander Motin 721489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, ntohs(gre->cid)); 722489290e9SAlexander Motin if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) { 7239bee7adfSArchie Cobbs priv->stats.recvBadCID++; 72483beeed9SGleb Smirnoff ERROUT(EINVAL); 7259bee7adfSArchie Cobbs } 726489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 727add85a1dSArchie Cobbs 728add85a1dSArchie Cobbs /* Look for peer ack */ 729add85a1dSArchie Cobbs if (gre->hasAck) { 730add85a1dSArchie Cobbs const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); 731489290e9SAlexander Motin const int index = ack - hpriv->recvAck - 1; 73222dfb9bdSArchie Cobbs long sample; 733add85a1dSArchie Cobbs long diff; 734add85a1dSArchie Cobbs 735add85a1dSArchie Cobbs /* Sanity check ack value */ 736489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) { 7379bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 7389bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 7399bee7adfSArchie Cobbs } 740489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0) 7419bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 742489290e9SAlexander Motin hpriv->recvAck = ack; 743add85a1dSArchie Cobbs 744add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 745489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 746489290e9SAlexander Motin sample = ng_pptpgre_time() - hpriv->timeSent[index]; 747489290e9SAlexander Motin diff = sample - hpriv->rtt; 748489290e9SAlexander Motin hpriv->rtt += PPTP_ACK_ALPHA(diff); 749add85a1dSArchie Cobbs if (diff < 0) 750add85a1dSArchie Cobbs diff = -diff; 751489290e9SAlexander Motin hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev); 752f8e159d6SGleb Smirnoff /* +2 to compensate low precision of int math */ 753489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2); 754489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT) 755489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 756489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT) 757489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT; 758e962a823SArchie Cobbs 759e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 760489290e9SAlexander Motin bcopy(hpriv->timeSent + index + 1, hpriv->timeSent, 761489290e9SAlexander Motin sizeof(*hpriv->timeSent) 762922ee196SArchie Cobbs * (PPTP_XMIT_WIN - (index + 1))); 763e962a823SArchie Cobbs 764922ee196SArchie Cobbs /* If we sent an entire window, increase window size */ 765489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0 766489290e9SAlexander Motin && hpriv->xmitWin < PPTP_XMIT_WIN) { 767489290e9SAlexander Motin hpriv->xmitWin++; 768489290e9SAlexander Motin hpriv->winAck = ack + hpriv->xmitWin; 769add85a1dSArchie Cobbs } 770add85a1dSArchie Cobbs 7719bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 772489290e9SAlexander Motin ng_pptpgre_stop_recv_ack_timer(hpriv); 773489290e9SAlexander Motin if (hpriv->recvAck != hpriv->xmitSeq) 774489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv); 775add85a1dSArchie Cobbs } 776922ee196SArchie Cobbs } 7779bee7adfSArchie Cobbs badAck: 778add85a1dSArchie Cobbs 779add85a1dSArchie Cobbs /* See if frame contains any data */ 780add85a1dSArchie Cobbs if (gre->hasSeq) { 781add85a1dSArchie Cobbs const u_int32_t seq = ntohl(gre->data[0]); 782add85a1dSArchie Cobbs 783add85a1dSArchie Cobbs /* Sanity check sequence number */ 784489290e9SAlexander Motin if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) { 785489290e9SAlexander Motin if (seq == hpriv->recvSeq) 7869bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 7879bee7adfSArchie Cobbs else 7889bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 789489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 79083beeed9SGleb Smirnoff ERROUT(EINVAL); 7919bee7adfSArchie Cobbs } 792489290e9SAlexander Motin hpriv->recvSeq = seq; 793add85a1dSArchie Cobbs 794add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 795489290e9SAlexander Motin if (!(callout_pending(&hpriv->sackTimer))) { 796678f9e33SArchie Cobbs /* If delayed ACK is disabled, send it now */ 797489290e9SAlexander Motin if (!hpriv->conf.enableDelayedAck) { /* ack now */ 798489290e9SAlexander Motin ng_pptpgre_xmit(hpriv, NULL); 799489290e9SAlexander Motin /* ng_pptpgre_xmit() drops the mutex */ 80083beeed9SGleb Smirnoff } else { /* ack later */ 801489290e9SAlexander Motin /* Take 1/4 of the estimated round trip time */ 802489290e9SAlexander Motin int maxWait = (hpriv->rtt >> 2); 8034a48abb2SArchie Cobbs if (maxWait < PPTP_MIN_ACK_DELAY) 8044a48abb2SArchie Cobbs maxWait = PPTP_MIN_ACK_DELAY; 805489290e9SAlexander Motin else if (maxWait > PPTP_MAX_ACK_DELAY) 8063cd7db22SArchie Cobbs maxWait = PPTP_MAX_ACK_DELAY; 807489290e9SAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv, maxWait); 808489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 809add85a1dSArchie Cobbs } 810489290e9SAlexander Motin } else 811489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 812add85a1dSArchie Cobbs 813add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 814add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 815add85a1dSArchie Cobbs if (extralen > 0) 816add85a1dSArchie Cobbs m_adj(m, -extralen); 817add85a1dSArchie Cobbs 818489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 819489290e9SAlexander Motin 820add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 821489290e9SAlexander Motin NG_FWD_NEW_DATA(error, item, hpriv->hook, m); 8229bee7adfSArchie Cobbs } else { 8239bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 824489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 825069154d5SJulian Elischer NG_FREE_ITEM(item); 826069154d5SJulian Elischer NG_FREE_M(m); /* no data to deliver */ 8279bee7adfSArchie Cobbs } 82883beeed9SGleb Smirnoff 82983beeed9SGleb Smirnoff return (error); 83083beeed9SGleb Smirnoff 83183beeed9SGleb Smirnoff done: 83283beeed9SGleb Smirnoff NG_FREE_ITEM(item); 83383beeed9SGleb Smirnoff NG_FREE_M(m); 834add85a1dSArchie Cobbs return (error); 835add85a1dSArchie Cobbs } 836add85a1dSArchie Cobbs 837add85a1dSArchie Cobbs /************************************************************************* 838add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 839add85a1dSArchie Cobbs *************************************************************************/ 840add85a1dSArchie Cobbs 841add85a1dSArchie Cobbs /* 8429bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 843add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 844add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 845add85a1dSArchie Cobbs * recv ack timer, if any. 846add85a1dSArchie Cobbs */ 847add85a1dSArchie Cobbs static void 848489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv) 849add85a1dSArchie Cobbs { 850678f9e33SArchie Cobbs int remain, ticks; 851add85a1dSArchie Cobbs 852add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 853add85a1dSArchie Cobbs and reset the timer to that time. */ 854489290e9SAlexander Motin remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time(); 855add85a1dSArchie Cobbs if (remain < 0) 856add85a1dSArchie Cobbs remain = 0; 8579bee7adfSArchie Cobbs 8584a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 859678f9e33SArchie Cobbs ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; 860489290e9SAlexander Motin ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook, 861489290e9SAlexander Motin ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0); 8624a48abb2SArchie Cobbs } 8634a48abb2SArchie Cobbs 8644a48abb2SArchie Cobbs /* 8654a48abb2SArchie Cobbs * Stop receive ack timer. 8664a48abb2SArchie Cobbs */ 8674a48abb2SArchie Cobbs static void 868489290e9SAlexander Motin ng_pptpgre_stop_recv_ack_timer(hpriv_p hpriv) 8694a48abb2SArchie Cobbs { 870489290e9SAlexander Motin ng_uncallout(&hpriv->rackTimer, hpriv->node); 871add85a1dSArchie Cobbs } 872add85a1dSArchie Cobbs 873add85a1dSArchie Cobbs /* 874add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 875add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 876add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 877add85a1dSArchie Cobbs */ 878add85a1dSArchie Cobbs static void 879089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 880add85a1dSArchie Cobbs { 88130400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 882489290e9SAlexander Motin const hpriv_p hpriv = arg1; 8839bee7adfSArchie Cobbs 884add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 8859bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 886489290e9SAlexander Motin hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */ 887489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev); 888489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT) 889489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 890489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT) 891489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT; 892678f9e33SArchie Cobbs 893e962a823SArchie Cobbs /* Reset ack and sliding window */ 894489290e9SAlexander Motin hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */ 895489290e9SAlexander Motin hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */ 896489290e9SAlexander Motin hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */ 897add85a1dSArchie Cobbs } 898add85a1dSArchie Cobbs 899add85a1dSArchie Cobbs /* 9009bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 9019bee7adfSArchie Cobbs * already running. 9029bee7adfSArchie Cobbs */ 9039bee7adfSArchie Cobbs static void 904489290e9SAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv, int ackTimeout) 9059bee7adfSArchie Cobbs { 906678f9e33SArchie Cobbs int ticks; 9079bee7adfSArchie Cobbs 9084a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 909678f9e33SArchie Cobbs ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); 910489290e9SAlexander Motin ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook, 911489290e9SAlexander Motin ticks, ng_pptpgre_send_ack_timeout, hpriv, 0); 9124a48abb2SArchie Cobbs } 9134a48abb2SArchie Cobbs 9144a48abb2SArchie Cobbs /* 9154a48abb2SArchie Cobbs * Stop send ack timer. 9164a48abb2SArchie Cobbs */ 9174a48abb2SArchie Cobbs static void 918489290e9SAlexander Motin ng_pptpgre_stop_send_ack_timer(hpriv_p hpriv) 9194a48abb2SArchie Cobbs { 920489290e9SAlexander Motin ng_uncallout(&hpriv->sackTimer, hpriv->node); 9219bee7adfSArchie Cobbs } 9229bee7adfSArchie Cobbs 9239bee7adfSArchie Cobbs /* 924add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 925add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 926add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 927add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 928add85a1dSArchie Cobbs */ 929add85a1dSArchie Cobbs static void 930089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 931add85a1dSArchie Cobbs { 932489290e9SAlexander Motin const hpriv_p hpriv = arg1; 93383beeed9SGleb Smirnoff 934489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 9359bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 936489290e9SAlexander Motin ng_pptpgre_xmit(hpriv, NULL); 937489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 938add85a1dSArchie Cobbs } 939add85a1dSArchie Cobbs 940add85a1dSArchie Cobbs /************************************************************************* 941add85a1dSArchie Cobbs MISC FUNCTIONS 942add85a1dSArchie Cobbs *************************************************************************/ 943add85a1dSArchie Cobbs 944add85a1dSArchie Cobbs /* 945489290e9SAlexander Motin * Find the hook with a given session ID. 946489290e9SAlexander Motin */ 947489290e9SAlexander Motin static hpriv_p 948489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid) 949489290e9SAlexander Motin { 950489290e9SAlexander Motin uint16_t hash = SESSHASH(cid); 951489290e9SAlexander Motin hpriv_p hpriv = NULL; 952489290e9SAlexander Motin 953489290e9SAlexander Motin LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) { 954489290e9SAlexander Motin if (hpriv->conf.cid == cid) 955489290e9SAlexander Motin break; 956489290e9SAlexander Motin } 957489290e9SAlexander Motin 958489290e9SAlexander Motin return (hpriv); 959489290e9SAlexander Motin } 960489290e9SAlexander Motin 961489290e9SAlexander Motin /* 962489290e9SAlexander Motin * Reset state (must be called with lock held or from writer) 963add85a1dSArchie Cobbs */ 964add85a1dSArchie Cobbs static void 965489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv) 966add85a1dSArchie Cobbs { 967add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 968489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 969489290e9SAlexander Motin hpriv->rtt = hpriv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ 970489290e9SAlexander Motin if (hpriv->rtt < PPTP_MIN_RTT) 971489290e9SAlexander Motin hpriv->rtt = PPTP_MIN_RTT; 972489290e9SAlexander Motin hpriv->dev = 0; 973489290e9SAlexander Motin hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2; 974489290e9SAlexander Motin if (hpriv->xmitWin < 2) /* often the first packet is lost */ 975489290e9SAlexander Motin hpriv->xmitWin = 2; /* because the peer isn't ready */ 976489290e9SAlexander Motin else if (hpriv->xmitWin > PPTP_XMIT_WIN) 977489290e9SAlexander Motin hpriv->xmitWin = PPTP_XMIT_WIN; 978489290e9SAlexander Motin hpriv->winAck = hpriv->xmitWin; 979add85a1dSArchie Cobbs 980add85a1dSArchie Cobbs /* Reset sequence numbers */ 981489290e9SAlexander Motin hpriv->recvSeq = ~0; 982489290e9SAlexander Motin hpriv->recvAck = ~0; 983489290e9SAlexander Motin hpriv->xmitSeq = ~0; 984489290e9SAlexander Motin hpriv->xmitAck = ~0; 985add85a1dSArchie Cobbs 9864a48abb2SArchie Cobbs /* Stop timers */ 987489290e9SAlexander Motin ng_pptpgre_stop_send_ack_timer(hpriv); 988489290e9SAlexander Motin ng_pptpgre_stop_recv_ack_timer(hpriv); 989add85a1dSArchie Cobbs } 990add85a1dSArchie Cobbs 991add85a1dSArchie Cobbs /* 992add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 993add85a1dSArchie Cobbs */ 994add85a1dSArchie Cobbs static pptptime_t 995489290e9SAlexander Motin ng_pptpgre_time(void) 996add85a1dSArchie Cobbs { 997add85a1dSArchie Cobbs struct timeval tv; 998678f9e33SArchie Cobbs pptptime_t t; 999add85a1dSArchie Cobbs 1000678f9e33SArchie Cobbs microuptime(&tv); 1001678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; 1002678f9e33SArchie Cobbs t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE); 1003678f9e33SArchie Cobbs return(t); 1004add85a1dSArchie Cobbs } 1005