1c398230bSWarner Losh /*- 2901fadf7SArchie Cobbs * Copyright (c) 2001-2002 Packet Design, LLC. 3901fadf7SArchie Cobbs * All rights reserved. 4901fadf7SArchie Cobbs * 5901fadf7SArchie Cobbs * Subject to the following obligations and disclaimer of warranty, 6901fadf7SArchie Cobbs * use and redistribution of this software, in source or object code 7901fadf7SArchie Cobbs * forms, with or without modifications are expressly permitted by 8901fadf7SArchie Cobbs * Packet Design; provided, however, that: 9901fadf7SArchie Cobbs * 10901fadf7SArchie Cobbs * (i) Any and all reproductions of the source or object code 11901fadf7SArchie Cobbs * must include the copyright notice above and the following 12901fadf7SArchie Cobbs * disclaimer of warranties; and 13901fadf7SArchie Cobbs * (ii) No rights are granted, in any manner or form, to use 14901fadf7SArchie Cobbs * Packet Design trademarks, including the mark "PACKET DESIGN" 15901fadf7SArchie Cobbs * on advertising, endorsements, or otherwise except as such 16901fadf7SArchie Cobbs * appears in the above copyright notice or in the software. 17901fadf7SArchie Cobbs * 18901fadf7SArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND 19901fadf7SArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO 20901fadf7SArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING 21901fadf7SArchie Cobbs * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED 22901fadf7SArchie Cobbs * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 23901fadf7SArchie Cobbs * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, 24901fadf7SArchie Cobbs * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS 25901fadf7SArchie Cobbs * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, 26901fadf7SArchie Cobbs * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE 27901fadf7SArchie Cobbs * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE 28901fadf7SArchie Cobbs * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, 29901fadf7SArchie Cobbs * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL 30901fadf7SArchie Cobbs * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF 31901fadf7SArchie Cobbs * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF 32901fadf7SArchie Cobbs * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33901fadf7SArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34901fadf7SArchie Cobbs * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF 35901fadf7SArchie Cobbs * THE POSSIBILITY OF SUCH DAMAGE. 36901fadf7SArchie Cobbs * 37901fadf7SArchie Cobbs * Author: Archie Cobbs <archie@freebsd.org> 38901fadf7SArchie Cobbs * 39901fadf7SArchie Cobbs * $FreeBSD$ 40901fadf7SArchie Cobbs */ 41901fadf7SArchie Cobbs 42901fadf7SArchie Cobbs /* 43901fadf7SArchie Cobbs * L2TP netgraph node type. 44901fadf7SArchie Cobbs * 45901fadf7SArchie Cobbs * This node type implements the lower layer of the 46901fadf7SArchie Cobbs * L2TP protocol as specified in RFC 2661. 47901fadf7SArchie Cobbs */ 48901fadf7SArchie Cobbs 49901fadf7SArchie Cobbs #include <sys/param.h> 50901fadf7SArchie Cobbs #include <sys/systm.h> 51901fadf7SArchie Cobbs #include <sys/kernel.h> 52901fadf7SArchie Cobbs #include <sys/time.h> 53901fadf7SArchie Cobbs #include <sys/conf.h> 54901fadf7SArchie Cobbs #include <sys/mbuf.h> 55901fadf7SArchie Cobbs #include <sys/malloc.h> 56901fadf7SArchie Cobbs #include <sys/errno.h> 57*ae04d304SGleb Smirnoff #include <sys/epoch.h> 5886fea6beSBosko Milekic #include <sys/libkern.h> 59901fadf7SArchie Cobbs 60*ae04d304SGleb Smirnoff #include <net/vnet.h> 61*ae04d304SGleb Smirnoff 62901fadf7SArchie Cobbs #include <netgraph/ng_message.h> 63901fadf7SArchie Cobbs #include <netgraph/netgraph.h> 64901fadf7SArchie Cobbs #include <netgraph/ng_parse.h> 65901fadf7SArchie Cobbs #include <netgraph/ng_l2tp.h> 66901fadf7SArchie Cobbs 67901fadf7SArchie Cobbs #ifdef NG_SEPARATE_MALLOC 68d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_L2TP, "netgraph_l2tp", "netgraph l2tp node"); 69901fadf7SArchie Cobbs #else 70901fadf7SArchie Cobbs #define M_NETGRAPH_L2TP M_NETGRAPH 71901fadf7SArchie Cobbs #endif 72901fadf7SArchie Cobbs 73901fadf7SArchie Cobbs /* L2TP header format (first 2 bytes only) */ 74901fadf7SArchie Cobbs #define L2TP_HDR_CTRL 0x8000 /* control packet */ 75901fadf7SArchie Cobbs #define L2TP_HDR_LEN 0x4000 /* has length field */ 76901fadf7SArchie Cobbs #define L2TP_HDR_SEQ 0x0800 /* has ns, nr fields */ 77901fadf7SArchie Cobbs #define L2TP_HDR_OFF 0x0200 /* has offset field */ 78901fadf7SArchie Cobbs #define L2TP_HDR_PRIO 0x0100 /* give priority */ 79901fadf7SArchie Cobbs #define L2TP_HDR_VERS_MASK 0x000f /* version field mask */ 80901fadf7SArchie Cobbs #define L2TP_HDR_VERSION 0x0002 /* version field */ 81901fadf7SArchie Cobbs 82901fadf7SArchie Cobbs /* Bits that must be zero or one in first two bytes of header */ 83901fadf7SArchie Cobbs #define L2TP_CTRL_0BITS 0x030d /* ctrl: must be 0 */ 84901fadf7SArchie Cobbs #define L2TP_CTRL_1BITS 0xc802 /* ctrl: must be 1 */ 85901fadf7SArchie Cobbs #define L2TP_DATA_0BITS 0x800d /* data: must be 0 */ 86901fadf7SArchie Cobbs #define L2TP_DATA_1BITS 0x0002 /* data: must be 1 */ 87901fadf7SArchie Cobbs 88901fadf7SArchie Cobbs /* Standard xmit ctrl and data header bits */ 89901fadf7SArchie Cobbs #define L2TP_CTRL_HDR (L2TP_HDR_CTRL | L2TP_HDR_LEN \ 90901fadf7SArchie Cobbs | L2TP_HDR_SEQ | L2TP_HDR_VERSION) 91901fadf7SArchie Cobbs #define L2TP_DATA_HDR (L2TP_HDR_VERSION) /* optional: len, seq */ 92901fadf7SArchie Cobbs 93901fadf7SArchie Cobbs /* Some hard coded values */ 9452b9b77fSAlexander Motin #define L2TP_MAX_XWIN 128 /* my max xmit window */ 95901fadf7SArchie Cobbs #define L2TP_MAX_REXMIT 5 /* default max rexmit */ 96901fadf7SArchie Cobbs #define L2TP_MAX_REXMIT_TO 30 /* default rexmit to */ 97901fadf7SArchie Cobbs #define L2TP_DELAYED_ACK ((hz + 19) / 20) /* delayed ack: 50 ms */ 98901fadf7SArchie Cobbs 99901fadf7SArchie Cobbs /* Default data sequence number configuration for new sessions */ 100901fadf7SArchie Cobbs #define L2TP_CONTROL_DSEQ 1 /* we are the lns */ 101901fadf7SArchie Cobbs #define L2TP_ENABLE_DSEQ 1 /* enable data seq # */ 102901fadf7SArchie Cobbs 103901fadf7SArchie Cobbs /* Compare sequence numbers using circular math */ 104e5d72e64SGleb Smirnoff #define L2TP_SEQ_DIFF(x, y) ((int16_t)((x) - (y))) 105901fadf7SArchie Cobbs 1064e759763SAlexander Motin #define SESSHASHSIZE 0x0020 1074e759763SAlexander Motin #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1)) 1084e759763SAlexander Motin 1094e759763SAlexander Motin /* Hook private data (data session hooks only) */ 1104e759763SAlexander Motin struct ng_l2tp_hook_private { 1114e759763SAlexander Motin struct ng_l2tp_sess_config conf; /* hook/session config */ 1124e759763SAlexander Motin struct ng_l2tp_session_stats stats; /* per sessions statistics */ 1134e759763SAlexander Motin hook_p hook; /* hook reference */ 1144e759763SAlexander Motin u_int16_t ns; /* data ns sequence number */ 1154e759763SAlexander Motin u_int16_t nr; /* data nr sequence number */ 1164e759763SAlexander Motin LIST_ENTRY(ng_l2tp_hook_private) sessions; 1174e759763SAlexander Motin }; 1184e759763SAlexander Motin typedef struct ng_l2tp_hook_private *hookpriv_p; 1194e759763SAlexander Motin 120901fadf7SArchie Cobbs /* 121901fadf7SArchie Cobbs * Sequence number state 122901fadf7SArchie Cobbs * 123901fadf7SArchie Cobbs * Invariants: 124901fadf7SArchie Cobbs * - If cwnd < ssth, we're doing slow start, otherwise congestion avoidance 125901fadf7SArchie Cobbs * - The number of unacknowledged xmit packets is (ns - rack) <= seq->wmax 126901fadf7SArchie Cobbs * - The first (ns - rack) mbuf's in xwin[] array are copies of these 127901fadf7SArchie Cobbs * unacknowledged packets; the remainder of xwin[] consists first of 128901fadf7SArchie Cobbs * zero or more further untransmitted packets in the transmit queue 129901fadf7SArchie Cobbs * - We try to keep the peer's receive window as full as possible. 130901fadf7SArchie Cobbs * Therefore, (i < cwnd && xwin[i] != NULL) implies (ns - rack) > i. 131901fadf7SArchie Cobbs * - rack_timer is running iff (ns - rack) > 0 (unack'd xmit'd pkts) 132901fadf7SArchie Cobbs * - If xack != nr, there are unacknowledged recv packet(s) (delayed ack) 133901fadf7SArchie Cobbs * - xack_timer is running iff xack != nr (unack'd rec'd pkts) 134901fadf7SArchie Cobbs */ 135901fadf7SArchie Cobbs struct l2tp_seq { 136901fadf7SArchie Cobbs u_int16_t ns; /* next xmit seq we send */ 137901fadf7SArchie Cobbs u_int16_t nr; /* next recv seq we expect */ 138af63939cSAlexander Motin u_int16_t inproc; /* packet is in processing */ 139901fadf7SArchie Cobbs u_int16_t rack; /* last 'nr' we rec'd */ 140901fadf7SArchie Cobbs u_int16_t xack; /* last 'nr' we sent */ 141901fadf7SArchie Cobbs u_int16_t wmax; /* peer's max recv window */ 142901fadf7SArchie Cobbs u_int16_t cwnd; /* current congestion window */ 143901fadf7SArchie Cobbs u_int16_t ssth; /* slow start threshold */ 144901fadf7SArchie Cobbs u_int16_t acks; /* # consecutive acks rec'd */ 145901fadf7SArchie Cobbs u_int16_t rexmits; /* # retransmits sent */ 146901fadf7SArchie Cobbs struct callout rack_timer; /* retransmit timer */ 147901fadf7SArchie Cobbs struct callout xack_timer; /* delayed ack timer */ 148901fadf7SArchie Cobbs struct mbuf *xwin[L2TP_MAX_XWIN]; /* transmit window */ 149702f9895SAlexander Motin struct mtx mtx; /* seq mutex */ 150901fadf7SArchie Cobbs }; 151901fadf7SArchie Cobbs 152901fadf7SArchie Cobbs /* Node private data */ 153901fadf7SArchie Cobbs struct ng_l2tp_private { 154901fadf7SArchie Cobbs node_p node; /* back pointer to node */ 155901fadf7SArchie Cobbs hook_p ctrl; /* hook to upper layers */ 156901fadf7SArchie Cobbs hook_p lower; /* hook to lower layers */ 157901fadf7SArchie Cobbs struct ng_l2tp_config conf; /* node configuration */ 158901fadf7SArchie Cobbs struct ng_l2tp_stats stats; /* node statistics */ 159901fadf7SArchie Cobbs struct l2tp_seq seq; /* ctrl sequence number state */ 160901fadf7SArchie Cobbs ng_ID_t ftarget; /* failure message target */ 1614e759763SAlexander Motin LIST_HEAD(, ng_l2tp_hook_private) sesshash[SESSHASHSIZE]; 162901fadf7SArchie Cobbs }; 163901fadf7SArchie Cobbs typedef struct ng_l2tp_private *priv_p; 164901fadf7SArchie Cobbs 165901fadf7SArchie Cobbs /* Netgraph node methods */ 166901fadf7SArchie Cobbs static ng_constructor_t ng_l2tp_constructor; 167901fadf7SArchie Cobbs static ng_rcvmsg_t ng_l2tp_rcvmsg; 168901fadf7SArchie Cobbs static ng_shutdown_t ng_l2tp_shutdown; 169901fadf7SArchie Cobbs static ng_newhook_t ng_l2tp_newhook; 170901fadf7SArchie Cobbs static ng_rcvdata_t ng_l2tp_rcvdata; 171bf741e4dSAlexander Motin static ng_rcvdata_t ng_l2tp_rcvdata_lower; 172bf741e4dSAlexander Motin static ng_rcvdata_t ng_l2tp_rcvdata_ctrl; 173901fadf7SArchie Cobbs static ng_disconnect_t ng_l2tp_disconnect; 174901fadf7SArchie Cobbs 175901fadf7SArchie Cobbs /* Internal functions */ 176901fadf7SArchie Cobbs static int ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns); 177901fadf7SArchie Cobbs 178901fadf7SArchie Cobbs static void ng_l2tp_seq_init(priv_p priv); 1791e031324SBjoern A. Zeeb static int ng_l2tp_seq_set(priv_p priv, 1801e031324SBjoern A. Zeeb const struct ng_l2tp_seq_config *conf); 181901fadf7SArchie Cobbs static int ng_l2tp_seq_adjust(priv_p priv, 182901fadf7SArchie Cobbs const struct ng_l2tp_config *conf); 183901fadf7SArchie Cobbs static void ng_l2tp_seq_reset(priv_p priv); 184901fadf7SArchie Cobbs static void ng_l2tp_seq_failure(priv_p priv); 185901fadf7SArchie Cobbs static void ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr); 186*ae04d304SGleb Smirnoff static void ng_l2tp_seq_xack_timeout(void *); 187*ae04d304SGleb Smirnoff static void ng_l2tp_seq_rack_timeout(void *); 188901fadf7SArchie Cobbs 1894e759763SAlexander Motin static hookpriv_p ng_l2tp_find_session(priv_p privp, u_int16_t sid); 190901fadf7SArchie Cobbs static ng_fn_eachhook ng_l2tp_reset_session; 191901fadf7SArchie Cobbs 1921e031324SBjoern A. Zeeb /* Parse type for struct ng_l2tp_seq_config. */ 1931e031324SBjoern A. Zeeb static const struct ng_parse_struct_field 1941e031324SBjoern A. Zeeb ng_l2tp_seq_config_fields[] = NG_L2TP_SEQ_CONFIG_TYPE_INFO; 1951e031324SBjoern A. Zeeb static const struct ng_parse_type ng_l2tp_seq_config_type = { 1961e031324SBjoern A. Zeeb &ng_parse_struct_type, 1971e031324SBjoern A. Zeeb &ng_l2tp_seq_config_fields 1981e031324SBjoern A. Zeeb }; 1991e031324SBjoern A. Zeeb 200901fadf7SArchie Cobbs /* Parse type for struct ng_l2tp_config */ 201901fadf7SArchie Cobbs static const struct ng_parse_struct_field 202901fadf7SArchie Cobbs ng_l2tp_config_type_fields[] = NG_L2TP_CONFIG_TYPE_INFO; 203901fadf7SArchie Cobbs static const struct ng_parse_type ng_l2tp_config_type = { 204901fadf7SArchie Cobbs &ng_parse_struct_type, 205901fadf7SArchie Cobbs &ng_l2tp_config_type_fields, 206901fadf7SArchie Cobbs }; 207901fadf7SArchie Cobbs 208901fadf7SArchie Cobbs /* Parse type for struct ng_l2tp_sess_config */ 209901fadf7SArchie Cobbs static const struct ng_parse_struct_field 210901fadf7SArchie Cobbs ng_l2tp_sess_config_type_fields[] = NG_L2TP_SESS_CONFIG_TYPE_INFO; 211901fadf7SArchie Cobbs static const struct ng_parse_type ng_l2tp_sess_config_type = { 212901fadf7SArchie Cobbs &ng_parse_struct_type, 213901fadf7SArchie Cobbs &ng_l2tp_sess_config_type_fields, 214901fadf7SArchie Cobbs }; 215901fadf7SArchie Cobbs 216901fadf7SArchie Cobbs /* Parse type for struct ng_l2tp_stats */ 217901fadf7SArchie Cobbs static const struct ng_parse_struct_field 218901fadf7SArchie Cobbs ng_l2tp_stats_type_fields[] = NG_L2TP_STATS_TYPE_INFO; 2192c9027fcSArchie Cobbs static const struct ng_parse_type ng_l2tp_stats_type = { 220901fadf7SArchie Cobbs &ng_parse_struct_type, 221901fadf7SArchie Cobbs &ng_l2tp_stats_type_fields 222901fadf7SArchie Cobbs }; 223901fadf7SArchie Cobbs 2244807330cSBjoern A. Zeeb /* Parse type for struct ng_l2tp_session_stats. */ 2254807330cSBjoern A. Zeeb static const struct ng_parse_struct_field 2264807330cSBjoern A. Zeeb ng_l2tp_session_stats_type_fields[] = NG_L2TP_SESSION_STATS_TYPE_INFO; 2274807330cSBjoern A. Zeeb static const struct ng_parse_type ng_l2tp_session_stats_type = { 2284807330cSBjoern A. Zeeb &ng_parse_struct_type, 2294807330cSBjoern A. Zeeb &ng_l2tp_session_stats_type_fields 2304807330cSBjoern A. Zeeb }; 2314807330cSBjoern A. Zeeb 232901fadf7SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 233901fadf7SArchie Cobbs static const struct ng_cmdlist ng_l2tp_cmdlist[] = { 234901fadf7SArchie Cobbs { 235901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 236901fadf7SArchie Cobbs NGM_L2TP_SET_CONFIG, 237901fadf7SArchie Cobbs "setconfig", 238901fadf7SArchie Cobbs &ng_l2tp_config_type, 239901fadf7SArchie Cobbs NULL 240901fadf7SArchie Cobbs }, 241901fadf7SArchie Cobbs { 242901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 243901fadf7SArchie Cobbs NGM_L2TP_GET_CONFIG, 244901fadf7SArchie Cobbs "getconfig", 245901fadf7SArchie Cobbs NULL, 246901fadf7SArchie Cobbs &ng_l2tp_config_type 247901fadf7SArchie Cobbs }, 248901fadf7SArchie Cobbs { 249901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 250901fadf7SArchie Cobbs NGM_L2TP_SET_SESS_CONFIG, 251901fadf7SArchie Cobbs "setsessconfig", 252901fadf7SArchie Cobbs &ng_l2tp_sess_config_type, 253901fadf7SArchie Cobbs NULL 254901fadf7SArchie Cobbs }, 255901fadf7SArchie Cobbs { 256901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 257901fadf7SArchie Cobbs NGM_L2TP_GET_SESS_CONFIG, 258901fadf7SArchie Cobbs "getsessconfig", 259901fadf7SArchie Cobbs &ng_parse_hint16_type, 260901fadf7SArchie Cobbs &ng_l2tp_sess_config_type 261901fadf7SArchie Cobbs }, 262901fadf7SArchie Cobbs { 263901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 264901fadf7SArchie Cobbs NGM_L2TP_GET_STATS, 265901fadf7SArchie Cobbs "getstats", 266901fadf7SArchie Cobbs NULL, 2672c9027fcSArchie Cobbs &ng_l2tp_stats_type 268901fadf7SArchie Cobbs }, 269901fadf7SArchie Cobbs { 270901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 271901fadf7SArchie Cobbs NGM_L2TP_CLR_STATS, 272901fadf7SArchie Cobbs "clrstats", 273901fadf7SArchie Cobbs NULL, 274901fadf7SArchie Cobbs NULL 275901fadf7SArchie Cobbs }, 276901fadf7SArchie Cobbs { 277901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 278901fadf7SArchie Cobbs NGM_L2TP_GETCLR_STATS, 279901fadf7SArchie Cobbs "getclrstats", 280901fadf7SArchie Cobbs NULL, 2812c9027fcSArchie Cobbs &ng_l2tp_stats_type 282901fadf7SArchie Cobbs }, 283901fadf7SArchie Cobbs { 284901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 2854807330cSBjoern A. Zeeb NGM_L2TP_GET_SESSION_STATS, 2864807330cSBjoern A. Zeeb "getsessstats", 2874807330cSBjoern A. Zeeb &ng_parse_int16_type, 2884807330cSBjoern A. Zeeb &ng_l2tp_session_stats_type 2894807330cSBjoern A. Zeeb }, 2904807330cSBjoern A. Zeeb { 2914807330cSBjoern A. Zeeb NGM_L2TP_COOKIE, 2924807330cSBjoern A. Zeeb NGM_L2TP_CLR_SESSION_STATS, 2934807330cSBjoern A. Zeeb "clrsessstats", 2944807330cSBjoern A. Zeeb &ng_parse_int16_type, 2954807330cSBjoern A. Zeeb NULL 2964807330cSBjoern A. Zeeb }, 2974807330cSBjoern A. Zeeb { 2984807330cSBjoern A. Zeeb NGM_L2TP_COOKIE, 2994807330cSBjoern A. Zeeb NGM_L2TP_GETCLR_SESSION_STATS, 3004807330cSBjoern A. Zeeb "getclrsessstats", 3014807330cSBjoern A. Zeeb &ng_parse_int16_type, 3024807330cSBjoern A. Zeeb &ng_l2tp_session_stats_type 3034807330cSBjoern A. Zeeb }, 3044807330cSBjoern A. Zeeb { 3054807330cSBjoern A. Zeeb NGM_L2TP_COOKIE, 306901fadf7SArchie Cobbs NGM_L2TP_ACK_FAILURE, 307901fadf7SArchie Cobbs "ackfailure", 308901fadf7SArchie Cobbs NULL, 309901fadf7SArchie Cobbs NULL 310901fadf7SArchie Cobbs }, 3111e031324SBjoern A. Zeeb { 3121e031324SBjoern A. Zeeb NGM_L2TP_COOKIE, 3131e031324SBjoern A. Zeeb NGM_L2TP_SET_SEQ, 3141e031324SBjoern A. Zeeb "setsequence", 3151e031324SBjoern A. Zeeb &ng_l2tp_seq_config_type, 3161e031324SBjoern A. Zeeb NULL 3171e031324SBjoern A. Zeeb }, 318901fadf7SArchie Cobbs { 0 } 319901fadf7SArchie Cobbs }; 320901fadf7SArchie Cobbs 321901fadf7SArchie Cobbs /* Node type descriptor */ 322901fadf7SArchie Cobbs static struct ng_type ng_l2tp_typestruct = { 323f8aae777SJulian Elischer .version = NG_ABI_VERSION, 324f8aae777SJulian Elischer .name = NG_L2TP_NODE_TYPE, 325f8aae777SJulian Elischer .constructor = ng_l2tp_constructor, 326f8aae777SJulian Elischer .rcvmsg = ng_l2tp_rcvmsg, 327f8aae777SJulian Elischer .shutdown = ng_l2tp_shutdown, 328f8aae777SJulian Elischer .newhook = ng_l2tp_newhook, 329f8aae777SJulian Elischer .rcvdata = ng_l2tp_rcvdata, 330f8aae777SJulian Elischer .disconnect = ng_l2tp_disconnect, 331f8aae777SJulian Elischer .cmdlist = ng_l2tp_cmdlist, 332901fadf7SArchie Cobbs }; 333901fadf7SArchie Cobbs NETGRAPH_INIT(l2tp, &ng_l2tp_typestruct); 334901fadf7SArchie Cobbs 3350a76c63dSGleb Smirnoff /* Sequence number state locking & sanity checking */ 336901fadf7SArchie Cobbs #ifdef INVARIANTS 3370a76c63dSGleb Smirnoff static void ng_l2tp_seq_check(struct l2tp_seq *seq); 3380a76c63dSGleb Smirnoff #define SEQ_LOCK(seq) do { \ 3390a76c63dSGleb Smirnoff mtx_lock(&(seq)->mtx); \ 3400a76c63dSGleb Smirnoff ng_l2tp_seq_check(seq); \ 3410a76c63dSGleb Smirnoff } while (0) 3420a76c63dSGleb Smirnoff #define SEQ_UNLOCK(seq) do { \ 3430a76c63dSGleb Smirnoff ng_l2tp_seq_check(seq); \ 3440a76c63dSGleb Smirnoff mtx_unlock(&(seq)->mtx); \ 3450a76c63dSGleb Smirnoff } while (0) 346901fadf7SArchie Cobbs #else 3470a76c63dSGleb Smirnoff #define SEQ_LOCK(seq) mtx_lock(&(seq)->mtx) 3480a76c63dSGleb Smirnoff #define SEQ_UNLOCK(seq) mtx_unlock(&(seq)->mtx) 349901fadf7SArchie Cobbs #endif 3500a76c63dSGleb Smirnoff #define SEQ_LOCK_ASSERT(seq) mtx_assert(&(seq)->mtx, MA_OWNED) 351901fadf7SArchie Cobbs 352901fadf7SArchie Cobbs /* Whether to use m_copypacket() or m_dup() */ 353901fadf7SArchie Cobbs #define L2TP_COPY_MBUF m_copypacket 354901fadf7SArchie Cobbs 355bf741e4dSAlexander Motin #define ERROUT(x) do { error = (x); goto done; } while (0) 356bf741e4dSAlexander Motin 357901fadf7SArchie Cobbs /************************************************************************ 358901fadf7SArchie Cobbs NETGRAPH NODE STUFF 359901fadf7SArchie Cobbs ************************************************************************/ 360901fadf7SArchie Cobbs 361901fadf7SArchie Cobbs /* 362901fadf7SArchie Cobbs * Node type constructor 363901fadf7SArchie Cobbs */ 364901fadf7SArchie Cobbs static int 365901fadf7SArchie Cobbs ng_l2tp_constructor(node_p node) 366901fadf7SArchie Cobbs { 367901fadf7SArchie Cobbs priv_p priv; 3684e759763SAlexander Motin int i; 369901fadf7SArchie Cobbs 370901fadf7SArchie Cobbs /* Allocate private structure */ 371674d86bfSGleb Smirnoff priv = malloc(sizeof(*priv), M_NETGRAPH_L2TP, M_WAITOK | M_ZERO); 372901fadf7SArchie Cobbs NG_NODE_SET_PRIVATE(node, priv); 373901fadf7SArchie Cobbs priv->node = node; 374901fadf7SArchie Cobbs 375901fadf7SArchie Cobbs /* Apply a semi-reasonable default configuration */ 376901fadf7SArchie Cobbs priv->conf.peer_win = 1; 377901fadf7SArchie Cobbs priv->conf.rexmit_max = L2TP_MAX_REXMIT; 378901fadf7SArchie Cobbs priv->conf.rexmit_max_to = L2TP_MAX_REXMIT_TO; 379901fadf7SArchie Cobbs 380901fadf7SArchie Cobbs /* Initialize sequence number state */ 381901fadf7SArchie Cobbs ng_l2tp_seq_init(priv); 382901fadf7SArchie Cobbs 3834e759763SAlexander Motin for (i = 0; i < SESSHASHSIZE; i++) 3844e759763SAlexander Motin LIST_INIT(&priv->sesshash[i]); 3854e759763SAlexander Motin 386901fadf7SArchie Cobbs /* Done */ 387901fadf7SArchie Cobbs return (0); 388901fadf7SArchie Cobbs } 389901fadf7SArchie Cobbs 390901fadf7SArchie Cobbs /* 391901fadf7SArchie Cobbs * Give our OK for a hook to be added. 392901fadf7SArchie Cobbs */ 393901fadf7SArchie Cobbs static int 394901fadf7SArchie Cobbs ng_l2tp_newhook(node_p node, hook_p hook, const char *name) 395901fadf7SArchie Cobbs { 396901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 397901fadf7SArchie Cobbs 398901fadf7SArchie Cobbs /* Check hook name */ 399901fadf7SArchie Cobbs if (strcmp(name, NG_L2TP_HOOK_CTRL) == 0) { 400901fadf7SArchie Cobbs if (priv->ctrl != NULL) 401901fadf7SArchie Cobbs return (EISCONN); 402901fadf7SArchie Cobbs priv->ctrl = hook; 403bf741e4dSAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_l2tp_rcvdata_ctrl); 404901fadf7SArchie Cobbs } else if (strcmp(name, NG_L2TP_HOOK_LOWER) == 0) { 405901fadf7SArchie Cobbs if (priv->lower != NULL) 406901fadf7SArchie Cobbs return (EISCONN); 407901fadf7SArchie Cobbs priv->lower = hook; 408bf741e4dSAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_l2tp_rcvdata_lower); 409901fadf7SArchie Cobbs } else { 410901fadf7SArchie Cobbs static const char hexdig[16] = "0123456789abcdef"; 411901fadf7SArchie Cobbs u_int16_t session_id; 412901fadf7SArchie Cobbs hookpriv_p hpriv; 4134e759763SAlexander Motin uint16_t hash; 414901fadf7SArchie Cobbs const char *hex; 415901fadf7SArchie Cobbs int i; 416901fadf7SArchie Cobbs int j; 417901fadf7SArchie Cobbs 418901fadf7SArchie Cobbs /* Parse hook name to get session ID */ 419901fadf7SArchie Cobbs if (strncmp(name, NG_L2TP_HOOK_SESSION_P, 420901fadf7SArchie Cobbs sizeof(NG_L2TP_HOOK_SESSION_P) - 1) != 0) 421901fadf7SArchie Cobbs return (EINVAL); 422901fadf7SArchie Cobbs hex = name + sizeof(NG_L2TP_HOOK_SESSION_P) - 1; 423901fadf7SArchie Cobbs for (session_id = i = 0; i < 4; i++) { 424901fadf7SArchie Cobbs for (j = 0; j < 16 && hex[i] != hexdig[j]; j++); 425901fadf7SArchie Cobbs if (j == 16) 426901fadf7SArchie Cobbs return (EINVAL); 427901fadf7SArchie Cobbs session_id = (session_id << 4) | j; 428901fadf7SArchie Cobbs } 429901fadf7SArchie Cobbs if (hex[i] != '\0') 430901fadf7SArchie Cobbs return (EINVAL); 431901fadf7SArchie Cobbs 432901fadf7SArchie Cobbs /* Create hook private structure */ 433e11e3f18SDag-Erling Smørgrav hpriv = malloc(sizeof(*hpriv), 434e11e3f18SDag-Erling Smørgrav M_NETGRAPH_L2TP, M_NOWAIT | M_ZERO); 435901fadf7SArchie Cobbs if (hpriv == NULL) 436901fadf7SArchie Cobbs return (ENOMEM); 437280d6bd7SAlexander Motin hpriv->conf.session_id = session_id; 438901fadf7SArchie Cobbs hpriv->conf.control_dseq = L2TP_CONTROL_DSEQ; 439901fadf7SArchie Cobbs hpriv->conf.enable_dseq = L2TP_ENABLE_DSEQ; 4404e759763SAlexander Motin hpriv->hook = hook; 441901fadf7SArchie Cobbs NG_HOOK_SET_PRIVATE(hook, hpriv); 4424e759763SAlexander Motin hash = SESSHASH(hpriv->conf.session_id); 4434e759763SAlexander Motin LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions); 444901fadf7SArchie Cobbs } 445901fadf7SArchie Cobbs 446901fadf7SArchie Cobbs /* Done */ 447901fadf7SArchie Cobbs return (0); 448901fadf7SArchie Cobbs } 449901fadf7SArchie Cobbs 450901fadf7SArchie Cobbs /* 451901fadf7SArchie Cobbs * Receive a control message. 452901fadf7SArchie Cobbs */ 453901fadf7SArchie Cobbs static int 454901fadf7SArchie Cobbs ng_l2tp_rcvmsg(node_p node, item_p item, hook_p lasthook) 455901fadf7SArchie Cobbs { 456901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 457901fadf7SArchie Cobbs struct ng_mesg *resp = NULL; 458901fadf7SArchie Cobbs struct ng_mesg *msg; 459901fadf7SArchie Cobbs int error = 0; 460901fadf7SArchie Cobbs 461901fadf7SArchie Cobbs NGI_GET_MSG(item, msg); 462901fadf7SArchie Cobbs switch (msg->header.typecookie) { 463901fadf7SArchie Cobbs case NGM_L2TP_COOKIE: 464901fadf7SArchie Cobbs switch (msg->header.cmd) { 465901fadf7SArchie Cobbs case NGM_L2TP_SET_CONFIG: 466901fadf7SArchie Cobbs { 467901fadf7SArchie Cobbs struct ng_l2tp_config *const conf = 468901fadf7SArchie Cobbs (struct ng_l2tp_config *)msg->data; 469901fadf7SArchie Cobbs 470901fadf7SArchie Cobbs /* Check for invalid or illegal config */ 471901fadf7SArchie Cobbs if (msg->header.arglen != sizeof(*conf)) { 472901fadf7SArchie Cobbs error = EINVAL; 473901fadf7SArchie Cobbs break; 474901fadf7SArchie Cobbs } 475901fadf7SArchie Cobbs conf->enabled = !!conf->enabled; 476901fadf7SArchie Cobbs conf->match_id = !!conf->match_id; 477901fadf7SArchie Cobbs if (priv->conf.enabled 478901fadf7SArchie Cobbs && ((priv->conf.tunnel_id != 0 479901fadf7SArchie Cobbs && conf->tunnel_id != priv->conf.tunnel_id) 480901fadf7SArchie Cobbs || ((priv->conf.peer_id != 0 481901fadf7SArchie Cobbs && conf->peer_id != priv->conf.peer_id)))) { 482901fadf7SArchie Cobbs error = EBUSY; 483901fadf7SArchie Cobbs break; 484901fadf7SArchie Cobbs } 485901fadf7SArchie Cobbs 486901fadf7SArchie Cobbs /* Save calling node as failure target */ 487901fadf7SArchie Cobbs priv->ftarget = NGI_RETADDR(item); 488901fadf7SArchie Cobbs 489901fadf7SArchie Cobbs /* Adjust sequence number state */ 490901fadf7SArchie Cobbs if ((error = ng_l2tp_seq_adjust(priv, conf)) != 0) 491901fadf7SArchie Cobbs break; 492901fadf7SArchie Cobbs 493901fadf7SArchie Cobbs /* Update node's config */ 494901fadf7SArchie Cobbs priv->conf = *conf; 495901fadf7SArchie Cobbs break; 496901fadf7SArchie Cobbs } 497901fadf7SArchie Cobbs case NGM_L2TP_GET_CONFIG: 498901fadf7SArchie Cobbs { 499901fadf7SArchie Cobbs struct ng_l2tp_config *conf; 500901fadf7SArchie Cobbs 501901fadf7SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); 502901fadf7SArchie Cobbs if (resp == NULL) { 503901fadf7SArchie Cobbs error = ENOMEM; 504901fadf7SArchie Cobbs break; 505901fadf7SArchie Cobbs } 506901fadf7SArchie Cobbs conf = (struct ng_l2tp_config *)resp->data; 507901fadf7SArchie Cobbs *conf = priv->conf; 508901fadf7SArchie Cobbs break; 509901fadf7SArchie Cobbs } 510901fadf7SArchie Cobbs case NGM_L2TP_SET_SESS_CONFIG: 511901fadf7SArchie Cobbs { 512901fadf7SArchie Cobbs struct ng_l2tp_sess_config *const conf = 513901fadf7SArchie Cobbs (struct ng_l2tp_sess_config *)msg->data; 514901fadf7SArchie Cobbs hookpriv_p hpriv; 515901fadf7SArchie Cobbs 5161e031324SBjoern A. Zeeb /* Check for invalid or illegal config. */ 517901fadf7SArchie Cobbs if (msg->header.arglen != sizeof(*conf)) { 518901fadf7SArchie Cobbs error = EINVAL; 519901fadf7SArchie Cobbs break; 520901fadf7SArchie Cobbs } 521901fadf7SArchie Cobbs 522901fadf7SArchie Cobbs /* Find matching hook */ 5234e759763SAlexander Motin hpriv = ng_l2tp_find_session(priv, conf->session_id); 5244e759763SAlexander Motin if (hpriv == NULL) { 525901fadf7SArchie Cobbs error = ENOENT; 526901fadf7SArchie Cobbs break; 527901fadf7SArchie Cobbs } 528901fadf7SArchie Cobbs 529901fadf7SArchie Cobbs /* Update hook's config */ 530901fadf7SArchie Cobbs hpriv->conf = *conf; 531901fadf7SArchie Cobbs break; 532901fadf7SArchie Cobbs } 533901fadf7SArchie Cobbs case NGM_L2TP_GET_SESS_CONFIG: 534901fadf7SArchie Cobbs { 535901fadf7SArchie Cobbs struct ng_l2tp_sess_config *conf; 536901fadf7SArchie Cobbs u_int16_t session_id; 537901fadf7SArchie Cobbs hookpriv_p hpriv; 538901fadf7SArchie Cobbs 539901fadf7SArchie Cobbs /* Get session ID */ 540901fadf7SArchie Cobbs if (msg->header.arglen != sizeof(session_id)) { 541901fadf7SArchie Cobbs error = EINVAL; 542901fadf7SArchie Cobbs break; 543901fadf7SArchie Cobbs } 544901fadf7SArchie Cobbs memcpy(&session_id, msg->data, 2); 545901fadf7SArchie Cobbs 546901fadf7SArchie Cobbs /* Find matching hook */ 5474e759763SAlexander Motin hpriv = ng_l2tp_find_session(priv, session_id); 5484e759763SAlexander Motin if (hpriv == NULL) { 549901fadf7SArchie Cobbs error = ENOENT; 550901fadf7SArchie Cobbs break; 551901fadf7SArchie Cobbs } 552901fadf7SArchie Cobbs 553901fadf7SArchie Cobbs /* Send response */ 554901fadf7SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT); 555901fadf7SArchie Cobbs if (resp == NULL) { 556901fadf7SArchie Cobbs error = ENOMEM; 557901fadf7SArchie Cobbs break; 558901fadf7SArchie Cobbs } 559901fadf7SArchie Cobbs conf = (struct ng_l2tp_sess_config *)resp->data; 560901fadf7SArchie Cobbs *conf = hpriv->conf; 561901fadf7SArchie Cobbs break; 562901fadf7SArchie Cobbs } 563901fadf7SArchie Cobbs case NGM_L2TP_GET_STATS: 564901fadf7SArchie Cobbs case NGM_L2TP_CLR_STATS: 565901fadf7SArchie Cobbs case NGM_L2TP_GETCLR_STATS: 566901fadf7SArchie Cobbs { 567901fadf7SArchie Cobbs if (msg->header.cmd != NGM_L2TP_CLR_STATS) { 568901fadf7SArchie Cobbs NG_MKRESPONSE(resp, msg, 569901fadf7SArchie Cobbs sizeof(priv->stats), M_NOWAIT); 570901fadf7SArchie Cobbs if (resp == NULL) { 571901fadf7SArchie Cobbs error = ENOMEM; 572901fadf7SArchie Cobbs break; 573901fadf7SArchie Cobbs } 574901fadf7SArchie Cobbs memcpy(resp->data, 575901fadf7SArchie Cobbs &priv->stats, sizeof(priv->stats)); 576901fadf7SArchie Cobbs } 577901fadf7SArchie Cobbs if (msg->header.cmd != NGM_L2TP_GET_STATS) 578901fadf7SArchie Cobbs memset(&priv->stats, 0, sizeof(priv->stats)); 579901fadf7SArchie Cobbs break; 580901fadf7SArchie Cobbs } 5814807330cSBjoern A. Zeeb case NGM_L2TP_GET_SESSION_STATS: 5824807330cSBjoern A. Zeeb case NGM_L2TP_CLR_SESSION_STATS: 5834807330cSBjoern A. Zeeb case NGM_L2TP_GETCLR_SESSION_STATS: 5844807330cSBjoern A. Zeeb { 5854807330cSBjoern A. Zeeb uint16_t session_id; 5864807330cSBjoern A. Zeeb hookpriv_p hpriv; 5874807330cSBjoern A. Zeeb 5884807330cSBjoern A. Zeeb /* Get session ID. */ 5894807330cSBjoern A. Zeeb if (msg->header.arglen != sizeof(session_id)) { 5904807330cSBjoern A. Zeeb error = EINVAL; 5914807330cSBjoern A. Zeeb break; 5924807330cSBjoern A. Zeeb } 5934807330cSBjoern A. Zeeb bcopy(msg->data, &session_id, sizeof(uint16_t)); 5944807330cSBjoern A. Zeeb 5954807330cSBjoern A. Zeeb /* Find matching hook. */ 5964e759763SAlexander Motin hpriv = ng_l2tp_find_session(priv, session_id); 5974e759763SAlexander Motin if (hpriv == NULL) { 5984807330cSBjoern A. Zeeb error = ENOENT; 5994807330cSBjoern A. Zeeb break; 6004807330cSBjoern A. Zeeb } 6014807330cSBjoern A. Zeeb 6024807330cSBjoern A. Zeeb if (msg->header.cmd != NGM_L2TP_CLR_SESSION_STATS) { 6034807330cSBjoern A. Zeeb NG_MKRESPONSE(resp, msg, 6044807330cSBjoern A. Zeeb sizeof(hpriv->stats), M_NOWAIT); 6054807330cSBjoern A. Zeeb if (resp == NULL) { 6064807330cSBjoern A. Zeeb error = ENOMEM; 6074807330cSBjoern A. Zeeb break; 6084807330cSBjoern A. Zeeb } 6094807330cSBjoern A. Zeeb bcopy(&hpriv->stats, resp->data, 6104807330cSBjoern A. Zeeb sizeof(hpriv->stats)); 6114807330cSBjoern A. Zeeb } 6124807330cSBjoern A. Zeeb if (msg->header.cmd != NGM_L2TP_GET_SESSION_STATS) 6134807330cSBjoern A. Zeeb bzero(&hpriv->stats, sizeof(hpriv->stats)); 6144807330cSBjoern A. Zeeb break; 6154807330cSBjoern A. Zeeb } 6161e031324SBjoern A. Zeeb case NGM_L2TP_SET_SEQ: 6171e031324SBjoern A. Zeeb { 6181e031324SBjoern A. Zeeb struct ng_l2tp_seq_config *const conf = 6191e031324SBjoern A. Zeeb (struct ng_l2tp_seq_config *)msg->data; 6201e031324SBjoern A. Zeeb 6211e031324SBjoern A. Zeeb /* Check for invalid or illegal seq config. */ 6221e031324SBjoern A. Zeeb if (msg->header.arglen != sizeof(*conf)) { 6231e031324SBjoern A. Zeeb error = EINVAL; 6241e031324SBjoern A. Zeeb break; 6251e031324SBjoern A. Zeeb } 6261e031324SBjoern A. Zeeb conf->ns = htons(conf->ns); 6271e031324SBjoern A. Zeeb conf->nr = htons(conf->nr); 6281e031324SBjoern A. Zeeb conf->rack = htons(conf->rack); 6291e031324SBjoern A. Zeeb conf->xack = htons(conf->xack); 6301e031324SBjoern A. Zeeb 6311e031324SBjoern A. Zeeb /* Set sequence numbers. */ 6321e031324SBjoern A. Zeeb error = ng_l2tp_seq_set(priv, conf); 6331e031324SBjoern A. Zeeb break; 6341e031324SBjoern A. Zeeb } 635901fadf7SArchie Cobbs default: 636901fadf7SArchie Cobbs error = EINVAL; 637901fadf7SArchie Cobbs break; 638901fadf7SArchie Cobbs } 639901fadf7SArchie Cobbs break; 640901fadf7SArchie Cobbs default: 641901fadf7SArchie Cobbs error = EINVAL; 642901fadf7SArchie Cobbs break; 643901fadf7SArchie Cobbs } 644901fadf7SArchie Cobbs 645901fadf7SArchie Cobbs /* Done */ 646901fadf7SArchie Cobbs NG_RESPOND_MSG(error, node, item, resp); 647901fadf7SArchie Cobbs NG_FREE_MSG(msg); 648901fadf7SArchie Cobbs return (error); 649901fadf7SArchie Cobbs } 650901fadf7SArchie Cobbs 651901fadf7SArchie Cobbs /* 652901fadf7SArchie Cobbs * Destroy node 653901fadf7SArchie Cobbs */ 654901fadf7SArchie Cobbs static int 655901fadf7SArchie Cobbs ng_l2tp_shutdown(node_p node) 656901fadf7SArchie Cobbs { 657901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 658901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 659901fadf7SArchie Cobbs 660901fadf7SArchie Cobbs /* Reset sequence number state */ 6610a76c63dSGleb Smirnoff SEQ_LOCK(seq); 662901fadf7SArchie Cobbs ng_l2tp_seq_reset(priv); 6630a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 664901fadf7SArchie Cobbs 665901fadf7SArchie Cobbs /* Free private data if neither timer is running */ 666*ae04d304SGleb Smirnoff callout_drain(&seq->rack_timer); 667*ae04d304SGleb Smirnoff callout_drain(&seq->xack_timer); 6680ef8db8fSGleb Smirnoff 669702f9895SAlexander Motin mtx_destroy(&seq->mtx); 670702f9895SAlexander Motin 6711ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH_L2TP); 672901fadf7SArchie Cobbs 673901fadf7SArchie Cobbs /* Unref node */ 674901fadf7SArchie Cobbs NG_NODE_UNREF(node); 675901fadf7SArchie Cobbs return (0); 676901fadf7SArchie Cobbs } 677901fadf7SArchie Cobbs 678901fadf7SArchie Cobbs /* 679901fadf7SArchie Cobbs * Hook disconnection 680901fadf7SArchie Cobbs */ 681901fadf7SArchie Cobbs static int 682901fadf7SArchie Cobbs ng_l2tp_disconnect(hook_p hook) 683901fadf7SArchie Cobbs { 684901fadf7SArchie Cobbs const node_p node = NG_HOOK_NODE(hook); 685901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 686901fadf7SArchie Cobbs 687901fadf7SArchie Cobbs /* Zero out hook pointer */ 688901fadf7SArchie Cobbs if (hook == priv->ctrl) 689901fadf7SArchie Cobbs priv->ctrl = NULL; 690901fadf7SArchie Cobbs else if (hook == priv->lower) 691901fadf7SArchie Cobbs priv->lower = NULL; 692901fadf7SArchie Cobbs else { 6934e759763SAlexander Motin const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 6944e759763SAlexander Motin LIST_REMOVE(hpriv, sessions); 6951ede983cSDag-Erling Smørgrav free(hpriv, M_NETGRAPH_L2TP); 696901fadf7SArchie Cobbs NG_HOOK_SET_PRIVATE(hook, NULL); 697901fadf7SArchie Cobbs } 698901fadf7SArchie Cobbs 699901fadf7SArchie Cobbs /* Go away if no longer connected to anything */ 700901fadf7SArchie Cobbs if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node)) 701901fadf7SArchie Cobbs ng_rmnode_self(node); 702901fadf7SArchie Cobbs return (0); 703901fadf7SArchie Cobbs } 704901fadf7SArchie Cobbs 705901fadf7SArchie Cobbs /************************************************************************* 706901fadf7SArchie Cobbs INTERNAL FUNCTIONS 707901fadf7SArchie Cobbs *************************************************************************/ 708901fadf7SArchie Cobbs 709901fadf7SArchie Cobbs /* 710280d6bd7SAlexander Motin * Find the hook with a given session ID. 711901fadf7SArchie Cobbs */ 7124e759763SAlexander Motin static hookpriv_p 7134e759763SAlexander Motin ng_l2tp_find_session(priv_p privp, u_int16_t sid) 714901fadf7SArchie Cobbs { 7154e759763SAlexander Motin uint16_t hash = SESSHASH(sid); 7164e759763SAlexander Motin hookpriv_p hpriv = NULL; 717901fadf7SArchie Cobbs 7184e759763SAlexander Motin LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) { 7194e759763SAlexander Motin if (hpriv->conf.session_id == sid) 7204e759763SAlexander Motin break; 7214e759763SAlexander Motin } 7224e759763SAlexander Motin 7234e759763SAlexander Motin return (hpriv); 724901fadf7SArchie Cobbs } 725901fadf7SArchie Cobbs 726901fadf7SArchie Cobbs /* 727901fadf7SArchie Cobbs * Reset a hook's session state. 728901fadf7SArchie Cobbs */ 729901fadf7SArchie Cobbs static int 730901fadf7SArchie Cobbs ng_l2tp_reset_session(hook_p hook, void *arg) 731901fadf7SArchie Cobbs { 732901fadf7SArchie Cobbs const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 733901fadf7SArchie Cobbs 734901fadf7SArchie Cobbs if (hpriv != NULL) { 735901fadf7SArchie Cobbs hpriv->conf.control_dseq = 0; 736901fadf7SArchie Cobbs hpriv->conf.enable_dseq = 0; 737b0239937SAlexander Motin bzero(&hpriv->stats, sizeof(struct ng_l2tp_session_stats)); 738901fadf7SArchie Cobbs hpriv->nr = 0; 739901fadf7SArchie Cobbs hpriv->ns = 0; 740901fadf7SArchie Cobbs } 741901fadf7SArchie Cobbs return (-1); 742901fadf7SArchie Cobbs } 743901fadf7SArchie Cobbs 744901fadf7SArchie Cobbs /* 745901fadf7SArchie Cobbs * Handle an incoming frame from below. 746901fadf7SArchie Cobbs */ 747901fadf7SArchie Cobbs static int 748bf741e4dSAlexander Motin ng_l2tp_rcvdata_lower(hook_p h, item_p item) 749901fadf7SArchie Cobbs { 750901fadf7SArchie Cobbs static const u_int16_t req_bits[2][2] = { 751901fadf7SArchie Cobbs { L2TP_DATA_0BITS, L2TP_DATA_1BITS }, 752901fadf7SArchie Cobbs { L2TP_CTRL_0BITS, L2TP_CTRL_1BITS }, 753901fadf7SArchie Cobbs }; 754bf741e4dSAlexander Motin const node_p node = NG_HOOK_NODE(h); 755901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 756901fadf7SArchie Cobbs hookpriv_p hpriv = NULL; 757901fadf7SArchie Cobbs hook_p hook = NULL; 758901fadf7SArchie Cobbs struct mbuf *m; 759280d6bd7SAlexander Motin u_int16_t tid, sid; 760901fadf7SArchie Cobbs u_int16_t hdr; 761280d6bd7SAlexander Motin u_int16_t ns, nr; 762901fadf7SArchie Cobbs int is_ctrl; 763901fadf7SArchie Cobbs int error; 7644807330cSBjoern A. Zeeb int len, plen; 765901fadf7SArchie Cobbs 766bf741e4dSAlexander Motin /* If not configured, reject */ 767bf741e4dSAlexander Motin if (!priv->conf.enabled) { 768bf741e4dSAlexander Motin NG_FREE_ITEM(item); 769bf741e4dSAlexander Motin ERROUT(ENXIO); 770bf741e4dSAlexander Motin } 771bf741e4dSAlexander Motin 772901fadf7SArchie Cobbs /* Grab mbuf */ 773901fadf7SArchie Cobbs NGI_GET_M(item, m); 774901fadf7SArchie Cobbs 7754807330cSBjoern A. Zeeb /* Remember full packet length; needed for per session accounting. */ 7764807330cSBjoern A. Zeeb plen = m->m_pkthdr.len; 7774807330cSBjoern A. Zeeb 778901fadf7SArchie Cobbs /* Update stats */ 779901fadf7SArchie Cobbs priv->stats.recvPackets++; 7804807330cSBjoern A. Zeeb priv->stats.recvOctets += plen; 781901fadf7SArchie Cobbs 782901fadf7SArchie Cobbs /* Get initial header */ 783901fadf7SArchie Cobbs if (m->m_pkthdr.len < 6) { 784901fadf7SArchie Cobbs priv->stats.recvRunts++; 785901fadf7SArchie Cobbs NG_FREE_ITEM(item); 786901fadf7SArchie Cobbs NG_FREE_M(m); 787bf741e4dSAlexander Motin ERROUT(EINVAL); 788901fadf7SArchie Cobbs } 789901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 790901fadf7SArchie Cobbs priv->stats.memoryFailures++; 791901fadf7SArchie Cobbs NG_FREE_ITEM(item); 792bf741e4dSAlexander Motin ERROUT(EINVAL); 793901fadf7SArchie Cobbs } 794148ac1daSAlexander Motin hdr = (mtod(m, uint8_t *)[0] << 8) + mtod(m, uint8_t *)[1]; 795901fadf7SArchie Cobbs m_adj(m, 2); 796901fadf7SArchie Cobbs 797901fadf7SArchie Cobbs /* Check required header bits and minimum length */ 798901fadf7SArchie Cobbs is_ctrl = (hdr & L2TP_HDR_CTRL) != 0; 799901fadf7SArchie Cobbs if ((hdr & req_bits[is_ctrl][0]) != 0 800901fadf7SArchie Cobbs || (~hdr & req_bits[is_ctrl][1]) != 0) { 801901fadf7SArchie Cobbs priv->stats.recvInvalid++; 802901fadf7SArchie Cobbs NG_FREE_ITEM(item); 803901fadf7SArchie Cobbs NG_FREE_M(m); 804bf741e4dSAlexander Motin ERROUT(EINVAL); 805901fadf7SArchie Cobbs } 806901fadf7SArchie Cobbs if (m->m_pkthdr.len < 4 /* tunnel, session id */ 807901fadf7SArchie Cobbs + (2 * ((hdr & L2TP_HDR_LEN) != 0)) /* length field */ 808901fadf7SArchie Cobbs + (4 * ((hdr & L2TP_HDR_SEQ) != 0)) /* seq # fields */ 809901fadf7SArchie Cobbs + (2 * ((hdr & L2TP_HDR_OFF) != 0))) { /* offset field */ 810901fadf7SArchie Cobbs priv->stats.recvRunts++; 811901fadf7SArchie Cobbs NG_FREE_ITEM(item); 812901fadf7SArchie Cobbs NG_FREE_M(m); 813bf741e4dSAlexander Motin ERROUT(EINVAL); 814901fadf7SArchie Cobbs } 815901fadf7SArchie Cobbs 816901fadf7SArchie Cobbs /* Get and validate length field if present */ 817901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_LEN) != 0) { 818901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 819901fadf7SArchie Cobbs priv->stats.memoryFailures++; 820901fadf7SArchie Cobbs NG_FREE_ITEM(item); 821bf741e4dSAlexander Motin ERROUT(EINVAL); 822901fadf7SArchie Cobbs } 823148ac1daSAlexander Motin len = (mtod(m, uint8_t *)[0] << 8) + mtod(m, uint8_t *)[1] - 4; 824901fadf7SArchie Cobbs m_adj(m, 2); 825901fadf7SArchie Cobbs if (len < 0 || len > m->m_pkthdr.len) { 826901fadf7SArchie Cobbs priv->stats.recvInvalid++; 827901fadf7SArchie Cobbs NG_FREE_ITEM(item); 828901fadf7SArchie Cobbs NG_FREE_M(m); 829bf741e4dSAlexander Motin ERROUT(EINVAL); 830901fadf7SArchie Cobbs } 831901fadf7SArchie Cobbs if (len < m->m_pkthdr.len) /* trim extra bytes */ 832901fadf7SArchie Cobbs m_adj(m, -(m->m_pkthdr.len - len)); 833901fadf7SArchie Cobbs } 834901fadf7SArchie Cobbs 835901fadf7SArchie Cobbs /* Get tunnel ID and session ID */ 836901fadf7SArchie Cobbs if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { 837901fadf7SArchie Cobbs priv->stats.memoryFailures++; 838901fadf7SArchie Cobbs NG_FREE_ITEM(item); 839bf741e4dSAlexander Motin ERROUT(EINVAL); 840901fadf7SArchie Cobbs } 841280d6bd7SAlexander Motin tid = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 842280d6bd7SAlexander Motin sid = (mtod(m, u_int8_t *)[2] << 8) + mtod(m, u_int8_t *)[3]; 843901fadf7SArchie Cobbs m_adj(m, 4); 844901fadf7SArchie Cobbs 845901fadf7SArchie Cobbs /* Check tunnel ID */ 846280d6bd7SAlexander Motin if (tid != priv->conf.tunnel_id && 847280d6bd7SAlexander Motin (priv->conf.match_id || tid != 0)) { 848901fadf7SArchie Cobbs priv->stats.recvWrongTunnel++; 849901fadf7SArchie Cobbs NG_FREE_ITEM(item); 850901fadf7SArchie Cobbs NG_FREE_M(m); 851bf741e4dSAlexander Motin ERROUT(EADDRNOTAVAIL); 852901fadf7SArchie Cobbs } 853901fadf7SArchie Cobbs 854901fadf7SArchie Cobbs /* Check session ID (for data packets only) */ 855901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_CTRL) == 0) { 856280d6bd7SAlexander Motin hpriv = ng_l2tp_find_session(priv, sid); 8574e759763SAlexander Motin if (hpriv == NULL) { 858901fadf7SArchie Cobbs priv->stats.recvUnknownSID++; 859901fadf7SArchie Cobbs NG_FREE_ITEM(item); 860901fadf7SArchie Cobbs NG_FREE_M(m); 861bf741e4dSAlexander Motin ERROUT(ENOTCONN); 862901fadf7SArchie Cobbs } 8634e759763SAlexander Motin hook = hpriv->hook; 864901fadf7SArchie Cobbs } 865901fadf7SArchie Cobbs 866901fadf7SArchie Cobbs /* Get Ns, Nr fields if present */ 867901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_SEQ) != 0) { 868901fadf7SArchie Cobbs if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { 869901fadf7SArchie Cobbs priv->stats.memoryFailures++; 870901fadf7SArchie Cobbs NG_FREE_ITEM(item); 871bf741e4dSAlexander Motin ERROUT(EINVAL); 872901fadf7SArchie Cobbs } 873280d6bd7SAlexander Motin ns = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 874280d6bd7SAlexander Motin nr = (mtod(m, u_int8_t *)[2] << 8) + mtod(m, u_int8_t *)[3]; 875901fadf7SArchie Cobbs m_adj(m, 4); 876280d6bd7SAlexander Motin } else 877280d6bd7SAlexander Motin ns = nr = 0; 878901fadf7SArchie Cobbs 879901fadf7SArchie Cobbs /* Strip offset padding if present */ 880901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_OFF) != 0) { 881901fadf7SArchie Cobbs u_int16_t offset; 882901fadf7SArchie Cobbs 883901fadf7SArchie Cobbs /* Get length of offset padding */ 884901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 885901fadf7SArchie Cobbs priv->stats.memoryFailures++; 886901fadf7SArchie Cobbs NG_FREE_ITEM(item); 887bf741e4dSAlexander Motin ERROUT(EINVAL); 888901fadf7SArchie Cobbs } 889280d6bd7SAlexander Motin offset = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 890901fadf7SArchie Cobbs 891901fadf7SArchie Cobbs /* Trim offset padding */ 892ddb72294SBjoern A. Zeeb if ((2+offset) > m->m_pkthdr.len) { 893901fadf7SArchie Cobbs priv->stats.recvInvalid++; 894901fadf7SArchie Cobbs NG_FREE_ITEM(item); 895901fadf7SArchie Cobbs NG_FREE_M(m); 896bf741e4dSAlexander Motin ERROUT(EINVAL); 897901fadf7SArchie Cobbs } 898ddb72294SBjoern A. Zeeb m_adj(m, 2+offset); 899901fadf7SArchie Cobbs } 900901fadf7SArchie Cobbs 901901fadf7SArchie Cobbs /* Handle control packets */ 902901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_CTRL) != 0) { 903af63939cSAlexander Motin struct l2tp_seq *const seq = &priv->seq; 904901fadf7SArchie Cobbs 9050a76c63dSGleb Smirnoff SEQ_LOCK(seq); 9060a76c63dSGleb Smirnoff 907901fadf7SArchie Cobbs /* Handle receive ack sequence number Nr */ 908901fadf7SArchie Cobbs ng_l2tp_seq_recv_nr(priv, nr); 909901fadf7SArchie Cobbs 910901fadf7SArchie Cobbs /* Discard ZLB packets */ 911901fadf7SArchie Cobbs if (m->m_pkthdr.len == 0) { 9120a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 913901fadf7SArchie Cobbs priv->stats.recvZLBs++; 914901fadf7SArchie Cobbs NG_FREE_ITEM(item); 915901fadf7SArchie Cobbs NG_FREE_M(m); 916bf741e4dSAlexander Motin ERROUT(0); 917901fadf7SArchie Cobbs } 918901fadf7SArchie Cobbs 919901fadf7SArchie Cobbs /* 920af63939cSAlexander Motin * If not what we expect or we are busy, drop packet and 921af63939cSAlexander Motin * send an immediate ZLB ack. 922901fadf7SArchie Cobbs */ 923af63939cSAlexander Motin if (ns != seq->nr || seq->inproc) { 924af63939cSAlexander Motin if (L2TP_SEQ_DIFF(ns, seq->nr) <= 0) 925af63939cSAlexander Motin priv->stats.recvDuplicates++; 926af63939cSAlexander Motin else 927af63939cSAlexander Motin priv->stats.recvOutOfOrder++; 928af63939cSAlexander Motin ng_l2tp_xmit_ctrl(priv, NULL, seq->ns); 929af63939cSAlexander Motin NG_FREE_ITEM(item); 930af63939cSAlexander Motin NG_FREE_M(m); 931af63939cSAlexander Motin ERROUT(0); 932af63939cSAlexander Motin } 933af63939cSAlexander Motin 934af63939cSAlexander Motin /* Prepend session ID to packet. */ 935eb1b1807SGleb Smirnoff M_PREPEND(m, 2, M_NOWAIT); 936901fadf7SArchie Cobbs if (m == NULL) { 9370a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 938901fadf7SArchie Cobbs priv->stats.memoryFailures++; 939901fadf7SArchie Cobbs NG_FREE_ITEM(item); 940bf741e4dSAlexander Motin ERROUT(ENOBUFS); 941901fadf7SArchie Cobbs } 942280d6bd7SAlexander Motin mtod(m, u_int8_t *)[0] = sid >> 8; 943280d6bd7SAlexander Motin mtod(m, u_int8_t *)[1] = sid & 0xff; 944901fadf7SArchie Cobbs 9450a76c63dSGleb Smirnoff /* 9460a76c63dSGleb Smirnoff * Until we deliver this packet we can't receive next one as 9470a76c63dSGleb Smirnoff * we have no information for sending ack. 9480a76c63dSGleb Smirnoff */ 9490a76c63dSGleb Smirnoff seq->inproc = 1; 9500a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 9510a76c63dSGleb Smirnoff 952901fadf7SArchie Cobbs /* Deliver packet to upper layers */ 953901fadf7SArchie Cobbs NG_FWD_NEW_DATA(error, item, priv->ctrl, m); 954af63939cSAlexander Motin 9550a76c63dSGleb Smirnoff SEQ_LOCK(seq); 956af63939cSAlexander Motin /* Ready to process next packet. */ 957af63939cSAlexander Motin seq->inproc = 0; 958af63939cSAlexander Motin 959af63939cSAlexander Motin /* If packet was successfully delivered send ack. */ 960af63939cSAlexander Motin if (error == 0) { 961af63939cSAlexander Motin /* Update recv sequence number */ 962af63939cSAlexander Motin seq->nr++; 963af63939cSAlexander Motin /* Start receive ack timer, if not already running */ 964af63939cSAlexander Motin if (!callout_active(&seq->xack_timer)) { 965*ae04d304SGleb Smirnoff callout_reset(&seq->xack_timer, 966af63939cSAlexander Motin L2TP_DELAYED_ACK, ng_l2tp_seq_xack_timeout, 967*ae04d304SGleb Smirnoff node); 968af63939cSAlexander Motin } 969af63939cSAlexander Motin } 9700a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 971af63939cSAlexander Motin 972bf741e4dSAlexander Motin ERROUT(error); 973901fadf7SArchie Cobbs } 974901fadf7SArchie Cobbs 9754807330cSBjoern A. Zeeb /* Per session packet, account it. */ 9764807330cSBjoern A. Zeeb hpriv->stats.recvPackets++; 9774807330cSBjoern A. Zeeb hpriv->stats.recvOctets += plen; 9784807330cSBjoern A. Zeeb 979901fadf7SArchie Cobbs /* Follow peer's lead in data sequencing, if configured to do so */ 980901fadf7SArchie Cobbs if (!hpriv->conf.control_dseq) 981901fadf7SArchie Cobbs hpriv->conf.enable_dseq = ((hdr & L2TP_HDR_SEQ) != 0); 982901fadf7SArchie Cobbs 983901fadf7SArchie Cobbs /* Handle data sequence numbers if present and enabled */ 984901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_SEQ) != 0) { 985901fadf7SArchie Cobbs if (hpriv->conf.enable_dseq 986901fadf7SArchie Cobbs && L2TP_SEQ_DIFF(ns, hpriv->nr) < 0) { 987901fadf7SArchie Cobbs NG_FREE_ITEM(item); /* duplicate or out of order */ 988901fadf7SArchie Cobbs NG_FREE_M(m); 989901fadf7SArchie Cobbs priv->stats.recvDataDrops++; 990bf741e4dSAlexander Motin ERROUT(0); 991901fadf7SArchie Cobbs } 992901fadf7SArchie Cobbs hpriv->nr = ns + 1; 993901fadf7SArchie Cobbs } 994901fadf7SArchie Cobbs 995901fadf7SArchie Cobbs /* Drop empty data packets */ 996901fadf7SArchie Cobbs if (m->m_pkthdr.len == 0) { 997901fadf7SArchie Cobbs NG_FREE_ITEM(item); 998901fadf7SArchie Cobbs NG_FREE_M(m); 999bf741e4dSAlexander Motin ERROUT(0); 1000901fadf7SArchie Cobbs } 1001901fadf7SArchie Cobbs 1002901fadf7SArchie Cobbs /* Deliver data */ 1003901fadf7SArchie Cobbs NG_FWD_NEW_DATA(error, item, hook, m); 1004bf741e4dSAlexander Motin done: 1005901fadf7SArchie Cobbs return (error); 1006901fadf7SArchie Cobbs } 1007901fadf7SArchie Cobbs 1008901fadf7SArchie Cobbs /* 1009901fadf7SArchie Cobbs * Handle an outgoing control frame. 1010901fadf7SArchie Cobbs */ 1011901fadf7SArchie Cobbs static int 1012bf741e4dSAlexander Motin ng_l2tp_rcvdata_ctrl(hook_p hook, item_p item) 1013901fadf7SArchie Cobbs { 1014bf741e4dSAlexander Motin const node_p node = NG_HOOK_NODE(hook); 1015901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 1016901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1017901fadf7SArchie Cobbs struct mbuf *m; 1018bf741e4dSAlexander Motin int error; 1019901fadf7SArchie Cobbs int i; 1020702f9895SAlexander Motin u_int16_t ns; 1021901fadf7SArchie Cobbs 1022bf741e4dSAlexander Motin /* If not configured, reject */ 1023bf741e4dSAlexander Motin if (!priv->conf.enabled) { 1024bf741e4dSAlexander Motin NG_FREE_ITEM(item); 1025bf741e4dSAlexander Motin ERROUT(ENXIO); 1026bf741e4dSAlexander Motin } 1027bf741e4dSAlexander Motin 1028901fadf7SArchie Cobbs /* Grab mbuf and discard other stuff XXX */ 1029901fadf7SArchie Cobbs NGI_GET_M(item, m); 1030901fadf7SArchie Cobbs NG_FREE_ITEM(item); 1031901fadf7SArchie Cobbs 1032901fadf7SArchie Cobbs /* Packet should have session ID prepended */ 1033901fadf7SArchie Cobbs if (m->m_pkthdr.len < 2) { 1034901fadf7SArchie Cobbs priv->stats.xmitInvalid++; 1035901fadf7SArchie Cobbs m_freem(m); 1036bf741e4dSAlexander Motin ERROUT(EINVAL); 1037901fadf7SArchie Cobbs } 1038901fadf7SArchie Cobbs 1039901fadf7SArchie Cobbs /* Check max length */ 1040901fadf7SArchie Cobbs if (m->m_pkthdr.len >= 0x10000 - 14) { 1041901fadf7SArchie Cobbs priv->stats.xmitTooBig++; 1042901fadf7SArchie Cobbs m_freem(m); 1043bf741e4dSAlexander Motin ERROUT(EOVERFLOW); 1044901fadf7SArchie Cobbs } 1045901fadf7SArchie Cobbs 10460a76c63dSGleb Smirnoff SEQ_LOCK(seq); 1047702f9895SAlexander Motin 1048901fadf7SArchie Cobbs /* Find next empty slot in transmit queue */ 1049901fadf7SArchie Cobbs for (i = 0; i < L2TP_MAX_XWIN && seq->xwin[i] != NULL; i++); 1050901fadf7SArchie Cobbs if (i == L2TP_MAX_XWIN) { 10510a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 1052901fadf7SArchie Cobbs priv->stats.xmitDrops++; 1053901fadf7SArchie Cobbs m_freem(m); 1054bf741e4dSAlexander Motin ERROUT(ENOBUFS); 1055901fadf7SArchie Cobbs } 1056901fadf7SArchie Cobbs seq->xwin[i] = m; 1057901fadf7SArchie Cobbs 1058901fadf7SArchie Cobbs /* If peer's receive window is already full, nothing else to do */ 1059702f9895SAlexander Motin if (i >= seq->cwnd) { 10600a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 1061bf741e4dSAlexander Motin ERROUT(0); 1062702f9895SAlexander Motin } 1063901fadf7SArchie Cobbs 1064901fadf7SArchie Cobbs /* Start retransmit timer if not already running */ 1065206fa244SAlexander Motin if (!callout_active(&seq->rack_timer)) 1066*ae04d304SGleb Smirnoff callout_reset(&seq->rack_timer, hz, ng_l2tp_seq_rack_timeout, 1067*ae04d304SGleb Smirnoff node); 1068901fadf7SArchie Cobbs 1069702f9895SAlexander Motin ns = seq->ns++; 1070702f9895SAlexander Motin 1071901fadf7SArchie Cobbs /* Copy packet */ 1072eb1b1807SGleb Smirnoff if ((m = L2TP_COPY_MBUF(m, M_NOWAIT)) == NULL) { 10730a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 1074901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1075bf741e4dSAlexander Motin ERROUT(ENOBUFS); 1076901fadf7SArchie Cobbs } 1077901fadf7SArchie Cobbs 1078901fadf7SArchie Cobbs /* Send packet and increment xmit sequence number */ 1079702f9895SAlexander Motin error = ng_l2tp_xmit_ctrl(priv, m, ns); 1080bf741e4dSAlexander Motin done: 1081bf741e4dSAlexander Motin return (error); 1082901fadf7SArchie Cobbs } 1083901fadf7SArchie Cobbs 1084901fadf7SArchie Cobbs /* 1085901fadf7SArchie Cobbs * Handle an outgoing data frame. 1086901fadf7SArchie Cobbs */ 1087901fadf7SArchie Cobbs static int 1088bf741e4dSAlexander Motin ng_l2tp_rcvdata(hook_p hook, item_p item) 1089901fadf7SArchie Cobbs { 1090bf741e4dSAlexander Motin const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1091bf741e4dSAlexander Motin const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 1092901fadf7SArchie Cobbs struct mbuf *m; 1093148ac1daSAlexander Motin uint8_t *p; 1094901fadf7SArchie Cobbs u_int16_t hdr; 1095901fadf7SArchie Cobbs int error; 1096148ac1daSAlexander Motin int i = 2; 1097901fadf7SArchie Cobbs 1098bf741e4dSAlexander Motin /* If not configured, reject */ 1099bf741e4dSAlexander Motin if (!priv->conf.enabled) { 1100bf741e4dSAlexander Motin NG_FREE_ITEM(item); 1101bf741e4dSAlexander Motin ERROUT(ENXIO); 1102bf741e4dSAlexander Motin } 1103bf741e4dSAlexander Motin 1104901fadf7SArchie Cobbs /* Get mbuf */ 1105901fadf7SArchie Cobbs NGI_GET_M(item, m); 1106901fadf7SArchie Cobbs 1107901fadf7SArchie Cobbs /* Check max length */ 1108901fadf7SArchie Cobbs if (m->m_pkthdr.len >= 0x10000 - 12) { 1109901fadf7SArchie Cobbs priv->stats.xmitDataTooBig++; 1110901fadf7SArchie Cobbs NG_FREE_ITEM(item); 1111901fadf7SArchie Cobbs NG_FREE_M(m); 1112bf741e4dSAlexander Motin ERROUT(EOVERFLOW); 1113901fadf7SArchie Cobbs } 1114901fadf7SArchie Cobbs 1115901fadf7SArchie Cobbs /* Prepend L2TP header */ 1116901fadf7SArchie Cobbs M_PREPEND(m, 6 1117901fadf7SArchie Cobbs + (2 * (hpriv->conf.include_length != 0)) 1118901fadf7SArchie Cobbs + (4 * (hpriv->conf.enable_dseq != 0)), 1119eb1b1807SGleb Smirnoff M_NOWAIT); 1120901fadf7SArchie Cobbs if (m == NULL) { 1121901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1122901fadf7SArchie Cobbs NG_FREE_ITEM(item); 1123bf741e4dSAlexander Motin ERROUT(ENOBUFS); 1124901fadf7SArchie Cobbs } 1125148ac1daSAlexander Motin p = mtod(m, uint8_t *); 1126901fadf7SArchie Cobbs hdr = L2TP_DATA_HDR; 1127901fadf7SArchie Cobbs if (hpriv->conf.include_length) { 1128901fadf7SArchie Cobbs hdr |= L2TP_HDR_LEN; 1129148ac1daSAlexander Motin p[i++] = m->m_pkthdr.len >> 8; 1130148ac1daSAlexander Motin p[i++] = m->m_pkthdr.len & 0xff; 1131901fadf7SArchie Cobbs } 1132148ac1daSAlexander Motin p[i++] = priv->conf.peer_id >> 8; 1133148ac1daSAlexander Motin p[i++] = priv->conf.peer_id & 0xff; 1134148ac1daSAlexander Motin p[i++] = hpriv->conf.peer_id >> 8; 1135148ac1daSAlexander Motin p[i++] = hpriv->conf.peer_id & 0xff; 1136901fadf7SArchie Cobbs if (hpriv->conf.enable_dseq) { 1137901fadf7SArchie Cobbs hdr |= L2TP_HDR_SEQ; 1138148ac1daSAlexander Motin p[i++] = hpriv->ns >> 8; 1139148ac1daSAlexander Motin p[i++] = hpriv->ns & 0xff; 1140148ac1daSAlexander Motin p[i++] = hpriv->nr >> 8; 1141148ac1daSAlexander Motin p[i++] = hpriv->nr & 0xff; 1142901fadf7SArchie Cobbs hpriv->ns++; 1143901fadf7SArchie Cobbs } 1144148ac1daSAlexander Motin p[0] = hdr >> 8; 1145148ac1daSAlexander Motin p[1] = hdr & 0xff; 1146901fadf7SArchie Cobbs 11474807330cSBjoern A. Zeeb /* Update per session stats. */ 11484807330cSBjoern A. Zeeb hpriv->stats.xmitPackets++; 11494807330cSBjoern A. Zeeb hpriv->stats.xmitOctets += m->m_pkthdr.len; 11504807330cSBjoern A. Zeeb 115134d16c64SAlexander Motin /* And the global one. */ 115234d16c64SAlexander Motin priv->stats.xmitPackets++; 115334d16c64SAlexander Motin priv->stats.xmitOctets += m->m_pkthdr.len; 115434d16c64SAlexander Motin 1155901fadf7SArchie Cobbs /* Send packet */ 1156901fadf7SArchie Cobbs NG_FWD_NEW_DATA(error, item, priv->lower, m); 1157bf741e4dSAlexander Motin done: 1158901fadf7SArchie Cobbs return (error); 1159901fadf7SArchie Cobbs } 1160901fadf7SArchie Cobbs 1161901fadf7SArchie Cobbs /* 1162901fadf7SArchie Cobbs * Send a message to our controlling node that we've failed. 1163901fadf7SArchie Cobbs */ 1164901fadf7SArchie Cobbs static void 1165901fadf7SArchie Cobbs ng_l2tp_seq_failure(priv_p priv) 1166901fadf7SArchie Cobbs { 1167901fadf7SArchie Cobbs struct ng_mesg *msg; 1168901fadf7SArchie Cobbs int error; 1169901fadf7SArchie Cobbs 1170901fadf7SArchie Cobbs NG_MKMESSAGE(msg, NGM_L2TP_COOKIE, NGM_L2TP_ACK_FAILURE, 0, M_NOWAIT); 1171901fadf7SArchie Cobbs if (msg == NULL) 1172901fadf7SArchie Cobbs return; 1173facfd889SArchie Cobbs NG_SEND_MSG_ID(error, priv->node, msg, priv->ftarget, 0); 1174901fadf7SArchie Cobbs } 1175901fadf7SArchie Cobbs 1176901fadf7SArchie Cobbs /************************************************************************ 1177901fadf7SArchie Cobbs SEQUENCE NUMBER HANDLING 1178901fadf7SArchie Cobbs ************************************************************************/ 1179901fadf7SArchie Cobbs 1180901fadf7SArchie Cobbs /* 1181901fadf7SArchie Cobbs * Initialize sequence number state. 1182901fadf7SArchie Cobbs */ 1183901fadf7SArchie Cobbs static void 1184901fadf7SArchie Cobbs ng_l2tp_seq_init(priv_p priv) 1185901fadf7SArchie Cobbs { 1186901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1187901fadf7SArchie Cobbs 1188901fadf7SArchie Cobbs KASSERT(priv->conf.peer_win >= 1, 11897e2b43eeSDavid E. O'Brien ("%s: peer_win is zero", __func__)); 1190901fadf7SArchie Cobbs memset(seq, 0, sizeof(*seq)); 1191901fadf7SArchie Cobbs seq->cwnd = 1; 1192901fadf7SArchie Cobbs seq->wmax = priv->conf.peer_win; 1193901fadf7SArchie Cobbs if (seq->wmax > L2TP_MAX_XWIN) 1194901fadf7SArchie Cobbs seq->wmax = L2TP_MAX_XWIN; 1195901fadf7SArchie Cobbs seq->ssth = seq->wmax; 1196702f9895SAlexander Motin mtx_init(&seq->mtx, "ng_l2tp", NULL, MTX_DEF); 119789042ff7SGleb Smirnoff callout_init_mtx(&seq->rack_timer, &seq->mtx, CALLOUT_RETURNUNLOCKED); 119889042ff7SGleb Smirnoff callout_init_mtx(&seq->xack_timer, &seq->mtx, CALLOUT_RETURNUNLOCKED); 1199901fadf7SArchie Cobbs } 1200901fadf7SArchie Cobbs 1201901fadf7SArchie Cobbs /* 12021e031324SBjoern A. Zeeb * Set sequence number state as given from user. 12031e031324SBjoern A. Zeeb */ 12041e031324SBjoern A. Zeeb static int 12051e031324SBjoern A. Zeeb ng_l2tp_seq_set(priv_p priv, const struct ng_l2tp_seq_config *conf) 12061e031324SBjoern A. Zeeb { 12071e031324SBjoern A. Zeeb struct l2tp_seq *const seq = &priv->seq; 12081e031324SBjoern A. Zeeb 12091e031324SBjoern A. Zeeb /* If node is enabled, deny update to sequence numbers. */ 12101e031324SBjoern A. Zeeb if (priv->conf.enabled) 12111e031324SBjoern A. Zeeb return (EBUSY); 12121e031324SBjoern A. Zeeb 12131e031324SBjoern A. Zeeb /* We only can handle the simple cases. */ 12141e031324SBjoern A. Zeeb if (conf->xack != conf->nr || conf->ns != conf->rack) 12151e031324SBjoern A. Zeeb return (EINVAL); 12161e031324SBjoern A. Zeeb 12171e031324SBjoern A. Zeeb /* Set ns,nr,rack,xack parameters. */ 12181e031324SBjoern A. Zeeb seq->ns = conf->ns; 12191e031324SBjoern A. Zeeb seq->nr = conf->nr; 12201e031324SBjoern A. Zeeb seq->rack = conf->rack; 12211e031324SBjoern A. Zeeb seq->xack = conf->xack; 12221e031324SBjoern A. Zeeb 12231e031324SBjoern A. Zeeb return (0); 12241e031324SBjoern A. Zeeb } 12251e031324SBjoern A. Zeeb 12261e031324SBjoern A. Zeeb /* 1227901fadf7SArchie Cobbs * Adjust sequence number state accordingly after reconfiguration. 1228901fadf7SArchie Cobbs */ 1229901fadf7SArchie Cobbs static int 1230901fadf7SArchie Cobbs ng_l2tp_seq_adjust(priv_p priv, const struct ng_l2tp_config *conf) 1231901fadf7SArchie Cobbs { 1232901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1233901fadf7SArchie Cobbs u_int16_t new_wmax; 12340a76c63dSGleb Smirnoff int error = 0; 1235901fadf7SArchie Cobbs 12360a76c63dSGleb Smirnoff SEQ_LOCK(seq); 1237901fadf7SArchie Cobbs /* If disabling node, reset state sequence number */ 1238901fadf7SArchie Cobbs if (!conf->enabled) { 1239901fadf7SArchie Cobbs ng_l2tp_seq_reset(priv); 12400a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 1241901fadf7SArchie Cobbs return (0); 1242901fadf7SArchie Cobbs } 1243901fadf7SArchie Cobbs 1244901fadf7SArchie Cobbs /* Adjust peer's max recv window; it can only increase */ 1245901fadf7SArchie Cobbs new_wmax = conf->peer_win; 1246901fadf7SArchie Cobbs if (new_wmax > L2TP_MAX_XWIN) 1247901fadf7SArchie Cobbs new_wmax = L2TP_MAX_XWIN; 1248901fadf7SArchie Cobbs if (new_wmax == 0) 12490a76c63dSGleb Smirnoff ERROUT(EINVAL); 1250901fadf7SArchie Cobbs if (new_wmax < seq->wmax) 12510a76c63dSGleb Smirnoff ERROUT(EBUSY); 1252901fadf7SArchie Cobbs seq->wmax = new_wmax; 1253901fadf7SArchie Cobbs 12540a76c63dSGleb Smirnoff done: 12550a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 12560a76c63dSGleb Smirnoff return (error); 1257901fadf7SArchie Cobbs } 1258901fadf7SArchie Cobbs 1259901fadf7SArchie Cobbs /* 1260901fadf7SArchie Cobbs * Reset sequence number state. 1261901fadf7SArchie Cobbs */ 1262901fadf7SArchie Cobbs static void 1263901fadf7SArchie Cobbs ng_l2tp_seq_reset(priv_p priv) 1264901fadf7SArchie Cobbs { 1265901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1266901fadf7SArchie Cobbs hook_p hook; 1267901fadf7SArchie Cobbs int i; 1268901fadf7SArchie Cobbs 12690a76c63dSGleb Smirnoff SEQ_LOCK_ASSERT(seq); 1270901fadf7SArchie Cobbs 1271901fadf7SArchie Cobbs /* Stop timers */ 1272*ae04d304SGleb Smirnoff (void )callout_stop(&seq->rack_timer); 1273*ae04d304SGleb Smirnoff (void )callout_stop(&seq->xack_timer); 1274901fadf7SArchie Cobbs 1275901fadf7SArchie Cobbs /* Free retransmit queue */ 1276901fadf7SArchie Cobbs for (i = 0; i < L2TP_MAX_XWIN; i++) { 1277901fadf7SArchie Cobbs if (seq->xwin[i] == NULL) 1278901fadf7SArchie Cobbs break; 1279901fadf7SArchie Cobbs m_freem(seq->xwin[i]); 1280901fadf7SArchie Cobbs } 1281901fadf7SArchie Cobbs 1282901fadf7SArchie Cobbs /* Reset session hooks' sequence number states */ 1283901fadf7SArchie Cobbs NG_NODE_FOREACH_HOOK(priv->node, ng_l2tp_reset_session, NULL, hook); 1284901fadf7SArchie Cobbs 1285901fadf7SArchie Cobbs /* Reset node's sequence number state */ 1286702f9895SAlexander Motin seq->ns = 0; 1287702f9895SAlexander Motin seq->nr = 0; 1288702f9895SAlexander Motin seq->rack = 0; 1289702f9895SAlexander Motin seq->xack = 0; 1290901fadf7SArchie Cobbs seq->wmax = L2TP_MAX_XWIN; 1291702f9895SAlexander Motin seq->cwnd = 1; 1292901fadf7SArchie Cobbs seq->ssth = seq->wmax; 1293702f9895SAlexander Motin seq->acks = 0; 1294702f9895SAlexander Motin seq->rexmits = 0; 1295702f9895SAlexander Motin bzero(seq->xwin, sizeof(seq->xwin)); 1296901fadf7SArchie Cobbs } 1297901fadf7SArchie Cobbs 1298901fadf7SArchie Cobbs /* 1299901fadf7SArchie Cobbs * Handle receipt of an acknowledgement value (Nr) from peer. 1300901fadf7SArchie Cobbs */ 1301901fadf7SArchie Cobbs static void 1302901fadf7SArchie Cobbs ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr) 1303901fadf7SArchie Cobbs { 1304901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1305702f9895SAlexander Motin struct mbuf *xwin[L2TP_MAX_XWIN]; /* partial local copy */ 1306901fadf7SArchie Cobbs int nack; 1307702f9895SAlexander Motin int i, j; 1308702f9895SAlexander Motin uint16_t ns; 1309702f9895SAlexander Motin 13100a76c63dSGleb Smirnoff SEQ_LOCK_ASSERT(seq); 1311901fadf7SArchie Cobbs 1312901fadf7SArchie Cobbs /* Verify peer's ACK is in range */ 13130a76c63dSGleb Smirnoff if ((nack = L2TP_SEQ_DIFF(nr, seq->rack)) <= 0) 1314901fadf7SArchie Cobbs return; /* duplicate ack */ 1315901fadf7SArchie Cobbs if (L2TP_SEQ_DIFF(nr, seq->ns) > 0) { 1316901fadf7SArchie Cobbs priv->stats.recvBadAcks++; /* ack for packet not sent */ 1317901fadf7SArchie Cobbs return; 1318901fadf7SArchie Cobbs } 1319901fadf7SArchie Cobbs KASSERT(nack <= L2TP_MAX_XWIN, 13207e2b43eeSDavid E. O'Brien ("%s: nack=%d > %d", __func__, nack, L2TP_MAX_XWIN)); 1321901fadf7SArchie Cobbs 1322901fadf7SArchie Cobbs /* Update receive ack stats */ 1323901fadf7SArchie Cobbs seq->rack = nr; 1324901fadf7SArchie Cobbs seq->rexmits = 0; 1325901fadf7SArchie Cobbs 1326901fadf7SArchie Cobbs /* Free acknowledged packets and shift up packets in the xmit queue */ 1327901fadf7SArchie Cobbs for (i = 0; i < nack; i++) 1328901fadf7SArchie Cobbs m_freem(seq->xwin[i]); 1329901fadf7SArchie Cobbs memmove(seq->xwin, seq->xwin + nack, 1330901fadf7SArchie Cobbs (L2TP_MAX_XWIN - nack) * sizeof(*seq->xwin)); 1331901fadf7SArchie Cobbs memset(seq->xwin + (L2TP_MAX_XWIN - nack), 0, 1332901fadf7SArchie Cobbs nack * sizeof(*seq->xwin)); 1333901fadf7SArchie Cobbs 1334901fadf7SArchie Cobbs /* 1335901fadf7SArchie Cobbs * Do slow-start/congestion avoidance windowing algorithm described 1336901fadf7SArchie Cobbs * in RFC 2661, Appendix A. Here we handle a multiple ACK as if each 1337901fadf7SArchie Cobbs * ACK had arrived separately. 1338901fadf7SArchie Cobbs */ 1339901fadf7SArchie Cobbs if (seq->cwnd < seq->wmax) { 1340901fadf7SArchie Cobbs /* Handle slow start phase */ 1341901fadf7SArchie Cobbs if (seq->cwnd < seq->ssth) { 1342901fadf7SArchie Cobbs seq->cwnd += nack; 1343901fadf7SArchie Cobbs nack = 0; 1344901fadf7SArchie Cobbs if (seq->cwnd > seq->ssth) { /* into cg.av. phase */ 1345901fadf7SArchie Cobbs nack = seq->cwnd - seq->ssth; 1346901fadf7SArchie Cobbs seq->cwnd = seq->ssth; 1347901fadf7SArchie Cobbs } 1348901fadf7SArchie Cobbs } 1349901fadf7SArchie Cobbs 1350901fadf7SArchie Cobbs /* Handle congestion avoidance phase */ 1351901fadf7SArchie Cobbs if (seq->cwnd >= seq->ssth) { 1352901fadf7SArchie Cobbs seq->acks += nack; 1353901fadf7SArchie Cobbs while (seq->acks >= seq->cwnd) { 1354901fadf7SArchie Cobbs seq->acks -= seq->cwnd; 1355901fadf7SArchie Cobbs if (seq->cwnd < seq->wmax) 1356901fadf7SArchie Cobbs seq->cwnd++; 1357901fadf7SArchie Cobbs } 1358901fadf7SArchie Cobbs } 1359901fadf7SArchie Cobbs } 1360901fadf7SArchie Cobbs 1361901fadf7SArchie Cobbs /* Stop xmit timer */ 1362206fa244SAlexander Motin if (callout_active(&seq->rack_timer)) 1363*ae04d304SGleb Smirnoff (void )callout_stop(&seq->rack_timer); 1364901fadf7SArchie Cobbs 1365901fadf7SArchie Cobbs /* If transmit queue is empty, we're done for now */ 13660a76c63dSGleb Smirnoff if (seq->xwin[0] == NULL) 1367901fadf7SArchie Cobbs return; 1368901fadf7SArchie Cobbs 1369901fadf7SArchie Cobbs /* Start restransmit timer again */ 1370*ae04d304SGleb Smirnoff callout_reset(&seq->rack_timer, hz, ng_l2tp_seq_rack_timeout, 1371*ae04d304SGleb Smirnoff priv->node); 1372901fadf7SArchie Cobbs 1373901fadf7SArchie Cobbs /* 1374901fadf7SArchie Cobbs * Send more packets, trying to keep peer's receive window full. 1375702f9895SAlexander Motin * Make copy of everything we need before lock release. 1376702f9895SAlexander Motin */ 1377702f9895SAlexander Motin ns = seq->ns; 1378702f9895SAlexander Motin j = 0; 1379702f9895SAlexander Motin while ((i = L2TP_SEQ_DIFF(seq->ns, seq->rack)) < seq->cwnd 1380702f9895SAlexander Motin && seq->xwin[i] != NULL) { 1381702f9895SAlexander Motin xwin[j++] = seq->xwin[i]; 1382702f9895SAlexander Motin seq->ns++; 1383702f9895SAlexander Motin } 1384702f9895SAlexander Motin 1385702f9895SAlexander Motin /* 1386702f9895SAlexander Motin * Send prepared. 1387901fadf7SArchie Cobbs * If there is a memory error, pretend packet was sent, as it 1388901fadf7SArchie Cobbs * will get retransmitted later anyway. 1389901fadf7SArchie Cobbs */ 1390702f9895SAlexander Motin for (i = 0; i < j; i++) { 1391702f9895SAlexander Motin struct mbuf *m; 1392eb1b1807SGleb Smirnoff if ((m = L2TP_COPY_MBUF(xwin[i], M_NOWAIT)) == NULL) 1393901fadf7SArchie Cobbs priv->stats.memoryFailures++; 13940a76c63dSGleb Smirnoff else { 1395702f9895SAlexander Motin ng_l2tp_xmit_ctrl(priv, m, ns); 13960a76c63dSGleb Smirnoff SEQ_LOCK(seq); 13970a76c63dSGleb Smirnoff } 1398702f9895SAlexander Motin ns++; 1399901fadf7SArchie Cobbs } 1400901fadf7SArchie Cobbs } 1401901fadf7SArchie Cobbs 1402901fadf7SArchie Cobbs /* 1403901fadf7SArchie Cobbs * Handle an ack timeout. We have an outstanding ack that we 1404901fadf7SArchie Cobbs * were hoping to piggy-back, but haven't, so send a ZLB. 1405901fadf7SArchie Cobbs */ 1406901fadf7SArchie Cobbs static void 1407*ae04d304SGleb Smirnoff ng_l2tp_seq_xack_timeout(void *arg) 1408901fadf7SArchie Cobbs { 1409*ae04d304SGleb Smirnoff const node_p node = arg; 1410901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 1411*ae04d304SGleb Smirnoff struct epoch_tracker et; 1412901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1413901fadf7SArchie Cobbs 141489042ff7SGleb Smirnoff SEQ_LOCK_ASSERT(seq); 141589042ff7SGleb Smirnoff MPASS(!callout_pending(&seq->xack_timer)); 141689042ff7SGleb Smirnoff MPASS(callout_active(&seq->xack_timer)); 1417901fadf7SArchie Cobbs 1418*ae04d304SGleb Smirnoff NET_EPOCH_ENTER(et); 1419*ae04d304SGleb Smirnoff CURVNET_SET(node->nd_vnet); 1420206fa244SAlexander Motin /* Send a ZLB */ 1421901fadf7SArchie Cobbs ng_l2tp_xmit_ctrl(priv, NULL, seq->ns); 1422*ae04d304SGleb Smirnoff CURVNET_RESTORE(); 1423*ae04d304SGleb Smirnoff NET_EPOCH_EXIT(et); 1424901fadf7SArchie Cobbs 1425206fa244SAlexander Motin /* callout_deactivate() is not needed here 1426*ae04d304SGleb Smirnoff as callout_stop() was called by ng_l2tp_xmit_ctrl() */ 1427901fadf7SArchie Cobbs } 1428901fadf7SArchie Cobbs 1429901fadf7SArchie Cobbs /* 1430901fadf7SArchie Cobbs * Handle a transmit timeout. The peer has failed to respond 1431901fadf7SArchie Cobbs * with an ack for our packet, so retransmit it. 1432901fadf7SArchie Cobbs */ 1433901fadf7SArchie Cobbs static void 1434*ae04d304SGleb Smirnoff ng_l2tp_seq_rack_timeout(void *arg) 1435901fadf7SArchie Cobbs { 1436*ae04d304SGleb Smirnoff const node_p node = arg; 1437901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 1438*ae04d304SGleb Smirnoff struct epoch_tracker et; 1439901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1440901fadf7SArchie Cobbs struct mbuf *m; 1441901fadf7SArchie Cobbs u_int delay; 1442901fadf7SArchie Cobbs 144389042ff7SGleb Smirnoff SEQ_LOCK_ASSERT(seq); 144489042ff7SGleb Smirnoff MPASS(seq->xwin[0]); 144589042ff7SGleb Smirnoff MPASS(!callout_pending(&seq->rack_timer)); 144689042ff7SGleb Smirnoff MPASS(callout_active(&seq->rack_timer)); 1447e62e4b85SMark Johnston 1448*ae04d304SGleb Smirnoff NET_EPOCH_ENTER(et); 1449*ae04d304SGleb Smirnoff CURVNET_SET(node->nd_vnet); 1450*ae04d304SGleb Smirnoff 1451901fadf7SArchie Cobbs priv->stats.xmitRetransmits++; 1452901fadf7SArchie Cobbs 1453901fadf7SArchie Cobbs /* Have we reached the retransmit limit? If so, notify owner. */ 145440097c5dSAlexander Motin if (seq->rexmits++ >= priv->conf.rexmit_max) 1455901fadf7SArchie Cobbs ng_l2tp_seq_failure(priv); 1456901fadf7SArchie Cobbs 1457901fadf7SArchie Cobbs /* Restart timer, this time with an increased delay */ 1458901fadf7SArchie Cobbs delay = (seq->rexmits > 12) ? (1 << 12) : (1 << seq->rexmits); 145940097c5dSAlexander Motin if (delay > priv->conf.rexmit_max_to) 146040097c5dSAlexander Motin delay = priv->conf.rexmit_max_to; 1461*ae04d304SGleb Smirnoff callout_reset(&seq->rack_timer, hz * delay, ng_l2tp_seq_rack_timeout, 1462*ae04d304SGleb Smirnoff node); 1463901fadf7SArchie Cobbs 1464901fadf7SArchie Cobbs /* Do slow-start/congestion algorithm windowing algorithm */ 1465af63939cSAlexander Motin seq->ns = seq->rack; 1466901fadf7SArchie Cobbs seq->ssth = (seq->cwnd + 1) / 2; 1467901fadf7SArchie Cobbs seq->cwnd = 1; 1468901fadf7SArchie Cobbs seq->acks = 0; 1469901fadf7SArchie Cobbs 1470901fadf7SArchie Cobbs /* Retransmit oldest unack'd packet */ 147130b7addfSGleb Smirnoff m = L2TP_COPY_MBUF(seq->xwin[0], M_NOWAIT); 14720a76c63dSGleb Smirnoff if (m == NULL) { 14730a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 1474901fadf7SArchie Cobbs priv->stats.memoryFailures++; 14750a76c63dSGleb Smirnoff } else 1476af63939cSAlexander Motin ng_l2tp_xmit_ctrl(priv, m, seq->ns++); 1477901fadf7SArchie Cobbs 1478*ae04d304SGleb Smirnoff CURVNET_RESTORE(); 1479*ae04d304SGleb Smirnoff NET_EPOCH_EXIT(et); 1480*ae04d304SGleb Smirnoff 1481206fa244SAlexander Motin /* callout_deactivate() is not needed here 1482206fa244SAlexander Motin as ng_callout() is getting called each time */ 1483901fadf7SArchie Cobbs } 1484901fadf7SArchie Cobbs 1485901fadf7SArchie Cobbs /* 1486901fadf7SArchie Cobbs * Transmit a control stream packet, payload optional. 1487901fadf7SArchie Cobbs * The transmit sequence number is not incremented. 14880a76c63dSGleb Smirnoff * Requires seq lock, returns unlocked. 1489901fadf7SArchie Cobbs */ 1490901fadf7SArchie Cobbs static int 1491901fadf7SArchie Cobbs ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns) 1492901fadf7SArchie Cobbs { 1493901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1494148ac1daSAlexander Motin uint8_t *p; 14950a76c63dSGleb Smirnoff uint16_t nr, session_id = 0; 1496901fadf7SArchie Cobbs int error; 1497901fadf7SArchie Cobbs 14980a76c63dSGleb Smirnoff SEQ_LOCK_ASSERT(seq); 1499702f9895SAlexander Motin 1500206fa244SAlexander Motin /* Stop ack timer: we're sending an ack with this packet. 1501206fa244SAlexander Motin Doing this before to keep state predictable after error. */ 1502206fa244SAlexander Motin if (callout_active(&seq->xack_timer)) 1503*ae04d304SGleb Smirnoff (void )callout_stop(&seq->xack_timer); 1504206fa244SAlexander Motin 15050a76c63dSGleb Smirnoff nr = seq->xack = seq->nr; 1506206fa244SAlexander Motin 15070a76c63dSGleb Smirnoff SEQ_UNLOCK(seq); 1508702f9895SAlexander Motin 1509901fadf7SArchie Cobbs /* If no mbuf passed, send an empty packet (ZLB) */ 1510901fadf7SArchie Cobbs if (m == NULL) { 1511901fadf7SArchie Cobbs /* Create a new mbuf for ZLB packet */ 1512eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 1513901fadf7SArchie Cobbs if (m == NULL) { 1514901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1515901fadf7SArchie Cobbs return (ENOBUFS); 1516901fadf7SArchie Cobbs } 1517901fadf7SArchie Cobbs m->m_len = m->m_pkthdr.len = 12; 1518901fadf7SArchie Cobbs m->m_pkthdr.rcvif = NULL; 1519901fadf7SArchie Cobbs priv->stats.xmitZLBs++; 1520901fadf7SArchie Cobbs } else { 1521901fadf7SArchie Cobbs /* Strip off session ID */ 1522901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 1523901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1524901fadf7SArchie Cobbs return (ENOBUFS); 1525901fadf7SArchie Cobbs } 1526280d6bd7SAlexander Motin session_id = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 1527901fadf7SArchie Cobbs 1528901fadf7SArchie Cobbs /* Make room for L2TP header */ 1529eb1b1807SGleb Smirnoff M_PREPEND(m, 10, M_NOWAIT); /* - 2 + 12 = 10 */ 1530901fadf7SArchie Cobbs if (m == NULL) { 1531901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1532901fadf7SArchie Cobbs return (ENOBUFS); 1533901fadf7SArchie Cobbs } 1534310dc5a4SBjoern A. Zeeb 1535310dc5a4SBjoern A. Zeeb /* 1536310dc5a4SBjoern A. Zeeb * The below requires 12 contiguous bytes for the L2TP header 1537310dc5a4SBjoern A. Zeeb * to be written into. 1538310dc5a4SBjoern A. Zeeb */ 1539310dc5a4SBjoern A. Zeeb m = m_pullup(m, 12); 1540310dc5a4SBjoern A. Zeeb if (m == NULL) { 1541310dc5a4SBjoern A. Zeeb priv->stats.memoryFailures++; 1542310dc5a4SBjoern A. Zeeb return (ENOBUFS); 1543310dc5a4SBjoern A. Zeeb } 1544901fadf7SArchie Cobbs } 1545901fadf7SArchie Cobbs 1546901fadf7SArchie Cobbs /* Fill in L2TP header */ 1547148ac1daSAlexander Motin p = mtod(m, u_int8_t *); 1548148ac1daSAlexander Motin p[0] = L2TP_CTRL_HDR >> 8; 1549148ac1daSAlexander Motin p[1] = L2TP_CTRL_HDR & 0xff; 1550148ac1daSAlexander Motin p[2] = m->m_pkthdr.len >> 8; 1551148ac1daSAlexander Motin p[3] = m->m_pkthdr.len & 0xff; 1552148ac1daSAlexander Motin p[4] = priv->conf.peer_id >> 8; 1553148ac1daSAlexander Motin p[5] = priv->conf.peer_id & 0xff; 1554148ac1daSAlexander Motin p[6] = session_id >> 8; 1555148ac1daSAlexander Motin p[7] = session_id & 0xff; 1556148ac1daSAlexander Motin p[8] = ns >> 8; 1557148ac1daSAlexander Motin p[9] = ns & 0xff; 15580a76c63dSGleb Smirnoff p[10] = nr >> 8; 15590a76c63dSGleb Smirnoff p[11] = nr & 0xff; 1560901fadf7SArchie Cobbs 1561901fadf7SArchie Cobbs /* Update sequence number info and stats */ 1562901fadf7SArchie Cobbs priv->stats.xmitPackets++; 1563901fadf7SArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 1564901fadf7SArchie Cobbs 1565901fadf7SArchie Cobbs /* Send packet */ 15663ca24c28SJulian Elischer NG_SEND_DATA_ONLY(error, priv->lower, m); 1567901fadf7SArchie Cobbs return (error); 1568901fadf7SArchie Cobbs } 1569901fadf7SArchie Cobbs 1570901fadf7SArchie Cobbs #ifdef INVARIANTS 1571901fadf7SArchie Cobbs /* 1572901fadf7SArchie Cobbs * Sanity check sequence number state. 1573901fadf7SArchie Cobbs */ 1574901fadf7SArchie Cobbs static void 1575901fadf7SArchie Cobbs ng_l2tp_seq_check(struct l2tp_seq *seq) 1576901fadf7SArchie Cobbs { 1577702f9895SAlexander Motin int self_unack, peer_unack; 1578901fadf7SArchie Cobbs int i; 1579901fadf7SArchie Cobbs 15807e2b43eeSDavid E. O'Brien #define CHECK(p) KASSERT((p), ("%s: not: %s", __func__, #p)) 1581901fadf7SArchie Cobbs 15820a76c63dSGleb Smirnoff SEQ_LOCK_ASSERT(seq); 1583702f9895SAlexander Motin 1584702f9895SAlexander Motin self_unack = L2TP_SEQ_DIFF(seq->nr, seq->xack); 1585702f9895SAlexander Motin peer_unack = L2TP_SEQ_DIFF(seq->ns, seq->rack); 1586901fadf7SArchie Cobbs CHECK(seq->wmax <= L2TP_MAX_XWIN); 1587901fadf7SArchie Cobbs CHECK(seq->cwnd >= 1); 1588901fadf7SArchie Cobbs CHECK(seq->cwnd <= seq->wmax); 1589901fadf7SArchie Cobbs CHECK(seq->ssth >= 1); 1590901fadf7SArchie Cobbs CHECK(seq->ssth <= seq->wmax); 1591901fadf7SArchie Cobbs if (seq->cwnd < seq->ssth) 1592901fadf7SArchie Cobbs CHECK(seq->acks == 0); 1593901fadf7SArchie Cobbs else 1594901fadf7SArchie Cobbs CHECK(seq->acks <= seq->cwnd); 1595901fadf7SArchie Cobbs CHECK(self_unack >= 0); 1596901fadf7SArchie Cobbs CHECK(peer_unack >= 0); 1597901fadf7SArchie Cobbs CHECK(peer_unack <= seq->wmax); 1598206fa244SAlexander Motin CHECK((self_unack == 0) ^ callout_active(&seq->xack_timer)); 1599206fa244SAlexander Motin CHECK((peer_unack == 0) ^ callout_active(&seq->rack_timer)); 1600901fadf7SArchie Cobbs for (i = 0; i < peer_unack; i++) 1601901fadf7SArchie Cobbs CHECK(seq->xwin[i] != NULL); 1602901fadf7SArchie Cobbs for ( ; i < seq->cwnd; i++) /* verify peer's recv window full */ 1603901fadf7SArchie Cobbs CHECK(seq->xwin[i] == NULL); 1604901fadf7SArchie Cobbs 1605901fadf7SArchie Cobbs #undef CHECK 1606901fadf7SArchie Cobbs } 1607901fadf7SArchie Cobbs #endif /* INVARIANTS */ 1608