1139823Simp/*- 2102195Sarchie * Copyright (c) 2001-2002 Packet Design, LLC. 3102195Sarchie * All rights reserved. 4102195Sarchie * 5102195Sarchie * Subject to the following obligations and disclaimer of warranty, 6102195Sarchie * use and redistribution of this software, in source or object code 7102195Sarchie * forms, with or without modifications are expressly permitted by 8102195Sarchie * Packet Design; provided, however, that: 9102195Sarchie * 10102195Sarchie * (i) Any and all reproductions of the source or object code 11102195Sarchie * must include the copyright notice above and the following 12102195Sarchie * disclaimer of warranties; and 13102195Sarchie * (ii) No rights are granted, in any manner or form, to use 14102195Sarchie * Packet Design trademarks, including the mark "PACKET DESIGN" 15102195Sarchie * on advertising, endorsements, or otherwise except as such 16102195Sarchie * appears in the above copyright notice or in the software. 17102195Sarchie * 18102195Sarchie * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND 19102195Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO 20102195Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING 21102195Sarchie * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED 22102195Sarchie * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 23102195Sarchie * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, 24102195Sarchie * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS 25102195Sarchie * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, 26102195Sarchie * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE 27102195Sarchie * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE 28102195Sarchie * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, 29102195Sarchie * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL 30102195Sarchie * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF 31102195Sarchie * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF 32102195Sarchie * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33102195Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34102195Sarchie * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF 35102195Sarchie * THE POSSIBILITY OF SUCH DAMAGE. 36102195Sarchie * 37102195Sarchie * Author: Archie Cobbs <archie@freebsd.org> 38102195Sarchie * 39102195Sarchie * $FreeBSD$ 40102195Sarchie */ 41102195Sarchie 42102195Sarchie/* 43102195Sarchie * L2TP netgraph node type. 44102195Sarchie * 45102195Sarchie * This node type implements the lower layer of the 46102195Sarchie * L2TP protocol as specified in RFC 2661. 47102195Sarchie */ 48102195Sarchie 49102195Sarchie#include <sys/param.h> 50102195Sarchie#include <sys/systm.h> 51102195Sarchie#include <sys/kernel.h> 52102195Sarchie#include <sys/time.h> 53102195Sarchie#include <sys/conf.h> 54102195Sarchie#include <sys/mbuf.h> 55102195Sarchie#include <sys/malloc.h> 56102195Sarchie#include <sys/errno.h> 57108107Sbmilekic#include <sys/libkern.h> 58102195Sarchie 59102195Sarchie#include <netgraph/ng_message.h> 60102195Sarchie#include <netgraph/netgraph.h> 61102195Sarchie#include <netgraph/ng_parse.h> 62102195Sarchie#include <netgraph/ng_l2tp.h> 63102195Sarchie 64102195Sarchie#ifdef NG_SEPARATE_MALLOC 65227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_L2TP, "netgraph_l2tp", "netgraph l2tp node"); 66102195Sarchie#else 67102195Sarchie#define M_NETGRAPH_L2TP M_NETGRAPH 68102195Sarchie#endif 69102195Sarchie 70102195Sarchie/* L2TP header format (first 2 bytes only) */ 71102195Sarchie#define L2TP_HDR_CTRL 0x8000 /* control packet */ 72102195Sarchie#define L2TP_HDR_LEN 0x4000 /* has length field */ 73102195Sarchie#define L2TP_HDR_SEQ 0x0800 /* has ns, nr fields */ 74102195Sarchie#define L2TP_HDR_OFF 0x0200 /* has offset field */ 75102195Sarchie#define L2TP_HDR_PRIO 0x0100 /* give priority */ 76102195Sarchie#define L2TP_HDR_VERS_MASK 0x000f /* version field mask */ 77102195Sarchie#define L2TP_HDR_VERSION 0x0002 /* version field */ 78102195Sarchie 79102195Sarchie/* Bits that must be zero or one in first two bytes of header */ 80102195Sarchie#define L2TP_CTRL_0BITS 0x030d /* ctrl: must be 0 */ 81102195Sarchie#define L2TP_CTRL_1BITS 0xc802 /* ctrl: must be 1 */ 82102195Sarchie#define L2TP_DATA_0BITS 0x800d /* data: must be 0 */ 83102195Sarchie#define L2TP_DATA_1BITS 0x0002 /* data: must be 1 */ 84102195Sarchie 85102195Sarchie/* Standard xmit ctrl and data header bits */ 86102195Sarchie#define L2TP_CTRL_HDR (L2TP_HDR_CTRL | L2TP_HDR_LEN \ 87102195Sarchie | L2TP_HDR_SEQ | L2TP_HDR_VERSION) 88102195Sarchie#define L2TP_DATA_HDR (L2TP_HDR_VERSION) /* optional: len, seq */ 89102195Sarchie 90102195Sarchie/* Some hard coded values */ 91174554Smav#define L2TP_MAX_XWIN 128 /* my max xmit window */ 92102195Sarchie#define L2TP_MAX_REXMIT 5 /* default max rexmit */ 93102195Sarchie#define L2TP_MAX_REXMIT_TO 30 /* default rexmit to */ 94102195Sarchie#define L2TP_DELAYED_ACK ((hz + 19) / 20) /* delayed ack: 50 ms */ 95102195Sarchie 96102195Sarchie/* Default data sequence number configuration for new sessions */ 97102195Sarchie#define L2TP_CONTROL_DSEQ 1 /* we are the lns */ 98102195Sarchie#define L2TP_ENABLE_DSEQ 1 /* enable data seq # */ 99102195Sarchie 100102195Sarchie/* Compare sequence numbers using circular math */ 101261009Sglebius#define L2TP_SEQ_DIFF(x, y) ((int16_t)((x) - (y))) 102102195Sarchie 103177279Smav#define SESSHASHSIZE 0x0020 104177279Smav#define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1)) 105177279Smav 106177279Smav/* Hook private data (data session hooks only) */ 107177279Smavstruct ng_l2tp_hook_private { 108177279Smav struct ng_l2tp_sess_config conf; /* hook/session config */ 109177279Smav struct ng_l2tp_session_stats stats; /* per sessions statistics */ 110177279Smav hook_p hook; /* hook reference */ 111177279Smav u_int16_t ns; /* data ns sequence number */ 112177279Smav u_int16_t nr; /* data nr sequence number */ 113177279Smav LIST_ENTRY(ng_l2tp_hook_private) sessions; 114177279Smav}; 115177279Smavtypedef struct ng_l2tp_hook_private *hookpriv_p; 116177279Smav 117102195Sarchie/* 118102195Sarchie * Sequence number state 119102195Sarchie * 120102195Sarchie * Invariants: 121102195Sarchie * - If cwnd < ssth, we're doing slow start, otherwise congestion avoidance 122102195Sarchie * - The number of unacknowledged xmit packets is (ns - rack) <= seq->wmax 123102195Sarchie * - The first (ns - rack) mbuf's in xwin[] array are copies of these 124102195Sarchie * unacknowledged packets; the remainder of xwin[] consists first of 125102195Sarchie * zero or more further untransmitted packets in the transmit queue 126102195Sarchie * - We try to keep the peer's receive window as full as possible. 127102195Sarchie * Therefore, (i < cwnd && xwin[i] != NULL) implies (ns - rack) > i. 128102195Sarchie * - rack_timer is running iff (ns - rack) > 0 (unack'd xmit'd pkts) 129102195Sarchie * - If xack != nr, there are unacknowledged recv packet(s) (delayed ack) 130102195Sarchie * - xack_timer is running iff xack != nr (unack'd rec'd pkts) 131102195Sarchie */ 132102195Sarchiestruct l2tp_seq { 133102195Sarchie u_int16_t ns; /* next xmit seq we send */ 134102195Sarchie u_int16_t nr; /* next recv seq we expect */ 135176962Smav u_int16_t inproc; /* packet is in processing */ 136102195Sarchie u_int16_t rack; /* last 'nr' we rec'd */ 137102195Sarchie u_int16_t xack; /* last 'nr' we sent */ 138102195Sarchie u_int16_t wmax; /* peer's max recv window */ 139102195Sarchie u_int16_t cwnd; /* current congestion window */ 140102195Sarchie u_int16_t ssth; /* slow start threshold */ 141102195Sarchie u_int16_t acks; /* # consecutive acks rec'd */ 142102195Sarchie u_int16_t rexmits; /* # retransmits sent */ 143102195Sarchie struct callout rack_timer; /* retransmit timer */ 144102195Sarchie struct callout xack_timer; /* delayed ack timer */ 145102195Sarchie struct mbuf *xwin[L2TP_MAX_XWIN]; /* transmit window */ 146172565Smav struct mtx mtx; /* seq mutex */ 147102195Sarchie}; 148102195Sarchie 149102195Sarchie/* Node private data */ 150102195Sarchiestruct ng_l2tp_private { 151102195Sarchie node_p node; /* back pointer to node */ 152102195Sarchie hook_p ctrl; /* hook to upper layers */ 153102195Sarchie hook_p lower; /* hook to lower layers */ 154102195Sarchie struct ng_l2tp_config conf; /* node configuration */ 155102195Sarchie struct ng_l2tp_stats stats; /* node statistics */ 156102195Sarchie struct l2tp_seq seq; /* ctrl sequence number state */ 157102195Sarchie ng_ID_t ftarget; /* failure message target */ 158177279Smav LIST_HEAD(, ng_l2tp_hook_private) sesshash[SESSHASHSIZE]; 159102195Sarchie}; 160102195Sarchietypedef struct ng_l2tp_private *priv_p; 161102195Sarchie 162102195Sarchie/* Netgraph node methods */ 163102195Sarchiestatic ng_constructor_t ng_l2tp_constructor; 164102195Sarchiestatic ng_rcvmsg_t ng_l2tp_rcvmsg; 165102195Sarchiestatic ng_shutdown_t ng_l2tp_shutdown; 166102195Sarchiestatic ng_newhook_t ng_l2tp_newhook; 167102195Sarchiestatic ng_rcvdata_t ng_l2tp_rcvdata; 168172563Smavstatic ng_rcvdata_t ng_l2tp_rcvdata_lower; 169172563Smavstatic ng_rcvdata_t ng_l2tp_rcvdata_ctrl; 170102195Sarchiestatic ng_disconnect_t ng_l2tp_disconnect; 171102195Sarchie 172102195Sarchie/* Internal functions */ 173102195Sarchiestatic int ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns); 174102195Sarchie 175102195Sarchiestatic void ng_l2tp_seq_init(priv_p priv); 176133058Sbzstatic int ng_l2tp_seq_set(priv_p priv, 177133058Sbz const struct ng_l2tp_seq_config *conf); 178102195Sarchiestatic int ng_l2tp_seq_adjust(priv_p priv, 179102195Sarchie const struct ng_l2tp_config *conf); 180102195Sarchiestatic void ng_l2tp_seq_reset(priv_p priv); 181102195Sarchiestatic void ng_l2tp_seq_failure(priv_p priv); 182102195Sarchiestatic void ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr); 183140064Sglebiusstatic void ng_l2tp_seq_xack_timeout(node_p node, hook_p hook, 184140064Sglebius void *arg1, int arg2); 185140064Sglebiusstatic void ng_l2tp_seq_rack_timeout(node_p node, hook_p hook, 186140064Sglebius void *arg1, int arg2); 187102195Sarchie 188177279Smavstatic hookpriv_p ng_l2tp_find_session(priv_p privp, u_int16_t sid); 189102195Sarchiestatic ng_fn_eachhook ng_l2tp_reset_session; 190102195Sarchie 191102195Sarchie#ifdef INVARIANTS 192102195Sarchiestatic void ng_l2tp_seq_check(struct l2tp_seq *seq); 193102195Sarchie#endif 194102195Sarchie 195133058Sbz/* Parse type for struct ng_l2tp_seq_config. */ 196133058Sbzstatic const struct ng_parse_struct_field 197133058Sbz ng_l2tp_seq_config_fields[] = NG_L2TP_SEQ_CONFIG_TYPE_INFO; 198133058Sbzstatic const struct ng_parse_type ng_l2tp_seq_config_type = { 199133058Sbz &ng_parse_struct_type, 200133058Sbz &ng_l2tp_seq_config_fields 201133058Sbz}; 202133058Sbz 203102195Sarchie/* Parse type for struct ng_l2tp_config */ 204102195Sarchiestatic const struct ng_parse_struct_field 205102195Sarchie ng_l2tp_config_type_fields[] = NG_L2TP_CONFIG_TYPE_INFO; 206102195Sarchiestatic const struct ng_parse_type ng_l2tp_config_type = { 207102195Sarchie &ng_parse_struct_type, 208102195Sarchie &ng_l2tp_config_type_fields, 209102195Sarchie}; 210102195Sarchie 211102195Sarchie/* Parse type for struct ng_l2tp_sess_config */ 212102195Sarchiestatic const struct ng_parse_struct_field 213102195Sarchie ng_l2tp_sess_config_type_fields[] = NG_L2TP_SESS_CONFIG_TYPE_INFO; 214102195Sarchiestatic const struct ng_parse_type ng_l2tp_sess_config_type = { 215102195Sarchie &ng_parse_struct_type, 216102195Sarchie &ng_l2tp_sess_config_type_fields, 217102195Sarchie}; 218102195Sarchie 219102195Sarchie/* Parse type for struct ng_l2tp_stats */ 220102195Sarchiestatic const struct ng_parse_struct_field 221102195Sarchie ng_l2tp_stats_type_fields[] = NG_L2TP_STATS_TYPE_INFO; 222127866Sarchiestatic const struct ng_parse_type ng_l2tp_stats_type = { 223102195Sarchie &ng_parse_struct_type, 224102195Sarchie &ng_l2tp_stats_type_fields 225102195Sarchie}; 226102195Sarchie 227133060Sbz/* Parse type for struct ng_l2tp_session_stats. */ 228133060Sbzstatic const struct ng_parse_struct_field 229133060Sbz ng_l2tp_session_stats_type_fields[] = NG_L2TP_SESSION_STATS_TYPE_INFO; 230133060Sbzstatic const struct ng_parse_type ng_l2tp_session_stats_type = { 231133060Sbz &ng_parse_struct_type, 232133060Sbz &ng_l2tp_session_stats_type_fields 233133060Sbz}; 234133060Sbz 235102195Sarchie/* List of commands and how to convert arguments to/from ASCII */ 236102195Sarchiestatic const struct ng_cmdlist ng_l2tp_cmdlist[] = { 237102195Sarchie { 238102195Sarchie NGM_L2TP_COOKIE, 239102195Sarchie NGM_L2TP_SET_CONFIG, 240102195Sarchie "setconfig", 241102195Sarchie &ng_l2tp_config_type, 242102195Sarchie NULL 243102195Sarchie }, 244102195Sarchie { 245102195Sarchie NGM_L2TP_COOKIE, 246102195Sarchie NGM_L2TP_GET_CONFIG, 247102195Sarchie "getconfig", 248102195Sarchie NULL, 249102195Sarchie &ng_l2tp_config_type 250102195Sarchie }, 251102195Sarchie { 252102195Sarchie NGM_L2TP_COOKIE, 253102195Sarchie NGM_L2TP_SET_SESS_CONFIG, 254102195Sarchie "setsessconfig", 255102195Sarchie &ng_l2tp_sess_config_type, 256102195Sarchie NULL 257102195Sarchie }, 258102195Sarchie { 259102195Sarchie NGM_L2TP_COOKIE, 260102195Sarchie NGM_L2TP_GET_SESS_CONFIG, 261102195Sarchie "getsessconfig", 262102195Sarchie &ng_parse_hint16_type, 263102195Sarchie &ng_l2tp_sess_config_type 264102195Sarchie }, 265102195Sarchie { 266102195Sarchie NGM_L2TP_COOKIE, 267102195Sarchie NGM_L2TP_GET_STATS, 268102195Sarchie "getstats", 269102195Sarchie NULL, 270127866Sarchie &ng_l2tp_stats_type 271102195Sarchie }, 272102195Sarchie { 273102195Sarchie NGM_L2TP_COOKIE, 274102195Sarchie NGM_L2TP_CLR_STATS, 275102195Sarchie "clrstats", 276102195Sarchie NULL, 277102195Sarchie NULL 278102195Sarchie }, 279102195Sarchie { 280102195Sarchie NGM_L2TP_COOKIE, 281102195Sarchie NGM_L2TP_GETCLR_STATS, 282102195Sarchie "getclrstats", 283102195Sarchie NULL, 284127866Sarchie &ng_l2tp_stats_type 285102195Sarchie }, 286102195Sarchie { 287102195Sarchie NGM_L2TP_COOKIE, 288133060Sbz NGM_L2TP_GET_SESSION_STATS, 289133060Sbz "getsessstats", 290133060Sbz &ng_parse_int16_type, 291133060Sbz &ng_l2tp_session_stats_type 292133060Sbz }, 293133060Sbz { 294133060Sbz NGM_L2TP_COOKIE, 295133060Sbz NGM_L2TP_CLR_SESSION_STATS, 296133060Sbz "clrsessstats", 297133060Sbz &ng_parse_int16_type, 298133060Sbz NULL 299133060Sbz }, 300133060Sbz { 301133060Sbz NGM_L2TP_COOKIE, 302133060Sbz NGM_L2TP_GETCLR_SESSION_STATS, 303133060Sbz "getclrsessstats", 304133060Sbz &ng_parse_int16_type, 305133060Sbz &ng_l2tp_session_stats_type 306133060Sbz }, 307133060Sbz { 308133060Sbz NGM_L2TP_COOKIE, 309102195Sarchie NGM_L2TP_ACK_FAILURE, 310102195Sarchie "ackfailure", 311102195Sarchie NULL, 312102195Sarchie NULL 313102195Sarchie }, 314133058Sbz { 315133058Sbz NGM_L2TP_COOKIE, 316133058Sbz NGM_L2TP_SET_SEQ, 317133058Sbz "setsequence", 318133058Sbz &ng_l2tp_seq_config_type, 319133058Sbz NULL 320133058Sbz }, 321102195Sarchie { 0 } 322102195Sarchie}; 323102195Sarchie 324102195Sarchie/* Node type descriptor */ 325102195Sarchiestatic struct ng_type ng_l2tp_typestruct = { 326129823Sjulian .version = NG_ABI_VERSION, 327129823Sjulian .name = NG_L2TP_NODE_TYPE, 328129823Sjulian .constructor = ng_l2tp_constructor, 329129823Sjulian .rcvmsg = ng_l2tp_rcvmsg, 330129823Sjulian .shutdown = ng_l2tp_shutdown, 331129823Sjulian .newhook = ng_l2tp_newhook, 332129823Sjulian .rcvdata = ng_l2tp_rcvdata, 333129823Sjulian .disconnect = ng_l2tp_disconnect, 334129823Sjulian .cmdlist = ng_l2tp_cmdlist, 335102195Sarchie}; 336102195SarchieNETGRAPH_INIT(l2tp, &ng_l2tp_typestruct); 337102195Sarchie 338102195Sarchie/* Sequence number state sanity checking */ 339102195Sarchie#ifdef INVARIANTS 340102195Sarchie#define L2TP_SEQ_CHECK(seq) ng_l2tp_seq_check(seq) 341102195Sarchie#else 342102195Sarchie#define L2TP_SEQ_CHECK(x) do { } while (0) 343102195Sarchie#endif 344102195Sarchie 345102195Sarchie/* Whether to use m_copypacket() or m_dup() */ 346102195Sarchie#define L2TP_COPY_MBUF m_copypacket 347102195Sarchie 348172563Smav#define ERROUT(x) do { error = (x); goto done; } while (0) 349172563Smav 350102195Sarchie/************************************************************************ 351102195Sarchie NETGRAPH NODE STUFF 352102195Sarchie************************************************************************/ 353102195Sarchie 354102195Sarchie/* 355102195Sarchie * Node type constructor 356102195Sarchie */ 357102195Sarchiestatic int 358102195Sarchieng_l2tp_constructor(node_p node) 359102195Sarchie{ 360102195Sarchie priv_p priv; 361177279Smav int i; 362102195Sarchie 363102195Sarchie /* Allocate private structure */ 364220768Sglebius priv = malloc(sizeof(*priv), M_NETGRAPH_L2TP, M_WAITOK | M_ZERO); 365102195Sarchie NG_NODE_SET_PRIVATE(node, priv); 366102195Sarchie priv->node = node; 367102195Sarchie 368102195Sarchie /* Apply a semi-reasonable default configuration */ 369102195Sarchie priv->conf.peer_win = 1; 370102195Sarchie priv->conf.rexmit_max = L2TP_MAX_REXMIT; 371102195Sarchie priv->conf.rexmit_max_to = L2TP_MAX_REXMIT_TO; 372102195Sarchie 373102195Sarchie /* Initialize sequence number state */ 374102195Sarchie ng_l2tp_seq_init(priv); 375102195Sarchie 376177279Smav for (i = 0; i < SESSHASHSIZE; i++) 377177279Smav LIST_INIT(&priv->sesshash[i]); 378177279Smav 379102195Sarchie /* Done */ 380102195Sarchie return (0); 381102195Sarchie} 382102195Sarchie 383102195Sarchie/* 384102195Sarchie * Give our OK for a hook to be added. 385102195Sarchie */ 386102195Sarchiestatic int 387102195Sarchieng_l2tp_newhook(node_p node, hook_p hook, const char *name) 388102195Sarchie{ 389102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 390102195Sarchie 391102195Sarchie /* Check hook name */ 392102195Sarchie if (strcmp(name, NG_L2TP_HOOK_CTRL) == 0) { 393102195Sarchie if (priv->ctrl != NULL) 394102195Sarchie return (EISCONN); 395102195Sarchie priv->ctrl = hook; 396172563Smav NG_HOOK_SET_RCVDATA(hook, ng_l2tp_rcvdata_ctrl); 397102195Sarchie } else if (strcmp(name, NG_L2TP_HOOK_LOWER) == 0) { 398102195Sarchie if (priv->lower != NULL) 399102195Sarchie return (EISCONN); 400102195Sarchie priv->lower = hook; 401172563Smav NG_HOOK_SET_RCVDATA(hook, ng_l2tp_rcvdata_lower); 402102195Sarchie } else { 403102195Sarchie static const char hexdig[16] = "0123456789abcdef"; 404102195Sarchie u_int16_t session_id; 405102195Sarchie hookpriv_p hpriv; 406177279Smav uint16_t hash; 407102195Sarchie const char *hex; 408102195Sarchie int i; 409102195Sarchie int j; 410102195Sarchie 411102195Sarchie /* Parse hook name to get session ID */ 412102195Sarchie if (strncmp(name, NG_L2TP_HOOK_SESSION_P, 413102195Sarchie sizeof(NG_L2TP_HOOK_SESSION_P) - 1) != 0) 414102195Sarchie return (EINVAL); 415102195Sarchie hex = name + sizeof(NG_L2TP_HOOK_SESSION_P) - 1; 416102195Sarchie for (session_id = i = 0; i < 4; i++) { 417102195Sarchie for (j = 0; j < 16 && hex[i] != hexdig[j]; j++); 418102195Sarchie if (j == 16) 419102195Sarchie return (EINVAL); 420102195Sarchie session_id = (session_id << 4) | j; 421102195Sarchie } 422102195Sarchie if (hex[i] != '\0') 423102195Sarchie return (EINVAL); 424102195Sarchie 425102195Sarchie /* Create hook private structure */ 426184214Sdes hpriv = malloc(sizeof(*hpriv), 427184214Sdes M_NETGRAPH_L2TP, M_NOWAIT | M_ZERO); 428102195Sarchie if (hpriv == NULL) 429102195Sarchie return (ENOMEM); 430180943Smav hpriv->conf.session_id = session_id; 431102195Sarchie hpriv->conf.control_dseq = L2TP_CONTROL_DSEQ; 432102195Sarchie hpriv->conf.enable_dseq = L2TP_ENABLE_DSEQ; 433177279Smav hpriv->hook = hook; 434102195Sarchie NG_HOOK_SET_PRIVATE(hook, hpriv); 435177279Smav hash = SESSHASH(hpriv->conf.session_id); 436177279Smav LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions); 437102195Sarchie } 438102195Sarchie 439102195Sarchie /* Done */ 440102195Sarchie return (0); 441102195Sarchie} 442102195Sarchie 443102195Sarchie/* 444102195Sarchie * Receive a control message. 445102195Sarchie */ 446102195Sarchiestatic int 447102195Sarchieng_l2tp_rcvmsg(node_p node, item_p item, hook_p lasthook) 448102195Sarchie{ 449102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 450102195Sarchie struct ng_mesg *resp = NULL; 451102195Sarchie struct ng_mesg *msg; 452102195Sarchie int error = 0; 453102195Sarchie 454102195Sarchie NGI_GET_MSG(item, msg); 455102195Sarchie switch (msg->header.typecookie) { 456102195Sarchie case NGM_L2TP_COOKIE: 457102195Sarchie switch (msg->header.cmd) { 458102195Sarchie case NGM_L2TP_SET_CONFIG: 459102195Sarchie { 460102195Sarchie struct ng_l2tp_config *const conf = 461102195Sarchie (struct ng_l2tp_config *)msg->data; 462102195Sarchie 463102195Sarchie /* Check for invalid or illegal config */ 464102195Sarchie if (msg->header.arglen != sizeof(*conf)) { 465102195Sarchie error = EINVAL; 466102195Sarchie break; 467102195Sarchie } 468102195Sarchie conf->enabled = !!conf->enabled; 469102195Sarchie conf->match_id = !!conf->match_id; 470102195Sarchie if (priv->conf.enabled 471102195Sarchie && ((priv->conf.tunnel_id != 0 472102195Sarchie && conf->tunnel_id != priv->conf.tunnel_id) 473102195Sarchie || ((priv->conf.peer_id != 0 474102195Sarchie && conf->peer_id != priv->conf.peer_id)))) { 475102195Sarchie error = EBUSY; 476102195Sarchie break; 477102195Sarchie } 478102195Sarchie 479102195Sarchie /* Save calling node as failure target */ 480102195Sarchie priv->ftarget = NGI_RETADDR(item); 481102195Sarchie 482102195Sarchie /* Adjust sequence number state */ 483102195Sarchie if ((error = ng_l2tp_seq_adjust(priv, conf)) != 0) 484102195Sarchie break; 485102195Sarchie 486102195Sarchie /* Update node's config */ 487102195Sarchie priv->conf = *conf; 488102195Sarchie break; 489102195Sarchie } 490102195Sarchie case NGM_L2TP_GET_CONFIG: 491102195Sarchie { 492102195Sarchie struct ng_l2tp_config *conf; 493102195Sarchie 494102195Sarchie NG_MKRESPONSE(resp, msg, sizeof(*conf), M_NOWAIT); 495102195Sarchie if (resp == NULL) { 496102195Sarchie error = ENOMEM; 497102195Sarchie break; 498102195Sarchie } 499102195Sarchie conf = (struct ng_l2tp_config *)resp->data; 500102195Sarchie *conf = priv->conf; 501102195Sarchie break; 502102195Sarchie } 503102195Sarchie case NGM_L2TP_SET_SESS_CONFIG: 504102195Sarchie { 505102195Sarchie struct ng_l2tp_sess_config *const conf = 506102195Sarchie (struct ng_l2tp_sess_config *)msg->data; 507102195Sarchie hookpriv_p hpriv; 508102195Sarchie 509133058Sbz /* Check for invalid or illegal config. */ 510102195Sarchie if (msg->header.arglen != sizeof(*conf)) { 511102195Sarchie error = EINVAL; 512102195Sarchie break; 513102195Sarchie } 514102195Sarchie 515102195Sarchie /* Find matching hook */ 516177279Smav hpriv = ng_l2tp_find_session(priv, conf->session_id); 517177279Smav if (hpriv == NULL) { 518102195Sarchie error = ENOENT; 519102195Sarchie break; 520102195Sarchie } 521102195Sarchie 522102195Sarchie /* Update hook's config */ 523102195Sarchie hpriv->conf = *conf; 524102195Sarchie break; 525102195Sarchie } 526102195Sarchie case NGM_L2TP_GET_SESS_CONFIG: 527102195Sarchie { 528102195Sarchie struct ng_l2tp_sess_config *conf; 529102195Sarchie u_int16_t session_id; 530102195Sarchie hookpriv_p hpriv; 531102195Sarchie 532102195Sarchie /* Get session ID */ 533102195Sarchie if (msg->header.arglen != sizeof(session_id)) { 534102195Sarchie error = EINVAL; 535102195Sarchie break; 536102195Sarchie } 537102195Sarchie memcpy(&session_id, msg->data, 2); 538102195Sarchie 539102195Sarchie /* Find matching hook */ 540177279Smav hpriv = ng_l2tp_find_session(priv, session_id); 541177279Smav if (hpriv == NULL) { 542102195Sarchie error = ENOENT; 543102195Sarchie break; 544102195Sarchie } 545102195Sarchie 546102195Sarchie /* Send response */ 547102195Sarchie NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT); 548102195Sarchie if (resp == NULL) { 549102195Sarchie error = ENOMEM; 550102195Sarchie break; 551102195Sarchie } 552102195Sarchie conf = (struct ng_l2tp_sess_config *)resp->data; 553102195Sarchie *conf = hpriv->conf; 554102195Sarchie break; 555102195Sarchie } 556102195Sarchie case NGM_L2TP_GET_STATS: 557102195Sarchie case NGM_L2TP_CLR_STATS: 558102195Sarchie case NGM_L2TP_GETCLR_STATS: 559102195Sarchie { 560102195Sarchie if (msg->header.cmd != NGM_L2TP_CLR_STATS) { 561102195Sarchie NG_MKRESPONSE(resp, msg, 562102195Sarchie sizeof(priv->stats), M_NOWAIT); 563102195Sarchie if (resp == NULL) { 564102195Sarchie error = ENOMEM; 565102195Sarchie break; 566102195Sarchie } 567102195Sarchie memcpy(resp->data, 568102195Sarchie &priv->stats, sizeof(priv->stats)); 569102195Sarchie } 570102195Sarchie if (msg->header.cmd != NGM_L2TP_GET_STATS) 571102195Sarchie memset(&priv->stats, 0, sizeof(priv->stats)); 572102195Sarchie break; 573102195Sarchie } 574133060Sbz case NGM_L2TP_GET_SESSION_STATS: 575133060Sbz case NGM_L2TP_CLR_SESSION_STATS: 576133060Sbz case NGM_L2TP_GETCLR_SESSION_STATS: 577133060Sbz { 578133060Sbz uint16_t session_id; 579133060Sbz hookpriv_p hpriv; 580133060Sbz 581133060Sbz /* Get session ID. */ 582133060Sbz if (msg->header.arglen != sizeof(session_id)) { 583133060Sbz error = EINVAL; 584133060Sbz break; 585133060Sbz } 586133060Sbz bcopy(msg->data, &session_id, sizeof(uint16_t)); 587133060Sbz 588133060Sbz /* Find matching hook. */ 589177279Smav hpriv = ng_l2tp_find_session(priv, session_id); 590177279Smav if (hpriv == NULL) { 591133060Sbz error = ENOENT; 592133060Sbz break; 593133060Sbz } 594133060Sbz 595133060Sbz if (msg->header.cmd != NGM_L2TP_CLR_SESSION_STATS) { 596133060Sbz NG_MKRESPONSE(resp, msg, 597133060Sbz sizeof(hpriv->stats), M_NOWAIT); 598133060Sbz if (resp == NULL) { 599133060Sbz error = ENOMEM; 600133060Sbz break; 601133060Sbz } 602133060Sbz bcopy(&hpriv->stats, resp->data, 603133060Sbz sizeof(hpriv->stats)); 604133060Sbz } 605133060Sbz if (msg->header.cmd != NGM_L2TP_GET_SESSION_STATS) 606133060Sbz bzero(&hpriv->stats, sizeof(hpriv->stats)); 607133060Sbz break; 608133060Sbz } 609133058Sbz case NGM_L2TP_SET_SEQ: 610133058Sbz { 611133058Sbz struct ng_l2tp_seq_config *const conf = 612133058Sbz (struct ng_l2tp_seq_config *)msg->data; 613133058Sbz 614133058Sbz /* Check for invalid or illegal seq config. */ 615133058Sbz if (msg->header.arglen != sizeof(*conf)) { 616133058Sbz error = EINVAL; 617133058Sbz break; 618133058Sbz } 619133058Sbz conf->ns = htons(conf->ns); 620133058Sbz conf->nr = htons(conf->nr); 621133058Sbz conf->rack = htons(conf->rack); 622133058Sbz conf->xack = htons(conf->xack); 623133058Sbz 624133058Sbz /* Set sequence numbers. */ 625133058Sbz error = ng_l2tp_seq_set(priv, conf); 626133058Sbz break; 627133058Sbz } 628102195Sarchie default: 629102195Sarchie error = EINVAL; 630102195Sarchie break; 631102195Sarchie } 632102195Sarchie break; 633102195Sarchie default: 634102195Sarchie error = EINVAL; 635102195Sarchie break; 636102195Sarchie } 637102195Sarchie 638102195Sarchie /* Done */ 639102195Sarchie NG_RESPOND_MSG(error, node, item, resp); 640102195Sarchie NG_FREE_MSG(msg); 641102195Sarchie return (error); 642102195Sarchie} 643102195Sarchie 644102195Sarchie/* 645102195Sarchie * Destroy node 646102195Sarchie */ 647102195Sarchiestatic int 648102195Sarchieng_l2tp_shutdown(node_p node) 649102195Sarchie{ 650102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 651102195Sarchie struct l2tp_seq *const seq = &priv->seq; 652102195Sarchie 653102195Sarchie /* Sanity check */ 654102195Sarchie L2TP_SEQ_CHECK(seq); 655102195Sarchie 656102195Sarchie /* Reset sequence number state */ 657102195Sarchie ng_l2tp_seq_reset(priv); 658102195Sarchie 659102195Sarchie /* Free private data if neither timer is running */ 660140064Sglebius ng_uncallout(&seq->rack_timer, node); 661140064Sglebius ng_uncallout(&seq->xack_timer, node); 662102195Sarchie 663172565Smav mtx_destroy(&seq->mtx); 664172565Smav 665184205Sdes free(priv, M_NETGRAPH_L2TP); 666140064Sglebius 667102195Sarchie /* Unref node */ 668102195Sarchie NG_NODE_UNREF(node); 669102195Sarchie return (0); 670102195Sarchie} 671102195Sarchie 672102195Sarchie/* 673102195Sarchie * Hook disconnection 674102195Sarchie */ 675102195Sarchiestatic int 676102195Sarchieng_l2tp_disconnect(hook_p hook) 677102195Sarchie{ 678102195Sarchie const node_p node = NG_HOOK_NODE(hook); 679102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 680102195Sarchie 681102195Sarchie /* Zero out hook pointer */ 682102195Sarchie if (hook == priv->ctrl) 683102195Sarchie priv->ctrl = NULL; 684102195Sarchie else if (hook == priv->lower) 685102195Sarchie priv->lower = NULL; 686102195Sarchie else { 687177279Smav const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 688177279Smav LIST_REMOVE(hpriv, sessions); 689184205Sdes free(hpriv, M_NETGRAPH_L2TP); 690102195Sarchie NG_HOOK_SET_PRIVATE(hook, NULL); 691102195Sarchie } 692102195Sarchie 693102195Sarchie /* Go away if no longer connected to anything */ 694102195Sarchie if (NG_NODE_NUMHOOKS(node) == 0 && NG_NODE_IS_VALID(node)) 695102195Sarchie ng_rmnode_self(node); 696102195Sarchie return (0); 697102195Sarchie} 698102195Sarchie 699102195Sarchie/************************************************************************* 700102195Sarchie INTERNAL FUNCTIONS 701102195Sarchie*************************************************************************/ 702102195Sarchie 703102195Sarchie/* 704180943Smav * Find the hook with a given session ID. 705102195Sarchie */ 706177279Smavstatic hookpriv_p 707177279Smavng_l2tp_find_session(priv_p privp, u_int16_t sid) 708102195Sarchie{ 709177279Smav uint16_t hash = SESSHASH(sid); 710177279Smav hookpriv_p hpriv = NULL; 711102195Sarchie 712177279Smav LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) { 713177279Smav if (hpriv->conf.session_id == sid) 714177279Smav break; 715177279Smav } 716177279Smav 717177279Smav return (hpriv); 718102195Sarchie} 719102195Sarchie 720102195Sarchie/* 721102195Sarchie * Reset a hook's session state. 722102195Sarchie */ 723102195Sarchiestatic int 724102195Sarchieng_l2tp_reset_session(hook_p hook, void *arg) 725102195Sarchie{ 726102195Sarchie const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 727102195Sarchie 728102195Sarchie if (hpriv != NULL) { 729102195Sarchie hpriv->conf.control_dseq = 0; 730102195Sarchie hpriv->conf.enable_dseq = 0; 731185182Smav bzero(&hpriv->stats, sizeof(struct ng_l2tp_session_stats)); 732102195Sarchie hpriv->nr = 0; 733102195Sarchie hpriv->ns = 0; 734102195Sarchie } 735102195Sarchie return (-1); 736102195Sarchie} 737102195Sarchie 738102195Sarchie/* 739102195Sarchie * Handle an incoming frame from below. 740102195Sarchie */ 741102195Sarchiestatic int 742172563Smavng_l2tp_rcvdata_lower(hook_p h, item_p item) 743102195Sarchie{ 744102195Sarchie static const u_int16_t req_bits[2][2] = { 745102195Sarchie { L2TP_DATA_0BITS, L2TP_DATA_1BITS }, 746102195Sarchie { L2TP_CTRL_0BITS, L2TP_CTRL_1BITS }, 747102195Sarchie }; 748172563Smav const node_p node = NG_HOOK_NODE(h); 749102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 750102195Sarchie hookpriv_p hpriv = NULL; 751102195Sarchie hook_p hook = NULL; 752102195Sarchie struct mbuf *m; 753180943Smav u_int16_t tid, sid; 754102195Sarchie u_int16_t hdr; 755180943Smav u_int16_t ns, nr; 756102195Sarchie int is_ctrl; 757102195Sarchie int error; 758133060Sbz int len, plen; 759102195Sarchie 760172563Smav /* Sanity check */ 761172563Smav L2TP_SEQ_CHECK(&priv->seq); 762172563Smav 763172563Smav /* If not configured, reject */ 764172563Smav if (!priv->conf.enabled) { 765172563Smav NG_FREE_ITEM(item); 766172563Smav ERROUT(ENXIO); 767172563Smav } 768172563Smav 769102195Sarchie /* Grab mbuf */ 770102195Sarchie NGI_GET_M(item, m); 771102195Sarchie 772133060Sbz /* Remember full packet length; needed for per session accounting. */ 773133060Sbz plen = m->m_pkthdr.len; 774133060Sbz 775102195Sarchie /* Update stats */ 776102195Sarchie priv->stats.recvPackets++; 777133060Sbz priv->stats.recvOctets += plen; 778102195Sarchie 779102195Sarchie /* Get initial header */ 780102195Sarchie if (m->m_pkthdr.len < 6) { 781102195Sarchie priv->stats.recvRunts++; 782102195Sarchie NG_FREE_ITEM(item); 783102195Sarchie NG_FREE_M(m); 784172563Smav ERROUT(EINVAL); 785102195Sarchie } 786102195Sarchie if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 787102195Sarchie priv->stats.memoryFailures++; 788102195Sarchie NG_FREE_ITEM(item); 789172563Smav ERROUT(EINVAL); 790102195Sarchie } 791206015Smav hdr = (mtod(m, uint8_t *)[0] << 8) + mtod(m, uint8_t *)[1]; 792102195Sarchie m_adj(m, 2); 793102195Sarchie 794102195Sarchie /* Check required header bits and minimum length */ 795102195Sarchie is_ctrl = (hdr & L2TP_HDR_CTRL) != 0; 796102195Sarchie if ((hdr & req_bits[is_ctrl][0]) != 0 797102195Sarchie || (~hdr & req_bits[is_ctrl][1]) != 0) { 798102195Sarchie priv->stats.recvInvalid++; 799102195Sarchie NG_FREE_ITEM(item); 800102195Sarchie NG_FREE_M(m); 801172563Smav ERROUT(EINVAL); 802102195Sarchie } 803102195Sarchie if (m->m_pkthdr.len < 4 /* tunnel, session id */ 804102195Sarchie + (2 * ((hdr & L2TP_HDR_LEN) != 0)) /* length field */ 805102195Sarchie + (4 * ((hdr & L2TP_HDR_SEQ) != 0)) /* seq # fields */ 806102195Sarchie + (2 * ((hdr & L2TP_HDR_OFF) != 0))) { /* offset field */ 807102195Sarchie priv->stats.recvRunts++; 808102195Sarchie NG_FREE_ITEM(item); 809102195Sarchie NG_FREE_M(m); 810172563Smav ERROUT(EINVAL); 811102195Sarchie } 812102195Sarchie 813102195Sarchie /* Get and validate length field if present */ 814102195Sarchie if ((hdr & L2TP_HDR_LEN) != 0) { 815102195Sarchie if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 816102195Sarchie priv->stats.memoryFailures++; 817102195Sarchie NG_FREE_ITEM(item); 818172563Smav ERROUT(EINVAL); 819102195Sarchie } 820206015Smav len = (mtod(m, uint8_t *)[0] << 8) + mtod(m, uint8_t *)[1] - 4; 821102195Sarchie m_adj(m, 2); 822102195Sarchie if (len < 0 || len > m->m_pkthdr.len) { 823102195Sarchie priv->stats.recvInvalid++; 824102195Sarchie NG_FREE_ITEM(item); 825102195Sarchie NG_FREE_M(m); 826172563Smav ERROUT(EINVAL); 827102195Sarchie } 828102195Sarchie if (len < m->m_pkthdr.len) /* trim extra bytes */ 829102195Sarchie m_adj(m, -(m->m_pkthdr.len - len)); 830102195Sarchie } 831102195Sarchie 832102195Sarchie /* Get tunnel ID and session ID */ 833102195Sarchie if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { 834102195Sarchie priv->stats.memoryFailures++; 835102195Sarchie NG_FREE_ITEM(item); 836172563Smav ERROUT(EINVAL); 837102195Sarchie } 838180943Smav tid = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 839180943Smav sid = (mtod(m, u_int8_t *)[2] << 8) + mtod(m, u_int8_t *)[3]; 840102195Sarchie m_adj(m, 4); 841102195Sarchie 842102195Sarchie /* Check tunnel ID */ 843180943Smav if (tid != priv->conf.tunnel_id && 844180943Smav (priv->conf.match_id || tid != 0)) { 845102195Sarchie priv->stats.recvWrongTunnel++; 846102195Sarchie NG_FREE_ITEM(item); 847102195Sarchie NG_FREE_M(m); 848172563Smav ERROUT(EADDRNOTAVAIL); 849102195Sarchie } 850102195Sarchie 851102195Sarchie /* Check session ID (for data packets only) */ 852102195Sarchie if ((hdr & L2TP_HDR_CTRL) == 0) { 853180943Smav hpriv = ng_l2tp_find_session(priv, sid); 854177279Smav if (hpriv == NULL) { 855102195Sarchie priv->stats.recvUnknownSID++; 856102195Sarchie NG_FREE_ITEM(item); 857102195Sarchie NG_FREE_M(m); 858172563Smav ERROUT(ENOTCONN); 859102195Sarchie } 860177279Smav hook = hpriv->hook; 861102195Sarchie } 862102195Sarchie 863102195Sarchie /* Get Ns, Nr fields if present */ 864102195Sarchie if ((hdr & L2TP_HDR_SEQ) != 0) { 865102195Sarchie if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { 866102195Sarchie priv->stats.memoryFailures++; 867102195Sarchie NG_FREE_ITEM(item); 868172563Smav ERROUT(EINVAL); 869102195Sarchie } 870180943Smav ns = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 871180943Smav nr = (mtod(m, u_int8_t *)[2] << 8) + mtod(m, u_int8_t *)[3]; 872102195Sarchie m_adj(m, 4); 873180943Smav } else 874180943Smav ns = nr = 0; 875102195Sarchie 876102195Sarchie /* Strip offset padding if present */ 877102195Sarchie if ((hdr & L2TP_HDR_OFF) != 0) { 878102195Sarchie u_int16_t offset; 879102195Sarchie 880102195Sarchie /* Get length of offset padding */ 881102195Sarchie if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 882102195Sarchie priv->stats.memoryFailures++; 883102195Sarchie NG_FREE_ITEM(item); 884172563Smav ERROUT(EINVAL); 885102195Sarchie } 886180943Smav offset = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 887102195Sarchie 888102195Sarchie /* Trim offset padding */ 889133056Sbz if ((2+offset) > m->m_pkthdr.len) { 890102195Sarchie priv->stats.recvInvalid++; 891102195Sarchie NG_FREE_ITEM(item); 892102195Sarchie NG_FREE_M(m); 893172563Smav ERROUT(EINVAL); 894102195Sarchie } 895133056Sbz m_adj(m, 2+offset); 896102195Sarchie } 897102195Sarchie 898102195Sarchie /* Handle control packets */ 899102195Sarchie if ((hdr & L2TP_HDR_CTRL) != 0) { 900176962Smav struct l2tp_seq *const seq = &priv->seq; 901102195Sarchie 902102195Sarchie /* Handle receive ack sequence number Nr */ 903102195Sarchie ng_l2tp_seq_recv_nr(priv, nr); 904102195Sarchie 905102195Sarchie /* Discard ZLB packets */ 906102195Sarchie if (m->m_pkthdr.len == 0) { 907102195Sarchie priv->stats.recvZLBs++; 908102195Sarchie NG_FREE_ITEM(item); 909102195Sarchie NG_FREE_M(m); 910172563Smav ERROUT(0); 911102195Sarchie } 912102195Sarchie 913176962Smav mtx_lock(&seq->mtx); 914102195Sarchie /* 915176962Smav * If not what we expect or we are busy, drop packet and 916176962Smav * send an immediate ZLB ack. 917102195Sarchie */ 918176962Smav if (ns != seq->nr || seq->inproc) { 919176962Smav if (L2TP_SEQ_DIFF(ns, seq->nr) <= 0) 920176962Smav priv->stats.recvDuplicates++; 921176962Smav else 922176962Smav priv->stats.recvOutOfOrder++; 923176962Smav mtx_unlock(&seq->mtx); 924176962Smav ng_l2tp_xmit_ctrl(priv, NULL, seq->ns); 925176962Smav NG_FREE_ITEM(item); 926176962Smav NG_FREE_M(m); 927176962Smav ERROUT(0); 928176962Smav } 929176962Smav /* 930176962Smav * Until we deliver this packet we can't receive next one as 931176962Smav * we have no information for sending ack. 932176962Smav */ 933176962Smav seq->inproc = 1; 934176962Smav mtx_unlock(&seq->mtx); 935176962Smav 936176962Smav /* Prepend session ID to packet. */ 937243882Sglebius M_PREPEND(m, 2, M_NOWAIT); 938102195Sarchie if (m == NULL) { 939176971Smav seq->inproc = 0; 940102195Sarchie priv->stats.memoryFailures++; 941102195Sarchie NG_FREE_ITEM(item); 942172563Smav ERROUT(ENOBUFS); 943102195Sarchie } 944180943Smav mtod(m, u_int8_t *)[0] = sid >> 8; 945180943Smav mtod(m, u_int8_t *)[1] = sid & 0xff; 946102195Sarchie 947176962Smav /* Deliver packet to upper layers */ 948176962Smav NG_FWD_NEW_DATA(error, item, priv->ctrl, m); 949176962Smav 950176962Smav mtx_lock(&seq->mtx); 951176962Smav /* Ready to process next packet. */ 952176962Smav seq->inproc = 0; 953176962Smav 954176962Smav /* If packet was successfully delivered send ack. */ 955176962Smav if (error == 0) { 956176962Smav /* Update recv sequence number */ 957176962Smav seq->nr++; 958176962Smav /* Start receive ack timer, if not already running */ 959176962Smav if (!callout_active(&seq->xack_timer)) { 960176962Smav ng_callout(&seq->xack_timer, priv->node, NULL, 961176962Smav L2TP_DELAYED_ACK, ng_l2tp_seq_xack_timeout, 962176962Smav NULL, 0); 963176962Smav } 964102195Sarchie } 965176962Smav mtx_unlock(&seq->mtx); 966102195Sarchie 967172563Smav ERROUT(error); 968102195Sarchie } 969102195Sarchie 970133060Sbz /* Per session packet, account it. */ 971133060Sbz hpriv->stats.recvPackets++; 972133060Sbz hpriv->stats.recvOctets += plen; 973133060Sbz 974102195Sarchie /* Follow peer's lead in data sequencing, if configured to do so */ 975102195Sarchie if (!hpriv->conf.control_dseq) 976102195Sarchie hpriv->conf.enable_dseq = ((hdr & L2TP_HDR_SEQ) != 0); 977102195Sarchie 978102195Sarchie /* Handle data sequence numbers if present and enabled */ 979102195Sarchie if ((hdr & L2TP_HDR_SEQ) != 0) { 980102195Sarchie if (hpriv->conf.enable_dseq 981102195Sarchie && L2TP_SEQ_DIFF(ns, hpriv->nr) < 0) { 982102195Sarchie NG_FREE_ITEM(item); /* duplicate or out of order */ 983102195Sarchie NG_FREE_M(m); 984102195Sarchie priv->stats.recvDataDrops++; 985172563Smav ERROUT(0); 986102195Sarchie } 987102195Sarchie hpriv->nr = ns + 1; 988102195Sarchie } 989102195Sarchie 990102195Sarchie /* Drop empty data packets */ 991102195Sarchie if (m->m_pkthdr.len == 0) { 992102195Sarchie NG_FREE_ITEM(item); 993102195Sarchie NG_FREE_M(m); 994172563Smav ERROUT(0); 995102195Sarchie } 996102195Sarchie 997102195Sarchie /* Deliver data */ 998102195Sarchie NG_FWD_NEW_DATA(error, item, hook, m); 999172563Smavdone: 1000172563Smav /* Done */ 1001172563Smav L2TP_SEQ_CHECK(&priv->seq); 1002102195Sarchie return (error); 1003102195Sarchie} 1004102195Sarchie 1005102195Sarchie/* 1006102195Sarchie * Handle an outgoing control frame. 1007102195Sarchie */ 1008102195Sarchiestatic int 1009172563Smavng_l2tp_rcvdata_ctrl(hook_p hook, item_p item) 1010102195Sarchie{ 1011172563Smav const node_p node = NG_HOOK_NODE(hook); 1012102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 1013102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1014102195Sarchie struct mbuf *m; 1015172563Smav int error; 1016102195Sarchie int i; 1017172565Smav u_int16_t ns; 1018102195Sarchie 1019172563Smav /* Sanity check */ 1020172563Smav L2TP_SEQ_CHECK(&priv->seq); 1021172563Smav 1022172563Smav /* If not configured, reject */ 1023172563Smav if (!priv->conf.enabled) { 1024172563Smav NG_FREE_ITEM(item); 1025172563Smav ERROUT(ENXIO); 1026172563Smav } 1027172563Smav 1028102195Sarchie /* Grab mbuf and discard other stuff XXX */ 1029102195Sarchie NGI_GET_M(item, m); 1030102195Sarchie NG_FREE_ITEM(item); 1031102195Sarchie 1032102195Sarchie /* Packet should have session ID prepended */ 1033102195Sarchie if (m->m_pkthdr.len < 2) { 1034102195Sarchie priv->stats.xmitInvalid++; 1035102195Sarchie m_freem(m); 1036172563Smav ERROUT(EINVAL); 1037102195Sarchie } 1038102195Sarchie 1039102195Sarchie /* Check max length */ 1040102195Sarchie if (m->m_pkthdr.len >= 0x10000 - 14) { 1041102195Sarchie priv->stats.xmitTooBig++; 1042102195Sarchie m_freem(m); 1043172563Smav ERROUT(EOVERFLOW); 1044102195Sarchie } 1045102195Sarchie 1046172565Smav mtx_lock(&seq->mtx); 1047172565Smav 1048102195Sarchie /* Find next empty slot in transmit queue */ 1049102195Sarchie for (i = 0; i < L2TP_MAX_XWIN && seq->xwin[i] != NULL; i++); 1050102195Sarchie if (i == L2TP_MAX_XWIN) { 1051172565Smav mtx_unlock(&seq->mtx); 1052102195Sarchie priv->stats.xmitDrops++; 1053102195Sarchie m_freem(m); 1054172563Smav ERROUT(ENOBUFS); 1055102195Sarchie } 1056102195Sarchie seq->xwin[i] = m; 1057102195Sarchie 1058102195Sarchie /* If peer's receive window is already full, nothing else to do */ 1059172565Smav if (i >= seq->cwnd) { 1060172565Smav mtx_unlock(&seq->mtx); 1061172563Smav ERROUT(0); 1062172565Smav } 1063102195Sarchie 1064102195Sarchie /* Start retransmit timer if not already running */ 1065169004Smav if (!callout_active(&seq->rack_timer)) 1066140064Sglebius ng_callout(&seq->rack_timer, node, NULL, 1067140064Sglebius hz, ng_l2tp_seq_rack_timeout, NULL, 0); 1068172565Smav 1069172565Smav ns = seq->ns++; 1070172565Smav 1071172565Smav mtx_unlock(&seq->mtx); 1072102195Sarchie 1073102195Sarchie /* Copy packet */ 1074243882Sglebius if ((m = L2TP_COPY_MBUF(m, M_NOWAIT)) == NULL) { 1075102195Sarchie priv->stats.memoryFailures++; 1076172563Smav ERROUT(ENOBUFS); 1077102195Sarchie } 1078102195Sarchie 1079102195Sarchie /* Send packet and increment xmit sequence number */ 1080172565Smav error = ng_l2tp_xmit_ctrl(priv, m, ns); 1081172563Smavdone: 1082172563Smav /* Done */ 1083172563Smav L2TP_SEQ_CHECK(&priv->seq); 1084172563Smav return (error); 1085102195Sarchie} 1086102195Sarchie 1087102195Sarchie/* 1088102195Sarchie * Handle an outgoing data frame. 1089102195Sarchie */ 1090102195Sarchiestatic int 1091172563Smavng_l2tp_rcvdata(hook_p hook, item_p item) 1092102195Sarchie{ 1093172563Smav const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1094172563Smav const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); 1095102195Sarchie struct mbuf *m; 1096206015Smav uint8_t *p; 1097102195Sarchie u_int16_t hdr; 1098102195Sarchie int error; 1099206015Smav int i = 2; 1100102195Sarchie 1101172563Smav /* Sanity check */ 1102172563Smav L2TP_SEQ_CHECK(&priv->seq); 1103172563Smav 1104172563Smav /* If not configured, reject */ 1105172563Smav if (!priv->conf.enabled) { 1106172563Smav NG_FREE_ITEM(item); 1107172563Smav ERROUT(ENXIO); 1108172563Smav } 1109172563Smav 1110102195Sarchie /* Get mbuf */ 1111102195Sarchie NGI_GET_M(item, m); 1112102195Sarchie 1113102195Sarchie /* Check max length */ 1114102195Sarchie if (m->m_pkthdr.len >= 0x10000 - 12) { 1115102195Sarchie priv->stats.xmitDataTooBig++; 1116102195Sarchie NG_FREE_ITEM(item); 1117102195Sarchie NG_FREE_M(m); 1118172563Smav ERROUT(EOVERFLOW); 1119102195Sarchie } 1120102195Sarchie 1121102195Sarchie /* Prepend L2TP header */ 1122102195Sarchie M_PREPEND(m, 6 1123102195Sarchie + (2 * (hpriv->conf.include_length != 0)) 1124102195Sarchie + (4 * (hpriv->conf.enable_dseq != 0)), 1125243882Sglebius M_NOWAIT); 1126102195Sarchie if (m == NULL) { 1127102195Sarchie priv->stats.memoryFailures++; 1128102195Sarchie NG_FREE_ITEM(item); 1129172563Smav ERROUT(ENOBUFS); 1130102195Sarchie } 1131206015Smav p = mtod(m, uint8_t *); 1132102195Sarchie hdr = L2TP_DATA_HDR; 1133102195Sarchie if (hpriv->conf.include_length) { 1134102195Sarchie hdr |= L2TP_HDR_LEN; 1135206015Smav p[i++] = m->m_pkthdr.len >> 8; 1136206015Smav p[i++] = m->m_pkthdr.len & 0xff; 1137102195Sarchie } 1138206015Smav p[i++] = priv->conf.peer_id >> 8; 1139206015Smav p[i++] = priv->conf.peer_id & 0xff; 1140206015Smav p[i++] = hpriv->conf.peer_id >> 8; 1141206015Smav p[i++] = hpriv->conf.peer_id & 0xff; 1142102195Sarchie if (hpriv->conf.enable_dseq) { 1143102195Sarchie hdr |= L2TP_HDR_SEQ; 1144206015Smav p[i++] = hpriv->ns >> 8; 1145206015Smav p[i++] = hpriv->ns & 0xff; 1146206015Smav p[i++] = hpriv->nr >> 8; 1147206015Smav p[i++] = hpriv->nr & 0xff; 1148102195Sarchie hpriv->ns++; 1149102195Sarchie } 1150206015Smav p[0] = hdr >> 8; 1151206015Smav p[1] = hdr & 0xff; 1152102195Sarchie 1153133060Sbz /* Update per session stats. */ 1154133060Sbz hpriv->stats.xmitPackets++; 1155133060Sbz hpriv->stats.xmitOctets += m->m_pkthdr.len; 1156133060Sbz 1157168981Smav /* And the global one. */ 1158168981Smav priv->stats.xmitPackets++; 1159168981Smav priv->stats.xmitOctets += m->m_pkthdr.len; 1160168981Smav 1161102195Sarchie /* Send packet */ 1162102195Sarchie NG_FWD_NEW_DATA(error, item, priv->lower, m); 1163172563Smavdone: 1164172563Smav /* Done */ 1165172563Smav L2TP_SEQ_CHECK(&priv->seq); 1166102195Sarchie return (error); 1167102195Sarchie} 1168102195Sarchie 1169102195Sarchie/* 1170102195Sarchie * Send a message to our controlling node that we've failed. 1171102195Sarchie */ 1172102195Sarchiestatic void 1173102195Sarchieng_l2tp_seq_failure(priv_p priv) 1174102195Sarchie{ 1175102195Sarchie struct ng_mesg *msg; 1176102195Sarchie int error; 1177102195Sarchie 1178102195Sarchie NG_MKMESSAGE(msg, NGM_L2TP_COOKIE, NGM_L2TP_ACK_FAILURE, 0, M_NOWAIT); 1179102195Sarchie if (msg == NULL) 1180102195Sarchie return; 1181102244Sarchie NG_SEND_MSG_ID(error, priv->node, msg, priv->ftarget, 0); 1182102195Sarchie} 1183102195Sarchie 1184102195Sarchie/************************************************************************ 1185102195Sarchie SEQUENCE NUMBER HANDLING 1186102195Sarchie************************************************************************/ 1187102195Sarchie 1188102195Sarchie/* 1189102195Sarchie * Initialize sequence number state. 1190102195Sarchie */ 1191102195Sarchiestatic void 1192102195Sarchieng_l2tp_seq_init(priv_p priv) 1193102195Sarchie{ 1194102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1195102195Sarchie 1196102195Sarchie KASSERT(priv->conf.peer_win >= 1, 1197148915Sobrien ("%s: peer_win is zero", __func__)); 1198102195Sarchie memset(seq, 0, sizeof(*seq)); 1199102195Sarchie seq->cwnd = 1; 1200102195Sarchie seq->wmax = priv->conf.peer_win; 1201102195Sarchie if (seq->wmax > L2TP_MAX_XWIN) 1202102195Sarchie seq->wmax = L2TP_MAX_XWIN; 1203102195Sarchie seq->ssth = seq->wmax; 1204140064Sglebius ng_callout_init(&seq->rack_timer); 1205140064Sglebius ng_callout_init(&seq->xack_timer); 1206172565Smav mtx_init(&seq->mtx, "ng_l2tp", NULL, MTX_DEF); 1207102195Sarchie L2TP_SEQ_CHECK(seq); 1208102195Sarchie} 1209102195Sarchie 1210102195Sarchie/* 1211133058Sbz * Set sequence number state as given from user. 1212133058Sbz */ 1213133058Sbzstatic int 1214133058Sbzng_l2tp_seq_set(priv_p priv, const struct ng_l2tp_seq_config *conf) 1215133058Sbz{ 1216133058Sbz struct l2tp_seq *const seq = &priv->seq; 1217133058Sbz 1218133058Sbz /* If node is enabled, deny update to sequence numbers. */ 1219133058Sbz if (priv->conf.enabled) 1220133058Sbz return (EBUSY); 1221133058Sbz 1222133058Sbz /* We only can handle the simple cases. */ 1223133058Sbz if (conf->xack != conf->nr || conf->ns != conf->rack) 1224133058Sbz return (EINVAL); 1225133058Sbz 1226133058Sbz /* Set ns,nr,rack,xack parameters. */ 1227133058Sbz seq->ns = conf->ns; 1228133058Sbz seq->nr = conf->nr; 1229133058Sbz seq->rack = conf->rack; 1230133058Sbz seq->xack = conf->xack; 1231133058Sbz 1232133058Sbz return (0); 1233133058Sbz} 1234133058Sbz 1235133058Sbz/* 1236102195Sarchie * Adjust sequence number state accordingly after reconfiguration. 1237102195Sarchie */ 1238102195Sarchiestatic int 1239102195Sarchieng_l2tp_seq_adjust(priv_p priv, const struct ng_l2tp_config *conf) 1240102195Sarchie{ 1241102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1242102195Sarchie u_int16_t new_wmax; 1243102195Sarchie 1244102195Sarchie /* If disabling node, reset state sequence number */ 1245102195Sarchie if (!conf->enabled) { 1246102195Sarchie ng_l2tp_seq_reset(priv); 1247102195Sarchie return (0); 1248102195Sarchie } 1249102195Sarchie 1250102195Sarchie /* Adjust peer's max recv window; it can only increase */ 1251102195Sarchie new_wmax = conf->peer_win; 1252102195Sarchie if (new_wmax > L2TP_MAX_XWIN) 1253102195Sarchie new_wmax = L2TP_MAX_XWIN; 1254102195Sarchie if (new_wmax == 0) 1255102195Sarchie return (EINVAL); 1256102195Sarchie if (new_wmax < seq->wmax) 1257102195Sarchie return (EBUSY); 1258102195Sarchie seq->wmax = new_wmax; 1259102195Sarchie 1260102195Sarchie /* Done */ 1261102195Sarchie return (0); 1262102195Sarchie} 1263102195Sarchie 1264102195Sarchie/* 1265102195Sarchie * Reset sequence number state. 1266102195Sarchie */ 1267102195Sarchiestatic void 1268102195Sarchieng_l2tp_seq_reset(priv_p priv) 1269102195Sarchie{ 1270102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1271102195Sarchie hook_p hook; 1272102195Sarchie int i; 1273102195Sarchie 1274102195Sarchie /* Sanity check */ 1275102195Sarchie L2TP_SEQ_CHECK(seq); 1276102195Sarchie 1277102195Sarchie /* Stop timers */ 1278140064Sglebius ng_uncallout(&seq->rack_timer, priv->node); 1279140064Sglebius ng_uncallout(&seq->xack_timer, priv->node); 1280102195Sarchie 1281102195Sarchie /* Free retransmit queue */ 1282102195Sarchie for (i = 0; i < L2TP_MAX_XWIN; i++) { 1283102195Sarchie if (seq->xwin[i] == NULL) 1284102195Sarchie break; 1285102195Sarchie m_freem(seq->xwin[i]); 1286102195Sarchie } 1287102195Sarchie 1288102195Sarchie /* Reset session hooks' sequence number states */ 1289102195Sarchie NG_NODE_FOREACH_HOOK(priv->node, ng_l2tp_reset_session, NULL, hook); 1290102195Sarchie 1291102195Sarchie /* Reset node's sequence number state */ 1292172565Smav seq->ns = 0; 1293172565Smav seq->nr = 0; 1294172565Smav seq->rack = 0; 1295172565Smav seq->xack = 0; 1296172565Smav seq->wmax = L2TP_MAX_XWIN; 1297102195Sarchie seq->cwnd = 1; 1298102195Sarchie seq->ssth = seq->wmax; 1299172565Smav seq->acks = 0; 1300172565Smav seq->rexmits = 0; 1301172565Smav bzero(seq->xwin, sizeof(seq->xwin)); 1302102195Sarchie 1303102195Sarchie /* Done */ 1304102195Sarchie L2TP_SEQ_CHECK(seq); 1305102195Sarchie} 1306102195Sarchie 1307102195Sarchie/* 1308102195Sarchie * Handle receipt of an acknowledgement value (Nr) from peer. 1309102195Sarchie */ 1310102195Sarchiestatic void 1311102195Sarchieng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr) 1312102195Sarchie{ 1313102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1314172565Smav struct mbuf *xwin[L2TP_MAX_XWIN]; /* partial local copy */ 1315172565Smav int nack; 1316172565Smav int i, j; 1317172565Smav uint16_t ns; 1318102195Sarchie 1319172565Smav mtx_lock(&seq->mtx); 1320172565Smav 1321102195Sarchie /* Verify peer's ACK is in range */ 1322172565Smav if ((nack = L2TP_SEQ_DIFF(nr, seq->rack)) <= 0) { 1323172565Smav mtx_unlock(&seq->mtx); 1324102195Sarchie return; /* duplicate ack */ 1325172565Smav } 1326102195Sarchie if (L2TP_SEQ_DIFF(nr, seq->ns) > 0) { 1327172565Smav mtx_unlock(&seq->mtx); 1328102195Sarchie priv->stats.recvBadAcks++; /* ack for packet not sent */ 1329102195Sarchie return; 1330102195Sarchie } 1331102195Sarchie KASSERT(nack <= L2TP_MAX_XWIN, 1332148915Sobrien ("%s: nack=%d > %d", __func__, nack, L2TP_MAX_XWIN)); 1333102195Sarchie 1334102195Sarchie /* Update receive ack stats */ 1335102195Sarchie seq->rack = nr; 1336102195Sarchie seq->rexmits = 0; 1337102195Sarchie 1338102195Sarchie /* Free acknowledged packets and shift up packets in the xmit queue */ 1339102195Sarchie for (i = 0; i < nack; i++) 1340102195Sarchie m_freem(seq->xwin[i]); 1341102195Sarchie memmove(seq->xwin, seq->xwin + nack, 1342102195Sarchie (L2TP_MAX_XWIN - nack) * sizeof(*seq->xwin)); 1343102195Sarchie memset(seq->xwin + (L2TP_MAX_XWIN - nack), 0, 1344102195Sarchie nack * sizeof(*seq->xwin)); 1345102195Sarchie 1346102195Sarchie /* 1347102195Sarchie * Do slow-start/congestion avoidance windowing algorithm described 1348102195Sarchie * in RFC 2661, Appendix A. Here we handle a multiple ACK as if each 1349102195Sarchie * ACK had arrived separately. 1350102195Sarchie */ 1351102195Sarchie if (seq->cwnd < seq->wmax) { 1352102195Sarchie 1353102195Sarchie /* Handle slow start phase */ 1354102195Sarchie if (seq->cwnd < seq->ssth) { 1355102195Sarchie seq->cwnd += nack; 1356102195Sarchie nack = 0; 1357102195Sarchie if (seq->cwnd > seq->ssth) { /* into cg.av. phase */ 1358102195Sarchie nack = seq->cwnd - seq->ssth; 1359102195Sarchie seq->cwnd = seq->ssth; 1360102195Sarchie } 1361102195Sarchie } 1362102195Sarchie 1363102195Sarchie /* Handle congestion avoidance phase */ 1364102195Sarchie if (seq->cwnd >= seq->ssth) { 1365102195Sarchie seq->acks += nack; 1366102195Sarchie while (seq->acks >= seq->cwnd) { 1367102195Sarchie seq->acks -= seq->cwnd; 1368102195Sarchie if (seq->cwnd < seq->wmax) 1369102195Sarchie seq->cwnd++; 1370102195Sarchie } 1371102195Sarchie } 1372102195Sarchie } 1373102195Sarchie 1374102195Sarchie /* Stop xmit timer */ 1375169004Smav if (callout_active(&seq->rack_timer)) 1376140064Sglebius ng_uncallout(&seq->rack_timer, priv->node); 1377102195Sarchie 1378102195Sarchie /* If transmit queue is empty, we're done for now */ 1379172565Smav if (seq->xwin[0] == NULL) { 1380172565Smav mtx_unlock(&seq->mtx); 1381102195Sarchie return; 1382172565Smav } 1383102195Sarchie 1384102195Sarchie /* Start restransmit timer again */ 1385140064Sglebius ng_callout(&seq->rack_timer, priv->node, NULL, 1386140064Sglebius hz, ng_l2tp_seq_rack_timeout, NULL, 0); 1387102195Sarchie 1388102195Sarchie /* 1389102195Sarchie * Send more packets, trying to keep peer's receive window full. 1390172565Smav * Make copy of everything we need before lock release. 1391172565Smav */ 1392172565Smav ns = seq->ns; 1393172565Smav j = 0; 1394172565Smav while ((i = L2TP_SEQ_DIFF(seq->ns, seq->rack)) < seq->cwnd 1395172565Smav && seq->xwin[i] != NULL) { 1396172565Smav xwin[j++] = seq->xwin[i]; 1397172565Smav seq->ns++; 1398172565Smav } 1399172565Smav 1400172565Smav mtx_unlock(&seq->mtx); 1401172565Smav 1402172565Smav /* 1403172565Smav * Send prepared. 1404102195Sarchie * If there is a memory error, pretend packet was sent, as it 1405102195Sarchie * will get retransmitted later anyway. 1406102195Sarchie */ 1407172565Smav for (i = 0; i < j; i++) { 1408172565Smav struct mbuf *m; 1409243882Sglebius if ((m = L2TP_COPY_MBUF(xwin[i], M_NOWAIT)) == NULL) 1410102195Sarchie priv->stats.memoryFailures++; 1411102195Sarchie else 1412172565Smav ng_l2tp_xmit_ctrl(priv, m, ns); 1413172565Smav ns++; 1414102195Sarchie } 1415102195Sarchie} 1416102195Sarchie 1417102195Sarchie/* 1418102195Sarchie * Handle an ack timeout. We have an outstanding ack that we 1419102195Sarchie * were hoping to piggy-back, but haven't, so send a ZLB. 1420102195Sarchie */ 1421102195Sarchiestatic void 1422140064Sglebiusng_l2tp_seq_xack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 1423102195Sarchie{ 1424102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 1425102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1426102195Sarchie 1427169004Smav /* Make sure callout is still active before doing anything */ 1428169004Smav if (callout_pending(&seq->xack_timer) || 1429169004Smav (!callout_active(&seq->xack_timer))) 1430169004Smav return; 1431169004Smav 1432102195Sarchie /* Sanity check */ 1433102195Sarchie L2TP_SEQ_CHECK(seq); 1434102195Sarchie 1435169004Smav /* Send a ZLB */ 1436169004Smav ng_l2tp_xmit_ctrl(priv, NULL, seq->ns); 1437102195Sarchie 1438169004Smav /* callout_deactivate() is not needed here 1439169004Smav as ng_uncallout() was called by ng_l2tp_xmit_ctrl() */ 1440169004Smav 1441169004Smav /* Sanity check */ 1442102195Sarchie L2TP_SEQ_CHECK(seq); 1443102195Sarchie} 1444102195Sarchie 1445102195Sarchie/* 1446102195Sarchie * Handle a transmit timeout. The peer has failed to respond 1447102195Sarchie * with an ack for our packet, so retransmit it. 1448102195Sarchie */ 1449102195Sarchiestatic void 1450140064Sglebiusng_l2tp_seq_rack_timeout(node_p node, hook_p hook, void *arg1, int arg2) 1451102195Sarchie{ 1452102195Sarchie const priv_p priv = NG_NODE_PRIVATE(node); 1453102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1454102195Sarchie struct mbuf *m; 1455102195Sarchie u_int delay; 1456102195Sarchie 1457169004Smav /* Make sure callout is still active before doing anything */ 1458169004Smav if (callout_pending(&seq->rack_timer) || 1459169004Smav (!callout_active(&seq->rack_timer))) 1460169004Smav return; 1461169004Smav 1462102195Sarchie /* Sanity check */ 1463102195Sarchie L2TP_SEQ_CHECK(seq); 1464102195Sarchie 1465102195Sarchie priv->stats.xmitRetransmits++; 1466102195Sarchie 1467102195Sarchie /* Have we reached the retransmit limit? If so, notify owner. */ 1468172562Smav if (seq->rexmits++ >= priv->conf.rexmit_max) 1469102195Sarchie ng_l2tp_seq_failure(priv); 1470102195Sarchie 1471102195Sarchie /* Restart timer, this time with an increased delay */ 1472102195Sarchie delay = (seq->rexmits > 12) ? (1 << 12) : (1 << seq->rexmits); 1473172562Smav if (delay > priv->conf.rexmit_max_to) 1474172562Smav delay = priv->conf.rexmit_max_to; 1475140064Sglebius ng_callout(&seq->rack_timer, node, NULL, 1476140064Sglebius hz * delay, ng_l2tp_seq_rack_timeout, NULL, 0); 1477102195Sarchie 1478102195Sarchie /* Do slow-start/congestion algorithm windowing algorithm */ 1479176962Smav seq->ns = seq->rack; 1480102195Sarchie seq->ssth = (seq->cwnd + 1) / 2; 1481102195Sarchie seq->cwnd = 1; 1482102195Sarchie seq->acks = 0; 1483102195Sarchie 1484102195Sarchie /* Retransmit oldest unack'd packet */ 1485243882Sglebius if ((m = L2TP_COPY_MBUF(seq->xwin[0], M_NOWAIT)) == NULL) 1486102195Sarchie priv->stats.memoryFailures++; 1487102195Sarchie else 1488176962Smav ng_l2tp_xmit_ctrl(priv, m, seq->ns++); 1489102195Sarchie 1490169004Smav /* callout_deactivate() is not needed here 1491169004Smav as ng_callout() is getting called each time */ 1492169004Smav 1493169004Smav /* Sanity check */ 1494102195Sarchie L2TP_SEQ_CHECK(seq); 1495102195Sarchie} 1496102195Sarchie 1497102195Sarchie/* 1498102195Sarchie * Transmit a control stream packet, payload optional. 1499102195Sarchie * The transmit sequence number is not incremented. 1500102195Sarchie */ 1501102195Sarchiestatic int 1502102195Sarchieng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns) 1503102195Sarchie{ 1504102195Sarchie struct l2tp_seq *const seq = &priv->seq; 1505206015Smav uint8_t *p; 1506102195Sarchie u_int16_t session_id = 0; 1507102195Sarchie int error; 1508102195Sarchie 1509172565Smav mtx_lock(&seq->mtx); 1510172565Smav 1511169004Smav /* Stop ack timer: we're sending an ack with this packet. 1512169004Smav Doing this before to keep state predictable after error. */ 1513169004Smav if (callout_active(&seq->xack_timer)) 1514169004Smav ng_uncallout(&seq->xack_timer, priv->node); 1515169004Smav 1516169004Smav seq->xack = seq->nr; 1517169004Smav 1518172565Smav mtx_unlock(&seq->mtx); 1519172565Smav 1520102195Sarchie /* If no mbuf passed, send an empty packet (ZLB) */ 1521102195Sarchie if (m == NULL) { 1522102195Sarchie 1523102195Sarchie /* Create a new mbuf for ZLB packet */ 1524243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 1525102195Sarchie if (m == NULL) { 1526102195Sarchie priv->stats.memoryFailures++; 1527102195Sarchie return (ENOBUFS); 1528102195Sarchie } 1529102195Sarchie m->m_len = m->m_pkthdr.len = 12; 1530102195Sarchie m->m_pkthdr.rcvif = NULL; 1531102195Sarchie priv->stats.xmitZLBs++; 1532102195Sarchie } else { 1533102195Sarchie 1534102195Sarchie /* Strip off session ID */ 1535102195Sarchie if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { 1536102195Sarchie priv->stats.memoryFailures++; 1537102195Sarchie return (ENOBUFS); 1538102195Sarchie } 1539180943Smav session_id = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1]; 1540102195Sarchie 1541102195Sarchie /* Make room for L2TP header */ 1542243882Sglebius M_PREPEND(m, 10, M_NOWAIT); /* - 2 + 12 = 10 */ 1543102195Sarchie if (m == NULL) { 1544102195Sarchie priv->stats.memoryFailures++; 1545102195Sarchie return (ENOBUFS); 1546102195Sarchie } 1547102195Sarchie } 1548102195Sarchie 1549102195Sarchie /* Fill in L2TP header */ 1550206015Smav p = mtod(m, u_int8_t *); 1551206015Smav p[0] = L2TP_CTRL_HDR >> 8; 1552206015Smav p[1] = L2TP_CTRL_HDR & 0xff; 1553206015Smav p[2] = m->m_pkthdr.len >> 8; 1554206015Smav p[3] = m->m_pkthdr.len & 0xff; 1555206015Smav p[4] = priv->conf.peer_id >> 8; 1556206015Smav p[5] = priv->conf.peer_id & 0xff; 1557206015Smav p[6] = session_id >> 8; 1558206015Smav p[7] = session_id & 0xff; 1559206015Smav p[8] = ns >> 8; 1560206015Smav p[9] = ns & 0xff; 1561206015Smav p[10] = seq->nr >> 8; 1562206015Smav p[11] = seq->nr & 0xff; 1563102195Sarchie 1564102195Sarchie /* Update sequence number info and stats */ 1565102195Sarchie priv->stats.xmitPackets++; 1566102195Sarchie priv->stats.xmitOctets += m->m_pkthdr.len; 1567102195Sarchie 1568102195Sarchie /* Send packet */ 1569131155Sjulian NG_SEND_DATA_ONLY(error, priv->lower, m); 1570102195Sarchie return (error); 1571102195Sarchie} 1572102195Sarchie 1573102195Sarchie#ifdef INVARIANTS 1574102195Sarchie/* 1575102195Sarchie * Sanity check sequence number state. 1576102195Sarchie */ 1577102195Sarchiestatic void 1578102195Sarchieng_l2tp_seq_check(struct l2tp_seq *seq) 1579102195Sarchie{ 1580172565Smav int self_unack, peer_unack; 1581102195Sarchie int i; 1582102195Sarchie 1583148915Sobrien#define CHECK(p) KASSERT((p), ("%s: not: %s", __func__, #p)) 1584102195Sarchie 1585172565Smav mtx_lock(&seq->mtx); 1586172565Smav 1587172565Smav self_unack = L2TP_SEQ_DIFF(seq->nr, seq->xack); 1588172565Smav peer_unack = L2TP_SEQ_DIFF(seq->ns, seq->rack); 1589102195Sarchie CHECK(seq->wmax <= L2TP_MAX_XWIN); 1590102195Sarchie CHECK(seq->cwnd >= 1); 1591102195Sarchie CHECK(seq->cwnd <= seq->wmax); 1592102195Sarchie CHECK(seq->ssth >= 1); 1593102195Sarchie CHECK(seq->ssth <= seq->wmax); 1594102195Sarchie if (seq->cwnd < seq->ssth) 1595102195Sarchie CHECK(seq->acks == 0); 1596102195Sarchie else 1597102195Sarchie CHECK(seq->acks <= seq->cwnd); 1598102195Sarchie CHECK(self_unack >= 0); 1599102195Sarchie CHECK(peer_unack >= 0); 1600102195Sarchie CHECK(peer_unack <= seq->wmax); 1601169004Smav CHECK((self_unack == 0) ^ callout_active(&seq->xack_timer)); 1602169004Smav CHECK((peer_unack == 0) ^ callout_active(&seq->rack_timer)); 1603102195Sarchie for (i = 0; i < peer_unack; i++) 1604102195Sarchie CHECK(seq->xwin[i] != NULL); 1605102195Sarchie for ( ; i < seq->cwnd; i++) /* verify peer's recv window full */ 1606102195Sarchie CHECK(seq->xwin[i] == NULL); 1607102195Sarchie 1608172565Smav mtx_unlock(&seq->mtx); 1609172565Smav 1610102195Sarchie#undef CHECK 1611102195Sarchie} 1612102195Sarchie#endif /* INVARIANTS */ 1613