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 * $Whistle: ng_pptpgre.c,v 1.7 1999/12/08 00:10:06 archie Exp $
40add85a1dSArchie Cobbs */
41add85a1dSArchie Cobbs
42add85a1dSArchie Cobbs /*
43add85a1dSArchie Cobbs * PPTP/GRE netgraph node type.
44add85a1dSArchie Cobbs *
45add85a1dSArchie Cobbs * This node type does the GRE encapsulation as specified for the PPTP
46add85a1dSArchie Cobbs * protocol (RFC 2637, section 4). This includes sequencing and
47add85a1dSArchie Cobbs * retransmission of frames, but not the actual packet delivery nor
48add85a1dSArchie Cobbs * any of the TCP control stream protocol.
49add85a1dSArchie Cobbs *
50add85a1dSArchie Cobbs * The "upper" hook of this node is suitable for attaching to a "ppp"
51add85a1dSArchie Cobbs * node link hook. The "lower" hook of this node is suitable for attaching
52add85a1dSArchie Cobbs * to a "ksocket" node on hook "inet/raw/gre".
53add85a1dSArchie Cobbs */
54add85a1dSArchie Cobbs
55add85a1dSArchie Cobbs #include <sys/param.h>
56add85a1dSArchie Cobbs #include <sys/systm.h>
57add85a1dSArchie Cobbs #include <sys/kernel.h>
58add85a1dSArchie Cobbs #include <sys/time.h>
59f2ba84d7SGleb Smirnoff #include <sys/lock.h>
60add85a1dSArchie Cobbs #include <sys/malloc.h>
61f2ba84d7SGleb Smirnoff #include <sys/mbuf.h>
62f2ba84d7SGleb Smirnoff #include <sys/mutex.h>
6338f2d636SAlexander Motin #include <sys/endian.h>
64add85a1dSArchie Cobbs #include <sys/errno.h>
65a594f945SEugene Grosbein #include <sys/sysctl.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
76*9b8db664SDmitry Lukhtionov #ifdef NG_SEPARATE_MALLOC
77*9b8db664SDmitry Lukhtionov static MALLOC_DEFINE(M_NETGRAPH_PPTP, "netgraph_pptp", "netgraph pptpgre node");
78*9b8db664SDmitry Lukhtionov #else
79*9b8db664SDmitry Lukhtionov #define M_NETGRAPH_PPTP M_NETGRAPH
80*9b8db664SDmitry Lukhtionov #endif
81*9b8db664SDmitry Lukhtionov
82add85a1dSArchie Cobbs /* GRE packet format, as used by PPTP */
83add85a1dSArchie Cobbs struct greheader {
84add85a1dSArchie Cobbs #if BYTE_ORDER == LITTLE_ENDIAN
85add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */
86add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */
87add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */
88add85a1dSArchie Cobbs u_char hasKey:1; /* key present */
89add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */
90add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */
91add85a1dSArchie Cobbs u_char vers:3; /* version */
92add85a1dSArchie Cobbs u_char flags:4; /* flags */
93add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */
94add85a1dSArchie Cobbs #elif BYTE_ORDER == BIG_ENDIAN
95add85a1dSArchie Cobbs u_char hasSum:1; /* checksum present */
96add85a1dSArchie Cobbs u_char hasRoute:1; /* routing present */
97add85a1dSArchie Cobbs u_char hasKey:1; /* key present */
98add85a1dSArchie Cobbs u_char hasSeq:1; /* sequence number present */
99add85a1dSArchie Cobbs u_char ssr:1; /* strict source route */
100add85a1dSArchie Cobbs u_char recursion:3; /* recursion control */
101add85a1dSArchie Cobbs u_char hasAck:1; /* acknowlege number present */
102add85a1dSArchie Cobbs u_char flags:4; /* flags */
103add85a1dSArchie Cobbs u_char vers:3; /* version */
104add85a1dSArchie Cobbs #else
105add85a1dSArchie Cobbs #error BYTE_ORDER is not defined properly
106add85a1dSArchie Cobbs #endif
107add85a1dSArchie Cobbs u_int16_t proto; /* protocol (ethertype) */
108add85a1dSArchie Cobbs u_int16_t length; /* payload length */
109add85a1dSArchie Cobbs u_int16_t cid; /* call id */
110add85a1dSArchie Cobbs u_int32_t data[0]; /* opt. seq, ack, then data */
111add85a1dSArchie Cobbs };
112add85a1dSArchie Cobbs
113add85a1dSArchie Cobbs /* The PPTP protocol ID used in the GRE 'proto' field */
114add85a1dSArchie Cobbs #define PPTP_GRE_PROTO 0x880b
115add85a1dSArchie Cobbs
116add85a1dSArchie Cobbs /* Bits that must be set a certain way in all PPTP/GRE packets */
117add85a1dSArchie Cobbs #define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO)
118add85a1dSArchie Cobbs #define PPTP_INIT_MASK 0xef7fffff
119add85a1dSArchie Cobbs
120add85a1dSArchie Cobbs /* Min and max packet length */
121add85a1dSArchie Cobbs #define PPTP_MAX_PAYLOAD (0xffff - sizeof(struct greheader) - 8)
122add85a1dSArchie Cobbs
123add85a1dSArchie Cobbs /* All times are scaled by this (PPTP_TIME_SCALE time units = 1 sec.) */
124714f558bSAlexander Motin #define PPTP_TIME_SCALE 1024 /* milliseconds */
125678f9e33SArchie Cobbs typedef u_int64_t pptptime_t;
126add85a1dSArchie Cobbs
127add85a1dSArchie Cobbs /* Acknowledgment timeout parameters and functions */
128e962a823SArchie Cobbs #define PPTP_XMIT_WIN 16 /* max xmit window */
1294a48abb2SArchie Cobbs #define PPTP_MIN_TIMEOUT (PPTP_TIME_SCALE / 83) /* 12 milliseconds */
1300306463aSGleb Smirnoff #define PPTP_MAX_TIMEOUT (3 * PPTP_TIME_SCALE) /* 3 seconds */
131add85a1dSArchie Cobbs
132a594f945SEugene Grosbein #define PPTP_REORDER_TIMEOUT 1
133a594f945SEugene Grosbein
134053359b7SPedro F. Giffuni /* When we receive a packet, we wait to see if there's an outgoing packet
13540a57b00SGordon Bergling we can piggy-back the ACK off of. These parameters determine the minimum
136da010626SArchie Cobbs and maxmimum length of time we're willing to wait in order to do that.
137da010626SArchie Cobbs These have no effect unless "enableDelayedAck" is turned on. */
138da010626SArchie Cobbs #define PPTP_MIN_ACK_DELAY (PPTP_TIME_SCALE / 500) /* 2 milliseconds */
139da010626SArchie Cobbs #define PPTP_MAX_ACK_DELAY (PPTP_TIME_SCALE / 2) /* 500 milliseconds */
140da010626SArchie Cobbs
141e962a823SArchie Cobbs /* See RFC 2637 section 4.4 */
142f8e159d6SGleb Smirnoff #define PPTP_ACK_ALPHA(x) (((x) + 4) >> 3) /* alpha = 0.125 */
143f8e159d6SGleb Smirnoff #define PPTP_ACK_BETA(x) (((x) + 2) >> 2) /* beta = 0.25 */
144add85a1dSArchie Cobbs #define PPTP_ACK_CHI(x) ((x) << 2) /* chi = 4 */
145add85a1dSArchie Cobbs #define PPTP_ACK_DELTA(x) ((x) << 1) /* delta = 2 */
146add85a1dSArchie Cobbs
1479bee7adfSArchie Cobbs #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y))
1489bee7adfSArchie Cobbs
149489290e9SAlexander Motin #define SESSHASHSIZE 0x0020
150489290e9SAlexander Motin #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
151489290e9SAlexander Motin
1527029da5cSPawel Biernacki SYSCTL_NODE(_net_graph, OID_AUTO, pptpgre, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1537029da5cSPawel Biernacki "PPTPGRE");
154a594f945SEugene Grosbein
155a594f945SEugene Grosbein /*
156a594f945SEugene Grosbein * Reorder queue maximum length. Zero disables reorder.
157a594f945SEugene Grosbein *
158a594f945SEugene Grosbein * The node may keep reorder_max queue entries per session
159a594f945SEugene Grosbein * if reorder is enabled, plus allocate one more for short time.
160a594f945SEugene Grosbein *
161a594f945SEugene Grosbein * Be conservative in memory consumption by default.
162a594f945SEugene Grosbein * Lots of sessions with large queues can overflow M_NETGRAPH zone.
163a594f945SEugene Grosbein */
164a594f945SEugene Grosbein static int reorder_max = 1; /* reorder up to two swapped packets in a row */
165a594f945SEugene Grosbein SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_max, CTLFLAG_RWTUN,
166a594f945SEugene Grosbein &reorder_max, 0, "Reorder queue maximum length");
167a594f945SEugene Grosbein
168a594f945SEugene Grosbein static int reorder_timeout = PPTP_REORDER_TIMEOUT;
169a594f945SEugene Grosbein SYSCTL_UINT(_net_graph_pptpgre, OID_AUTO, reorder_timeout, CTLFLAG_RWTUN,
170a594f945SEugene Grosbein &reorder_timeout, 0, "Reorder timeout is milliseconds");
171a594f945SEugene Grosbein
172a594f945SEugene Grosbein /* Packet reorder FIFO queue */
173a594f945SEugene Grosbein struct ng_pptpgre_roq {
174a594f945SEugene Grosbein SLIST_ENTRY(ng_pptpgre_roq) next; /* next entry of the queue */
175a594f945SEugene Grosbein item_p item; /* netgraph item */
176a594f945SEugene Grosbein u_int32_t seq; /* packet sequence number */
177a594f945SEugene Grosbein };
178a594f945SEugene Grosbein SLIST_HEAD(ng_pptpgre_roq_head, ng_pptpgre_roq);
179a594f945SEugene Grosbein typedef struct ng_pptpgre_roq_head roqh;
180a594f945SEugene Grosbein
181add85a1dSArchie Cobbs /* We keep packet retransmit and acknowlegement state in this struct */
182489290e9SAlexander Motin struct ng_pptpgre_sess {
183489290e9SAlexander Motin node_p node; /* this node pointer */
184489290e9SAlexander Motin hook_p hook; /* hook to upper layers */
185489290e9SAlexander Motin struct ng_pptpgre_conf conf; /* configuration info */
186489290e9SAlexander Motin struct mtx mtx; /* session mutex */
187489290e9SAlexander Motin u_int32_t recvSeq; /* last seq # we rcv'd */
188489290e9SAlexander Motin u_int32_t xmitSeq; /* last seq # we sent */
189489290e9SAlexander Motin u_int32_t recvAck; /* last seq # peer ack'd */
190489290e9SAlexander Motin u_int32_t xmitAck; /* last seq # we ack'd */
191add85a1dSArchie Cobbs int32_t ato; /* adaptive time-out value */
192add85a1dSArchie Cobbs int32_t rtt; /* round trip time estimate */
193add85a1dSArchie Cobbs int32_t dev; /* deviation estimate */
194add85a1dSArchie Cobbs u_int16_t xmitWin; /* size of xmit window */
1954a48abb2SArchie Cobbs struct callout sackTimer; /* send ack timer */
1964a48abb2SArchie Cobbs struct callout rackTimer; /* recv ack timer */
1973cd7db22SArchie Cobbs u_int32_t winAck; /* seq when xmitWin will grow */
198add85a1dSArchie Cobbs pptptime_t timeSent[PPTP_XMIT_WIN];
199489290e9SAlexander Motin LIST_ENTRY(ng_pptpgre_sess) sessions;
200a594f945SEugene Grosbein roqh roq; /* reorder queue head */
201a594f945SEugene Grosbein u_int8_t roq_len; /* reorder queue length */
202a594f945SEugene Grosbein struct callout reorderTimer; /* reorder timeout handler */
203add85a1dSArchie Cobbs };
204489290e9SAlexander Motin typedef struct ng_pptpgre_sess *hpriv_p;
205add85a1dSArchie Cobbs
206add85a1dSArchie Cobbs /* Node private data */
207add85a1dSArchie Cobbs struct ng_pptpgre_private {
208add85a1dSArchie Cobbs hook_p upper; /* hook to upper layers */
209add85a1dSArchie Cobbs hook_p lower; /* hook to lower layers */
210489290e9SAlexander Motin struct ng_pptpgre_sess uppersess; /* default session for compat */
211489290e9SAlexander Motin LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE];
2129bee7adfSArchie Cobbs struct ng_pptpgre_stats stats; /* node statistics */
213add85a1dSArchie Cobbs };
214add85a1dSArchie Cobbs typedef struct ng_pptpgre_private *priv_p;
215add85a1dSArchie Cobbs
216add85a1dSArchie Cobbs /* Netgraph node methods */
217add85a1dSArchie Cobbs static ng_constructor_t ng_pptpgre_constructor;
218add85a1dSArchie Cobbs static ng_rcvmsg_t ng_pptpgre_rcvmsg;
219069154d5SJulian Elischer static ng_shutdown_t ng_pptpgre_shutdown;
220add85a1dSArchie Cobbs static ng_newhook_t ng_pptpgre_newhook;
221add85a1dSArchie Cobbs static ng_rcvdata_t ng_pptpgre_rcvdata;
222489290e9SAlexander Motin static ng_rcvdata_t ng_pptpgre_rcvdata_lower;
223add85a1dSArchie Cobbs static ng_disconnect_t ng_pptpgre_disconnect;
224add85a1dSArchie Cobbs
225add85a1dSArchie Cobbs /* Helper functions */
226489290e9SAlexander Motin static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item);
227714f558bSAlexander Motin static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv);
228489290e9SAlexander Motin static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv);
229a594f945SEugene Grosbein static void ng_pptpgre_start_reorder_timer(hpriv_p hpriv);
230089323f3SGleb Smirnoff static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook,
231089323f3SGleb Smirnoff void *arg1, int arg2);
232089323f3SGleb Smirnoff static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook,
233089323f3SGleb Smirnoff void *arg1, int arg2);
234a594f945SEugene Grosbein static void ng_pptpgre_reorder_timeout(node_p node, hook_p hook,
235a594f945SEugene Grosbein void *arg1, int arg2);
236489290e9SAlexander Motin static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid);
237489290e9SAlexander Motin static void ng_pptpgre_reset(hpriv_p hpriv);
238489290e9SAlexander Motin static pptptime_t ng_pptpgre_time(void);
239a594f945SEugene Grosbein static void ng_pptpgre_ack(const hpriv_p hpriv);
240a594f945SEugene Grosbein static int ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q,
241a594f945SEugene Grosbein const struct ng_pptpgre_roq *st);
242add85a1dSArchie Cobbs
243add85a1dSArchie Cobbs /* Parse type for struct ng_pptpgre_conf */
244f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[]
245f0184ff8SArchie Cobbs = NG_PPTPGRE_CONF_TYPE_INFO;
246add85a1dSArchie Cobbs static const struct ng_parse_type ng_pptpgre_conf_type = {
247add85a1dSArchie Cobbs &ng_parse_struct_type,
248f0184ff8SArchie Cobbs &ng_pptpgre_conf_type_fields,
249add85a1dSArchie Cobbs };
250add85a1dSArchie Cobbs
2519bee7adfSArchie Cobbs /* Parse type for struct ng_pptpgre_stats */
252f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pptpgre_stats_type_fields[]
253f0184ff8SArchie Cobbs = NG_PPTPGRE_STATS_TYPE_INFO;
2549bee7adfSArchie Cobbs static const struct ng_parse_type ng_pptp_stats_type = {
2559bee7adfSArchie Cobbs &ng_parse_struct_type,
256f0184ff8SArchie Cobbs &ng_pptpgre_stats_type_fields
2579bee7adfSArchie Cobbs };
2589bee7adfSArchie Cobbs
259add85a1dSArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
260add85a1dSArchie Cobbs static const struct ng_cmdlist ng_pptpgre_cmdlist[] = {
261add85a1dSArchie Cobbs {
262add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE,
263add85a1dSArchie Cobbs NGM_PPTPGRE_SET_CONFIG,
264add85a1dSArchie Cobbs "setconfig",
265add85a1dSArchie Cobbs &ng_pptpgre_conf_type,
266add85a1dSArchie Cobbs NULL
267add85a1dSArchie Cobbs },
268add85a1dSArchie Cobbs {
269add85a1dSArchie Cobbs NGM_PPTPGRE_COOKIE,
270add85a1dSArchie Cobbs NGM_PPTPGRE_GET_CONFIG,
271add85a1dSArchie Cobbs "getconfig",
272489290e9SAlexander Motin &ng_parse_hint16_type,
273add85a1dSArchie Cobbs &ng_pptpgre_conf_type
274add85a1dSArchie Cobbs },
2759bee7adfSArchie Cobbs {
2769bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE,
2779bee7adfSArchie Cobbs NGM_PPTPGRE_GET_STATS,
2789bee7adfSArchie Cobbs "getstats",
2799bee7adfSArchie Cobbs NULL,
2809bee7adfSArchie Cobbs &ng_pptp_stats_type
2819bee7adfSArchie Cobbs },
2829bee7adfSArchie Cobbs {
2839bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE,
2849bee7adfSArchie Cobbs NGM_PPTPGRE_CLR_STATS,
2859bee7adfSArchie Cobbs "clrstats",
2869bee7adfSArchie Cobbs NULL,
2879bee7adfSArchie Cobbs NULL
2889bee7adfSArchie Cobbs },
2899bee7adfSArchie Cobbs {
2909bee7adfSArchie Cobbs NGM_PPTPGRE_COOKIE,
2919bee7adfSArchie Cobbs NGM_PPTPGRE_GETCLR_STATS,
2929bee7adfSArchie Cobbs "getclrstats",
2939bee7adfSArchie Cobbs NULL,
2949bee7adfSArchie Cobbs &ng_pptp_stats_type
2959bee7adfSArchie Cobbs },
296add85a1dSArchie Cobbs { 0 }
297add85a1dSArchie Cobbs };
298add85a1dSArchie Cobbs
299add85a1dSArchie Cobbs /* Node type descriptor */
300add85a1dSArchie Cobbs static struct ng_type ng_pptpgre_typestruct = {
301f8aae777SJulian Elischer .version = NG_ABI_VERSION,
302f8aae777SJulian Elischer .name = NG_PPTPGRE_NODE_TYPE,
303f8aae777SJulian Elischer .constructor = ng_pptpgre_constructor,
304f8aae777SJulian Elischer .rcvmsg = ng_pptpgre_rcvmsg,
305f8aae777SJulian Elischer .shutdown = ng_pptpgre_shutdown,
306f8aae777SJulian Elischer .newhook = ng_pptpgre_newhook,
307f8aae777SJulian Elischer .rcvdata = ng_pptpgre_rcvdata,
308f8aae777SJulian Elischer .disconnect = ng_pptpgre_disconnect,
309f8aae777SJulian Elischer .cmdlist = ng_pptpgre_cmdlist,
310add85a1dSArchie Cobbs };
311add85a1dSArchie Cobbs NETGRAPH_INIT(pptpgre, &ng_pptpgre_typestruct);
312add85a1dSArchie Cobbs
313add85a1dSArchie Cobbs #define ERROUT(x) do { error = (x); goto done; } while (0)
314add85a1dSArchie Cobbs
315add85a1dSArchie Cobbs /************************************************************************
316add85a1dSArchie Cobbs NETGRAPH NODE STUFF
317add85a1dSArchie Cobbs ************************************************************************/
318add85a1dSArchie Cobbs
319add85a1dSArchie Cobbs /*
320add85a1dSArchie Cobbs * Node type constructor
321add85a1dSArchie Cobbs */
322add85a1dSArchie Cobbs static int
ng_pptpgre_constructor(node_p node)323069154d5SJulian Elischer ng_pptpgre_constructor(node_p node)
324add85a1dSArchie Cobbs {
325add85a1dSArchie Cobbs priv_p priv;
326489290e9SAlexander Motin int i;
327add85a1dSArchie Cobbs
328add85a1dSArchie Cobbs /* Allocate private structure */
329*9b8db664SDmitry Lukhtionov priv = malloc(sizeof(*priv), M_NETGRAPH_PPTP, M_WAITOK | M_ZERO);
330add85a1dSArchie Cobbs
33130400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, priv);
332add85a1dSArchie Cobbs
333add85a1dSArchie Cobbs /* Initialize state */
334489290e9SAlexander Motin mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF);
335489290e9SAlexander Motin ng_callout_init(&priv->uppersess.sackTimer);
336489290e9SAlexander Motin ng_callout_init(&priv->uppersess.rackTimer);
337489290e9SAlexander Motin priv->uppersess.node = node;
338489290e9SAlexander Motin
339a594f945SEugene Grosbein SLIST_INIT(&priv->uppersess.roq);
340a594f945SEugene Grosbein priv->uppersess.roq_len = 0;
341a594f945SEugene Grosbein ng_callout_init(&priv->uppersess.reorderTimer);
342a594f945SEugene Grosbein
343489290e9SAlexander Motin for (i = 0; i < SESSHASHSIZE; i++)
344489290e9SAlexander Motin LIST_INIT(&priv->sesshash[i]);
345489290e9SAlexander Motin
346489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions);
347add85a1dSArchie Cobbs
348add85a1dSArchie Cobbs /* Done */
349add85a1dSArchie Cobbs return (0);
350add85a1dSArchie Cobbs }
351add85a1dSArchie Cobbs
352add85a1dSArchie Cobbs /*
353add85a1dSArchie Cobbs * Give our OK for a hook to be added.
354add85a1dSArchie Cobbs */
355add85a1dSArchie Cobbs static int
ng_pptpgre_newhook(node_p node,hook_p hook,const char * name)356add85a1dSArchie Cobbs ng_pptpgre_newhook(node_p node, hook_p hook, const char *name)
357add85a1dSArchie Cobbs {
35830400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
359add85a1dSArchie Cobbs
360add85a1dSArchie Cobbs /* Check hook name */
361489290e9SAlexander Motin if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) {
362489290e9SAlexander Motin priv->upper = hook;
363489290e9SAlexander Motin priv->uppersess.hook = hook;
364489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, &priv->uppersess);
365489290e9SAlexander Motin } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) {
366489290e9SAlexander Motin priv->lower = hook;
367489290e9SAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower);
368489290e9SAlexander Motin } else {
369489290e9SAlexander Motin static const char hexdig[16] = "0123456789abcdef";
370489290e9SAlexander Motin const char *hex;
371489290e9SAlexander Motin hpriv_p hpriv;
372489290e9SAlexander Motin int i, j;
373489290e9SAlexander Motin uint16_t cid, hash;
374489290e9SAlexander Motin
375489290e9SAlexander Motin /* Parse hook name to get session ID */
376489290e9SAlexander Motin if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P,
377489290e9SAlexander Motin sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0)
378489290e9SAlexander Motin return (EINVAL);
379489290e9SAlexander Motin hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1;
380489290e9SAlexander Motin for (cid = i = 0; i < 4; i++) {
381489290e9SAlexander Motin for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
382489290e9SAlexander Motin if (j == 16)
383489290e9SAlexander Motin return (EINVAL);
384489290e9SAlexander Motin cid = (cid << 4) | j;
385489290e9SAlexander Motin }
386489290e9SAlexander Motin if (hex[i] != '\0')
387add85a1dSArchie Cobbs return (EINVAL);
388add85a1dSArchie Cobbs
389*9b8db664SDmitry Lukhtionov hpriv = malloc(sizeof(*hpriv), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
390489290e9SAlexander Motin if (hpriv == NULL)
391489290e9SAlexander Motin return (ENOMEM);
392add85a1dSArchie Cobbs
393489290e9SAlexander Motin /* Initialize state */
394489290e9SAlexander Motin mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF);
395489290e9SAlexander Motin ng_callout_init(&hpriv->sackTimer);
396489290e9SAlexander Motin ng_callout_init(&hpriv->rackTimer);
397489290e9SAlexander Motin hpriv->conf.cid = cid;
398489290e9SAlexander Motin hpriv->node = node;
399489290e9SAlexander Motin hpriv->hook = hook;
400a594f945SEugene Grosbein
401a594f945SEugene Grosbein SLIST_INIT(&hpriv->roq);
402a594f945SEugene Grosbein hpriv->roq_len = 0;
403a594f945SEugene Grosbein ng_callout_init(&hpriv->reorderTimer);
404a594f945SEugene Grosbein
405489290e9SAlexander Motin NG_HOOK_SET_PRIVATE(hook, hpriv);
406489290e9SAlexander Motin
407489290e9SAlexander Motin hash = SESSHASH(cid);
408489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions);
409489290e9SAlexander Motin }
410489290e9SAlexander Motin
411add85a1dSArchie Cobbs return (0);
412add85a1dSArchie Cobbs }
413add85a1dSArchie Cobbs
414add85a1dSArchie Cobbs /*
415add85a1dSArchie Cobbs * Receive a control message.
416add85a1dSArchie Cobbs */
417add85a1dSArchie Cobbs static int
ng_pptpgre_rcvmsg(node_p node,item_p item,hook_p lasthook)418069154d5SJulian Elischer ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook)
419add85a1dSArchie Cobbs {
42030400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
421add85a1dSArchie Cobbs struct ng_mesg *resp = NULL;
422add85a1dSArchie Cobbs int error = 0;
423069154d5SJulian Elischer struct ng_mesg *msg;
424add85a1dSArchie Cobbs
425069154d5SJulian Elischer NGI_GET_MSG(item, msg);
426add85a1dSArchie Cobbs switch (msg->header.typecookie) {
427add85a1dSArchie Cobbs case NGM_PPTPGRE_COOKIE:
428add85a1dSArchie Cobbs switch (msg->header.cmd) {
429add85a1dSArchie Cobbs case NGM_PPTPGRE_SET_CONFIG:
430add85a1dSArchie Cobbs {
431add85a1dSArchie Cobbs struct ng_pptpgre_conf *const newConf =
432add85a1dSArchie Cobbs (struct ng_pptpgre_conf *) msg->data;
433489290e9SAlexander Motin hpriv_p hpriv;
434489290e9SAlexander Motin uint16_t hash;
435add85a1dSArchie Cobbs
436add85a1dSArchie Cobbs /* Check for invalid or illegal config */
437add85a1dSArchie Cobbs if (msg->header.arglen != sizeof(*newConf))
438add85a1dSArchie Cobbs ERROUT(EINVAL);
439489290e9SAlexander Motin /* Try to find session by cid. */
440489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv, newConf->cid);
441489290e9SAlexander Motin /* If not present - use upper. */
442489290e9SAlexander Motin if (hpriv == NULL) {
443489290e9SAlexander Motin hpriv = &priv->uppersess;
444489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions);
445489290e9SAlexander Motin hash = SESSHASH(newConf->cid);
446489290e9SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv,
447489290e9SAlexander Motin sessions);
448489290e9SAlexander Motin }
449489290e9SAlexander Motin ng_pptpgre_reset(hpriv); /* reset on configure */
450489290e9SAlexander Motin hpriv->conf = *newConf;
451add85a1dSArchie Cobbs break;
452add85a1dSArchie Cobbs }
453add85a1dSArchie Cobbs case NGM_PPTPGRE_GET_CONFIG:
454489290e9SAlexander Motin {
455489290e9SAlexander Motin hpriv_p hpriv;
456489290e9SAlexander Motin
457489290e9SAlexander Motin if (msg->header.arglen == 2) {
458489290e9SAlexander Motin /* Try to find session by cid. */
459489290e9SAlexander Motin hpriv = ng_pptpgre_find_session(priv,
460489290e9SAlexander Motin *((uint16_t *)msg->data));
461489290e9SAlexander Motin if (hpriv == NULL)
462489290e9SAlexander Motin ERROUT(EINVAL);
463489290e9SAlexander Motin } else if (msg->header.arglen == 0) {
464489290e9SAlexander Motin /* Use upper. */
465489290e9SAlexander Motin hpriv = &priv->uppersess;
466489290e9SAlexander Motin } else
467489290e9SAlexander Motin ERROUT(EINVAL);
468489290e9SAlexander Motin NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT);
469add85a1dSArchie Cobbs if (resp == NULL)
470add85a1dSArchie Cobbs ERROUT(ENOMEM);
471489290e9SAlexander Motin bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf));
472add85a1dSArchie Cobbs break;
473489290e9SAlexander Motin }
4749bee7adfSArchie Cobbs case NGM_PPTPGRE_GET_STATS:
4759bee7adfSArchie Cobbs case NGM_PPTPGRE_CLR_STATS:
4769bee7adfSArchie Cobbs case NGM_PPTPGRE_GETCLR_STATS:
4779bee7adfSArchie Cobbs {
4789bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_CLR_STATS) {
4799bee7adfSArchie Cobbs NG_MKRESPONSE(resp, msg,
4809bee7adfSArchie Cobbs sizeof(priv->stats), M_NOWAIT);
4819bee7adfSArchie Cobbs if (resp == NULL)
4829bee7adfSArchie Cobbs ERROUT(ENOMEM);
4839bee7adfSArchie Cobbs bcopy(&priv->stats,
4849bee7adfSArchie Cobbs resp->data, sizeof(priv->stats));
4859bee7adfSArchie Cobbs }
4869bee7adfSArchie Cobbs if (msg->header.cmd != NGM_PPTPGRE_GET_STATS)
4879bee7adfSArchie Cobbs bzero(&priv->stats, sizeof(priv->stats));
4889bee7adfSArchie Cobbs break;
4899bee7adfSArchie Cobbs }
490add85a1dSArchie Cobbs default:
491add85a1dSArchie Cobbs error = EINVAL;
492add85a1dSArchie Cobbs break;
493add85a1dSArchie Cobbs }
494add85a1dSArchie Cobbs break;
495add85a1dSArchie Cobbs default:
496add85a1dSArchie Cobbs error = EINVAL;
497add85a1dSArchie Cobbs break;
498add85a1dSArchie Cobbs }
499589f6ed8SJulian Elischer done:
500069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp);
501069154d5SJulian Elischer NG_FREE_MSG(msg);
502add85a1dSArchie Cobbs return (error);
503add85a1dSArchie Cobbs }
504add85a1dSArchie Cobbs
505add85a1dSArchie Cobbs /*
506add85a1dSArchie Cobbs * Receive incoming data on a hook.
507add85a1dSArchie Cobbs */
508add85a1dSArchie Cobbs static int
ng_pptpgre_rcvdata(hook_p hook,item_p item)509069154d5SJulian Elischer ng_pptpgre_rcvdata(hook_p hook, item_p item)
510add85a1dSArchie Cobbs {
511489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
512f2ba84d7SGleb Smirnoff int rval;
513add85a1dSArchie Cobbs
514add85a1dSArchie Cobbs /* If not configured, reject */
515489290e9SAlexander Motin if (!hpriv->conf.enabled) {
516069154d5SJulian Elischer NG_FREE_ITEM(item);
517add85a1dSArchie Cobbs return (ENXIO);
518add85a1dSArchie Cobbs }
519add85a1dSArchie Cobbs
520489290e9SAlexander Motin mtx_lock(&hpriv->mtx);
521f2ba84d7SGleb Smirnoff
522489290e9SAlexander Motin rval = ng_pptpgre_xmit(hpriv, item);
523f2ba84d7SGleb Smirnoff
524489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED);
525f2ba84d7SGleb Smirnoff
526f2ba84d7SGleb Smirnoff return (rval);
527add85a1dSArchie Cobbs }
528add85a1dSArchie Cobbs
529add85a1dSArchie Cobbs /*
530489290e9SAlexander Motin * Hook disconnection
531489290e9SAlexander Motin */
532489290e9SAlexander Motin static int
ng_pptpgre_disconnect(hook_p hook)533489290e9SAlexander Motin ng_pptpgre_disconnect(hook_p hook)
534489290e9SAlexander Motin {
535489290e9SAlexander Motin const node_p node = NG_HOOK_NODE(hook);
536489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(node);
537489290e9SAlexander Motin const hpriv_p hpriv = NG_HOOK_PRIVATE(hook);
538489290e9SAlexander Motin
539489290e9SAlexander Motin /* Zero out hook pointer */
540489290e9SAlexander Motin if (hook == priv->upper) {
541489290e9SAlexander Motin priv->upper = NULL;
542489290e9SAlexander Motin priv->uppersess.hook = NULL;
543489290e9SAlexander Motin } else if (hook == priv->lower) {
544489290e9SAlexander Motin priv->lower = NULL;
545489290e9SAlexander Motin } else {
546489290e9SAlexander Motin /* Reset node (stops timers) */
547489290e9SAlexander Motin ng_pptpgre_reset(hpriv);
548489290e9SAlexander Motin
549489290e9SAlexander Motin LIST_REMOVE(hpriv, sessions);
550489290e9SAlexander Motin mtx_destroy(&hpriv->mtx);
551*9b8db664SDmitry Lukhtionov free(hpriv, M_NETGRAPH_PPTP);
552489290e9SAlexander Motin }
553489290e9SAlexander Motin
554489290e9SAlexander Motin /* Go away if no longer connected to anything */
555489290e9SAlexander Motin if ((NG_NODE_NUMHOOKS(node) == 0)
556489290e9SAlexander Motin && (NG_NODE_IS_VALID(node)))
557489290e9SAlexander Motin ng_rmnode_self(node);
558489290e9SAlexander Motin return (0);
559489290e9SAlexander Motin }
560489290e9SAlexander Motin
561489290e9SAlexander Motin /*
562add85a1dSArchie Cobbs * Destroy node
563add85a1dSArchie Cobbs */
564add85a1dSArchie Cobbs static int
ng_pptpgre_shutdown(node_p node)565069154d5SJulian Elischer ng_pptpgre_shutdown(node_p node)
566add85a1dSArchie Cobbs {
56730400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
568add85a1dSArchie Cobbs
569089323f3SGleb Smirnoff /* Reset node (stops timers) */
570489290e9SAlexander Motin ng_pptpgre_reset(&priv->uppersess);
571add85a1dSArchie Cobbs
572489290e9SAlexander Motin LIST_REMOVE(&priv->uppersess, sessions);
573489290e9SAlexander Motin mtx_destroy(&priv->uppersess.mtx);
574f2ba84d7SGleb Smirnoff
575*9b8db664SDmitry Lukhtionov free(priv, M_NETGRAPH_PPTP);
5764a48abb2SArchie Cobbs
5774a48abb2SArchie Cobbs /* Decrement ref count */
57830400f03SJulian Elischer NG_NODE_UNREF(node);
579add85a1dSArchie Cobbs return (0);
580add85a1dSArchie Cobbs }
581add85a1dSArchie Cobbs
582add85a1dSArchie Cobbs /*************************************************************************
583add85a1dSArchie Cobbs TRANSMIT AND RECEIVE FUNCTIONS
584add85a1dSArchie Cobbs *************************************************************************/
585add85a1dSArchie Cobbs
586add85a1dSArchie Cobbs /*
587add85a1dSArchie Cobbs * Transmit an outgoing frame, or just an ack if m is NULL.
588add85a1dSArchie Cobbs */
589add85a1dSArchie Cobbs static int
ng_pptpgre_xmit(hpriv_p hpriv,item_p item)590489290e9SAlexander Motin ng_pptpgre_xmit(hpriv_p hpriv, item_p item)
591add85a1dSArchie Cobbs {
592489290e9SAlexander Motin const priv_p priv = NG_NODE_PRIVATE(hpriv->node);
593add85a1dSArchie Cobbs u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)];
594add85a1dSArchie Cobbs struct greheader *const gre = (struct greheader *)buf;
595add85a1dSArchie Cobbs int grelen, error;
596069154d5SJulian Elischer struct mbuf *m;
597add85a1dSArchie Cobbs
598489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_OWNED);
59983beeed9SGleb Smirnoff
600069154d5SJulian Elischer if (item) {
601069154d5SJulian Elischer NGI_GET_M(item, m);
602069154d5SJulian Elischer } else {
603069154d5SJulian Elischer m = NULL;
604069154d5SJulian Elischer }
6059bee7adfSArchie Cobbs /* Check if there's data */
6069bee7adfSArchie Cobbs if (m != NULL) {
607922ee196SArchie Cobbs /* Check if windowing is enabled */
608489290e9SAlexander Motin if (hpriv->conf.enableWindowing) {
609add85a1dSArchie Cobbs /* Is our transmit window full? */
610489290e9SAlexander Motin if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq,
611489290e9SAlexander Motin hpriv->recvAck) >= hpriv->xmitWin) {
6129bee7adfSArchie Cobbs priv->stats.xmitDrops++;
61383beeed9SGleb Smirnoff ERROUT(ENOBUFS);
614add85a1dSArchie Cobbs }
615922ee196SArchie Cobbs }
616add85a1dSArchie Cobbs
617add85a1dSArchie Cobbs /* Sanity check frame length */
6182c2e2be7SAlexander Motin if (m->m_pkthdr.len > PPTP_MAX_PAYLOAD) {
6199bee7adfSArchie Cobbs priv->stats.xmitTooBig++;
62083beeed9SGleb Smirnoff ERROUT(EMSGSIZE);
621add85a1dSArchie Cobbs }
622069154d5SJulian Elischer } else {
6239bee7adfSArchie Cobbs priv->stats.xmitLoneAcks++;
624069154d5SJulian Elischer }
625add85a1dSArchie Cobbs
626add85a1dSArchie Cobbs /* Build GRE header */
62738f2d636SAlexander Motin be32enc(gre, PPTP_INIT_VALUE);
62838f2d636SAlexander Motin be16enc(&gre->length, (m != NULL) ? m->m_pkthdr.len : 0);
62938f2d636SAlexander Motin be16enc(&gre->cid, hpriv->conf.peerCid);
630add85a1dSArchie Cobbs
631add85a1dSArchie Cobbs /* Include sequence number if packet contains any data */
632add85a1dSArchie Cobbs if (m != NULL) {
633add85a1dSArchie Cobbs gre->hasSeq = 1;
634489290e9SAlexander Motin if (hpriv->conf.enableWindowing) {
635489290e9SAlexander Motin hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck]
636489290e9SAlexander Motin = ng_pptpgre_time();
637922ee196SArchie Cobbs }
638489290e9SAlexander Motin hpriv->xmitSeq++;
63938f2d636SAlexander Motin be32enc(&gre->data[0], hpriv->xmitSeq);
640add85a1dSArchie Cobbs }
641add85a1dSArchie Cobbs
642add85a1dSArchie Cobbs /* Include acknowledgement (and stop send ack timer) if needed */
643489290e9SAlexander Motin if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) {
644add85a1dSArchie Cobbs gre->hasAck = 1;
64538f2d636SAlexander Motin be32enc(&gre->data[gre->hasSeq], hpriv->recvSeq);
646489290e9SAlexander Motin hpriv->xmitAck = hpriv->recvSeq;
647714f558bSAlexander Motin if (hpriv->conf.enableDelayedAck)
648714f558bSAlexander Motin ng_uncallout(&hpriv->sackTimer, hpriv->node);
649da010626SArchie Cobbs }
650add85a1dSArchie Cobbs
651add85a1dSArchie Cobbs /* Prepend GRE header to outgoing frame */
652add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
653add85a1dSArchie Cobbs if (m == NULL) {
654eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA);
655add85a1dSArchie Cobbs if (m == NULL) {
656678f9e33SArchie Cobbs priv->stats.memoryFailures++;
65783beeed9SGleb Smirnoff ERROUT(ENOBUFS);
658add85a1dSArchie Cobbs }
659add85a1dSArchie Cobbs m->m_len = m->m_pkthdr.len = grelen;
660add85a1dSArchie Cobbs m->m_pkthdr.rcvif = NULL;
661add85a1dSArchie Cobbs } else {
662eb1b1807SGleb Smirnoff M_PREPEND(m, grelen, M_NOWAIT);
663add85a1dSArchie Cobbs if (m == NULL || (m->m_len < grelen
664add85a1dSArchie Cobbs && (m = m_pullup(m, grelen)) == NULL)) {
665678f9e33SArchie Cobbs priv->stats.memoryFailures++;
66683beeed9SGleb Smirnoff ERROUT(ENOBUFS);
667add85a1dSArchie Cobbs }
668add85a1dSArchie Cobbs }
669add85a1dSArchie Cobbs bcopy(gre, mtod(m, u_char *), grelen);
670add85a1dSArchie Cobbs
6719bee7adfSArchie Cobbs /* Update stats */
6729bee7adfSArchie Cobbs priv->stats.xmitPackets++;
6739bee7adfSArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len;
6749bee7adfSArchie Cobbs
67583beeed9SGleb Smirnoff /*
67683beeed9SGleb Smirnoff * XXX: we should reset timer only after an item has been sent
67783beeed9SGleb Smirnoff * successfully.
67883beeed9SGleb Smirnoff */
679489290e9SAlexander Motin if (hpriv->conf.enableWindowing &&
680489290e9SAlexander Motin gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1)
681489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv);
68283beeed9SGleb Smirnoff
683489290e9SAlexander Motin mtx_unlock(&hpriv->mtx);
68483beeed9SGleb Smirnoff
685add85a1dSArchie Cobbs /* Deliver packet */
686069154d5SJulian Elischer if (item) {
687069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, priv->lower, m);
688069154d5SJulian Elischer } else {
689069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m);
690069154d5SJulian Elischer }
691069154d5SJulian Elischer
69283beeed9SGleb Smirnoff return (error);
693678f9e33SArchie Cobbs
69483beeed9SGleb Smirnoff done:
695489290e9SAlexander Motin mtx_unlock(&hpriv->mtx);
69683beeed9SGleb Smirnoff NG_FREE_M(m);
69783beeed9SGleb Smirnoff if (item)
69883beeed9SGleb Smirnoff NG_FREE_ITEM(item);
699add85a1dSArchie Cobbs return (error);
700add85a1dSArchie Cobbs }
701add85a1dSArchie Cobbs
702a594f945SEugene Grosbein static void
ng_pptpgre_ack(const hpriv_p hpriv)703a594f945SEugene Grosbein ng_pptpgre_ack(const hpriv_p hpriv)
704a594f945SEugene Grosbein {
705a594f945SEugene Grosbein mtx_assert(&hpriv->mtx, MA_OWNED);
706a594f945SEugene Grosbein if (!(callout_pending(&hpriv->sackTimer))) {
707a594f945SEugene Grosbein /* If delayed ACK is disabled, send it now */
708a594f945SEugene Grosbein if (!hpriv->conf.enableDelayedAck) { /* ack now */
709a594f945SEugene Grosbein ng_pptpgre_xmit(hpriv, NULL);
710a594f945SEugene Grosbein /* ng_pptpgre_xmit() drops the mutex */
711a594f945SEugene Grosbein return;
712a594f945SEugene Grosbein }
713a594f945SEugene Grosbein /* ack later */
714a594f945SEugene Grosbein ng_pptpgre_start_send_ack_timer(hpriv);
715a594f945SEugene Grosbein mtx_unlock(&hpriv->mtx);
716a594f945SEugene Grosbein return;
717a594f945SEugene Grosbein }
718a594f945SEugene Grosbein mtx_unlock(&hpriv->mtx);
719a594f945SEugene Grosbein }
720a594f945SEugene Grosbein
721a594f945SEugene Grosbein /*
722a594f945SEugene Grosbein * Delivers packets from the queue "q" to upper layers. Frees delivered
723a594f945SEugene Grosbein * entries with the exception of one equal to "st" that is allocated
724a594f945SEugene Grosbein * on caller's stack and not on the heap.
725a594f945SEugene Grosbein */
726a594f945SEugene Grosbein static int
ng_pptpgre_sendq(const hpriv_p hpriv,roqh * q,const struct ng_pptpgre_roq * st)727a594f945SEugene Grosbein ng_pptpgre_sendq(const hpriv_p hpriv, roqh *q, const struct ng_pptpgre_roq *st)
728a594f945SEugene Grosbein {
729a594f945SEugene Grosbein struct ng_pptpgre_roq *np;
730a594f945SEugene Grosbein struct mbuf *m;
731a594f945SEugene Grosbein int error = 0;
732a594f945SEugene Grosbein
733a594f945SEugene Grosbein mtx_assert(&hpriv->mtx, MA_NOTOWNED);
734a594f945SEugene Grosbein while (!SLIST_EMPTY(q)) {
735a594f945SEugene Grosbein np = SLIST_FIRST(q);
736a594f945SEugene Grosbein SLIST_REMOVE_HEAD(q, next);
737a594f945SEugene Grosbein NGI_GET_M(np->item, m);
738a594f945SEugene Grosbein NG_FWD_NEW_DATA(error, np->item, hpriv->hook, m);
739a594f945SEugene Grosbein if (np != st)
740*9b8db664SDmitry Lukhtionov free(np, M_NETGRAPH_PPTP);
741a594f945SEugene Grosbein }
742a594f945SEugene Grosbein return (error);
743a594f945SEugene Grosbein }
744a594f945SEugene Grosbein
745add85a1dSArchie Cobbs /*
746add85a1dSArchie Cobbs * Handle an incoming packet. The packet includes the IP header.
747add85a1dSArchie Cobbs */
748add85a1dSArchie Cobbs static int
ng_pptpgre_rcvdata_lower(hook_p hook,item_p item)749489290e9SAlexander Motin ng_pptpgre_rcvdata_lower(hook_p hook, item_p item)
750add85a1dSArchie Cobbs {
751489290e9SAlexander Motin hpriv_p hpriv;
752489290e9SAlexander Motin node_p node = NG_HOOK_NODE(hook);
75330400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
754add85a1dSArchie Cobbs int iphlen, grelen, extralen;
755816b834fSArchie Cobbs const struct greheader *gre;
756816b834fSArchie Cobbs const struct ip *ip;
757add85a1dSArchie Cobbs int error = 0;
758069154d5SJulian Elischer struct mbuf *m;
759add85a1dSArchie Cobbs
760a594f945SEugene Grosbein roqh sendq = SLIST_HEAD_INITIALIZER(sendq); /* send queue on stack */
761a594f945SEugene Grosbein struct ng_pptpgre_roq *last = NULL; /* last packet in the sendq */
762a594f945SEugene Grosbein struct ng_pptpgre_roq *np, *prev;
763a594f945SEugene Grosbein struct ng_pptpgre_roq temp = { { NULL }, NULL, 0 };
764a594f945SEugene Grosbein long diff;
765a594f945SEugene Grosbein u_int32_t seq;
766a594f945SEugene Grosbein
767a594f945SEugene Grosbein m = NGI_M(item);
7689bee7adfSArchie Cobbs /* Update stats */
7699bee7adfSArchie Cobbs priv->stats.recvPackets++;
7709bee7adfSArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len;
7719bee7adfSArchie Cobbs
772add85a1dSArchie Cobbs /* Sanity check packet length */
773add85a1dSArchie Cobbs if (m->m_pkthdr.len < sizeof(*ip) + sizeof(*gre)) {
7749bee7adfSArchie Cobbs priv->stats.recvRunts++;
77583beeed9SGleb Smirnoff ERROUT(EINVAL);
776add85a1dSArchie Cobbs }
777add85a1dSArchie Cobbs
778add85a1dSArchie Cobbs /* Safely pull up the complete IP+GRE headers */
779a594f945SEugene Grosbein if (m->m_len < sizeof(*ip) + sizeof(*gre)) {
780a594f945SEugene Grosbein if ((m = m_pullup(m, sizeof(*ip) + sizeof(*gre))) == NULL) {
781678f9e33SArchie Cobbs priv->stats.memoryFailures++;
782a594f945SEugene Grosbein _NGI_M(item) = NULL;
78383beeed9SGleb Smirnoff ERROUT(ENOBUFS);
784add85a1dSArchie Cobbs }
785a594f945SEugene Grosbein _NGI_M(item) = m;
786a594f945SEugene Grosbein }
787816b834fSArchie Cobbs ip = mtod(m, const struct ip *);
788add85a1dSArchie Cobbs iphlen = ip->ip_hl << 2;
789add85a1dSArchie Cobbs if (m->m_len < iphlen + sizeof(*gre)) {
790add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + sizeof(*gre))) == NULL) {
791678f9e33SArchie Cobbs priv->stats.memoryFailures++;
792a594f945SEugene Grosbein _NGI_M(item) = NULL;
79383beeed9SGleb Smirnoff ERROUT(ENOBUFS);
794add85a1dSArchie Cobbs }
795a594f945SEugene Grosbein _NGI_M(item) = m;
796816b834fSArchie Cobbs ip = mtod(m, const struct ip *);
797add85a1dSArchie Cobbs }
798816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen);
799add85a1dSArchie Cobbs grelen = sizeof(*gre) + sizeof(u_int32_t) * (gre->hasSeq + gre->hasAck);
8009bee7adfSArchie Cobbs if (m->m_pkthdr.len < iphlen + grelen) {
8019bee7adfSArchie Cobbs priv->stats.recvRunts++;
80283beeed9SGleb Smirnoff ERROUT(EINVAL);
8039bee7adfSArchie Cobbs }
804add85a1dSArchie Cobbs if (m->m_len < iphlen + grelen) {
805add85a1dSArchie Cobbs if ((m = m_pullup(m, iphlen + grelen)) == NULL) {
806678f9e33SArchie Cobbs priv->stats.memoryFailures++;
807a594f945SEugene Grosbein _NGI_M(item) = NULL;
80883beeed9SGleb Smirnoff ERROUT(ENOBUFS);
809add85a1dSArchie Cobbs }
810a594f945SEugene Grosbein _NGI_M(item) = m;
811816b834fSArchie Cobbs ip = mtod(m, const struct ip *);
812816b834fSArchie Cobbs gre = (const struct greheader *)((const u_char *)ip + iphlen);
813add85a1dSArchie Cobbs }
814add85a1dSArchie Cobbs
815add85a1dSArchie Cobbs /* Sanity check packet length and GRE header bits */
816add85a1dSArchie Cobbs extralen = m->m_pkthdr.len
81738f2d636SAlexander Motin - (iphlen + grelen + gre->hasSeq * be16dec(&gre->length));
8189bee7adfSArchie Cobbs if (extralen < 0) {
8199bee7adfSArchie Cobbs priv->stats.recvBadGRE++;
82083beeed9SGleb Smirnoff ERROUT(EINVAL);
8219bee7adfSArchie Cobbs }
82238f2d636SAlexander Motin if ((be32dec(gre) & PPTP_INIT_MASK) != PPTP_INIT_VALUE) {
8239bee7adfSArchie Cobbs priv->stats.recvBadGRE++;
82483beeed9SGleb Smirnoff ERROUT(EINVAL);
8259bee7adfSArchie Cobbs }
826489290e9SAlexander Motin
82738f2d636SAlexander Motin hpriv = ng_pptpgre_find_session(priv, be16dec(&gre->cid));
828489290e9SAlexander Motin if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) {
8299bee7adfSArchie Cobbs priv->stats.recvBadCID++;
83083beeed9SGleb Smirnoff ERROUT(EINVAL);
8319bee7adfSArchie Cobbs }
832489290e9SAlexander Motin mtx_lock(&hpriv->mtx);
833add85a1dSArchie Cobbs
834add85a1dSArchie Cobbs /* Look for peer ack */
835add85a1dSArchie Cobbs if (gre->hasAck) {
83638f2d636SAlexander Motin const u_int32_t ack = be32dec(&gre->data[gre->hasSeq]);
837489290e9SAlexander Motin const int index = ack - hpriv->recvAck - 1;
83822dfb9bdSArchie Cobbs long sample;
839add85a1dSArchie Cobbs
840add85a1dSArchie Cobbs /* Sanity check ack value */
841489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) {
8429bee7adfSArchie Cobbs priv->stats.recvBadAcks++;
8439bee7adfSArchie Cobbs goto badAck; /* we never sent it! */
8449bee7adfSArchie Cobbs }
845489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0)
8469bee7adfSArchie Cobbs goto badAck; /* ack already timed out */
847489290e9SAlexander Motin hpriv->recvAck = ack;
848add85a1dSArchie Cobbs
849add85a1dSArchie Cobbs /* Update adaptive timeout stuff */
850489290e9SAlexander Motin if (hpriv->conf.enableWindowing) {
851489290e9SAlexander Motin sample = ng_pptpgre_time() - hpriv->timeSent[index];
852489290e9SAlexander Motin diff = sample - hpriv->rtt;
853489290e9SAlexander Motin hpriv->rtt += PPTP_ACK_ALPHA(diff);
854add85a1dSArchie Cobbs if (diff < 0)
855add85a1dSArchie Cobbs diff = -diff;
856489290e9SAlexander Motin hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev);
857f8e159d6SGleb Smirnoff /* +2 to compensate low precision of int math */
858489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2);
859489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT)
860489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT;
861489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT)
862489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT;
863e962a823SArchie Cobbs
864e962a823SArchie Cobbs /* Shift packet transmit times in our transmit window */
865489290e9SAlexander Motin bcopy(hpriv->timeSent + index + 1, hpriv->timeSent,
866489290e9SAlexander Motin sizeof(*hpriv->timeSent)
867922ee196SArchie Cobbs * (PPTP_XMIT_WIN - (index + 1)));
868e962a823SArchie Cobbs
869922ee196SArchie Cobbs /* If we sent an entire window, increase window size */
870489290e9SAlexander Motin if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0
871489290e9SAlexander Motin && hpriv->xmitWin < PPTP_XMIT_WIN) {
872489290e9SAlexander Motin hpriv->xmitWin++;
873489290e9SAlexander Motin hpriv->winAck = ack + hpriv->xmitWin;
874add85a1dSArchie Cobbs }
875add85a1dSArchie Cobbs
8769bee7adfSArchie Cobbs /* Stop/(re)start receive ACK timer as necessary */
877714f558bSAlexander Motin ng_uncallout(&hpriv->rackTimer, hpriv->node);
878489290e9SAlexander Motin if (hpriv->recvAck != hpriv->xmitSeq)
879489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv);
880add85a1dSArchie Cobbs }
881922ee196SArchie Cobbs }
8829bee7adfSArchie Cobbs badAck:
883add85a1dSArchie Cobbs
884add85a1dSArchie Cobbs /* See if frame contains any data */
885a594f945SEugene Grosbein if (!gre->hasSeq) { /* no data to deliver */
886a594f945SEugene Grosbein priv->stats.recvLoneAcks++;
887a594f945SEugene Grosbein mtx_unlock(&hpriv->mtx);
888a594f945SEugene Grosbein ERROUT(0);
889a594f945SEugene Grosbein }
890add85a1dSArchie Cobbs
891a594f945SEugene Grosbein seq = be32dec(&gre->data[0]);
892a594f945SEugene Grosbein
893a594f945SEugene Grosbein diff = PPTP_SEQ_DIFF(seq, hpriv->recvSeq);
894a594f945SEugene Grosbein if (diff <= 0) { /* late or duplicate packet */
895a594f945SEugene Grosbein if (diff < 0 && reorder_max == 0) /* reorder disabled */
896a594f945SEugene Grosbein priv->stats.recvOutOfOrder++; /* late */
8979bee7adfSArchie Cobbs else
898a594f945SEugene Grosbein priv->stats.recvDuplicates++; /* duplicate */
899489290e9SAlexander Motin mtx_unlock(&hpriv->mtx);
90083beeed9SGleb Smirnoff ERROUT(EINVAL);
9019bee7adfSArchie Cobbs }
902add85a1dSArchie Cobbs
903add85a1dSArchie Cobbs /* Trim mbuf down to internal payload */
904add85a1dSArchie Cobbs m_adj(m, iphlen + grelen);
905add85a1dSArchie Cobbs if (extralen > 0)
906add85a1dSArchie Cobbs m_adj(m, -extralen);
907add85a1dSArchie Cobbs
908a594f945SEugene Grosbein #define INIT_SENDQ(t) do { \
909a594f945SEugene Grosbein t.item = item; \
910a594f945SEugene Grosbein t.seq = seq; \
911a594f945SEugene Grosbein SLIST_INSERT_HEAD(&sendq, &t, next); \
912a594f945SEugene Grosbein last = &t; \
913a594f945SEugene Grosbein hpriv->recvSeq = seq; \
914a594f945SEugene Grosbein goto deliver; \
915a594f945SEugene Grosbein } while(0)
916489290e9SAlexander Motin
917a594f945SEugene Grosbein if (diff == 1)
918a594f945SEugene Grosbein /* the packet came in order, place it at the start of sendq */
919a594f945SEugene Grosbein INIT_SENDQ(temp);
920a594f945SEugene Grosbein
921a594f945SEugene Grosbein /* The packet came too early, try to enqueue it.
922a594f945SEugene Grosbein *
923a594f945SEugene Grosbein * Check for duplicate in the queue. After this loop, "prev" will be
924a594f945SEugene Grosbein * NULL if the packet should become new head of the queue,
925a594f945SEugene Grosbein * or else it should be inserted after the "prev".
926a594f945SEugene Grosbein */
927a594f945SEugene Grosbein prev = SLIST_FIRST(&hpriv->roq);
928a594f945SEugene Grosbein SLIST_FOREACH(np, &hpriv->roq, next) {
929a594f945SEugene Grosbein diff = PPTP_SEQ_DIFF(np->seq, seq);
930a594f945SEugene Grosbein if (diff == 0) { /* do not add duplicate, drop it */
931a594f945SEugene Grosbein priv->stats.recvDuplicates++;
932489290e9SAlexander Motin mtx_unlock(&hpriv->mtx);
933a594f945SEugene Grosbein ERROUT(EINVAL);
934a594f945SEugene Grosbein }
935a594f945SEugene Grosbein if (diff > 0) { /* we found newer packet */
936a594f945SEugene Grosbein if (np == prev) /* that is the head of the queue */
937a594f945SEugene Grosbein prev = NULL; /* put current packet to the head */
938a594f945SEugene Grosbein break;
939a594f945SEugene Grosbein }
940a594f945SEugene Grosbein prev = np;
9419bee7adfSArchie Cobbs }
94283beeed9SGleb Smirnoff
943a594f945SEugene Grosbein priv->stats.recvOutOfOrder++; /* duplicate not found */
944a594f945SEugene Grosbein if (hpriv->roq_len < reorder_max)
945a594f945SEugene Grosbein goto enqueue; /* reorder enabled and there is a room */
946a594f945SEugene Grosbein
947a594f945SEugene Grosbein /*
948a594f945SEugene Grosbein * There is no room in the queue or reorder disabled.
949a594f945SEugene Grosbein *
950a594f945SEugene Grosbein * It the latter case, we may still have non-empty reorder queue
951a594f945SEugene Grosbein * if reorder was disabled in process of reordering.
952a594f945SEugene Grosbein * Then we correctly deliver the queue without growing it.
953a594f945SEugene Grosbein *
954a594f945SEugene Grosbein * In both cases, no malloc()'s until the queue is shortened.
955a594f945SEugene Grosbein */
956a594f945SEugene Grosbein priv->stats.recvReorderOverflow++;
957a594f945SEugene Grosbein if (prev == NULL) { /* new packet goes before the head */
958a594f945SEugene Grosbein INIT_SENDQ(temp); /* of reorder queue, so put it to sendq */
959a594f945SEugene Grosbein }
960a594f945SEugene Grosbein #undef INIT_SENDQ
961a594f945SEugene Grosbein
962a594f945SEugene Grosbein /*
963a594f945SEugene Grosbein * Current packet goes after the head of reorder queue.
964a594f945SEugene Grosbein * Move the head to sendq to make room for current packet.
965a594f945SEugene Grosbein */
966a594f945SEugene Grosbein np = SLIST_FIRST(&hpriv->roq);
967a594f945SEugene Grosbein if (prev == np)
968a594f945SEugene Grosbein prev = NULL;
969a594f945SEugene Grosbein SLIST_REMOVE_HEAD(&hpriv->roq, next);
970a594f945SEugene Grosbein hpriv->roq_len--; /* we are allowed to use malloc() now */
971a594f945SEugene Grosbein SLIST_INSERT_HEAD(&sendq, np, next);
972a594f945SEugene Grosbein last = np;
973a594f945SEugene Grosbein hpriv->recvSeq = np->seq;
974a594f945SEugene Grosbein
975a594f945SEugene Grosbein enqueue:
976*9b8db664SDmitry Lukhtionov np = malloc(sizeof(*np), M_NETGRAPH_PPTP, M_NOWAIT | M_ZERO);
977a594f945SEugene Grosbein if (np == NULL) {
978a594f945SEugene Grosbein priv->stats.memoryFailures++;
979a594f945SEugene Grosbein /*
980a594f945SEugene Grosbein * Emergency: we cannot save new data.
981a594f945SEugene Grosbein * Flush the queue delivering all queued packets preceeding
982a594f945SEugene Grosbein * current one despite of gaps.
983a594f945SEugene Grosbein */
984a594f945SEugene Grosbein while (!SLIST_EMPTY(&hpriv->roq)) {
985a594f945SEugene Grosbein np = SLIST_FIRST(&hpriv->roq);
986a594f945SEugene Grosbein if (np->seq > seq)
987a594f945SEugene Grosbein break;
988a594f945SEugene Grosbein SLIST_REMOVE_HEAD(&hpriv->roq, next);
989a594f945SEugene Grosbein hpriv->roq_len--;
990a594f945SEugene Grosbein if (last == NULL)
991a594f945SEugene Grosbein SLIST_INSERT_HEAD(&sendq, np, next);
992a594f945SEugene Grosbein else
993a594f945SEugene Grosbein SLIST_INSERT_AFTER(last, np, next);
994a594f945SEugene Grosbein last = np;
995a594f945SEugene Grosbein }
996a594f945SEugene Grosbein
997a594f945SEugene Grosbein /*
998a594f945SEugene Grosbein * Pretend we got all packets till the current one
999a594f945SEugene Grosbein * and acknowledge it.
1000a594f945SEugene Grosbein */
1001a594f945SEugene Grosbein hpriv->recvSeq = seq;
1002a594f945SEugene Grosbein ng_pptpgre_ack(hpriv); /* drops lock */
1003a594f945SEugene Grosbein ng_pptpgre_sendq(hpriv, &sendq, &temp);
1004a594f945SEugene Grosbein NG_FWD_NEW_DATA(error, item, hpriv->hook, m);
1005a594f945SEugene Grosbein ERROUT(ENOMEM);
1006a594f945SEugene Grosbein }
1007a594f945SEugene Grosbein
1008a594f945SEugene Grosbein /* Add current (early) packet to the reorder queue. */
1009a594f945SEugene Grosbein np->item = item;
1010a594f945SEugene Grosbein np->seq = seq;
1011a594f945SEugene Grosbein if (prev == NULL)
1012a594f945SEugene Grosbein SLIST_INSERT_HEAD(&hpriv->roq, np, next);
1013a594f945SEugene Grosbein else
1014a594f945SEugene Grosbein SLIST_INSERT_AFTER(prev, np, next);
1015a594f945SEugene Grosbein hpriv->roq_len++;
1016a594f945SEugene Grosbein
1017a594f945SEugene Grosbein deliver:
1018a594f945SEugene Grosbein /* Look if we have some packets in sequence after sendq. */
1019a594f945SEugene Grosbein while (!SLIST_EMPTY(&hpriv->roq)) {
1020a594f945SEugene Grosbein np = SLIST_FIRST(&hpriv->roq);
1021a594f945SEugene Grosbein if (PPTP_SEQ_DIFF(np->seq, hpriv->recvSeq) > 1)
1022a594f945SEugene Grosbein break; /* the gap in the sequence */
1023a594f945SEugene Grosbein
1024a594f945SEugene Grosbein /* "np" is in sequence, move it to the sendq. */
1025a594f945SEugene Grosbein SLIST_REMOVE_HEAD(&hpriv->roq, next);
1026a594f945SEugene Grosbein hpriv->roq_len--;
1027a594f945SEugene Grosbein hpriv->recvSeq = np->seq;
1028a594f945SEugene Grosbein
1029a594f945SEugene Grosbein if (last == NULL)
1030a594f945SEugene Grosbein SLIST_INSERT_HEAD(&sendq, np, next);
1031a594f945SEugene Grosbein else
1032a594f945SEugene Grosbein SLIST_INSERT_AFTER(last, np, next);
1033a594f945SEugene Grosbein last = np;
1034a594f945SEugene Grosbein }
1035a594f945SEugene Grosbein
1036a594f945SEugene Grosbein if (SLIST_EMPTY(&hpriv->roq)) {
1037a594f945SEugene Grosbein if (callout_pending(&hpriv->reorderTimer))
1038a594f945SEugene Grosbein ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1039a594f945SEugene Grosbein } else {
1040a594f945SEugene Grosbein if (!callout_pending(&hpriv->reorderTimer))
1041a594f945SEugene Grosbein ng_pptpgre_start_reorder_timer(hpriv);
1042a594f945SEugene Grosbein }
1043a594f945SEugene Grosbein
1044a594f945SEugene Grosbein if (SLIST_EMPTY(&sendq)) {
1045a594f945SEugene Grosbein /* Current packet has been queued, nothing to free/deliver. */
1046a594f945SEugene Grosbein mtx_unlock(&hpriv->mtx);
1047a594f945SEugene Grosbein return (error);
1048a594f945SEugene Grosbein }
1049a594f945SEugene Grosbein
1050a594f945SEugene Grosbein /* We need to acknowledge last packet; do it soon... */
1051a594f945SEugene Grosbein ng_pptpgre_ack(hpriv); /* drops lock */
1052a594f945SEugene Grosbein ng_pptpgre_sendq(hpriv, &sendq, &temp);
105383beeed9SGleb Smirnoff return (error);
105483beeed9SGleb Smirnoff
105583beeed9SGleb Smirnoff done:
105683beeed9SGleb Smirnoff NG_FREE_ITEM(item);
1057add85a1dSArchie Cobbs return (error);
1058add85a1dSArchie Cobbs }
1059add85a1dSArchie Cobbs
1060add85a1dSArchie Cobbs /*************************************************************************
1061add85a1dSArchie Cobbs TIMER RELATED FUNCTIONS
1062add85a1dSArchie Cobbs *************************************************************************/
1063add85a1dSArchie Cobbs
1064add85a1dSArchie Cobbs /*
10659bee7adfSArchie Cobbs * Start a timer for the peer's acknowledging our oldest unacknowledged
1066add85a1dSArchie Cobbs * sequence number. If we get an ack for this sequence number before
1067add85a1dSArchie Cobbs * the timer goes off, we cancel the timer. Resets currently running
1068add85a1dSArchie Cobbs * recv ack timer, if any.
1069add85a1dSArchie Cobbs */
1070add85a1dSArchie Cobbs static void
ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)1071489290e9SAlexander Motin ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv)
1072add85a1dSArchie Cobbs {
1073678f9e33SArchie Cobbs int remain, ticks;
1074add85a1dSArchie Cobbs
1075add85a1dSArchie Cobbs /* Compute how long until oldest unack'd packet times out,
1076add85a1dSArchie Cobbs and reset the timer to that time. */
1077489290e9SAlexander Motin remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time();
1078add85a1dSArchie Cobbs if (remain < 0)
1079add85a1dSArchie Cobbs remain = 0;
10809bee7adfSArchie Cobbs
10814a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */
108255e0987aSPedro F. Giffuni ticks = howmany(remain * hz, PPTP_TIME_SCALE) + 1;
1083489290e9SAlexander Motin ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook,
1084489290e9SAlexander Motin ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0);
10854a48abb2SArchie Cobbs }
10864a48abb2SArchie Cobbs
10874a48abb2SArchie Cobbs /*
1088add85a1dSArchie Cobbs * The peer has failed to acknowledge the oldest unacknowledged sequence
1089add85a1dSArchie Cobbs * number within the time allotted. Update our adaptive timeout parameters
1090add85a1dSArchie Cobbs * and reset/restart the recv ack timer.
1091add85a1dSArchie Cobbs */
1092add85a1dSArchie Cobbs static void
ng_pptpgre_recv_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1093089323f3SGleb Smirnoff ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1094add85a1dSArchie Cobbs {
109530400f03SJulian Elischer const priv_p priv = NG_NODE_PRIVATE(node);
1096489290e9SAlexander Motin const hpriv_p hpriv = arg1;
10979bee7adfSArchie Cobbs
1098add85a1dSArchie Cobbs /* Update adaptive timeout stuff */
10999bee7adfSArchie Cobbs priv->stats.recvAckTimeouts++;
1100489290e9SAlexander Motin hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */
1101489290e9SAlexander Motin hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev);
1102489290e9SAlexander Motin if (hpriv->ato > PPTP_MAX_TIMEOUT)
1103489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT;
1104489290e9SAlexander Motin else if (hpriv->ato < PPTP_MIN_TIMEOUT)
1105489290e9SAlexander Motin hpriv->ato = PPTP_MIN_TIMEOUT;
1106678f9e33SArchie Cobbs
1107e962a823SArchie Cobbs /* Reset ack and sliding window */
1108489290e9SAlexander Motin hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */
1109489290e9SAlexander Motin hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */
1110489290e9SAlexander Motin hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */
1111add85a1dSArchie Cobbs }
1112add85a1dSArchie Cobbs
1113add85a1dSArchie Cobbs /*
11149bee7adfSArchie Cobbs * Start the send ack timer. This assumes the timer is not
11159bee7adfSArchie Cobbs * already running.
11169bee7adfSArchie Cobbs */
11179bee7adfSArchie Cobbs static void
ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)1118714f558bSAlexander Motin ng_pptpgre_start_send_ack_timer(hpriv_p hpriv)
11199bee7adfSArchie Cobbs {
1120714f558bSAlexander Motin int ackTimeout, ticks;
1121714f558bSAlexander Motin
1122714f558bSAlexander Motin /* Take 1/4 of the estimated round trip time */
1123714f558bSAlexander Motin ackTimeout = (hpriv->rtt >> 2);
1124714f558bSAlexander Motin if (ackTimeout < PPTP_MIN_ACK_DELAY)
1125714f558bSAlexander Motin ackTimeout = PPTP_MIN_ACK_DELAY;
1126714f558bSAlexander Motin else if (ackTimeout > PPTP_MAX_ACK_DELAY)
1127714f558bSAlexander Motin ackTimeout = PPTP_MAX_ACK_DELAY;
11289bee7adfSArchie Cobbs
11294a48abb2SArchie Cobbs /* Be conservative: timeout can happen up to 1 tick early */
113055e0987aSPedro F. Giffuni ticks = howmany(ackTimeout * hz, PPTP_TIME_SCALE);
1131489290e9SAlexander Motin ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook,
1132489290e9SAlexander Motin ticks, ng_pptpgre_send_ack_timeout, hpriv, 0);
11334a48abb2SArchie Cobbs }
11344a48abb2SArchie Cobbs
11354a48abb2SArchie Cobbs /*
1136add85a1dSArchie Cobbs * We've waited as long as we're willing to wait before sending an
1137add85a1dSArchie Cobbs * acknowledgement to the peer for received frames. We had hoped to
1138add85a1dSArchie Cobbs * be able to piggy back our acknowledgement on an outgoing data frame,
1139add85a1dSArchie Cobbs * but apparently there haven't been any since. So send the ack now.
1140add85a1dSArchie Cobbs */
1141add85a1dSArchie Cobbs static void
ng_pptpgre_send_ack_timeout(node_p node,hook_p hook,void * arg1,int arg2)1142089323f3SGleb Smirnoff ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1143add85a1dSArchie Cobbs {
1144489290e9SAlexander Motin const hpriv_p hpriv = arg1;
114583beeed9SGleb Smirnoff
1146489290e9SAlexander Motin mtx_lock(&hpriv->mtx);
11479bee7adfSArchie Cobbs /* Send a frame with an ack but no payload */
1148489290e9SAlexander Motin ng_pptpgre_xmit(hpriv, NULL);
1149489290e9SAlexander Motin mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1150add85a1dSArchie Cobbs }
1151add85a1dSArchie Cobbs
1152a594f945SEugene Grosbein /*
1153a594f945SEugene Grosbein * Start a timer for the reorder queue. This assumes the timer is not
1154a594f945SEugene Grosbein * already running.
1155a594f945SEugene Grosbein */
1156a594f945SEugene Grosbein static void
ng_pptpgre_start_reorder_timer(hpriv_p hpriv)1157a594f945SEugene Grosbein ng_pptpgre_start_reorder_timer(hpriv_p hpriv)
1158a594f945SEugene Grosbein {
1159a594f945SEugene Grosbein int ticks;
1160a594f945SEugene Grosbein
1161a594f945SEugene Grosbein /* Be conservative: timeout can happen up to 1 tick early */
1162a594f945SEugene Grosbein ticks = (((reorder_timeout * hz) + 1000 - 1) / 1000) + 1;
1163a594f945SEugene Grosbein ng_callout(&hpriv->reorderTimer, hpriv->node, hpriv->hook,
1164a594f945SEugene Grosbein ticks, ng_pptpgre_reorder_timeout, hpriv, 0);
1165a594f945SEugene Grosbein }
1166a594f945SEugene Grosbein
1167a594f945SEugene Grosbein /*
1168a594f945SEugene Grosbein * The oldest packet spent too much time in the reorder queue.
1169a594f945SEugene Grosbein * Deliver it and next packets in sequence, if any.
1170a594f945SEugene Grosbein */
1171a594f945SEugene Grosbein static void
ng_pptpgre_reorder_timeout(node_p node,hook_p hook,void * arg1,int arg2)1172a594f945SEugene Grosbein ng_pptpgre_reorder_timeout(node_p node, hook_p hook, void *arg1, int arg2)
1173a594f945SEugene Grosbein {
1174a594f945SEugene Grosbein const priv_p priv = NG_NODE_PRIVATE(node);
1175a594f945SEugene Grosbein const hpriv_p hpriv = arg1;
1176a594f945SEugene Grosbein roqh sendq = SLIST_HEAD_INITIALIZER(sendq);
1177a594f945SEugene Grosbein struct ng_pptpgre_roq *np, *last = NULL;
1178a594f945SEugene Grosbein
1179a594f945SEugene Grosbein priv->stats.recvReorderTimeouts++;
1180a594f945SEugene Grosbein mtx_lock(&hpriv->mtx);
1181a594f945SEugene Grosbein if (SLIST_EMPTY(&hpriv->roq)) { /* should not happen */
1182a594f945SEugene Grosbein mtx_unlock(&hpriv->mtx);
1183a594f945SEugene Grosbein return;
1184a594f945SEugene Grosbein }
1185a594f945SEugene Grosbein
1186a594f945SEugene Grosbein last = np = SLIST_FIRST(&hpriv->roq);
1187a594f945SEugene Grosbein hpriv->roq_len--;
1188a594f945SEugene Grosbein SLIST_REMOVE_HEAD(&hpriv->roq, next);
1189a594f945SEugene Grosbein SLIST_INSERT_HEAD(&sendq, np, next);
1190a594f945SEugene Grosbein
1191a594f945SEugene Grosbein /* Look if we have more packets in sequence */
1192a594f945SEugene Grosbein while (!SLIST_EMPTY(&hpriv->roq)) {
1193a594f945SEugene Grosbein np = SLIST_FIRST(&hpriv->roq);
1194a594f945SEugene Grosbein if (PPTP_SEQ_DIFF(np->seq, last->seq) > 1)
1195a594f945SEugene Grosbein break; /* the gap in the sequence */
1196a594f945SEugene Grosbein
1197a594f945SEugene Grosbein /* Next packet is in sequence, move it to the sendq. */
1198a594f945SEugene Grosbein hpriv->roq_len--;
1199a594f945SEugene Grosbein SLIST_REMOVE_HEAD(&hpriv->roq, next);
1200a594f945SEugene Grosbein SLIST_INSERT_AFTER(last, np, next);
1201a594f945SEugene Grosbein last = np;
1202a594f945SEugene Grosbein }
1203a594f945SEugene Grosbein
1204a594f945SEugene Grosbein hpriv->recvSeq = last->seq;
1205a594f945SEugene Grosbein if (!SLIST_EMPTY(&hpriv->roq))
1206a594f945SEugene Grosbein ng_pptpgre_start_reorder_timer(hpriv);
1207a594f945SEugene Grosbein
1208a594f945SEugene Grosbein /* We need to acknowledge last packet; do it soon... */
1209a594f945SEugene Grosbein ng_pptpgre_ack(hpriv); /* drops lock */
1210a594f945SEugene Grosbein ng_pptpgre_sendq(hpriv, &sendq, NULL);
1211a594f945SEugene Grosbein mtx_assert(&hpriv->mtx, MA_NOTOWNED);
1212a594f945SEugene Grosbein }
1213a594f945SEugene Grosbein
1214add85a1dSArchie Cobbs /*************************************************************************
1215add85a1dSArchie Cobbs MISC FUNCTIONS
1216add85a1dSArchie Cobbs *************************************************************************/
1217add85a1dSArchie Cobbs
1218add85a1dSArchie Cobbs /*
1219489290e9SAlexander Motin * Find the hook with a given session ID.
1220489290e9SAlexander Motin */
1221489290e9SAlexander Motin static hpriv_p
ng_pptpgre_find_session(priv_p privp,u_int16_t cid)1222489290e9SAlexander Motin ng_pptpgre_find_session(priv_p privp, u_int16_t cid)
1223489290e9SAlexander Motin {
1224489290e9SAlexander Motin uint16_t hash = SESSHASH(cid);
1225489290e9SAlexander Motin hpriv_p hpriv = NULL;
1226489290e9SAlexander Motin
1227489290e9SAlexander Motin LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
1228489290e9SAlexander Motin if (hpriv->conf.cid == cid)
1229489290e9SAlexander Motin break;
1230489290e9SAlexander Motin }
1231489290e9SAlexander Motin
1232489290e9SAlexander Motin return (hpriv);
1233489290e9SAlexander Motin }
1234489290e9SAlexander Motin
1235489290e9SAlexander Motin /*
1236489290e9SAlexander Motin * Reset state (must be called with lock held or from writer)
1237add85a1dSArchie Cobbs */
1238add85a1dSArchie Cobbs static void
ng_pptpgre_reset(hpriv_p hpriv)1239489290e9SAlexander Motin ng_pptpgre_reset(hpriv_p hpriv)
1240add85a1dSArchie Cobbs {
1241a594f945SEugene Grosbein struct ng_pptpgre_roq *np;
1242a594f945SEugene Grosbein
1243add85a1dSArchie Cobbs /* Reset adaptive timeout state */
1244489290e9SAlexander Motin hpriv->ato = PPTP_MAX_TIMEOUT;
1245714f558bSAlexander Motin hpriv->rtt = PPTP_TIME_SCALE / 10;
1246714f558bSAlexander Motin if (hpriv->conf.peerPpd > 1) /* ppd = 0 treat as = 1 */
1247714f558bSAlexander Motin hpriv->rtt *= hpriv->conf.peerPpd;
1248489290e9SAlexander Motin hpriv->dev = 0;
1249489290e9SAlexander Motin hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2;
1250489290e9SAlexander Motin if (hpriv->xmitWin < 2) /* often the first packet is lost */
1251489290e9SAlexander Motin hpriv->xmitWin = 2; /* because the peer isn't ready */
1252489290e9SAlexander Motin else if (hpriv->xmitWin > PPTP_XMIT_WIN)
1253489290e9SAlexander Motin hpriv->xmitWin = PPTP_XMIT_WIN;
1254489290e9SAlexander Motin hpriv->winAck = hpriv->xmitWin;
1255add85a1dSArchie Cobbs
1256add85a1dSArchie Cobbs /* Reset sequence numbers */
1257489290e9SAlexander Motin hpriv->recvSeq = ~0;
1258489290e9SAlexander Motin hpriv->recvAck = ~0;
1259489290e9SAlexander Motin hpriv->xmitSeq = ~0;
1260489290e9SAlexander Motin hpriv->xmitAck = ~0;
1261add85a1dSArchie Cobbs
12624a48abb2SArchie Cobbs /* Stop timers */
1263714f558bSAlexander Motin ng_uncallout(&hpriv->sackTimer, hpriv->node);
1264714f558bSAlexander Motin ng_uncallout(&hpriv->rackTimer, hpriv->node);
1265a594f945SEugene Grosbein ng_uncallout(&hpriv->reorderTimer, hpriv->node);
1266a594f945SEugene Grosbein
1267a594f945SEugene Grosbein /* Clear reorder queue */
1268a594f945SEugene Grosbein while (!SLIST_EMPTY(&hpriv->roq)) {
1269a594f945SEugene Grosbein np = SLIST_FIRST(&hpriv->roq);
1270a594f945SEugene Grosbein SLIST_REMOVE_HEAD(&hpriv->roq, next);
1271a594f945SEugene Grosbein NG_FREE_ITEM(np->item);
1272*9b8db664SDmitry Lukhtionov free(np, M_NETGRAPH_PPTP);
1273a594f945SEugene Grosbein }
1274a594f945SEugene Grosbein hpriv->roq_len = 0;
1275add85a1dSArchie Cobbs }
1276add85a1dSArchie Cobbs
1277add85a1dSArchie Cobbs /*
1278add85a1dSArchie Cobbs * Return the current time scaled & translated to our internally used format.
1279add85a1dSArchie Cobbs */
1280add85a1dSArchie Cobbs static pptptime_t
ng_pptpgre_time(void)1281489290e9SAlexander Motin ng_pptpgre_time(void)
1282add85a1dSArchie Cobbs {
1283add85a1dSArchie Cobbs struct timeval tv;
1284678f9e33SArchie Cobbs pptptime_t t;
1285add85a1dSArchie Cobbs
1286678f9e33SArchie Cobbs microuptime(&tv);
1287678f9e33SArchie Cobbs t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE;
1288714f558bSAlexander Motin t += tv.tv_usec / (1000000 / PPTP_TIME_SCALE);
1289678f9e33SArchie Cobbs return(t);
1290add85a1dSArchie Cobbs }
1291