1901fadf7SArchie Cobbs 2901fadf7SArchie Cobbs /* 3901fadf7SArchie Cobbs * Copyright (c) 2001-2002 Packet Design, LLC. 4901fadf7SArchie Cobbs * All rights reserved. 5901fadf7SArchie Cobbs * 6901fadf7SArchie Cobbs * Subject to the following obligations and disclaimer of warranty, 7901fadf7SArchie Cobbs * use and redistribution of this software, in source or object code 8901fadf7SArchie Cobbs * forms, with or without modifications are expressly permitted by 9901fadf7SArchie Cobbs * Packet Design; provided, however, that: 10901fadf7SArchie Cobbs * 11901fadf7SArchie Cobbs * (i) Any and all reproductions of the source or object code 12901fadf7SArchie Cobbs * must include the copyright notice above and the following 13901fadf7SArchie Cobbs * disclaimer of warranties; and 14901fadf7SArchie Cobbs * (ii) No rights are granted, in any manner or form, to use 15901fadf7SArchie Cobbs * Packet Design trademarks, including the mark "PACKET DESIGN" 16901fadf7SArchie Cobbs * on advertising, endorsements, or otherwise except as such 17901fadf7SArchie Cobbs * appears in the above copyright notice or in the software. 18901fadf7SArchie Cobbs * 19901fadf7SArchie Cobbs * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND 20901fadf7SArchie Cobbs * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO 21901fadf7SArchie Cobbs * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING 22901fadf7SArchie Cobbs * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED 23901fadf7SArchie Cobbs * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 24901fadf7SArchie Cobbs * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, 25901fadf7SArchie Cobbs * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS 26901fadf7SArchie Cobbs * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, 27901fadf7SArchie Cobbs * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE 28901fadf7SArchie Cobbs * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE 29901fadf7SArchie Cobbs * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, 30901fadf7SArchie Cobbs * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL 31901fadf7SArchie Cobbs * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF 32901fadf7SArchie Cobbs * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF 33901fadf7SArchie Cobbs * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34901fadf7SArchie Cobbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 35901fadf7SArchie Cobbs * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF 36901fadf7SArchie Cobbs * THE POSSIBILITY OF SUCH DAMAGE. 37901fadf7SArchie Cobbs * 38901fadf7SArchie Cobbs * Author: Archie Cobbs <archie@freebsd.org> 39901fadf7SArchie Cobbs * 40901fadf7SArchie Cobbs * $FreeBSD$ 41901fadf7SArchie Cobbs */ 42901fadf7SArchie Cobbs 43901fadf7SArchie Cobbs /* 44901fadf7SArchie Cobbs * L2TP netgraph node type. 45901fadf7SArchie Cobbs * 46901fadf7SArchie Cobbs * This node type implements the lower layer of the 47901fadf7SArchie Cobbs * L2TP protocol as specified in RFC 2661. 48901fadf7SArchie Cobbs */ 49901fadf7SArchie Cobbs 50901fadf7SArchie Cobbs #include <sys/param.h> 51901fadf7SArchie Cobbs #include <sys/systm.h> 52901fadf7SArchie Cobbs #include <sys/kernel.h> 53901fadf7SArchie Cobbs #include <sys/time.h> 54901fadf7SArchie Cobbs #include <sys/conf.h> 55901fadf7SArchie Cobbs #include <sys/mbuf.h> 56901fadf7SArchie Cobbs #include <sys/malloc.h> 57901fadf7SArchie Cobbs #include <sys/errno.h> 5886fea6beSBosko Milekic #include <sys/libkern.h> 59901fadf7SArchie Cobbs 60901fadf7SArchie Cobbs #include <netgraph/ng_message.h> 61901fadf7SArchie Cobbs #include <netgraph/netgraph.h> 62901fadf7SArchie Cobbs #include <netgraph/ng_parse.h> 63901fadf7SArchie Cobbs #include <netgraph/ng_l2tp.h> 64901fadf7SArchie Cobbs 65901fadf7SArchie Cobbs #ifdef NG_SEPARATE_MALLOC 66901fadf7SArchie Cobbs MALLOC_DEFINE(M_NETGRAPH_L2TP, "netgraph_l2tp", "netgraph l2tp node"); 67901fadf7SArchie Cobbs #else 68901fadf7SArchie Cobbs #define M_NETGRAPH_L2TP M_NETGRAPH 69901fadf7SArchie Cobbs #endif 70901fadf7SArchie Cobbs 71901fadf7SArchie Cobbs /* L2TP header format (first 2 bytes only) */ 72901fadf7SArchie Cobbs #define L2TP_HDR_CTRL 0x8000 /* control packet */ 73901fadf7SArchie Cobbs #define L2TP_HDR_LEN 0x4000 /* has length field */ 74901fadf7SArchie Cobbs #define L2TP_HDR_SEQ 0x0800 /* has ns, nr fields */ 75901fadf7SArchie Cobbs #define L2TP_HDR_OFF 0x0200 /* has offset field */ 76901fadf7SArchie Cobbs #define L2TP_HDR_PRIO 0x0100 /* give priority */ 77901fadf7SArchie Cobbs #define L2TP_HDR_VERS_MASK 0x000f /* version field mask */ 78901fadf7SArchie Cobbs #define L2TP_HDR_VERSION 0x0002 /* version field */ 79901fadf7SArchie Cobbs 80901fadf7SArchie Cobbs /* Bits that must be zero or one in first two bytes of header */ 81901fadf7SArchie Cobbs #define L2TP_CTRL_0BITS 0x030d /* ctrl: must be 0 */ 82901fadf7SArchie Cobbs #define L2TP_CTRL_1BITS 0xc802 /* ctrl: must be 1 */ 83901fadf7SArchie Cobbs #define L2TP_DATA_0BITS 0x800d /* data: must be 0 */ 84901fadf7SArchie Cobbs #define L2TP_DATA_1BITS 0x0002 /* data: must be 1 */ 85901fadf7SArchie Cobbs 86901fadf7SArchie Cobbs /* Standard xmit ctrl and data header bits */ 87901fadf7SArchie Cobbs #define L2TP_CTRL_HDR (L2TP_HDR_CTRL | L2TP_HDR_LEN \ 88901fadf7SArchie Cobbs | L2TP_HDR_SEQ | L2TP_HDR_VERSION) 89901fadf7SArchie Cobbs #define L2TP_DATA_HDR (L2TP_HDR_VERSION) /* optional: len, seq */ 90901fadf7SArchie Cobbs 91901fadf7SArchie Cobbs /* Some hard coded values */ 92901fadf7SArchie Cobbs #define L2TP_MAX_XWIN 16 /* my max xmit window */ 93901fadf7SArchie Cobbs #define L2TP_MAX_REXMIT 5 /* default max rexmit */ 94901fadf7SArchie Cobbs #define L2TP_MAX_REXMIT_TO 30 /* default rexmit to */ 95901fadf7SArchie Cobbs #define L2TP_DELAYED_ACK ((hz + 19) / 20) /* delayed ack: 50 ms */ 96901fadf7SArchie Cobbs 97901fadf7SArchie Cobbs /* Default data sequence number configuration for new sessions */ 98901fadf7SArchie Cobbs #define L2TP_CONTROL_DSEQ 1 /* we are the lns */ 99901fadf7SArchie Cobbs #define L2TP_ENABLE_DSEQ 1 /* enable data seq # */ 100901fadf7SArchie Cobbs 101901fadf7SArchie Cobbs /* Compare sequence numbers using circular math */ 102901fadf7SArchie Cobbs #define L2TP_SEQ_DIFF(x, y) ((int)((int16_t)(x) - (int16_t)(y))) 103901fadf7SArchie Cobbs 104901fadf7SArchie Cobbs /* 105901fadf7SArchie Cobbs * Sequence number state 106901fadf7SArchie Cobbs * 107901fadf7SArchie Cobbs * Invariants: 108901fadf7SArchie Cobbs * - If cwnd < ssth, we're doing slow start, otherwise congestion avoidance 109901fadf7SArchie Cobbs * - The number of unacknowledged xmit packets is (ns - rack) <= seq->wmax 110901fadf7SArchie Cobbs * - The first (ns - rack) mbuf's in xwin[] array are copies of these 111901fadf7SArchie Cobbs * unacknowledged packets; the remainder of xwin[] consists first of 112901fadf7SArchie Cobbs * zero or more further untransmitted packets in the transmit queue 113901fadf7SArchie Cobbs * - We try to keep the peer's receive window as full as possible. 114901fadf7SArchie Cobbs * Therefore, (i < cwnd && xwin[i] != NULL) implies (ns - rack) > i. 115901fadf7SArchie Cobbs * - rack_timer is running iff (ns - rack) > 0 (unack'd xmit'd pkts) 116901fadf7SArchie Cobbs * - If xack != nr, there are unacknowledged recv packet(s) (delayed ack) 117901fadf7SArchie Cobbs * - xack_timer is running iff xack != nr (unack'd rec'd pkts) 118901fadf7SArchie Cobbs */ 119901fadf7SArchie Cobbs struct l2tp_seq { 120901fadf7SArchie Cobbs u_int16_t ns; /* next xmit seq we send */ 121901fadf7SArchie Cobbs u_int16_t nr; /* next recv seq we expect */ 122901fadf7SArchie Cobbs u_int16_t rack; /* last 'nr' we rec'd */ 123901fadf7SArchie Cobbs u_int16_t xack; /* last 'nr' we sent */ 124901fadf7SArchie Cobbs u_int16_t wmax; /* peer's max recv window */ 125901fadf7SArchie Cobbs u_int16_t cwnd; /* current congestion window */ 126901fadf7SArchie Cobbs u_int16_t ssth; /* slow start threshold */ 127901fadf7SArchie Cobbs u_int16_t acks; /* # consecutive acks rec'd */ 128901fadf7SArchie Cobbs u_int16_t rexmits; /* # retransmits sent */ 129901fadf7SArchie Cobbs u_int16_t max_rexmits; /* max # retransmits sent */ 130901fadf7SArchie Cobbs u_int16_t max_rexmit_to; /* max retransmit timeout */ 131901fadf7SArchie Cobbs struct callout rack_timer; /* retransmit timer */ 132901fadf7SArchie Cobbs struct callout xack_timer; /* delayed ack timer */ 133901fadf7SArchie Cobbs u_char rack_timer_running; /* xmit timer running */ 134901fadf7SArchie Cobbs u_char xack_timer_running; /* ack timer running */ 135901fadf7SArchie Cobbs struct mbuf *xwin[L2TP_MAX_XWIN]; /* transmit window */ 136901fadf7SArchie Cobbs }; 137901fadf7SArchie Cobbs 138901fadf7SArchie Cobbs /* Node private data */ 139901fadf7SArchie Cobbs struct ng_l2tp_private { 140901fadf7SArchie Cobbs node_p node; /* back pointer to node */ 141901fadf7SArchie Cobbs hook_p ctrl; /* hook to upper layers */ 142901fadf7SArchie Cobbs hook_p lower; /* hook to lower layers */ 143901fadf7SArchie Cobbs struct ng_l2tp_config conf; /* node configuration */ 144901fadf7SArchie Cobbs struct ng_l2tp_stats stats; /* node statistics */ 145901fadf7SArchie Cobbs struct l2tp_seq seq; /* ctrl sequence number state */ 146901fadf7SArchie Cobbs ng_ID_t ftarget; /* failure message target */ 147901fadf7SArchie Cobbs }; 148901fadf7SArchie Cobbs typedef struct ng_l2tp_private *priv_p; 149901fadf7SArchie Cobbs 150901fadf7SArchie Cobbs /* Hook private data (data session hooks only) */ 151901fadf7SArchie Cobbs struct ng_l2tp_hook_private { 152901fadf7SArchie Cobbs struct ng_l2tp_sess_config conf; /* hook/session config */ 153901fadf7SArchie Cobbs u_int16_t ns; /* data ns sequence number */ 154901fadf7SArchie Cobbs u_int16_t nr; /* data nr sequence number */ 155901fadf7SArchie Cobbs }; 156901fadf7SArchie Cobbs typedef struct ng_l2tp_hook_private *hookpriv_p; 157901fadf7SArchie Cobbs 158901fadf7SArchie Cobbs /* Netgraph node methods */ 159901fadf7SArchie Cobbs static ng_constructor_t ng_l2tp_constructor; 160901fadf7SArchie Cobbs static ng_rcvmsg_t ng_l2tp_rcvmsg; 161901fadf7SArchie Cobbs static ng_shutdown_t ng_l2tp_shutdown; 162901fadf7SArchie Cobbs static ng_newhook_t ng_l2tp_newhook; 163901fadf7SArchie Cobbs static ng_rcvdata_t ng_l2tp_rcvdata; 164901fadf7SArchie Cobbs static ng_disconnect_t ng_l2tp_disconnect; 165901fadf7SArchie Cobbs 166901fadf7SArchie Cobbs /* Internal functions */ 167901fadf7SArchie Cobbs static int ng_l2tp_recv_lower(node_p node, item_p item); 168901fadf7SArchie Cobbs static int ng_l2tp_recv_ctrl(node_p node, item_p item); 169901fadf7SArchie Cobbs static int ng_l2tp_recv_data(node_p node, item_p item, hookpriv_p hpriv); 170901fadf7SArchie Cobbs 171901fadf7SArchie Cobbs static int ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns); 172901fadf7SArchie Cobbs 173901fadf7SArchie Cobbs static void ng_l2tp_seq_init(priv_p priv); 174901fadf7SArchie Cobbs static int ng_l2tp_seq_adjust(priv_p priv, 175901fadf7SArchie Cobbs const struct ng_l2tp_config *conf); 176901fadf7SArchie Cobbs static void ng_l2tp_seq_reset(priv_p priv); 177901fadf7SArchie Cobbs static void ng_l2tp_seq_failure(priv_p priv); 178901fadf7SArchie Cobbs static void ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr); 179901fadf7SArchie Cobbs static int ng_l2tp_seq_recv_ns(priv_p priv, u_int16_t ns); 180901fadf7SArchie Cobbs static void ng_l2tp_seq_xack_timeout(void *arg); 181901fadf7SArchie Cobbs static void ng_l2tp_seq_rack_timeout(void *arg); 182901fadf7SArchie Cobbs 183901fadf7SArchie Cobbs static ng_fn_eachhook ng_l2tp_find_session; 184901fadf7SArchie Cobbs static ng_fn_eachhook ng_l2tp_reset_session; 185901fadf7SArchie Cobbs 186901fadf7SArchie Cobbs #ifdef INVARIANTS 187901fadf7SArchie Cobbs static void ng_l2tp_seq_check(struct l2tp_seq *seq); 188901fadf7SArchie Cobbs #endif 189901fadf7SArchie Cobbs 190901fadf7SArchie Cobbs /* Parse type for struct ng_l2tp_config */ 191901fadf7SArchie Cobbs static const struct ng_parse_struct_field 192901fadf7SArchie Cobbs ng_l2tp_config_type_fields[] = NG_L2TP_CONFIG_TYPE_INFO; 193901fadf7SArchie Cobbs static const struct ng_parse_type ng_l2tp_config_type = { 194901fadf7SArchie Cobbs &ng_parse_struct_type, 195901fadf7SArchie Cobbs &ng_l2tp_config_type_fields, 196901fadf7SArchie Cobbs }; 197901fadf7SArchie Cobbs 198901fadf7SArchie Cobbs /* Parse type for struct ng_l2tp_sess_config */ 199901fadf7SArchie Cobbs static const struct ng_parse_struct_field 200901fadf7SArchie Cobbs ng_l2tp_sess_config_type_fields[] = NG_L2TP_SESS_CONFIG_TYPE_INFO; 201901fadf7SArchie Cobbs static const struct ng_parse_type ng_l2tp_sess_config_type = { 202901fadf7SArchie Cobbs &ng_parse_struct_type, 203901fadf7SArchie Cobbs &ng_l2tp_sess_config_type_fields, 204901fadf7SArchie Cobbs }; 205901fadf7SArchie Cobbs 206901fadf7SArchie Cobbs /* Parse type for struct ng_l2tp_stats */ 207901fadf7SArchie Cobbs static const struct ng_parse_struct_field 208901fadf7SArchie Cobbs ng_l2tp_stats_type_fields[] = NG_L2TP_STATS_TYPE_INFO; 2092c9027fcSArchie Cobbs static const struct ng_parse_type ng_l2tp_stats_type = { 210901fadf7SArchie Cobbs &ng_parse_struct_type, 211901fadf7SArchie Cobbs &ng_l2tp_stats_type_fields 212901fadf7SArchie Cobbs }; 213901fadf7SArchie Cobbs 214901fadf7SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 215901fadf7SArchie Cobbs static const struct ng_cmdlist ng_l2tp_cmdlist[] = { 216901fadf7SArchie Cobbs { 217901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 218901fadf7SArchie Cobbs NGM_L2TP_SET_CONFIG, 219901fadf7SArchie Cobbs "setconfig", 220901fadf7SArchie Cobbs &ng_l2tp_config_type, 221901fadf7SArchie Cobbs NULL 222901fadf7SArchie Cobbs }, 223901fadf7SArchie Cobbs { 224901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 225901fadf7SArchie Cobbs NGM_L2TP_GET_CONFIG, 226901fadf7SArchie Cobbs "getconfig", 227901fadf7SArchie Cobbs NULL, 228901fadf7SArchie Cobbs &ng_l2tp_config_type 229901fadf7SArchie Cobbs }, 230901fadf7SArchie Cobbs { 231901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 232901fadf7SArchie Cobbs NGM_L2TP_SET_SESS_CONFIG, 233901fadf7SArchie Cobbs "setsessconfig", 234901fadf7SArchie Cobbs &ng_l2tp_sess_config_type, 235901fadf7SArchie Cobbs NULL 236901fadf7SArchie Cobbs }, 237901fadf7SArchie Cobbs { 238901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 239901fadf7SArchie Cobbs NGM_L2TP_GET_SESS_CONFIG, 240901fadf7SArchie Cobbs "getsessconfig", 241901fadf7SArchie Cobbs &ng_parse_hint16_type, 242901fadf7SArchie Cobbs &ng_l2tp_sess_config_type 243901fadf7SArchie Cobbs }, 244901fadf7SArchie Cobbs { 245901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 246901fadf7SArchie Cobbs NGM_L2TP_GET_STATS, 247901fadf7SArchie Cobbs "getstats", 248901fadf7SArchie Cobbs NULL, 2492c9027fcSArchie Cobbs &ng_l2tp_stats_type 250901fadf7SArchie Cobbs }, 251901fadf7SArchie Cobbs { 252901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 253901fadf7SArchie Cobbs NGM_L2TP_CLR_STATS, 254901fadf7SArchie Cobbs "clrstats", 255901fadf7SArchie Cobbs NULL, 256901fadf7SArchie Cobbs NULL 257901fadf7SArchie Cobbs }, 258901fadf7SArchie Cobbs { 259901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 260901fadf7SArchie Cobbs NGM_L2TP_GETCLR_STATS, 261901fadf7SArchie Cobbs "getclrstats", 262901fadf7SArchie Cobbs NULL, 2632c9027fcSArchie Cobbs &ng_l2tp_stats_type 264901fadf7SArchie Cobbs }, 265901fadf7SArchie Cobbs { 266901fadf7SArchie Cobbs NGM_L2TP_COOKIE, 267901fadf7SArchie Cobbs NGM_L2TP_ACK_FAILURE, 268901fadf7SArchie Cobbs "ackfailure", 269901fadf7SArchie Cobbs NULL, 270901fadf7SArchie Cobbs NULL 271901fadf7SArchie Cobbs }, 272901fadf7SArchie Cobbs { 0 } 273901fadf7SArchie Cobbs }; 274901fadf7SArchie Cobbs 275901fadf7SArchie Cobbs /* Node type descriptor */ 276901fadf7SArchie Cobbs static struct ng_type ng_l2tp_typestruct = { 277f8aae777SJulian Elischer .version = NG_ABI_VERSION, 278f8aae777SJulian Elischer .name = NG_L2TP_NODE_TYPE, 279f8aae777SJulian Elischer .constructor = ng_l2tp_constructor, 280f8aae777SJulian Elischer .rcvmsg = ng_l2tp_rcvmsg, 281f8aae777SJulian Elischer .shutdown = ng_l2tp_shutdown, 282f8aae777SJulian Elischer .newhook = ng_l2tp_newhook, 283f8aae777SJulian Elischer .rcvdata = ng_l2tp_rcvdata, 284f8aae777SJulian Elischer .disconnect = ng_l2tp_disconnect, 285f8aae777SJulian Elischer .cmdlist = ng_l2tp_cmdlist, 286901fadf7SArchie Cobbs }; 287901fadf7SArchie Cobbs NETGRAPH_INIT(l2tp, &ng_l2tp_typestruct); 288901fadf7SArchie Cobbs 289901fadf7SArchie Cobbs /* Sequence number state sanity checking */ 290901fadf7SArchie Cobbs #ifdef INVARIANTS 291901fadf7SArchie Cobbs #define L2TP_SEQ_CHECK(seq) ng_l2tp_seq_check(seq) 292901fadf7SArchie Cobbs #else 293901fadf7SArchie Cobbs #define L2TP_SEQ_CHECK(x) do { } while (0) 294901fadf7SArchie Cobbs #endif 295901fadf7SArchie Cobbs 29686fea6beSBosko Milekic /* memmove macro */ 297f7854568SDag-Erling Smørgrav #define memmove(d, s, l) bcopy(s, d, l) 298901fadf7SArchie Cobbs 299901fadf7SArchie Cobbs /* Whether to use m_copypacket() or m_dup() */ 300901fadf7SArchie Cobbs #define L2TP_COPY_MBUF m_copypacket 301901fadf7SArchie Cobbs 302901fadf7SArchie Cobbs /************************************************************************ 303901fadf7SArchie Cobbs NETGRAPH NODE STUFF 304901fadf7SArchie Cobbs ************************************************************************/ 305901fadf7SArchie Cobbs 306901fadf7SArchie Cobbs /* 307901fadf7SArchie Cobbs * Node type constructor 308901fadf7SArchie Cobbs */ 309901fadf7SArchie Cobbs static int 310901fadf7SArchie Cobbs ng_l2tp_constructor(node_p node) 311901fadf7SArchie Cobbs { 312901fadf7SArchie Cobbs priv_p priv; 313901fadf7SArchie Cobbs 314901fadf7SArchie Cobbs /* Allocate private structure */ 315901fadf7SArchie Cobbs MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_L2TP, M_NOWAIT | M_ZERO); 316901fadf7SArchie Cobbs if (priv == NULL) 317901fadf7SArchie Cobbs return (ENOMEM); 318901fadf7SArchie Cobbs NG_NODE_SET_PRIVATE(node, priv); 319901fadf7SArchie Cobbs priv->node = node; 320901fadf7SArchie Cobbs 321901fadf7SArchie Cobbs /* Apply a semi-reasonable default configuration */ 322901fadf7SArchie Cobbs priv->conf.peer_win = 1; 323901fadf7SArchie Cobbs priv->conf.rexmit_max = L2TP_MAX_REXMIT; 324901fadf7SArchie Cobbs priv->conf.rexmit_max_to = L2TP_MAX_REXMIT_TO; 325901fadf7SArchie Cobbs 326901fadf7SArchie Cobbs /* Initialize sequence number state */ 327901fadf7SArchie Cobbs ng_l2tp_seq_init(priv); 328901fadf7SArchie Cobbs 329901fadf7SArchie Cobbs /* Done */ 330901fadf7SArchie Cobbs return (0); 331901fadf7SArchie Cobbs } 332901fadf7SArchie Cobbs 333901fadf7SArchie Cobbs /* 334901fadf7SArchie Cobbs * Give our OK for a hook to be added. 335901fadf7SArchie Cobbs */ 336901fadf7SArchie Cobbs static int 337901fadf7SArchie Cobbs ng_l2tp_newhook(node_p node, hook_p hook, const char *name) 338901fadf7SArchie Cobbs { 339901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 340901fadf7SArchie Cobbs 341901fadf7SArchie Cobbs /* Check hook name */ 342901fadf7SArchie Cobbs if (strcmp(name, NG_L2TP_HOOK_CTRL) == 0) { 343901fadf7SArchie Cobbs if (priv->ctrl != NULL) 344901fadf7SArchie Cobbs return (EISCONN); 345901fadf7SArchie Cobbs priv->ctrl = hook; 346901fadf7SArchie Cobbs } else if (strcmp(name, NG_L2TP_HOOK_LOWER) == 0) { 347901fadf7SArchie Cobbs if (priv->lower != NULL) 348901fadf7SArchie Cobbs return (EISCONN); 349901fadf7SArchie Cobbs priv->lower = hook; 350901fadf7SArchie Cobbs } else { 351901fadf7SArchie Cobbs static const char hexdig[16] = "0123456789abcdef"; 352901fadf7SArchie Cobbs u_int16_t session_id; 353901fadf7SArchie Cobbs hookpriv_p hpriv; 354901fadf7SArchie Cobbs const char *hex; 355901fadf7SArchie Cobbs int i; 356901fadf7SArchie Cobbs int j; 357901fadf7SArchie Cobbs 358901fadf7SArchie Cobbs /* Parse hook name to get session ID */ 359901fadf7SArchie Cobbs if (strncmp(name, NG_L2TP_HOOK_SESSION_P, 360901fadf7SArchie Cobbs sizeof(NG_L2TP_HOOK_SESSION_P) - 1) != 0) 361901fadf7SArchie Cobbs return (EINVAL); 362901fadf7SArchie Cobbs hex = name + sizeof(NG_L2TP_HOOK_SESSION_P) - 1; 363901fadf7SArchie Cobbs for (session_id = i = 0; i < 4; i++) { 364901fadf7SArchie Cobbs for (j = 0; j < 16 && hex[i] != hexdig[j]; j++); 365901fadf7SArchie Cobbs if (j == 16) 366901fadf7SArchie Cobbs return (EINVAL); 367901fadf7SArchie Cobbs session_id = (session_id << 4) | j; 368901fadf7SArchie Cobbs } 369901fadf7SArchie Cobbs if (hex[i] != '\0') 370901fadf7SArchie Cobbs return (EINVAL); 371901fadf7SArchie Cobbs 372901fadf7SArchie Cobbs /* Create hook private structure */ 373901fadf7SArchie Cobbs MALLOC(hpriv, hookpriv_p, 374901fadf7SArchie Cobbs sizeof(*hpriv), M_NETGRAPH_L2TP, M_NOWAIT | M_ZERO); 375901fadf7SArchie Cobbs if (hpriv == NULL) 376901fadf7SArchie Cobbs return (ENOMEM); 377901fadf7SArchie Cobbs hpriv->conf.session_id = htons(session_id); 378901fadf7SArchie Cobbs hpriv->conf.control_dseq = L2TP_CONTROL_DSEQ; 379901fadf7SArchie Cobbs hpriv->conf.enable_dseq = L2TP_ENABLE_DSEQ; 380901fadf7SArchie Cobbs NG_HOOK_SET_PRIVATE(hook, hpriv); 381901fadf7SArchie Cobbs } 382901fadf7SArchie Cobbs 383901fadf7SArchie Cobbs /* Done */ 384901fadf7SArchie Cobbs return (0); 385901fadf7SArchie Cobbs } 386901fadf7SArchie Cobbs 387901fadf7SArchie Cobbs /* 388901fadf7SArchie Cobbs * Receive a control message. 389901fadf7SArchie Cobbs */ 390901fadf7SArchie Cobbs static int 391901fadf7SArchie Cobbs ng_l2tp_rcvmsg(node_p node, item_p item, hook_p lasthook) 392901fadf7SArchie Cobbs { 393901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 394901fadf7SArchie Cobbs struct ng_mesg *resp = NULL; 395901fadf7SArchie Cobbs struct ng_mesg *msg; 396901fadf7SArchie Cobbs int error = 0; 397901fadf7SArchie Cobbs 398901fadf7SArchie Cobbs NGI_GET_MSG(item, msg); 399901fadf7SArchie Cobbs switch (msg->header.typecookie) { 400901fadf7SArchie Cobbs case NGM_L2TP_COOKIE: 401901fadf7SArchie Cobbs switch (msg->header.cmd) { 402901fadf7SArchie Cobbs case NGM_L2TP_SET_CONFIG: 403901fadf7SArchie Cobbs { 404901fadf7SArchie Cobbs struct ng_l2tp_config *const conf = 405901fadf7SArchie Cobbs (struct ng_l2tp_config *)msg->data; 406901fadf7SArchie Cobbs 407901fadf7SArchie Cobbs /* Check for invalid or illegal config */ 408901fadf7SArchie Cobbs if (msg->header.arglen != sizeof(*conf)) { 409901fadf7SArchie Cobbs error = EINVAL; 410901fadf7SArchie Cobbs break; 411901fadf7SArchie Cobbs } 412901fadf7SArchie Cobbs conf->enabled = !!conf->enabled; 413901fadf7SArchie Cobbs conf->match_id = !!conf->match_id; 414901fadf7SArchie Cobbs conf->tunnel_id = htons(conf->tunnel_id); 415901fadf7SArchie Cobbs conf->peer_id = htons(conf->peer_id); 416901fadf7SArchie Cobbs if (priv->conf.enabled 417901fadf7SArchie Cobbs && ((priv->conf.tunnel_id != 0 418901fadf7SArchie Cobbs && conf->tunnel_id != priv->conf.tunnel_id) 419901fadf7SArchie Cobbs || ((priv->conf.peer_id != 0 420901fadf7SArchie Cobbs && conf->peer_id != priv->conf.peer_id)))) { 421901fadf7SArchie Cobbs error = EBUSY; 422901fadf7SArchie Cobbs break; 423901fadf7SArchie Cobbs } 424901fadf7SArchie Cobbs 425901fadf7SArchie Cobbs /* Save calling node as failure target */ 426901fadf7SArchie Cobbs priv->ftarget = NGI_RETADDR(item); 427901fadf7SArchie Cobbs 428901fadf7SArchie Cobbs /* Adjust sequence number state */ 429901fadf7SArchie Cobbs if ((error = ng_l2tp_seq_adjust(priv, conf)) != 0) 430901fadf7SArchie Cobbs break; 431901fadf7SArchie Cobbs 432901fadf7SArchie Cobbs /* Update node's config */ 433901fadf7SArchie Cobbs priv->conf = *conf; 434901fadf7SArchie Cobbs break; 435901fadf7SArchie Cobbs } 436901fadf7SArchie Cobbs case NGM_L2TP_GET_CONFIG: 437901fadf7SArchie Cobbs { 438901fadf7SArchie Cobbs struct ng_l2tp_config *conf; 439901fadf7SArchie Cobbs 440901fadf7SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); 441901fadf7SArchie Cobbs if (resp == NULL) { 442901fadf7SArchie Cobbs error = ENOMEM; 443901fadf7SArchie Cobbs break; 444901fadf7SArchie Cobbs } 445901fadf7SArchie Cobbs conf = (struct ng_l2tp_config *)resp->data; 446901fadf7SArchie Cobbs *conf = priv->conf; 447901fadf7SArchie Cobbs 448901fadf7SArchie Cobbs /* Put ID's in host order */ 449901fadf7SArchie Cobbs conf->tunnel_id = ntohs(conf->tunnel_id); 450901fadf7SArchie Cobbs conf->peer_id = ntohs(conf->peer_id); 451901fadf7SArchie Cobbs break; 452901fadf7SArchie Cobbs } 453901fadf7SArchie Cobbs case NGM_L2TP_SET_SESS_CONFIG: 454901fadf7SArchie Cobbs { 455901fadf7SArchie Cobbs struct ng_l2tp_sess_config *const conf = 456901fadf7SArchie Cobbs (struct ng_l2tp_sess_config *)msg->data; 457901fadf7SArchie Cobbs hookpriv_p hpriv; 458901fadf7SArchie Cobbs hook_p hook; 459901fadf7SArchie Cobbs 460901fadf7SArchie Cobbs /* Check for invalid or illegal config */ 461901fadf7SArchie Cobbs if (msg->header.arglen != sizeof(*conf)) { 462901fadf7SArchie Cobbs error = EINVAL; 463901fadf7SArchie Cobbs break; 464901fadf7SArchie Cobbs } 465901fadf7SArchie Cobbs 466901fadf7SArchie Cobbs /* Put ID's in network order */ 467901fadf7SArchie Cobbs conf->session_id = htons(conf->session_id); 468901fadf7SArchie Cobbs conf->peer_id = htons(conf->peer_id); 469901fadf7SArchie Cobbs 470901fadf7SArchie Cobbs /* Find matching hook */ 471901fadf7SArchie Cobbs NG_NODE_FOREACH_HOOK(node, ng_l2tp_find_session, 472901fadf7SArchie Cobbs (void *)(uintptr_t)conf->session_id, hook); 473901fadf7SArchie Cobbs if (hook == NULL) { 474901fadf7SArchie Cobbs error = ENOENT; 475901fadf7SArchie Cobbs break; 476901fadf7SArchie Cobbs } 477901fadf7SArchie Cobbs hpriv = NG_HOOK_PRIVATE(hook); 478901fadf7SArchie Cobbs 479901fadf7SArchie Cobbs /* Update hook's config */ 480901fadf7SArchie Cobbs hpriv->conf = *conf; 481901fadf7SArchie Cobbs break; 482901fadf7SArchie Cobbs } 483901fadf7SArchie Cobbs case NGM_L2TP_GET_SESS_CONFIG: 484901fadf7SArchie Cobbs { 485901fadf7SArchie Cobbs struct ng_l2tp_sess_config *conf; 486901fadf7SArchie Cobbs u_int16_t session_id; 487901fadf7SArchie Cobbs hookpriv_p hpriv; 488901fadf7SArchie Cobbs hook_p hook; 489901fadf7SArchie Cobbs 490901fadf7SArchie Cobbs /* Get session ID */ 491901fadf7SArchie Cobbs if (msg->header.arglen != sizeof(session_id)) { 492901fadf7SArchie Cobbs error = EINVAL; 493901fadf7SArchie Cobbs break; 494901fadf7SArchie Cobbs } 495901fadf7SArchie Cobbs memcpy(&session_id, msg->data, 2); 496901fadf7SArchie Cobbs session_id = htons(session_id); 497901fadf7SArchie Cobbs 498901fadf7SArchie Cobbs /* Find matching hook */ 499901fadf7SArchie Cobbs NG_NODE_FOREACH_HOOK(node, ng_l2tp_find_session, 500901fadf7SArchie Cobbs (void *)(uintptr_t)session_id, hook); 501901fadf7SArchie Cobbs if (hook == NULL) { 502901fadf7SArchie Cobbs error = ENOENT; 503901fadf7SArchie Cobbs break; 504901fadf7SArchie Cobbs } 505901fadf7SArchie Cobbs hpriv = NG_HOOK_PRIVATE(hook); 506901fadf7SArchie Cobbs 507901fadf7SArchie Cobbs /* Send response */ 508901fadf7SArchie Cobbs NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT); 509901fadf7SArchie Cobbs if (resp == NULL) { 510901fadf7SArchie Cobbs error = ENOMEM; 511901fadf7SArchie Cobbs break; 512901fadf7SArchie Cobbs } 513901fadf7SArchie Cobbs conf = (struct ng_l2tp_sess_config *)resp->data; 514901fadf7SArchie Cobbs *conf = hpriv->conf; 515901fadf7SArchie Cobbs 516901fadf7SArchie Cobbs /* Put ID's in host order */ 517901fadf7SArchie Cobbs conf->session_id = ntohs(conf->session_id); 518901fadf7SArchie Cobbs conf->peer_id = ntohs(conf->peer_id); 519901fadf7SArchie Cobbs break; 520901fadf7SArchie Cobbs } 521901fadf7SArchie Cobbs case NGM_L2TP_GET_STATS: 522901fadf7SArchie Cobbs case NGM_L2TP_CLR_STATS: 523901fadf7SArchie Cobbs case NGM_L2TP_GETCLR_STATS: 524901fadf7SArchie Cobbs { 525901fadf7SArchie Cobbs if (msg->header.cmd != NGM_L2TP_CLR_STATS) { 526901fadf7SArchie Cobbs NG_MKRESPONSE(resp, msg, 527901fadf7SArchie Cobbs sizeof(priv->stats), M_NOWAIT); 528901fadf7SArchie Cobbs if (resp == NULL) { 529901fadf7SArchie Cobbs error = ENOMEM; 530901fadf7SArchie Cobbs break; 531901fadf7SArchie Cobbs } 532901fadf7SArchie Cobbs memcpy(resp->data, 533901fadf7SArchie Cobbs &priv->stats, sizeof(priv->stats)); 534901fadf7SArchie Cobbs } 535901fadf7SArchie Cobbs if (msg->header.cmd != NGM_L2TP_GET_STATS) 536901fadf7SArchie Cobbs memset(&priv->stats, 0, sizeof(priv->stats)); 537901fadf7SArchie Cobbs break; 538901fadf7SArchie Cobbs } 539901fadf7SArchie Cobbs default: 540901fadf7SArchie Cobbs error = EINVAL; 541901fadf7SArchie Cobbs break; 542901fadf7SArchie Cobbs } 543901fadf7SArchie Cobbs break; 544901fadf7SArchie Cobbs default: 545901fadf7SArchie Cobbs error = EINVAL; 546901fadf7SArchie Cobbs break; 547901fadf7SArchie Cobbs } 548901fadf7SArchie Cobbs 549901fadf7SArchie Cobbs /* Done */ 550901fadf7SArchie Cobbs NG_RESPOND_MSG(error, node, item, resp); 551901fadf7SArchie Cobbs NG_FREE_MSG(msg); 552901fadf7SArchie Cobbs return (error); 553901fadf7SArchie Cobbs } 554901fadf7SArchie Cobbs 555901fadf7SArchie Cobbs /* 556901fadf7SArchie Cobbs * Receive incoming data on a hook. 557901fadf7SArchie Cobbs */ 558901fadf7SArchie Cobbs static int 559901fadf7SArchie Cobbs ng_l2tp_rcvdata(hook_p hook, item_p item) 560901fadf7SArchie Cobbs { 561901fadf7SArchie Cobbs const node_p node = NG_HOOK_NODE(hook); 562901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 563901fadf7SArchie Cobbs int error; 564901fadf7SArchie Cobbs 565901fadf7SArchie Cobbs /* Sanity check */ 566901fadf7SArchie Cobbs L2TP_SEQ_CHECK(&priv->seq); 567901fadf7SArchie Cobbs 568901fadf7SArchie Cobbs /* If not configured, reject */ 569901fadf7SArchie Cobbs if (!priv->conf.enabled) { 570901fadf7SArchie Cobbs NG_FREE_ITEM(item); 571901fadf7SArchie Cobbs return (ENXIO); 572901fadf7SArchie Cobbs } 573901fadf7SArchie Cobbs 574901fadf7SArchie Cobbs /* Handle incoming frame from below */ 575901fadf7SArchie Cobbs if (hook == priv->lower) { 576901fadf7SArchie Cobbs error = ng_l2tp_recv_lower(node, item); 577901fadf7SArchie Cobbs goto done; 578901fadf7SArchie Cobbs } 579901fadf7SArchie Cobbs 580901fadf7SArchie Cobbs /* Handle outgoing control frame */ 581901fadf7SArchie Cobbs if (hook == priv->ctrl) { 582901fadf7SArchie Cobbs error = ng_l2tp_recv_ctrl(node, item); 583901fadf7SArchie Cobbs goto done; 584901fadf7SArchie Cobbs } 585901fadf7SArchie Cobbs 586901fadf7SArchie Cobbs /* Handle outgoing data frame */ 587901fadf7SArchie Cobbs error = ng_l2tp_recv_data(node, item, NG_HOOK_PRIVATE(hook)); 588901fadf7SArchie Cobbs 589901fadf7SArchie Cobbs done: 590901fadf7SArchie Cobbs /* Done */ 591901fadf7SArchie Cobbs L2TP_SEQ_CHECK(&priv->seq); 592901fadf7SArchie Cobbs return (error); 593901fadf7SArchie Cobbs } 594901fadf7SArchie Cobbs 595901fadf7SArchie Cobbs /* 596901fadf7SArchie Cobbs * Destroy node 597901fadf7SArchie Cobbs */ 598901fadf7SArchie Cobbs static int 599901fadf7SArchie Cobbs ng_l2tp_shutdown(node_p node) 600901fadf7SArchie Cobbs { 601901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 602901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 603901fadf7SArchie Cobbs 604901fadf7SArchie Cobbs /* Sanity check */ 605901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 606901fadf7SArchie Cobbs 607901fadf7SArchie Cobbs /* Reset sequence number state */ 608901fadf7SArchie Cobbs ng_l2tp_seq_reset(priv); 609901fadf7SArchie Cobbs 610901fadf7SArchie Cobbs /* Free private data if neither timer is running */ 611901fadf7SArchie Cobbs if (!seq->rack_timer_running && !seq->xack_timer_running) { 612901fadf7SArchie Cobbs FREE(priv, M_NETGRAPH_L2TP); 613901fadf7SArchie Cobbs NG_NODE_SET_PRIVATE(node, NULL); 614901fadf7SArchie Cobbs } 615901fadf7SArchie Cobbs 616901fadf7SArchie Cobbs /* Unref node */ 617901fadf7SArchie Cobbs NG_NODE_UNREF(node); 618901fadf7SArchie Cobbs return (0); 619901fadf7SArchie Cobbs } 620901fadf7SArchie Cobbs 621901fadf7SArchie Cobbs /* 622901fadf7SArchie Cobbs * Hook disconnection 623901fadf7SArchie Cobbs */ 624901fadf7SArchie Cobbs static int 625901fadf7SArchie Cobbs ng_l2tp_disconnect(hook_p hook) 626901fadf7SArchie Cobbs { 627901fadf7SArchie Cobbs const node_p node = NG_HOOK_NODE(hook); 628901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 629901fadf7SArchie Cobbs 630901fadf7SArchie Cobbs /* Zero out hook pointer */ 631901fadf7SArchie Cobbs if (hook == priv->ctrl) 632901fadf7SArchie Cobbs priv->ctrl = NULL; 633901fadf7SArchie Cobbs else if (hook == priv->lower) 634901fadf7SArchie Cobbs priv->lower = NULL; 635901fadf7SArchie Cobbs else { 636901fadf7SArchie Cobbs FREE(NG_HOOK_PRIVATE(hook), M_NETGRAPH_L2TP); 637901fadf7SArchie Cobbs NG_HOOK_SET_PRIVATE(hook, NULL); 638901fadf7SArchie Cobbs } 639901fadf7SArchie Cobbs 640901fadf7SArchie Cobbs /* Go away if no longer connected to anything */ 641901fadf7SArchie Cobbs if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node)) 642901fadf7SArchie Cobbs ng_rmnode_self(node); 643901fadf7SArchie Cobbs return (0); 644901fadf7SArchie Cobbs } 645901fadf7SArchie Cobbs 646901fadf7SArchie Cobbs /************************************************************************* 647901fadf7SArchie Cobbs INTERNAL FUNCTIONS 648901fadf7SArchie Cobbs *************************************************************************/ 649901fadf7SArchie Cobbs 650901fadf7SArchie Cobbs /* 651901fadf7SArchie Cobbs * Find the hook with a given session ID. 652901fadf7SArchie Cobbs */ 653901fadf7SArchie Cobbs static int 654901fadf7SArchie Cobbs ng_l2tp_find_session(hook_p hook, void *arg) 655901fadf7SArchie Cobbs { 656901fadf7SArchie Cobbs const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 657901fadf7SArchie Cobbs const u_int16_t sid = (u_int16_t)(uintptr_t)arg; 658901fadf7SArchie Cobbs 659901fadf7SArchie Cobbs if (hpriv == NULL || hpriv->conf.session_id != sid) 660901fadf7SArchie Cobbs return (-1); 661901fadf7SArchie Cobbs return (0); 662901fadf7SArchie Cobbs } 663901fadf7SArchie Cobbs 664901fadf7SArchie Cobbs /* 665901fadf7SArchie Cobbs * Reset a hook's session state. 666901fadf7SArchie Cobbs */ 667901fadf7SArchie Cobbs static int 668901fadf7SArchie Cobbs ng_l2tp_reset_session(hook_p hook, void *arg) 669901fadf7SArchie Cobbs { 670901fadf7SArchie Cobbs const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 671901fadf7SArchie Cobbs 672901fadf7SArchie Cobbs if (hpriv != NULL) { 673901fadf7SArchie Cobbs hpriv->conf.control_dseq = 0; 674901fadf7SArchie Cobbs hpriv->conf.enable_dseq = 0; 675901fadf7SArchie Cobbs hpriv->nr = 0; 676901fadf7SArchie Cobbs hpriv->ns = 0; 677901fadf7SArchie Cobbs } 678901fadf7SArchie Cobbs return (-1); 679901fadf7SArchie Cobbs } 680901fadf7SArchie Cobbs 681901fadf7SArchie Cobbs /* 682901fadf7SArchie Cobbs * Handle an incoming frame from below. 683901fadf7SArchie Cobbs */ 684901fadf7SArchie Cobbs static int 685901fadf7SArchie Cobbs ng_l2tp_recv_lower(node_p node, item_p item) 686901fadf7SArchie Cobbs { 687901fadf7SArchie Cobbs static const u_int16_t req_bits[2][2] = { 688901fadf7SArchie Cobbs { L2TP_DATA_0BITS, L2TP_DATA_1BITS }, 689901fadf7SArchie Cobbs { L2TP_CTRL_0BITS, L2TP_CTRL_1BITS }, 690901fadf7SArchie Cobbs }; 691901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 692901fadf7SArchie Cobbs hookpriv_p hpriv = NULL; 693901fadf7SArchie Cobbs hook_p hook = NULL; 694901fadf7SArchie Cobbs u_int16_t ids[2]; 695901fadf7SArchie Cobbs struct mbuf *m; 696901fadf7SArchie Cobbs u_int16_t hdr; 697901fadf7SArchie Cobbs u_int16_t ns; 698901fadf7SArchie Cobbs u_int16_t nr; 699901fadf7SArchie Cobbs int is_ctrl; 700901fadf7SArchie Cobbs int error; 701901fadf7SArchie Cobbs int len; 702901fadf7SArchie Cobbs 703901fadf7SArchie Cobbs /* Grab mbuf */ 704901fadf7SArchie Cobbs NGI_GET_M(item, m); 705901fadf7SArchie Cobbs 706901fadf7SArchie Cobbs /* Update stats */ 707901fadf7SArchie Cobbs priv->stats.recvPackets++; 708901fadf7SArchie Cobbs priv->stats.recvOctets += m->m_pkthdr.len; 709901fadf7SArchie Cobbs 710901fadf7SArchie Cobbs /* Get initial header */ 711901fadf7SArchie Cobbs if (m->m_pkthdr.len < 6) { 712901fadf7SArchie Cobbs priv->stats.recvRunts++; 713901fadf7SArchie Cobbs NG_FREE_ITEM(item); 714901fadf7SArchie Cobbs NG_FREE_M(m); 715901fadf7SArchie Cobbs return (EINVAL); 716901fadf7SArchie Cobbs } 717901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 718901fadf7SArchie Cobbs priv->stats.memoryFailures++; 719901fadf7SArchie Cobbs NG_FREE_ITEM(item); 720901fadf7SArchie Cobbs return (EINVAL); 721901fadf7SArchie Cobbs } 722901fadf7SArchie Cobbs hdr = ntohs(*mtod(m, u_int16_t *)); 723901fadf7SArchie Cobbs m_adj(m, 2); 724901fadf7SArchie Cobbs 725901fadf7SArchie Cobbs /* Check required header bits and minimum length */ 726901fadf7SArchie Cobbs is_ctrl = (hdr & L2TP_HDR_CTRL) != 0; 727901fadf7SArchie Cobbs if ((hdr & req_bits[is_ctrl][0]) != 0 728901fadf7SArchie Cobbs || (~hdr & req_bits[is_ctrl][1]) != 0) { 729901fadf7SArchie Cobbs priv->stats.recvInvalid++; 730901fadf7SArchie Cobbs NG_FREE_ITEM(item); 731901fadf7SArchie Cobbs NG_FREE_M(m); 732901fadf7SArchie Cobbs return (EINVAL); 733901fadf7SArchie Cobbs } 734901fadf7SArchie Cobbs if (m->m_pkthdr.len < 4 /* tunnel, session id */ 735901fadf7SArchie Cobbs + (2 * ((hdr & L2TP_HDR_LEN) != 0)) /* length field */ 736901fadf7SArchie Cobbs + (4 * ((hdr & L2TP_HDR_SEQ) != 0)) /* seq # fields */ 737901fadf7SArchie Cobbs + (2 * ((hdr & L2TP_HDR_OFF) != 0))) { /* offset field */ 738901fadf7SArchie Cobbs priv->stats.recvRunts++; 739901fadf7SArchie Cobbs NG_FREE_ITEM(item); 740901fadf7SArchie Cobbs NG_FREE_M(m); 741901fadf7SArchie Cobbs return (EINVAL); 742901fadf7SArchie Cobbs } 743901fadf7SArchie Cobbs 744901fadf7SArchie Cobbs /* Get and validate length field if present */ 745901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_LEN) != 0) { 746901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 747901fadf7SArchie Cobbs priv->stats.memoryFailures++; 748901fadf7SArchie Cobbs NG_FREE_ITEM(item); 749901fadf7SArchie Cobbs return (EINVAL); 750901fadf7SArchie Cobbs } 751901fadf7SArchie Cobbs len = (u_int16_t)ntohs(*mtod(m, u_int16_t *)) - 4; 752901fadf7SArchie Cobbs m_adj(m, 2); 753901fadf7SArchie Cobbs if (len < 0 || len > m->m_pkthdr.len) { 754901fadf7SArchie Cobbs priv->stats.recvInvalid++; 755901fadf7SArchie Cobbs NG_FREE_ITEM(item); 756901fadf7SArchie Cobbs NG_FREE_M(m); 757901fadf7SArchie Cobbs return (EINVAL); 758901fadf7SArchie Cobbs } 759901fadf7SArchie Cobbs if (len < m->m_pkthdr.len) /* trim extra bytes */ 760901fadf7SArchie Cobbs m_adj(m, -(m->m_pkthdr.len - len)); 761901fadf7SArchie Cobbs } 762901fadf7SArchie Cobbs 763901fadf7SArchie Cobbs /* Get tunnel ID and session ID */ 764901fadf7SArchie Cobbs if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { 765901fadf7SArchie Cobbs priv->stats.memoryFailures++; 766901fadf7SArchie Cobbs NG_FREE_ITEM(item); 767901fadf7SArchie Cobbs return (EINVAL); 768901fadf7SArchie Cobbs } 769901fadf7SArchie Cobbs memcpy(ids, mtod(m, u_int16_t *), 4); 770901fadf7SArchie Cobbs m_adj(m, 4); 771901fadf7SArchie Cobbs 772901fadf7SArchie Cobbs /* Check tunnel ID */ 773901fadf7SArchie Cobbs if (ids[0] != priv->conf.tunnel_id 774901fadf7SArchie Cobbs && (priv->conf.match_id || ids[0] != 0)) { 775901fadf7SArchie Cobbs priv->stats.recvWrongTunnel++; 776901fadf7SArchie Cobbs NG_FREE_ITEM(item); 777901fadf7SArchie Cobbs NG_FREE_M(m); 778901fadf7SArchie Cobbs return (EADDRNOTAVAIL); 779901fadf7SArchie Cobbs } 780901fadf7SArchie Cobbs 781901fadf7SArchie Cobbs /* Check session ID (for data packets only) */ 782901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_CTRL) == 0) { 783901fadf7SArchie Cobbs NG_NODE_FOREACH_HOOK(node, ng_l2tp_find_session, 784901fadf7SArchie Cobbs (void *)(uintptr_t)ids[1], hook); 785901fadf7SArchie Cobbs if (hook == NULL) { 786901fadf7SArchie Cobbs priv->stats.recvUnknownSID++; 787901fadf7SArchie Cobbs NG_FREE_ITEM(item); 788901fadf7SArchie Cobbs NG_FREE_M(m); 789901fadf7SArchie Cobbs return (ENOTCONN); 790901fadf7SArchie Cobbs } 791901fadf7SArchie Cobbs hpriv = NG_HOOK_PRIVATE(hook); 792901fadf7SArchie Cobbs } 793901fadf7SArchie Cobbs 794901fadf7SArchie Cobbs /* Get Ns, Nr fields if present */ 795901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_SEQ) != 0) { 796901fadf7SArchie Cobbs if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { 797901fadf7SArchie Cobbs priv->stats.memoryFailures++; 798901fadf7SArchie Cobbs NG_FREE_ITEM(item); 799901fadf7SArchie Cobbs return (EINVAL); 800901fadf7SArchie Cobbs } 801901fadf7SArchie Cobbs memcpy(&ns, &mtod(m, u_int16_t *)[0], 2); 802901fadf7SArchie Cobbs ns = ntohs(ns); 803901fadf7SArchie Cobbs memcpy(&nr, &mtod(m, u_int16_t *)[1], 2); 804901fadf7SArchie Cobbs nr = ntohs(nr); 805901fadf7SArchie Cobbs m_adj(m, 4); 806901fadf7SArchie Cobbs } 807901fadf7SArchie Cobbs 808901fadf7SArchie Cobbs /* Strip offset padding if present */ 809901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_OFF) != 0) { 810901fadf7SArchie Cobbs u_int16_t offset; 811901fadf7SArchie Cobbs 812901fadf7SArchie Cobbs /* Get length of offset padding */ 813901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 814901fadf7SArchie Cobbs priv->stats.memoryFailures++; 815901fadf7SArchie Cobbs NG_FREE_ITEM(item); 816901fadf7SArchie Cobbs return (EINVAL); 817901fadf7SArchie Cobbs } 818901fadf7SArchie Cobbs memcpy(&offset, mtod(m, u_int16_t *), 2); 819901fadf7SArchie Cobbs offset = ntohs(offset); 820901fadf7SArchie Cobbs 821901fadf7SArchie Cobbs /* Trim offset padding */ 822901fadf7SArchie Cobbs if (offset <= 2 || offset > m->m_pkthdr.len) { 823901fadf7SArchie Cobbs priv->stats.recvInvalid++; 824901fadf7SArchie Cobbs NG_FREE_ITEM(item); 825901fadf7SArchie Cobbs NG_FREE_M(m); 826901fadf7SArchie Cobbs return (EINVAL); 827901fadf7SArchie Cobbs } 828901fadf7SArchie Cobbs m_adj(m, offset); 829901fadf7SArchie Cobbs } 830901fadf7SArchie Cobbs 831901fadf7SArchie Cobbs /* Handle control packets */ 832901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_CTRL) != 0) { 833901fadf7SArchie Cobbs 834901fadf7SArchie Cobbs /* Handle receive ack sequence number Nr */ 835901fadf7SArchie Cobbs ng_l2tp_seq_recv_nr(priv, nr); 836901fadf7SArchie Cobbs 837901fadf7SArchie Cobbs /* Discard ZLB packets */ 838901fadf7SArchie Cobbs if (m->m_pkthdr.len == 0) { 839901fadf7SArchie Cobbs priv->stats.recvZLBs++; 840901fadf7SArchie Cobbs NG_FREE_ITEM(item); 841901fadf7SArchie Cobbs NG_FREE_M(m); 842901fadf7SArchie Cobbs return (0); 843901fadf7SArchie Cobbs } 844901fadf7SArchie Cobbs 845901fadf7SArchie Cobbs /* 846901fadf7SArchie Cobbs * Prepend session ID to packet here: we don't want to accept 847901fadf7SArchie Cobbs * the send sequence number Ns if we have to drop the packet 848901fadf7SArchie Cobbs * later because of a memory error, because then the upper 849901fadf7SArchie Cobbs * layer would never get the packet. 850901fadf7SArchie Cobbs */ 851a163d034SWarner Losh M_PREPEND(m, 2, M_DONTWAIT); 852901fadf7SArchie Cobbs if (m == NULL) { 853901fadf7SArchie Cobbs priv->stats.memoryFailures++; 854901fadf7SArchie Cobbs NG_FREE_ITEM(item); 855901fadf7SArchie Cobbs return (ENOBUFS); 856901fadf7SArchie Cobbs } 857901fadf7SArchie Cobbs memcpy(mtod(m, u_int16_t *), &ids[1], 2); 858901fadf7SArchie Cobbs 859901fadf7SArchie Cobbs /* Now handle send sequence number */ 860901fadf7SArchie Cobbs if (ng_l2tp_seq_recv_ns(priv, ns) == -1) { 861901fadf7SArchie Cobbs NG_FREE_ITEM(item); 862901fadf7SArchie Cobbs NG_FREE_M(m); 863901fadf7SArchie Cobbs return (0); 864901fadf7SArchie Cobbs } 865901fadf7SArchie Cobbs 866901fadf7SArchie Cobbs /* Deliver packet to upper layers */ 867901fadf7SArchie Cobbs NG_FWD_NEW_DATA(error, item, priv->ctrl, m); 868901fadf7SArchie Cobbs return (error); 869901fadf7SArchie Cobbs } 870901fadf7SArchie Cobbs 871901fadf7SArchie Cobbs /* Follow peer's lead in data sequencing, if configured to do so */ 872901fadf7SArchie Cobbs if (!hpriv->conf.control_dseq) 873901fadf7SArchie Cobbs hpriv->conf.enable_dseq = ((hdr & L2TP_HDR_SEQ) != 0); 874901fadf7SArchie Cobbs 875901fadf7SArchie Cobbs /* Handle data sequence numbers if present and enabled */ 876901fadf7SArchie Cobbs if ((hdr & L2TP_HDR_SEQ) != 0) { 877901fadf7SArchie Cobbs if (hpriv->conf.enable_dseq 878901fadf7SArchie Cobbs && L2TP_SEQ_DIFF(ns, hpriv->nr) < 0) { 879901fadf7SArchie Cobbs NG_FREE_ITEM(item); /* duplicate or out of order */ 880901fadf7SArchie Cobbs NG_FREE_M(m); 881901fadf7SArchie Cobbs priv->stats.recvDataDrops++; 882901fadf7SArchie Cobbs return (0); 883901fadf7SArchie Cobbs } 884901fadf7SArchie Cobbs hpriv->nr = ns + 1; 885901fadf7SArchie Cobbs } 886901fadf7SArchie Cobbs 887901fadf7SArchie Cobbs /* Drop empty data packets */ 888901fadf7SArchie Cobbs if (m->m_pkthdr.len == 0) { 889901fadf7SArchie Cobbs NG_FREE_ITEM(item); 890901fadf7SArchie Cobbs NG_FREE_M(m); 891901fadf7SArchie Cobbs return (0); 892901fadf7SArchie Cobbs } 893901fadf7SArchie Cobbs 894901fadf7SArchie Cobbs /* Deliver data */ 895901fadf7SArchie Cobbs NG_FWD_NEW_DATA(error, item, hook, m); 896901fadf7SArchie Cobbs return (error); 897901fadf7SArchie Cobbs } 898901fadf7SArchie Cobbs 899901fadf7SArchie Cobbs /* 900901fadf7SArchie Cobbs * Handle an outgoing control frame. 901901fadf7SArchie Cobbs */ 902901fadf7SArchie Cobbs static int 903901fadf7SArchie Cobbs ng_l2tp_recv_ctrl(node_p node, item_p item) 904901fadf7SArchie Cobbs { 905901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 906901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 907901fadf7SArchie Cobbs struct mbuf *m; 908901fadf7SArchie Cobbs int i; 909901fadf7SArchie Cobbs 910901fadf7SArchie Cobbs /* Grab mbuf and discard other stuff XXX */ 911901fadf7SArchie Cobbs NGI_GET_M(item, m); 912901fadf7SArchie Cobbs NG_FREE_ITEM(item); 913901fadf7SArchie Cobbs 914901fadf7SArchie Cobbs /* Packet should have session ID prepended */ 915901fadf7SArchie Cobbs if (m->m_pkthdr.len < 2) { 916901fadf7SArchie Cobbs priv->stats.xmitInvalid++; 917901fadf7SArchie Cobbs m_freem(m); 918901fadf7SArchie Cobbs return (EINVAL); 919901fadf7SArchie Cobbs } 920901fadf7SArchie Cobbs 921901fadf7SArchie Cobbs /* Check max length */ 922901fadf7SArchie Cobbs if (m->m_pkthdr.len >= 0x10000 - 14) { 923901fadf7SArchie Cobbs priv->stats.xmitTooBig++; 924901fadf7SArchie Cobbs m_freem(m); 925901fadf7SArchie Cobbs return (EOVERFLOW); 926901fadf7SArchie Cobbs } 927901fadf7SArchie Cobbs 928901fadf7SArchie Cobbs /* Find next empty slot in transmit queue */ 929901fadf7SArchie Cobbs for (i = 0; i < L2TP_MAX_XWIN && seq->xwin[i] != NULL; i++); 930901fadf7SArchie Cobbs if (i == L2TP_MAX_XWIN) { 931901fadf7SArchie Cobbs priv->stats.xmitDrops++; 932901fadf7SArchie Cobbs m_freem(m); 933901fadf7SArchie Cobbs return (ENOBUFS); 934901fadf7SArchie Cobbs } 935901fadf7SArchie Cobbs seq->xwin[i] = m; 936901fadf7SArchie Cobbs 937901fadf7SArchie Cobbs /* Sanity check receive ack timer state */ 938901fadf7SArchie Cobbs KASSERT((i == 0) ^ seq->rack_timer_running, 939901fadf7SArchie Cobbs ("%s: xwin %d full but rack timer %srunning", 940901fadf7SArchie Cobbs __FUNCTION__, i, seq->rack_timer_running ? "" : "not ")); 941901fadf7SArchie Cobbs 942901fadf7SArchie Cobbs /* If peer's receive window is already full, nothing else to do */ 943901fadf7SArchie Cobbs if (i >= seq->cwnd) 944901fadf7SArchie Cobbs return (0); 945901fadf7SArchie Cobbs 946901fadf7SArchie Cobbs /* Start retransmit timer if not already running */ 947901fadf7SArchie Cobbs if (!seq->rack_timer_running) { 948901fadf7SArchie Cobbs callout_reset(&seq->rack_timer, 949901fadf7SArchie Cobbs hz, ng_l2tp_seq_rack_timeout, node); 950901fadf7SArchie Cobbs seq->rack_timer_running = 1; 951901fadf7SArchie Cobbs NG_NODE_REF(node); 952901fadf7SArchie Cobbs } 953901fadf7SArchie Cobbs 954901fadf7SArchie Cobbs /* Copy packet */ 955a163d034SWarner Losh if ((m = L2TP_COPY_MBUF(seq->xwin[i], M_DONTWAIT)) == NULL) { 956901fadf7SArchie Cobbs priv->stats.memoryFailures++; 957901fadf7SArchie Cobbs return (ENOBUFS); 958901fadf7SArchie Cobbs } 959901fadf7SArchie Cobbs 960901fadf7SArchie Cobbs /* Send packet and increment xmit sequence number */ 961901fadf7SArchie Cobbs return (ng_l2tp_xmit_ctrl(priv, m, seq->ns++)); 962901fadf7SArchie Cobbs } 963901fadf7SArchie Cobbs 964901fadf7SArchie Cobbs /* 965901fadf7SArchie Cobbs * Handle an outgoing data frame. 966901fadf7SArchie Cobbs */ 967901fadf7SArchie Cobbs static int 968901fadf7SArchie Cobbs ng_l2tp_recv_data(node_p node, item_p item, hookpriv_p hpriv) 969901fadf7SArchie Cobbs { 970901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 971901fadf7SArchie Cobbs struct mbuf *m; 972901fadf7SArchie Cobbs u_int16_t hdr; 973901fadf7SArchie Cobbs int error; 974901fadf7SArchie Cobbs int i = 1; 975901fadf7SArchie Cobbs 976901fadf7SArchie Cobbs /* Get mbuf */ 977901fadf7SArchie Cobbs NGI_GET_M(item, m); 978901fadf7SArchie Cobbs 979901fadf7SArchie Cobbs /* Check max length */ 980901fadf7SArchie Cobbs if (m->m_pkthdr.len >= 0x10000 - 12) { 981901fadf7SArchie Cobbs priv->stats.xmitDataTooBig++; 982901fadf7SArchie Cobbs NG_FREE_ITEM(item); 983901fadf7SArchie Cobbs NG_FREE_M(m); 984901fadf7SArchie Cobbs return (EOVERFLOW); 985901fadf7SArchie Cobbs } 986901fadf7SArchie Cobbs 987901fadf7SArchie Cobbs /* Prepend L2TP header */ 988901fadf7SArchie Cobbs M_PREPEND(m, 6 989901fadf7SArchie Cobbs + (2 * (hpriv->conf.include_length != 0)) 990901fadf7SArchie Cobbs + (4 * (hpriv->conf.enable_dseq != 0)), 991a163d034SWarner Losh M_DONTWAIT); 992901fadf7SArchie Cobbs if (m == NULL) { 993901fadf7SArchie Cobbs priv->stats.memoryFailures++; 994901fadf7SArchie Cobbs NG_FREE_ITEM(item); 995901fadf7SArchie Cobbs return (ENOBUFS); 996901fadf7SArchie Cobbs } 997901fadf7SArchie Cobbs hdr = L2TP_DATA_HDR; 998901fadf7SArchie Cobbs if (hpriv->conf.include_length) { 999901fadf7SArchie Cobbs hdr |= L2TP_HDR_LEN; 1000901fadf7SArchie Cobbs mtod(m, u_int16_t *)[i++] = htons(m->m_pkthdr.len); 1001901fadf7SArchie Cobbs } 1002901fadf7SArchie Cobbs mtod(m, u_int16_t *)[i++] = priv->conf.peer_id; 1003901fadf7SArchie Cobbs mtod(m, u_int16_t *)[i++] = hpriv->conf.peer_id; 1004901fadf7SArchie Cobbs if (hpriv->conf.enable_dseq) { 1005901fadf7SArchie Cobbs hdr |= L2TP_HDR_SEQ; 1006901fadf7SArchie Cobbs mtod(m, u_int16_t *)[i++] = htons(hpriv->ns); 1007901fadf7SArchie Cobbs mtod(m, u_int16_t *)[i++] = htons(hpriv->nr); 1008901fadf7SArchie Cobbs hpriv->ns++; 1009901fadf7SArchie Cobbs } 1010901fadf7SArchie Cobbs mtod(m, u_int16_t *)[0] = htons(hdr); 1011901fadf7SArchie Cobbs 1012901fadf7SArchie Cobbs /* Send packet */ 1013901fadf7SArchie Cobbs NG_FWD_NEW_DATA(error, item, priv->lower, m); 1014901fadf7SArchie Cobbs return (error); 1015901fadf7SArchie Cobbs } 1016901fadf7SArchie Cobbs 1017901fadf7SArchie Cobbs /* 1018901fadf7SArchie Cobbs * Send a message to our controlling node that we've failed. 1019901fadf7SArchie Cobbs */ 1020901fadf7SArchie Cobbs static void 1021901fadf7SArchie Cobbs ng_l2tp_seq_failure(priv_p priv) 1022901fadf7SArchie Cobbs { 1023901fadf7SArchie Cobbs struct ng_mesg *msg; 1024901fadf7SArchie Cobbs int error; 1025901fadf7SArchie Cobbs 1026901fadf7SArchie Cobbs NG_MKMESSAGE(msg, NGM_L2TP_COOKIE, NGM_L2TP_ACK_FAILURE, 0, M_NOWAIT); 1027901fadf7SArchie Cobbs if (msg == NULL) 1028901fadf7SArchie Cobbs return; 1029facfd889SArchie Cobbs NG_SEND_MSG_ID(error, priv->node, msg, priv->ftarget, 0); 1030901fadf7SArchie Cobbs } 1031901fadf7SArchie Cobbs 1032901fadf7SArchie Cobbs /************************************************************************ 1033901fadf7SArchie Cobbs SEQUENCE NUMBER HANDLING 1034901fadf7SArchie Cobbs ************************************************************************/ 1035901fadf7SArchie Cobbs 1036901fadf7SArchie Cobbs /* 1037901fadf7SArchie Cobbs * Initialize sequence number state. 1038901fadf7SArchie Cobbs */ 1039901fadf7SArchie Cobbs static void 1040901fadf7SArchie Cobbs ng_l2tp_seq_init(priv_p priv) 1041901fadf7SArchie Cobbs { 1042901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1043901fadf7SArchie Cobbs 1044901fadf7SArchie Cobbs KASSERT(priv->conf.peer_win >= 1, 1045901fadf7SArchie Cobbs ("%s: peer_win is zero", __FUNCTION__)); 1046901fadf7SArchie Cobbs memset(seq, 0, sizeof(*seq)); 1047901fadf7SArchie Cobbs seq->cwnd = 1; 1048901fadf7SArchie Cobbs seq->wmax = priv->conf.peer_win; 1049901fadf7SArchie Cobbs if (seq->wmax > L2TP_MAX_XWIN) 1050901fadf7SArchie Cobbs seq->wmax = L2TP_MAX_XWIN; 1051901fadf7SArchie Cobbs seq->ssth = seq->wmax; 1052901fadf7SArchie Cobbs seq->max_rexmits = priv->conf.rexmit_max; 1053901fadf7SArchie Cobbs seq->max_rexmit_to = priv->conf.rexmit_max_to; 1054901fadf7SArchie Cobbs callout_init(&seq->rack_timer, 0); 1055901fadf7SArchie Cobbs callout_init(&seq->xack_timer, 0); 1056901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 1057901fadf7SArchie Cobbs } 1058901fadf7SArchie Cobbs 1059901fadf7SArchie Cobbs /* 1060901fadf7SArchie Cobbs * Adjust sequence number state accordingly after reconfiguration. 1061901fadf7SArchie Cobbs */ 1062901fadf7SArchie Cobbs static int 1063901fadf7SArchie Cobbs ng_l2tp_seq_adjust(priv_p priv, const struct ng_l2tp_config *conf) 1064901fadf7SArchie Cobbs { 1065901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1066901fadf7SArchie Cobbs u_int16_t new_wmax; 1067901fadf7SArchie Cobbs 1068901fadf7SArchie Cobbs /* If disabling node, reset state sequence number */ 1069901fadf7SArchie Cobbs if (!conf->enabled) { 1070901fadf7SArchie Cobbs ng_l2tp_seq_reset(priv); 1071901fadf7SArchie Cobbs return (0); 1072901fadf7SArchie Cobbs } 1073901fadf7SArchie Cobbs 1074901fadf7SArchie Cobbs /* Adjust peer's max recv window; it can only increase */ 1075901fadf7SArchie Cobbs new_wmax = conf->peer_win; 1076901fadf7SArchie Cobbs if (new_wmax > L2TP_MAX_XWIN) 1077901fadf7SArchie Cobbs new_wmax = L2TP_MAX_XWIN; 1078901fadf7SArchie Cobbs if (new_wmax == 0) 1079901fadf7SArchie Cobbs return (EINVAL); 1080901fadf7SArchie Cobbs if (new_wmax < seq->wmax) 1081901fadf7SArchie Cobbs return (EBUSY); 1082901fadf7SArchie Cobbs seq->wmax = new_wmax; 1083901fadf7SArchie Cobbs 1084901fadf7SArchie Cobbs /* Update retransmit parameters */ 1085901fadf7SArchie Cobbs seq->max_rexmits = conf->rexmit_max; 1086901fadf7SArchie Cobbs seq->max_rexmit_to = conf->rexmit_max_to; 1087901fadf7SArchie Cobbs 1088901fadf7SArchie Cobbs /* Done */ 1089901fadf7SArchie Cobbs return (0); 1090901fadf7SArchie Cobbs } 1091901fadf7SArchie Cobbs 1092901fadf7SArchie Cobbs /* 1093901fadf7SArchie Cobbs * Reset sequence number state. 1094901fadf7SArchie Cobbs */ 1095901fadf7SArchie Cobbs static void 1096901fadf7SArchie Cobbs ng_l2tp_seq_reset(priv_p priv) 1097901fadf7SArchie Cobbs { 1098901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1099901fadf7SArchie Cobbs hook_p hook; 1100901fadf7SArchie Cobbs int i; 1101901fadf7SArchie Cobbs 1102901fadf7SArchie Cobbs /* Sanity check */ 1103901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 1104901fadf7SArchie Cobbs 1105901fadf7SArchie Cobbs /* Stop timers */ 1106901fadf7SArchie Cobbs if (seq->rack_timer_running && callout_stop(&seq->rack_timer) == 1) { 1107901fadf7SArchie Cobbs seq->rack_timer_running = 0; 1108901fadf7SArchie Cobbs NG_NODE_UNREF(priv->node); 1109901fadf7SArchie Cobbs } 1110901fadf7SArchie Cobbs if (seq->xack_timer_running && callout_stop(&seq->xack_timer) == 1) { 1111901fadf7SArchie Cobbs seq->xack_timer_running = 0; 1112901fadf7SArchie Cobbs NG_NODE_UNREF(priv->node); 1113901fadf7SArchie Cobbs } 1114901fadf7SArchie Cobbs 1115901fadf7SArchie Cobbs /* Free retransmit queue */ 1116901fadf7SArchie Cobbs for (i = 0; i < L2TP_MAX_XWIN; i++) { 1117901fadf7SArchie Cobbs if (seq->xwin[i] == NULL) 1118901fadf7SArchie Cobbs break; 1119901fadf7SArchie Cobbs m_freem(seq->xwin[i]); 1120901fadf7SArchie Cobbs } 1121901fadf7SArchie Cobbs 1122901fadf7SArchie Cobbs /* Reset session hooks' sequence number states */ 1123901fadf7SArchie Cobbs NG_NODE_FOREACH_HOOK(priv->node, ng_l2tp_reset_session, NULL, hook); 1124901fadf7SArchie Cobbs 1125901fadf7SArchie Cobbs /* Reset node's sequence number state */ 1126901fadf7SArchie Cobbs memset(seq, 0, sizeof(*seq)); 1127901fadf7SArchie Cobbs seq->cwnd = 1; 1128901fadf7SArchie Cobbs seq->wmax = L2TP_MAX_XWIN; 1129901fadf7SArchie Cobbs seq->ssth = seq->wmax; 1130901fadf7SArchie Cobbs 1131901fadf7SArchie Cobbs /* Done */ 1132901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 1133901fadf7SArchie Cobbs } 1134901fadf7SArchie Cobbs 1135901fadf7SArchie Cobbs /* 1136901fadf7SArchie Cobbs * Handle receipt of an acknowledgement value (Nr) from peer. 1137901fadf7SArchie Cobbs */ 1138901fadf7SArchie Cobbs static void 1139901fadf7SArchie Cobbs ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr) 1140901fadf7SArchie Cobbs { 1141901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1142901fadf7SArchie Cobbs struct mbuf *m; 1143901fadf7SArchie Cobbs int nack; 1144901fadf7SArchie Cobbs int i; 1145901fadf7SArchie Cobbs 1146901fadf7SArchie Cobbs /* Verify peer's ACK is in range */ 1147901fadf7SArchie Cobbs if ((nack = L2TP_SEQ_DIFF(nr, seq->rack)) <= 0) 1148901fadf7SArchie Cobbs return; /* duplicate ack */ 1149901fadf7SArchie Cobbs if (L2TP_SEQ_DIFF(nr, seq->ns) > 0) { 1150901fadf7SArchie Cobbs priv->stats.recvBadAcks++; /* ack for packet not sent */ 1151901fadf7SArchie Cobbs return; 1152901fadf7SArchie Cobbs } 1153901fadf7SArchie Cobbs KASSERT(nack <= L2TP_MAX_XWIN, 1154901fadf7SArchie Cobbs ("%s: nack=%d > %d", __FUNCTION__, nack, L2TP_MAX_XWIN)); 1155901fadf7SArchie Cobbs 1156901fadf7SArchie Cobbs /* Update receive ack stats */ 1157901fadf7SArchie Cobbs seq->rack = nr; 1158901fadf7SArchie Cobbs seq->rexmits = 0; 1159901fadf7SArchie Cobbs 1160901fadf7SArchie Cobbs /* Free acknowledged packets and shift up packets in the xmit queue */ 1161901fadf7SArchie Cobbs for (i = 0; i < nack; i++) 1162901fadf7SArchie Cobbs m_freem(seq->xwin[i]); 1163901fadf7SArchie Cobbs memmove(seq->xwin, seq->xwin + nack, 1164901fadf7SArchie Cobbs (L2TP_MAX_XWIN - nack) * sizeof(*seq->xwin)); 1165901fadf7SArchie Cobbs memset(seq->xwin + (L2TP_MAX_XWIN - nack), 0, 1166901fadf7SArchie Cobbs nack * sizeof(*seq->xwin)); 1167901fadf7SArchie Cobbs 1168901fadf7SArchie Cobbs /* 1169901fadf7SArchie Cobbs * Do slow-start/congestion avoidance windowing algorithm described 1170901fadf7SArchie Cobbs * in RFC 2661, Appendix A. Here we handle a multiple ACK as if each 1171901fadf7SArchie Cobbs * ACK had arrived separately. 1172901fadf7SArchie Cobbs */ 1173901fadf7SArchie Cobbs if (seq->cwnd < seq->wmax) { 1174901fadf7SArchie Cobbs 1175901fadf7SArchie Cobbs /* Handle slow start phase */ 1176901fadf7SArchie Cobbs if (seq->cwnd < seq->ssth) { 1177901fadf7SArchie Cobbs seq->cwnd += nack; 1178901fadf7SArchie Cobbs nack = 0; 1179901fadf7SArchie Cobbs if (seq->cwnd > seq->ssth) { /* into cg.av. phase */ 1180901fadf7SArchie Cobbs nack = seq->cwnd - seq->ssth; 1181901fadf7SArchie Cobbs seq->cwnd = seq->ssth; 1182901fadf7SArchie Cobbs } 1183901fadf7SArchie Cobbs } 1184901fadf7SArchie Cobbs 1185901fadf7SArchie Cobbs /* Handle congestion avoidance phase */ 1186901fadf7SArchie Cobbs if (seq->cwnd >= seq->ssth) { 1187901fadf7SArchie Cobbs seq->acks += nack; 1188901fadf7SArchie Cobbs while (seq->acks >= seq->cwnd) { 1189901fadf7SArchie Cobbs seq->acks -= seq->cwnd; 1190901fadf7SArchie Cobbs if (seq->cwnd < seq->wmax) 1191901fadf7SArchie Cobbs seq->cwnd++; 1192901fadf7SArchie Cobbs } 1193901fadf7SArchie Cobbs } 1194901fadf7SArchie Cobbs } 1195901fadf7SArchie Cobbs 1196901fadf7SArchie Cobbs /* Stop xmit timer */ 1197901fadf7SArchie Cobbs if (seq->rack_timer_running && callout_stop(&seq->rack_timer) == 1) { 1198901fadf7SArchie Cobbs seq->rack_timer_running = 0; 1199901fadf7SArchie Cobbs NG_NODE_UNREF(priv->node); 1200901fadf7SArchie Cobbs } 1201901fadf7SArchie Cobbs 1202901fadf7SArchie Cobbs /* If transmit queue is empty, we're done for now */ 1203901fadf7SArchie Cobbs if (seq->xwin[0] == NULL) 1204901fadf7SArchie Cobbs return; 1205901fadf7SArchie Cobbs 1206901fadf7SArchie Cobbs /* Start restransmit timer again */ 1207901fadf7SArchie Cobbs callout_reset(&seq->rack_timer, 1208901fadf7SArchie Cobbs hz, ng_l2tp_seq_rack_timeout, priv->node); 1209901fadf7SArchie Cobbs seq->rack_timer_running = 1; 1210901fadf7SArchie Cobbs NG_NODE_REF(priv->node); 1211901fadf7SArchie Cobbs 1212901fadf7SArchie Cobbs /* 1213901fadf7SArchie Cobbs * Send more packets, trying to keep peer's receive window full. 1214901fadf7SArchie Cobbs * If there is a memory error, pretend packet was sent, as it 1215901fadf7SArchie Cobbs * will get retransmitted later anyway. 1216901fadf7SArchie Cobbs */ 1217901fadf7SArchie Cobbs while ((i = L2TP_SEQ_DIFF(seq->ns, seq->rack)) < seq->cwnd 1218901fadf7SArchie Cobbs && seq->xwin[i] != NULL) { 1219a163d034SWarner Losh if ((m = L2TP_COPY_MBUF(seq->xwin[i], M_DONTWAIT)) == NULL) 1220901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1221901fadf7SArchie Cobbs else 1222901fadf7SArchie Cobbs ng_l2tp_xmit_ctrl(priv, m, seq->ns); 1223901fadf7SArchie Cobbs seq->ns++; 1224901fadf7SArchie Cobbs } 1225901fadf7SArchie Cobbs } 1226901fadf7SArchie Cobbs 1227901fadf7SArchie Cobbs /* 1228901fadf7SArchie Cobbs * Handle receipt of a sequence number value (Ns) from peer. 1229901fadf7SArchie Cobbs * We make no attempt to re-order out of order packets. 1230901fadf7SArchie Cobbs * 1231901fadf7SArchie Cobbs * This function should only be called for non-ZLB packets. 1232901fadf7SArchie Cobbs * 1233901fadf7SArchie Cobbs * Returns: 1234901fadf7SArchie Cobbs * 0 Accept packet 1235901fadf7SArchie Cobbs * -1 Drop packet 1236901fadf7SArchie Cobbs */ 1237901fadf7SArchie Cobbs static int 1238901fadf7SArchie Cobbs ng_l2tp_seq_recv_ns(priv_p priv, u_int16_t ns) 1239901fadf7SArchie Cobbs { 1240901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1241901fadf7SArchie Cobbs 1242901fadf7SArchie Cobbs /* If not what we expect, drop packet and send an immediate ZLB ack */ 1243901fadf7SArchie Cobbs if (ns != seq->nr) { 1244901fadf7SArchie Cobbs if (L2TP_SEQ_DIFF(ns, seq->nr) < 0) 1245901fadf7SArchie Cobbs priv->stats.recvDuplicates++; 1246901fadf7SArchie Cobbs else 1247901fadf7SArchie Cobbs priv->stats.recvOutOfOrder++; 1248901fadf7SArchie Cobbs ng_l2tp_xmit_ctrl(priv, NULL, seq->ns); 1249901fadf7SArchie Cobbs return (-1); 1250901fadf7SArchie Cobbs } 1251901fadf7SArchie Cobbs 1252901fadf7SArchie Cobbs /* Update recv sequence number */ 1253901fadf7SArchie Cobbs seq->nr++; 1254901fadf7SArchie Cobbs 1255901fadf7SArchie Cobbs /* Start receive ack timer, if not already running */ 1256901fadf7SArchie Cobbs if (!seq->xack_timer_running) { 1257901fadf7SArchie Cobbs callout_reset(&seq->xack_timer, 1258901fadf7SArchie Cobbs L2TP_DELAYED_ACK, ng_l2tp_seq_xack_timeout, priv->node); 1259901fadf7SArchie Cobbs seq->xack_timer_running = 1; 1260901fadf7SArchie Cobbs NG_NODE_REF(priv->node); 1261901fadf7SArchie Cobbs } 1262901fadf7SArchie Cobbs 1263901fadf7SArchie Cobbs /* Accept packet */ 1264901fadf7SArchie Cobbs return (0); 1265901fadf7SArchie Cobbs } 1266901fadf7SArchie Cobbs 1267901fadf7SArchie Cobbs /* 1268901fadf7SArchie Cobbs * Handle an ack timeout. We have an outstanding ack that we 1269901fadf7SArchie Cobbs * were hoping to piggy-back, but haven't, so send a ZLB. 1270901fadf7SArchie Cobbs */ 1271901fadf7SArchie Cobbs static void 1272901fadf7SArchie Cobbs ng_l2tp_seq_xack_timeout(void *arg) 1273901fadf7SArchie Cobbs { 1274901fadf7SArchie Cobbs const node_p node = arg; 1275901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 1276901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1277901fadf7SArchie Cobbs int s; 1278901fadf7SArchie Cobbs 1279901fadf7SArchie Cobbs /* Check if node is going away */ 1280901fadf7SArchie Cobbs s = splnet(); 1281901fadf7SArchie Cobbs if (NG_NODE_NOT_VALID(node)) { 1282901fadf7SArchie Cobbs seq->xack_timer_running = 0; 1283901fadf7SArchie Cobbs if (!seq->rack_timer_running) { 1284901fadf7SArchie Cobbs FREE(priv, M_NETGRAPH_L2TP); 1285901fadf7SArchie Cobbs NG_NODE_SET_PRIVATE(node, NULL); 1286901fadf7SArchie Cobbs } 1287901fadf7SArchie Cobbs NG_NODE_UNREF(node); 1288901fadf7SArchie Cobbs splx(s); 1289901fadf7SArchie Cobbs return; 1290901fadf7SArchie Cobbs } 1291901fadf7SArchie Cobbs 1292901fadf7SArchie Cobbs /* Sanity check */ 1293901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 1294901fadf7SArchie Cobbs 1295901fadf7SArchie Cobbs /* If ack is still outstanding, send a ZLB */ 1296901fadf7SArchie Cobbs if (seq->xack != seq->nr) 1297901fadf7SArchie Cobbs ng_l2tp_xmit_ctrl(priv, NULL, seq->ns); 1298901fadf7SArchie Cobbs 1299901fadf7SArchie Cobbs /* Done */ 1300901fadf7SArchie Cobbs seq->xack_timer_running = 0; 1301901fadf7SArchie Cobbs NG_NODE_UNREF(node); 1302901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 1303901fadf7SArchie Cobbs splx(s); 1304901fadf7SArchie Cobbs } 1305901fadf7SArchie Cobbs 1306901fadf7SArchie Cobbs /* 1307901fadf7SArchie Cobbs * Handle a transmit timeout. The peer has failed to respond 1308901fadf7SArchie Cobbs * with an ack for our packet, so retransmit it. 1309901fadf7SArchie Cobbs */ 1310901fadf7SArchie Cobbs static void 1311901fadf7SArchie Cobbs ng_l2tp_seq_rack_timeout(void *arg) 1312901fadf7SArchie Cobbs { 1313901fadf7SArchie Cobbs const node_p node = arg; 1314901fadf7SArchie Cobbs const priv_p priv = NG_NODE_PRIVATE(node); 1315901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1316901fadf7SArchie Cobbs struct mbuf *m; 1317901fadf7SArchie Cobbs u_int delay; 1318901fadf7SArchie Cobbs int s; 1319901fadf7SArchie Cobbs 1320901fadf7SArchie Cobbs /* Check if node is going away */ 1321901fadf7SArchie Cobbs s = splnet(); 1322901fadf7SArchie Cobbs if (NG_NODE_NOT_VALID(node)) { 1323901fadf7SArchie Cobbs seq->rack_timer_running = 0; 1324901fadf7SArchie Cobbs if (!seq->xack_timer_running) { 1325901fadf7SArchie Cobbs FREE(priv, M_NETGRAPH_L2TP); 1326901fadf7SArchie Cobbs NG_NODE_SET_PRIVATE(node, NULL); 1327901fadf7SArchie Cobbs } 1328901fadf7SArchie Cobbs NG_NODE_UNREF(node); 1329901fadf7SArchie Cobbs splx(s); 1330901fadf7SArchie Cobbs return; 1331901fadf7SArchie Cobbs } 1332901fadf7SArchie Cobbs 1333901fadf7SArchie Cobbs /* Sanity check */ 1334901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 1335901fadf7SArchie Cobbs 1336901fadf7SArchie Cobbs /* Make sure peer's ack is still outstanding before doing anything */ 1337901fadf7SArchie Cobbs if (seq->rack == seq->ns) { 1338901fadf7SArchie Cobbs seq->rack_timer_running = 0; 1339901fadf7SArchie Cobbs NG_NODE_UNREF(node); 1340901fadf7SArchie Cobbs goto done; 1341901fadf7SArchie Cobbs } 1342901fadf7SArchie Cobbs priv->stats.xmitRetransmits++; 1343901fadf7SArchie Cobbs 1344901fadf7SArchie Cobbs /* Have we reached the retransmit limit? If so, notify owner. */ 1345901fadf7SArchie Cobbs if (seq->rexmits++ >= seq->max_rexmits) 1346901fadf7SArchie Cobbs ng_l2tp_seq_failure(priv); 1347901fadf7SArchie Cobbs 1348901fadf7SArchie Cobbs /* Restart timer, this time with an increased delay */ 1349901fadf7SArchie Cobbs delay = (seq->rexmits > 12) ? (1 << 12) : (1 << seq->rexmits); 1350901fadf7SArchie Cobbs if (delay > seq->max_rexmit_to) 1351901fadf7SArchie Cobbs delay = seq->max_rexmit_to; 1352901fadf7SArchie Cobbs callout_reset(&seq->rack_timer, 1353901fadf7SArchie Cobbs hz * delay, ng_l2tp_seq_rack_timeout, node); 1354901fadf7SArchie Cobbs 1355901fadf7SArchie Cobbs /* Do slow-start/congestion algorithm windowing algorithm */ 1356901fadf7SArchie Cobbs seq->ssth = (seq->cwnd + 1) / 2; 1357901fadf7SArchie Cobbs seq->cwnd = 1; 1358901fadf7SArchie Cobbs seq->acks = 0; 1359901fadf7SArchie Cobbs 1360901fadf7SArchie Cobbs /* Retransmit oldest unack'd packet */ 1361a163d034SWarner Losh if ((m = L2TP_COPY_MBUF(seq->xwin[0], M_DONTWAIT)) == NULL) 1362901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1363901fadf7SArchie Cobbs else 1364901fadf7SArchie Cobbs ng_l2tp_xmit_ctrl(priv, m, seq->rack); 1365901fadf7SArchie Cobbs 1366901fadf7SArchie Cobbs done: 1367901fadf7SArchie Cobbs /* Done */ 1368901fadf7SArchie Cobbs L2TP_SEQ_CHECK(seq); 1369901fadf7SArchie Cobbs splx(s); 1370901fadf7SArchie Cobbs } 1371901fadf7SArchie Cobbs 1372901fadf7SArchie Cobbs /* 1373901fadf7SArchie Cobbs * Transmit a control stream packet, payload optional. 1374901fadf7SArchie Cobbs * The transmit sequence number is not incremented. 1375901fadf7SArchie Cobbs */ 1376901fadf7SArchie Cobbs static int 1377901fadf7SArchie Cobbs ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns) 1378901fadf7SArchie Cobbs { 1379901fadf7SArchie Cobbs struct l2tp_seq *const seq = &priv->seq; 1380901fadf7SArchie Cobbs u_int16_t session_id = 0; 1381901fadf7SArchie Cobbs meta_p meta = NULL; 1382901fadf7SArchie Cobbs int error; 1383901fadf7SArchie Cobbs 1384901fadf7SArchie Cobbs /* If no mbuf passed, send an empty packet (ZLB) */ 1385901fadf7SArchie Cobbs if (m == NULL) { 1386901fadf7SArchie Cobbs 1387901fadf7SArchie Cobbs /* Create a new mbuf for ZLB packet */ 1388a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 1389901fadf7SArchie Cobbs if (m == NULL) { 1390901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1391901fadf7SArchie Cobbs return (ENOBUFS); 1392901fadf7SArchie Cobbs } 1393901fadf7SArchie Cobbs m->m_len = m->m_pkthdr.len = 12; 1394901fadf7SArchie Cobbs m->m_pkthdr.rcvif = NULL; 1395901fadf7SArchie Cobbs priv->stats.xmitZLBs++; 1396901fadf7SArchie Cobbs } else { 1397901fadf7SArchie Cobbs 1398901fadf7SArchie Cobbs /* Strip off session ID */ 1399901fadf7SArchie Cobbs if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 1400901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1401901fadf7SArchie Cobbs return (ENOBUFS); 1402901fadf7SArchie Cobbs } 1403901fadf7SArchie Cobbs memcpy(&session_id, mtod(m, u_int16_t *), 2); 1404901fadf7SArchie Cobbs m_adj(m, 2); 1405901fadf7SArchie Cobbs 1406901fadf7SArchie Cobbs /* Make room for L2TP header */ 1407a163d034SWarner Losh M_PREPEND(m, 12, M_DONTWAIT); 1408901fadf7SArchie Cobbs if (m == NULL) { 1409901fadf7SArchie Cobbs priv->stats.memoryFailures++; 1410901fadf7SArchie Cobbs return (ENOBUFS); 1411901fadf7SArchie Cobbs } 1412901fadf7SArchie Cobbs } 1413901fadf7SArchie Cobbs 1414901fadf7SArchie Cobbs /* Fill in L2TP header */ 1415901fadf7SArchie Cobbs mtod(m, u_int16_t *)[0] = htons(L2TP_CTRL_HDR); 1416901fadf7SArchie Cobbs mtod(m, u_int16_t *)[1] = htons(m->m_pkthdr.len); 1417901fadf7SArchie Cobbs mtod(m, u_int16_t *)[2] = priv->conf.peer_id; 1418901fadf7SArchie Cobbs mtod(m, u_int16_t *)[3] = session_id; 1419901fadf7SArchie Cobbs mtod(m, u_int16_t *)[4] = htons(ns); 1420901fadf7SArchie Cobbs mtod(m, u_int16_t *)[5] = htons(seq->nr); 1421901fadf7SArchie Cobbs 1422901fadf7SArchie Cobbs /* Update sequence number info and stats */ 1423901fadf7SArchie Cobbs priv->stats.xmitPackets++; 1424901fadf7SArchie Cobbs priv->stats.xmitOctets += m->m_pkthdr.len; 1425901fadf7SArchie Cobbs 1426901fadf7SArchie Cobbs /* Stop ack timer: we're sending an ack with this packet */ 1427901fadf7SArchie Cobbs if (seq->xack_timer_running && callout_stop(&seq->xack_timer) == 1) { 1428901fadf7SArchie Cobbs seq->xack_timer_running = 0; 1429901fadf7SArchie Cobbs NG_NODE_UNREF(priv->node); 1430901fadf7SArchie Cobbs } 1431901fadf7SArchie Cobbs seq->xack = seq->nr; 1432901fadf7SArchie Cobbs 1433901fadf7SArchie Cobbs /* Send packet */ 1434901fadf7SArchie Cobbs NG_SEND_DATA(error, priv->lower, m, meta); 1435901fadf7SArchie Cobbs return (error); 1436901fadf7SArchie Cobbs } 1437901fadf7SArchie Cobbs 1438901fadf7SArchie Cobbs #ifdef INVARIANTS 1439901fadf7SArchie Cobbs /* 1440901fadf7SArchie Cobbs * Sanity check sequence number state. 1441901fadf7SArchie Cobbs */ 1442901fadf7SArchie Cobbs static void 1443901fadf7SArchie Cobbs ng_l2tp_seq_check(struct l2tp_seq *seq) 1444901fadf7SArchie Cobbs { 1445901fadf7SArchie Cobbs const int self_unack = L2TP_SEQ_DIFF(seq->nr, seq->xack); 1446901fadf7SArchie Cobbs const int peer_unack = L2TP_SEQ_DIFF(seq->ns, seq->rack); 1447901fadf7SArchie Cobbs int i; 1448901fadf7SArchie Cobbs 1449901fadf7SArchie Cobbs #define CHECK(p) KASSERT((p), ("%s: not: %s", __FUNCTION__, #p)) 1450901fadf7SArchie Cobbs 1451901fadf7SArchie Cobbs CHECK(seq->wmax <= L2TP_MAX_XWIN); 1452901fadf7SArchie Cobbs CHECK(seq->cwnd >= 1); 1453901fadf7SArchie Cobbs CHECK(seq->cwnd <= seq->wmax); 1454901fadf7SArchie Cobbs CHECK(seq->ssth >= 1); 1455901fadf7SArchie Cobbs CHECK(seq->ssth <= seq->wmax); 1456901fadf7SArchie Cobbs if (seq->cwnd < seq->ssth) 1457901fadf7SArchie Cobbs CHECK(seq->acks == 0); 1458901fadf7SArchie Cobbs else 1459901fadf7SArchie Cobbs CHECK(seq->acks <= seq->cwnd); 1460901fadf7SArchie Cobbs CHECK(self_unack >= 0); 1461901fadf7SArchie Cobbs CHECK(peer_unack >= 0); 1462901fadf7SArchie Cobbs CHECK(peer_unack <= seq->wmax); 1463901fadf7SArchie Cobbs CHECK((self_unack == 0) ^ seq->xack_timer_running); 1464901fadf7SArchie Cobbs CHECK((peer_unack == 0) ^ seq->rack_timer_running); 1465901fadf7SArchie Cobbs CHECK(seq->rack_timer_running || !callout_pending(&seq->rack_timer)); 1466901fadf7SArchie Cobbs CHECK(seq->xack_timer_running || !callout_pending(&seq->xack_timer)); 1467901fadf7SArchie Cobbs for (i = 0; i < peer_unack; i++) 1468901fadf7SArchie Cobbs CHECK(seq->xwin[i] != NULL); 1469901fadf7SArchie Cobbs for ( ; i < seq->cwnd; i++) /* verify peer's recv window full */ 1470901fadf7SArchie Cobbs CHECK(seq->xwin[i] == NULL); 1471901fadf7SArchie Cobbs 1472901fadf7SArchie Cobbs #undef CHECK 1473901fadf7SArchie Cobbs } 1474901fadf7SArchie Cobbs #endif /* INVARIANTS */ 1475901fadf7SArchie Cobbs 1476901fadf7SArchie Cobbs 1477