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 */ 284674d86bfSGleb Smirnoff priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 285add85a1dSArchie Cobbs 28630400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv); 287add85a1dSArchie Cobbs 288add85a1dSArchie Cobbs /* Initialize state */ 289489290e9SAlexander Motin mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF); 290489290e9SAlexander Motin ng_callout_init(&priv->uppersess.sackTimer); 291489290e9SAlexander Motin ng_callout_init(&priv->uppersess.rackTimer); 292489290e9SAlexander Motin priv->uppersess.node = node; 293489290e9SAlexander Motin 294489290e9SAlexander Motin for (i = 0; i < SESSHASHSIZE; i++) 295489290e9SAlexander Motin LIST_INIT(&priv->sesshash[i]); 296489290e9SAlexander Motin 297489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions); 298add85a1dSArchie Cobbs 299add85a1dSArchie Cobbs /* Done */ 300add85a1dSArchie Cobbs return (0); 301add85a1dSArchie Cobbs } 302add85a1dSArchie Cobbs 303add85a1dSArchie Cobbs /* 304add85a1dSArchie Cobbs * Give our OK for a hook to be added. 305add85a1dSArchie Cobbs */ 306add85a1dSArchie Cobbs static int 307add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) 308add85a1dSArchie Cobbs { 30930400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 310add85a1dSArchie Cobbs 311add85a1dSArchie Cobbs /* Check hook name */ 312489290e9SAlexander Motin if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) { 313489290e9SAlexander Motin priv->upper = hook; 314489290e9SAlexander Motin priv->uppersess.hook = hook; 315489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, &priv->uppersess); 316489290e9SAlexander Motin } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) { 317489290e9SAlexander Motin priv->lower = hook; 318489290e9SAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower); 319489290e9SAlexander Motin } else { 320489290e9SAlexander Motin static const char hexdig[16] = "0123456789abcdef"; 321489290e9SAlexander Motin const char *hex; 322489290e9SAlexander Motin hpriv_p hpriv; 323489290e9SAlexander Motin int i, j; 324489290e9SAlexander Motin uint16_t cid, hash; 325489290e9SAlexander Motin 326489290e9SAlexander Motin /* Parse hook name to get session ID */ 327489290e9SAlexander Motin if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P, 328489290e9SAlexander Motin sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0) 329489290e9SAlexander Motin return (EINVAL); 330489290e9SAlexander Motin hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1; 331489290e9SAlexander Motin for (cid = i = 0; i < 4; i++) { 332489290e9SAlexander Motin for (j = 0; j < 16 && hex[i] != hexdig[j]; j++); 333489290e9SAlexander Motin if (j == 16) 334489290e9SAlexander Motin return (EINVAL); 335489290e9SAlexander Motin cid = (cid << 4) | j; 336489290e9SAlexander Motin } 337489290e9SAlexander Motin if (hex[i] != '\0') 338add85a1dSArchie Cobbs return (EINVAL); 339add85a1dSArchie Cobbs 340489290e9SAlexander Motin hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO); 341489290e9SAlexander Motin if (hpriv == NULL) 342489290e9SAlexander Motin return (ENOMEM); 343add85a1dSArchie Cobbs 344489290e9SAlexander Motin /* Initialize state */ 345489290e9SAlexander Motin mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF); 346489290e9SAlexander Motin ng_callout_init(&hpriv->sackTimer); 347489290e9SAlexander Motin ng_callout_init(&hpriv->rackTimer); 348489290e9SAlexander Motin hpriv->conf.cid = cid; 349489290e9SAlexander Motin hpriv->node = node; 350489290e9SAlexander Motin hpriv->hook = hook; 351489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, hpriv); 352489290e9SAlexander Motin 353489290e9SAlexander Motin hash = SESSHASH(cid); 354489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions); 355489290e9SAlexander Motin } 356489290e9SAlexander Motin 357add85a1dSArchie Cobbs return (0); 358add85a1dSArchie Cobbs } 359add85a1dSArchie Cobbs 360add85a1dSArchie Cobbs /* 361add85a1dSArchie Cobbs * Receive a control message. 362add85a1dSArchie Cobbs */ 363add85a1dSArchie Cobbs static int 364069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook) 365add85a1dSArchie Cobbs { 36630400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 367add85a1dSArchie Cobbs struct ng_mesg *resp = NULL; 368add85a1dSArchie Cobbs int error = 0; 369069154d5SJulian Elischer struct ng_mesg *msg; 370add85a1dSArchie Cobbs 371069154d5SJulian Elischer NGI_GET_MSG(item, msg); 372add85a1dSArchie Cobbs switch (msg->header.typecookie) { 373add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE: 374add85a1dSArchie Cobbs switch (msg->header.cmd) { 375add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG: 376add85a1dSArchie Cobbs { 377add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf = 378add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data; 379489290e9SAlexander Motin hpriv_p hpriv; 380489290e9SAlexander Motin uint16_t hash; 381add85a1dSArchie Cobbs 382add85a1dSArchie Cobbs /* Check for invalid or illegal config */ 383add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf)) 384add85a1dSArchie Cobbs ERROUT(EINVAL); 385489290e9SAlexander Motin /* Try to find session by cid. */ 386489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, newConf->cid); 387489290e9SAlexander Motin /* If not present - use upper. */ 388489290e9SAlexander Motin if (hpriv == NULL) { 389489290e9SAlexander Motin hpriv = &priv->uppersess; 390489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions); 391489290e9SAlexander Motin hash = SESSHASH(newConf->cid); 392489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, 393489290e9SAlexander Motin sessions); 394489290e9SAlexander Motin } 395489290e9SAlexander Motin ng_pptpgre_reset(hpriv); /* reset on configure */ 396489290e9SAlexander Motin hpriv->conf = *newConf; 397add85a1dSArchie Cobbs break; 398add85a1dSArchie Cobbs } 399add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG: 400489290e9SAlexander Motin { 401489290e9SAlexander Motin hpriv_p hpriv; 402489290e9SAlexander Motin 403489290e9SAlexander Motin if (msg->header.arglen == 2) { 404489290e9SAlexander Motin /* Try to find session by cid. */ 405489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, 406489290e9SAlexander Motin *((uint16_t *)msg->data)); 407489290e9SAlexander Motin if (hpriv == NULL) 408489290e9SAlexander Motin ERROUT(EINVAL); 409489290e9SAlexander Motin } else if (msg->header.arglen == 0) { 410489290e9SAlexander Motin /* Use upper. */ 411489290e9SAlexander Motin hpriv = &priv->uppersess; 412489290e9SAlexander Motin } else 413489290e9SAlexander Motin ERROUT(EINVAL); 414489290e9SAlexander Motin NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT); 415add85a1dSArchie Cobbs if (resp == NULL) 416add85a1dSArchie Cobbs ERROUT(ENOMEM); 417489290e9SAlexander Motin bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf)); 418add85a1dSArchie Cobbs break; 419489290e9SAlexander Motin } 4209bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS: 4219bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS: 4229bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS: 4239bee7adfSArchie Cobbs { 4249bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) { 4259bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg, 4269bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT); 4279bee7adfSArchie Cobbs if (resp == NULL) 4289bee7adfSArchie Cobbs ERROUT(ENOMEM); 4299bee7adfSArchie Cobbs bcopy(&priv->stats, 4309bee7adfSArchie Cobbs resp->data, sizeof(priv->stats)); 4319bee7adfSArchie Cobbs } 4329bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS) 4339bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats)); 4349bee7adfSArchie Cobbs break; 4359bee7adfSArchie Cobbs } 436add85a1dSArchie Cobbs default: 437add85a1dSArchie Cobbs error = EINVAL; 438add85a1dSArchie Cobbs break; 439add85a1dSArchie Cobbs } 440add85a1dSArchie Cobbs break; 441add85a1dSArchie Cobbs default: 442add85a1dSArchie Cobbs error = EINVAL; 443add85a1dSArchie Cobbs break; 444add85a1dSArchie Cobbs } 445589f6ed8SJulian Elischer done: 446069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 447069154d5SJulian Elischer NG_FREE_MSG(msg); 448add85a1dSArchie Cobbs return (error); 449add85a1dSArchie Cobbs } 450add85a1dSArchie Cobbs 451add85a1dSArchie Cobbs /* 452add85a1dSArchie Cobbs * Receive incoming data on a hook. 453add85a1dSArchie Cobbs */ 454add85a1dSArchie Cobbs static int 455069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item) 456add85a1dSArchie Cobbs { 457489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 458f2ba84d7SGleb Smirnoff int rval; 459add85a1dSArchie Cobbs 460add85a1dSArchie Cobbs /* If not configured, reject */ 461489290e9SAlexander Motin if (!hpriv->conf.enabled) { 462069154d5SJulian Elischer NG_FREE_ITEM(item); 463add85a1dSArchie Cobbs return (ENXIO); 464add85a1dSArchie Cobbs } 465add85a1dSArchie Cobbs 466489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 467f2ba84d7SGleb Smirnoff 468489290e9SAlexander Motin rval = ng_pptpgre_xmit(hpriv, item); 469f2ba84d7SGleb Smirnoff 470489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 471f2ba84d7SGleb Smirnoff 472f2ba84d7SGleb Smirnoff return (rval); 473add85a1dSArchie Cobbs } 474add85a1dSArchie Cobbs 475add85a1dSArchie Cobbs /* 476489290e9SAlexander Motin * Hook disconnection 477489290e9SAlexander Motin */ 478489290e9SAlexander Motin static int 479489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook) 480489290e9SAlexander Motin { 481489290e9SAlexander Motin const node_p node = NG_HOOK_NODE(hook); 482489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(node); 483489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); 484489290e9SAlexander Motin 485489290e9SAlexander Motin /* Zero out hook pointer */ 486489290e9SAlexander Motin if (hook == priv->upper) { 487489290e9SAlexander Motin priv->upper = NULL; 488489290e9SAlexander Motin priv->uppersess.hook = NULL; 489489290e9SAlexander Motin } else if (hook == priv->lower) { 490489290e9SAlexander Motin priv->lower = NULL; 491489290e9SAlexander Motin } else { 492489290e9SAlexander Motin /* Reset node (stops timers) */ 493489290e9SAlexander Motin ng_pptpgre_reset(hpriv); 494489290e9SAlexander Motin 495489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions); 496489290e9SAlexander Motin mtx_destroy(&hpriv->mtx); 497489290e9SAlexander Motin free(hpriv, M_NETGRAPH); 498489290e9SAlexander Motin } 499489290e9SAlexander Motin 500489290e9SAlexander Motin /* Go away if no longer connected to anything */ 501489290e9SAlexander Motin if ((NG_NODE_NUMHOOKS(node) == 0) 502489290e9SAlexander Motin && (NG_NODE_IS_VALID(node))) 503489290e9SAlexander Motin ng_rmnode_self(node); 504489290e9SAlexander Motin return (0); 505489290e9SAlexander Motin } 506489290e9SAlexander Motin 507489290e9SAlexander Motin /* 508add85a1dSArchie Cobbs * Destroy node 509add85a1dSArchie Cobbs */ 510add85a1dSArchie Cobbs static int 511069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node) 512add85a1dSArchie Cobbs { 51330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 514add85a1dSArchie Cobbs 515089323f3SGleb Smirnoff /* Reset node (stops timers) */ 516489290e9SAlexander Motin ng_pptpgre_reset(&priv->uppersess); 517add85a1dSArchie Cobbs 518489290e9SAlexander Motin LIST_REMOVE(&priv->uppersess, sessions); 519489290e9SAlexander Motin mtx_destroy(&priv->uppersess.mtx); 520f2ba84d7SGleb Smirnoff 5211ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH); 5224a48abb2SArchie Cobbs 5234a48abb2SArchie Cobbs /* Decrement ref count */ 52430400f03SJulian Elischer NG_NODE_UNREF(node); 525add85a1dSArchie Cobbs return (0); 526add85a1dSArchie Cobbs } 527add85a1dSArchie Cobbs 528add85a1dSArchie Cobbs /************************************************************************* 529add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS 530add85a1dSArchie Cobbs *************************************************************************/ 531add85a1dSArchie Cobbs 532add85a1dSArchie Cobbs /* 533add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL. 534add85a1dSArchie Cobbs */ 535add85a1dSArchie Cobbs static int 536489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item) 537add85a1dSArchie Cobbs { 538489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(hpriv->node); 539add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; 540add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf; 541add85a1dSArchie Cobbs int grelen, error; 542069154d5SJulian Elischer struct mbuf *m; 543add85a1dSArchie Cobbs 544489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_OWNED); 54583beeed9SGleb Smirnoff 546069154d5SJulian Elischer if (item) { 547069154d5SJulian Elischer NGI_GET_M(item, m); 548069154d5SJulian Elischer } else { 549069154d5SJulian Elischer m = NULL; 550069154d5SJulian Elischer } 5519bee7adfSArchie Cobbs /* Check if there's data */ 5529bee7adfSArchie Cobbs if (m != NULL) { 5539bee7adfSArchie Cobbs 554922ee196SArchie Cobbs /* Check if windowing is enabled */ 555489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 556add85a1dSArchie Cobbs /* Is our transmit window full? */ 557489290e9SAlexander Motin if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq, 558489290e9SAlexander Motin hpriv->recvAck) >= hpriv->xmitWin) { 5599bee7adfSArchie Cobbs priv->stats.xmitDrops++; 56083beeed9SGleb Smirnoff ERROUT(ENOBUFS); 561add85a1dSArchie Cobbs } 562922ee196SArchie Cobbs } 563add85a1dSArchie Cobbs 564add85a1dSArchie Cobbs /* Sanity check frame length */ 565*2c2e2be7SAlexander Motin if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) { 5669bee7adfSArchie Cobbs priv->stats.xmitTooBig++; 56783beeed9SGleb Smirnoff ERROUT(EMSGSIZE); 568add85a1dSArchie Cobbs } 569069154d5SJulian Elischer } else { 5709bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++; 571069154d5SJulian Elischer } 572add85a1dSArchie Cobbs 573add85a1dSArchie Cobbs /* Build GRE header */ 57438f2d636SAlexander Motin be32enc(gre, PPTP_INIT_VALUE); 57538f2d636SAlexander Motin be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0); 57638f2d636SAlexander Motin be16enc(&gre->cid, hpriv->conf.peerCid); 577add85a1dSArchie Cobbs 578add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */ 579add85a1dSArchie Cobbs if (m != NULL) { 580add85a1dSArchie Cobbs gre->hasSeq = 1; 581489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 582489290e9SAlexander Motin hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck] 583489290e9SAlexander Motin = ng_pptpgre_time(); 584922ee196SArchie Cobbs } 585489290e9SAlexander Motin hpriv->xmitSeq++; 58638f2d636SAlexander Motin be32enc(&gre->data[0], hpriv->xmitSeq); 587add85a1dSArchie Cobbs } 588add85a1dSArchie Cobbs 589add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */ 590489290e9SAlexander Motin if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) { 591add85a1dSArchie Cobbs gre->hasAck = 1; 59238f2d636SAlexander Motin be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq); 593489290e9SAlexander Motin hpriv->xmitAck = hpriv->recvSeq; 594714f558bSAlexander Motin if (hpriv->conf.enableDelayedAck) 595714f558bSAlexander Motin ng_uncallout(&hpriv->sackTimer, hpriv->node); 596da010626SArchie Cobbs } 597add85a1dSArchie Cobbs 598add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */ 599add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 600add85a1dSArchie Cobbs if (m == NULL) { 601a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 602add85a1dSArchie Cobbs if (m == NULL) { 603678f9e33SArchie Cobbs priv->stats.memoryFailures++; 60483beeed9SGleb Smirnoff ERROUT(ENOBUFS); 605add85a1dSArchie Cobbs } 606add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen; 607add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL; 608add85a1dSArchie Cobbs } else { 609a163d034SWarner Losh M_PREPEND(m, grelen, M_DONTWAIT); 610add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen 611add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) { 612678f9e33SArchie Cobbs priv->stats.memoryFailures++; 61383beeed9SGleb Smirnoff ERROUT(ENOBUFS); 614add85a1dSArchie Cobbs } 615add85a1dSArchie Cobbs } 616add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen); 617add85a1dSArchie Cobbs 6189bee7adfSArchie Cobbs /* Update stats */ 6199bee7adfSArchie Cobbs priv->stats.xmitPackets++; 6209bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 6219bee7adfSArchie Cobbs 62283beeed9SGleb Smirnoff /* 62383beeed9SGleb Smirnoff * XXX: we should reset timer only after an item has been sent 62483beeed9SGleb Smirnoff * successfully. 62583beeed9SGleb Smirnoff */ 626489290e9SAlexander Motin if (hpriv->conf.enableWindowing && 627489290e9SAlexander Motin gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1) 628489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv); 62983beeed9SGleb Smirnoff 630489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 63183beeed9SGleb Smirnoff 632add85a1dSArchie Cobbs /* Deliver packet */ 633069154d5SJulian Elischer if (item) { 634069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->lower, m); 635069154d5SJulian Elischer } else { 636069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m); 637069154d5SJulian Elischer } 638069154d5SJulian Elischer 63983beeed9SGleb Smirnoff return (error); 640678f9e33SArchie Cobbs 64183beeed9SGleb Smirnoff done: 642489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 64383beeed9SGleb Smirnoff NG_FREE_M(m); 64483beeed9SGleb Smirnoff if (item) 64583beeed9SGleb Smirnoff NG_FREE_ITEM(item); 646add85a1dSArchie Cobbs return (error); 647add85a1dSArchie Cobbs } 648add85a1dSArchie Cobbs 649add85a1dSArchie Cobbs /* 650add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header. 651add85a1dSArchie Cobbs */ 652add85a1dSArchie Cobbs static int 653489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item) 654add85a1dSArchie Cobbs { 655489290e9SAlexander Motin hpriv_p hpriv; 656489290e9SAlexander Motin node_p node = NG_HOOK_NODE(hook); 65730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 658add85a1dSArchie Cobbs int iphlen, grelen, extralen; 659816b834fSArchie Cobbs const struct greheader *gre; 660816b834fSArchie Cobbs const struct ip *ip; 661add85a1dSArchie Cobbs int error = 0; 662069154d5SJulian Elischer struct mbuf *m; 663add85a1dSArchie Cobbs 664069154d5SJulian Elischer NGI_GET_M(item, m); 6659bee7adfSArchie Cobbs /* Update stats */ 6669bee7adfSArchie Cobbs priv->stats.recvPackets++; 6679bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 6689bee7adfSArchie Cobbs 669add85a1dSArchie Cobbs /* Sanity check packet length */ 670add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) { 6719bee7adfSArchie Cobbs priv->stats.recvRunts++; 67283beeed9SGleb Smirnoff ERROUT(EINVAL); 673add85a1dSArchie Cobbs } 674add85a1dSArchie Cobbs 675add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */ 676add85a1dSArchie Cobbs if (m->m_len < sizeof(*ip) + sizeof(*gre) 677add85a1dSArchie Cobbs && (m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) { 678678f9e33SArchie Cobbs priv->stats.memoryFailures++; 67983beeed9SGleb Smirnoff ERROUT(ENOBUFS); 680add85a1dSArchie Cobbs } 681816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 682add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2; 683add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) { 684add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) { 685678f9e33SArchie Cobbs priv->stats.memoryFailures++; 68683beeed9SGleb Smirnoff ERROUT(ENOBUFS); 687add85a1dSArchie Cobbs } 688816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 689add85a1dSArchie Cobbs } 690816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 691add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck); 6929bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) { 6939bee7adfSArchie Cobbs priv->stats.recvRunts++; 69483beeed9SGleb Smirnoff ERROUT(EINVAL); 6959bee7adfSArchie Cobbs } 696add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) { 697add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) { 698678f9e33SArchie Cobbs priv->stats.memoryFailures++; 69983beeed9SGleb Smirnoff ERROUT(ENOBUFS); 700add85a1dSArchie Cobbs } 701816b834fSArchie Cobbs ip = mtod(m, const struct ip *); 702816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen); 703add85a1dSArchie Cobbs } 704add85a1dSArchie Cobbs 705add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */ 706add85a1dSArchie Cobbs extralen = m->m_pkthdr.len 70738f2d636SAlexander Motin - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length)); 7089bee7adfSArchie Cobbs if (extralen < 0) { 7099bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 71083beeed9SGleb Smirnoff ERROUT(EINVAL); 7119bee7adfSArchie Cobbs } 71238f2d636SAlexander Motin if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) { 7139bee7adfSArchie Cobbs priv->stats.recvBadGRE++; 71483beeed9SGleb Smirnoff ERROUT(EINVAL); 7159bee7adfSArchie Cobbs } 716489290e9SAlexander Motin 71738f2d636SAlexander Motin hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid)); 718489290e9SAlexander Motin if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) { 7199bee7adfSArchie Cobbs priv->stats.recvBadCID++; 72083beeed9SGleb Smirnoff ERROUT(EINVAL); 7219bee7adfSArchie Cobbs } 722489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 723add85a1dSArchie Cobbs 724add85a1dSArchie Cobbs /* Look for peer ack */ 725add85a1dSArchie Cobbs if (gre->hasAck) { 72638f2d636SAlexander Motin const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]); 727489290e9SAlexander Motin const int index = ack - hpriv->recvAck - 1; 72822dfb9bdSArchie Cobbs long sample; 729add85a1dSArchie Cobbs long diff; 730add85a1dSArchie Cobbs 731add85a1dSArchie Cobbs /* Sanity check ack value */ 732489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) { 7339bee7adfSArchie Cobbs priv->stats.recvBadAcks++; 7349bee7adfSArchie Cobbs goto badAck; /* we never sent it! */ 7359bee7adfSArchie Cobbs } 736489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0) 7379bee7adfSArchie Cobbs goto badAck; /* ack already timed out */ 738489290e9SAlexander Motin hpriv->recvAck = ack; 739add85a1dSArchie Cobbs 740add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 741489290e9SAlexander Motin if (hpriv->conf.enableWindowing) { 742489290e9SAlexander Motin sample = ng_pptpgre_time() - hpriv->timeSent[index]; 743489290e9SAlexander Motin diff = sample - hpriv->rtt; 744489290e9SAlexander Motin hpriv->rtt += PPTP_ACK_ALPHA(diff); 745add85a1dSArchie Cobbs if (diff < 0) 746add85a1dSArchie Cobbs diff = -diff; 747489290e9SAlexander Motin hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev); 748f8e159d6SGleb Smirnoff /* +2 to compensate low precision of int math */ 749489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2); 750489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT) 751489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 752489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT) 753489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT; 754e962a823SArchie Cobbs 755e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */ 756489290e9SAlexander Motin bcopy(hpriv->timeSent + index + 1, hpriv->timeSent, 757489290e9SAlexander Motin sizeof(*hpriv->timeSent) 758922ee196SArchie Cobbs * (PPTP_XMIT_WIN - (index + 1))); 759e962a823SArchie Cobbs 760922ee196SArchie Cobbs /* If we sent an entire window, increase window size */ 761489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0 762489290e9SAlexander Motin && hpriv->xmitWin < PPTP_XMIT_WIN) { 763489290e9SAlexander Motin hpriv->xmitWin++; 764489290e9SAlexander Motin hpriv->winAck = ack + hpriv->xmitWin; 765add85a1dSArchie Cobbs } 766add85a1dSArchie Cobbs 7679bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */ 768714f558bSAlexander Motin ng_uncallout(&hpriv->rackTimer, hpriv->node); 769489290e9SAlexander Motin if (hpriv->recvAck != hpriv->xmitSeq) 770489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv); 771add85a1dSArchie Cobbs } 772922ee196SArchie Cobbs } 7739bee7adfSArchie Cobbs badAck: 774add85a1dSArchie Cobbs 775add85a1dSArchie Cobbs /* See if frame contains any data */ 776add85a1dSArchie Cobbs if (gre->hasSeq) { 77738f2d636SAlexander Motin const u_int32_t seq = be32dec(&gre->data[0]); 778add85a1dSArchie Cobbs 779add85a1dSArchie Cobbs /* Sanity check sequence number */ 780489290e9SAlexander Motin if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) { 781489290e9SAlexander Motin if (seq == hpriv->recvSeq) 7829bee7adfSArchie Cobbs priv->stats.recvDuplicates++; 7839bee7adfSArchie Cobbs else 7849bee7adfSArchie Cobbs priv->stats.recvOutOfOrder++; 785489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 78683beeed9SGleb Smirnoff ERROUT(EINVAL); 7879bee7adfSArchie Cobbs } 788489290e9SAlexander Motin hpriv->recvSeq = seq; 789add85a1dSArchie Cobbs 790add85a1dSArchie Cobbs /* We need to acknowledge this packet; do it soon... */ 791489290e9SAlexander Motin if (!(callout_pending(&hpriv->sackTimer))) { 792678f9e33SArchie Cobbs /* If delayed ACK is disabled, send it now */ 793489290e9SAlexander Motin if (!hpriv->conf.enableDelayedAck) { /* ack now */ 794489290e9SAlexander Motin ng_pptpgre_xmit(hpriv, NULL); 795489290e9SAlexander Motin /* ng_pptpgre_xmit() drops the mutex */ 79683beeed9SGleb Smirnoff } else { /* ack later */ 797714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv); 798489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 799add85a1dSArchie Cobbs } 800489290e9SAlexander Motin } else 801489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 802add85a1dSArchie Cobbs 803add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */ 804add85a1dSArchie Cobbs m_adj(m, iphlen + grelen); 805add85a1dSArchie Cobbs if (extralen > 0) 806add85a1dSArchie Cobbs m_adj(m, -extralen); 807add85a1dSArchie Cobbs 808489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 809489290e9SAlexander Motin 810add85a1dSArchie Cobbs /* Deliver frame to upper layers */ 811489290e9SAlexander Motin NG_FWD_NEW_DATA(error, item, hpriv->hook, m); 8129bee7adfSArchie Cobbs } else { 8139bee7adfSArchie Cobbs priv->stats.recvLoneAcks++; 814489290e9SAlexander Motin mtx_unlock(&hpriv->mtx); 815069154d5SJulian Elischer NG_FREE_ITEM(item); 816069154d5SJulian Elischer NG_FREE_M(m); /* no data to deliver */ 8179bee7adfSArchie Cobbs } 81883beeed9SGleb Smirnoff 81983beeed9SGleb Smirnoff return (error); 82083beeed9SGleb Smirnoff 82183beeed9SGleb Smirnoff done: 82283beeed9SGleb Smirnoff NG_FREE_ITEM(item); 82383beeed9SGleb Smirnoff NG_FREE_M(m); 824add85a1dSArchie Cobbs return (error); 825add85a1dSArchie Cobbs } 826add85a1dSArchie Cobbs 827add85a1dSArchie Cobbs /************************************************************************* 828add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS 829add85a1dSArchie Cobbs *************************************************************************/ 830add85a1dSArchie Cobbs 831add85a1dSArchie Cobbs /* 8329bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged 833add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before 834add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running 835add85a1dSArchie Cobbs * recv ack timer, if any. 836add85a1dSArchie Cobbs */ 837add85a1dSArchie Cobbs static void 838489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv) 839add85a1dSArchie Cobbs { 840678f9e33SArchie Cobbs int remain, ticks; 841add85a1dSArchie Cobbs 842add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out, 843add85a1dSArchie Cobbs and reset the timer to that time. */ 844489290e9SAlexander Motin remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time(); 845add85a1dSArchie Cobbs if (remain < 0) 846add85a1dSArchie Cobbs remain = 0; 8479bee7adfSArchie Cobbs 8484a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 849678f9e33SArchie Cobbs ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; 850489290e9SAlexander Motin ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook, 851489290e9SAlexander Motin ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0); 8524a48abb2SArchie Cobbs } 8534a48abb2SArchie Cobbs 8544a48abb2SArchie Cobbs /* 855add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence 856add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters 857add85a1dSArchie Cobbs * and reset/restart the recv ack timer. 858add85a1dSArchie Cobbs */ 859add85a1dSArchie Cobbs static void 860089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 861add85a1dSArchie Cobbs { 86230400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node); 863489290e9SAlexander Motin const hpriv_p hpriv = arg1; 8649bee7adfSArchie Cobbs 865add85a1dSArchie Cobbs /* Update adaptive timeout stuff */ 8669bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++; 867489290e9SAlexander Motin hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */ 868489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev); 869489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT) 870489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 871489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT) 872489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT; 873678f9e33SArchie Cobbs 874e962a823SArchie Cobbs /* Reset ack and sliding window */ 875489290e9SAlexander Motin hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */ 876489290e9SAlexander Motin hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */ 877489290e9SAlexander Motin hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */ 878add85a1dSArchie Cobbs } 879add85a1dSArchie Cobbs 880add85a1dSArchie Cobbs /* 8819bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not 8829bee7adfSArchie Cobbs * already running. 8839bee7adfSArchie Cobbs */ 8849bee7adfSArchie Cobbs static void 885714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv) 8869bee7adfSArchie Cobbs { 887714f558bSAlexander Motin int ackTimeout, ticks; 888714f558bSAlexander Motin 889714f558bSAlexander Motin /* Take 1/4 of the estimated round trip time */ 890714f558bSAlexander Motin ackTimeout = (hpriv->rtt >> 2); 891714f558bSAlexander Motin if (ackTimeout < PPTP_MIN_ACK_DELAY) 892714f558bSAlexander Motin ackTimeout = PPTP_MIN_ACK_DELAY; 893714f558bSAlexander Motin else if (ackTimeout > PPTP_MAX_ACK_DELAY) 894714f558bSAlexander Motin ackTimeout = PPTP_MAX_ACK_DELAY; 8959bee7adfSArchie Cobbs 8964a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */ 897678f9e33SArchie Cobbs ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); 898489290e9SAlexander Motin ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook, 899489290e9SAlexander Motin ticks, ng_pptpgre_send_ack_timeout, hpriv, 0); 9004a48abb2SArchie Cobbs } 9014a48abb2SArchie Cobbs 9024a48abb2SArchie Cobbs /* 903add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an 904add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to 905add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame, 906add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now. 907add85a1dSArchie Cobbs */ 908add85a1dSArchie Cobbs static void 909089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 910add85a1dSArchie Cobbs { 911489290e9SAlexander Motin const hpriv_p hpriv = arg1; 91283beeed9SGleb Smirnoff 913489290e9SAlexander Motin mtx_lock(&hpriv->mtx); 9149bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */ 915489290e9SAlexander Motin ng_pptpgre_xmit(hpriv, NULL); 916489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED); 917add85a1dSArchie Cobbs } 918add85a1dSArchie Cobbs 919add85a1dSArchie Cobbs /************************************************************************* 920add85a1dSArchie Cobbs MISC FUNCTIONS 921add85a1dSArchie Cobbs *************************************************************************/ 922add85a1dSArchie Cobbs 923add85a1dSArchie Cobbs /* 924489290e9SAlexander Motin * Find the hook with a given session ID. 925489290e9SAlexander Motin */ 926489290e9SAlexander Motin static hpriv_p 927489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid) 928489290e9SAlexander Motin { 929489290e9SAlexander Motin uint16_t hash = SESSHASH(cid); 930489290e9SAlexander Motin hpriv_p hpriv = NULL; 931489290e9SAlexander Motin 932489290e9SAlexander Motin LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) { 933489290e9SAlexander Motin if (hpriv->conf.cid == cid) 934489290e9SAlexander Motin break; 935489290e9SAlexander Motin } 936489290e9SAlexander Motin 937489290e9SAlexander Motin return (hpriv); 938489290e9SAlexander Motin } 939489290e9SAlexander Motin 940489290e9SAlexander Motin /* 941489290e9SAlexander Motin * Reset state (must be called with lock held or from writer) 942add85a1dSArchie Cobbs */ 943add85a1dSArchie Cobbs static void 944489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv) 945add85a1dSArchie Cobbs { 946add85a1dSArchie Cobbs /* Reset adaptive timeout state */ 947489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT; 948714f558bSAlexander Motin hpriv->rtt = PPTP_TIME_SCALE / 10; 949714f558bSAlexander Motin if (hpriv->conf.peerPpd > 1) /* ppd = 0 treat as = 1 */ 950714f558bSAlexander Motin hpriv->rtt *= hpriv->conf.peerPpd; 951489290e9SAlexander Motin hpriv->dev = 0; 952489290e9SAlexander Motin hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2; 953489290e9SAlexander Motin if (hpriv->xmitWin < 2) /* often the first packet is lost */ 954489290e9SAlexander Motin hpriv->xmitWin = 2; /* because the peer isn't ready */ 955489290e9SAlexander Motin else if (hpriv->xmitWin > PPTP_XMIT_WIN) 956489290e9SAlexander Motin hpriv->xmitWin = PPTP_XMIT_WIN; 957489290e9SAlexander Motin hpriv->winAck = hpriv->xmitWin; 958add85a1dSArchie Cobbs 959add85a1dSArchie Cobbs /* Reset sequence numbers */ 960489290e9SAlexander Motin hpriv->recvSeq = ~0; 961489290e9SAlexander Motin hpriv->recvAck = ~0; 962489290e9SAlexander Motin hpriv->xmitSeq = ~0; 963489290e9SAlexander Motin hpriv->xmitAck = ~0; 964add85a1dSArchie Cobbs 9654a48abb2SArchie Cobbs /* Stop timers */ 966714f558bSAlexander Motin ng_uncallout(&hpriv->sackTimer, hpriv->node); 967714f558bSAlexander Motin ng_uncallout(&hpriv->rackTimer, hpriv->node); 968add85a1dSArchie Cobbs } 969add85a1dSArchie Cobbs 970add85a1dSArchie Cobbs /* 971add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format. 972add85a1dSArchie Cobbs */ 973add85a1dSArchie Cobbs static pptptime_t 974489290e9SAlexander Motin ng_pptpgre_time(void) 975add85a1dSArchie Cobbs { 976add85a1dSArchie Cobbs struct timeval tv; 977678f9e33SArchie Cobbs pptptime_t t; 978add85a1dSArchie Cobbs 979678f9e33SArchie Cobbs microuptime(&tv); 980678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; 981714f558bSAlexander Motin t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE); 982678f9e33SArchie Cobbs return(t); 983add85a1dSArchie Cobbs } 984