51#include <sys/kernel.h>
55#include <sys/malloc.h>
58#include <sys/libkern.h>
67#ifdef NG_SEPARATE_MALLOC
70#define M_NETGRAPH_L2TP M_NETGRAPH
74#define L2TP_HDR_CTRL 0x8000
75#define L2TP_HDR_LEN 0x4000
76#define L2TP_HDR_SEQ 0x0800
77#define L2TP_HDR_OFF 0x0200
78#define L2TP_HDR_PRIO 0x0100
79#define L2TP_HDR_VERS_MASK 0x000f
80#define L2TP_HDR_VERSION 0x0002
83#define L2TP_CTRL_0BITS 0x030d
84#define L2TP_CTRL_1BITS 0xc802
85#define L2TP_DATA_0BITS 0x800d
86#define L2TP_DATA_1BITS 0x0002
89#define L2TP_CTRL_HDR (L2TP_HDR_CTRL | L2TP_HDR_LEN \
90 | L2TP_HDR_SEQ | L2TP_HDR_VERSION)
91#define L2TP_DATA_HDR (L2TP_HDR_VERSION)
94#define L2TP_MAX_XWIN 128
95#define L2TP_MAX_REXMIT 5
96#define L2TP_MAX_REXMIT_TO 30
97#define L2TP_DELAYED_ACK ((hz + 19) / 20)
100#define L2TP_CONTROL_DSEQ 1
101#define L2TP_ENABLE_DSEQ 1
104#define L2TP_SEQ_DIFF(x, y) ((int16_t)((x) - (y)))
106#define SESSHASHSIZE 0x0020
107#define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
337static void ng_l2tp_seq_check(
struct l2tp_seq *seq);
338#define SEQ_LOCK(seq) do { \
339 mtx_lock(&(seq)->mtx); \
340 ng_l2tp_seq_check(seq); \
342#define SEQ_UNLOCK(seq) do { \
343 ng_l2tp_seq_check(seq); \
344 mtx_unlock(&(seq)->mtx); \
347#define SEQ_LOCK(seq) mtx_lock(&(seq)->mtx)
348#define SEQ_UNLOCK(seq) mtx_unlock(&(seq)->mtx)
350#define SEQ_LOCK_ASSERT(seq) mtx_assert(&(seq)->mtx, MA_OWNED)
353#define L2TP_COPY_MBUF m_copypacket
355#define ERROUT(x) do { error = (x); goto done; } while (0)
376 priv->conf.peer_win = 1;
384 LIST_INIT(&
priv->sesshash[i]);
400 if (
priv->ctrl != NULL)
410 static const char hexdig[16] =
"0123456789abcdef";
411 u_int16_t session_id;
423 for (session_id = i = 0; i < 4; i++) {
424 for (j = 0; j < 16 && hex[i] != hexdig[j]; j++);
427 session_id = (session_id << 4) | j;
433 hpriv = malloc(
sizeof(*hpriv),
443 LIST_INSERT_HEAD(&
priv->sesshash[hash], hpriv, sessions);
478 && ((
priv->conf.tunnel_id != 0
480 || ((
priv->conf.peer_id != 0
604 sizeof(hpriv->
stats), M_NOWAIT);
610 sizeof(hpriv->
stats));
626 conf->
ns = htons(conf->
ns);
627 conf->
nr = htons(conf->
nr);
669 mtx_destroy(&seq->
mtx);
688 if (hook ==
priv->ctrl)
694 LIST_REMOVE(hpriv, sessions);
718 LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) {
750 static const u_int16_t req_bits[2][2] = {
776 plen = m->m_pkthdr.len;
783 if (m->m_pkthdr.len < 6) {
789 if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
794 hdr = (mtod(m, uint8_t *)[0] << 8) + mtod(m, uint8_t *)[1];
799 if ((hdr & req_bits[is_ctrl][0]) != 0
800 || (~hdr & req_bits[is_ctrl][1]) != 0) {
806 if (m->m_pkthdr.len < 4
818 if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
823 len = (mtod(m, uint8_t *)[0] << 8) + mtod(m, uint8_t *)[1] - 4;
825 if (len < 0 || len > m->m_pkthdr.len) {
831 if (len < m->m_pkthdr.len)
832 m_adj(m, -(m->m_pkthdr.len - len));
836 if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
841 tid = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1];
842 sid = (mtod(m, u_int8_t *)[2] << 8) + mtod(m, u_int8_t *)[3];
846 if (tid !=
priv->conf.tunnel_id &&
847 (
priv->conf.match_id || tid != 0)) {
868 if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) {
873 ns = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1];
874 nr = (mtod(m, u_int8_t *)[2] << 8) + mtod(m, u_int8_t *)[3];
884 if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
889 offset = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1];
892 if ((2+offset) > m->m_pkthdr.len) {
911 if (m->m_pkthdr.len == 0) {
935 M_PREPEND(m, 2, M_NOWAIT);
942 mtod(m, u_int8_t *)[0] = sid >> 8;
943 mtod(m, u_int8_t *)[1] = sid & 0xff;
996 if (m->m_pkthdr.len == 0) {
1033 if (m->m_pkthdr.len < 2) {
1040 if (m->m_pkthdr.len >= 0x10000 - 14) {
1059 if (i >= seq->
cwnd) {
1108 if (m->m_pkthdr.len >= 0x10000 - 12) {
1125 p = mtod(m, uint8_t *);
1129 p[i++] = m->m_pkthdr.len >> 8;
1130 p[i++] = m->m_pkthdr.len & 0xff;
1132 p[i++] =
priv->conf.peer_id >> 8;
1133 p[i++] =
priv->conf.peer_id & 0xff;
1138 p[i++] = hpriv->
ns >> 8;
1139 p[i++] = hpriv->
ns & 0xff;
1140 p[i++] = hpriv->
nr >> 8;
1141 p[i++] = hpriv->
nr & 0xff;
1153 priv->
stats.xmitOctets += m->m_pkthdr.len;
1188 KASSERT(
priv->conf.peer_win >= 1,
1189 (
"%s: peer_win is zero", __func__));
1190 memset(seq, 0,
sizeof(*seq));
1196 mtx_init(&seq->
mtx,
"ng_l2tp", NULL, MTX_DEF);
1197 callout_init_mtx(&seq->
rack_timer, &seq->
mtx, CALLOUT_RETURNUNLOCKED);
1198 callout_init_mtx(&seq->
xack_timer, &seq->
mtx, CALLOUT_RETURNUNLOCKED);
1250 if (new_wmax < seq->
wmax)
1252 seq->
wmax = new_wmax;
1277 if (seq->
xwin[i] == NULL)
1279 m_freem(seq->
xwin[i]);
1295 bzero(seq->
xwin,
sizeof(seq->
xwin));
1327 for (i = 0; i < nack; i++)
1328 m_freem(seq->
xwin[i]);
1329 memmove(seq->
xwin, seq->
xwin + nack,
1332 nack *
sizeof(*seq->
xwin));
1366 if (seq->
xwin[0] == NULL)
1380 && seq->
xwin[i] != NULL) {
1381 xwin[j++] = seq->
xwin[i];
1390 for (i = 0; i < j; i++) {
1411 struct epoch_tracker et;
1418 NET_EPOCH_ENTER(et);
1438 struct epoch_tracker et;
1444 MPASS(seq->
xwin[0]);
1448 NET_EPOCH_ENTER(et);
1459 if (delay >
priv->conf.rexmit_max_to)
1460 delay =
priv->conf.rexmit_max_to;
1495 uint16_t
nr, session_id = 0;
1512 MGETHDR(m, M_NOWAIT, MT_DATA);
1517 m->m_len = m->m_pkthdr.len = 12;
1518 m->m_pkthdr.rcvif = NULL;
1522 if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) {
1526 session_id = (mtod(m, u_int8_t *)[0] << 8) + mtod(m, u_int8_t *)[1];
1529 M_PREPEND(m, 10, M_NOWAIT);
1539 m = m_pullup(m, 12);
1547 p = mtod(m, u_int8_t *);
1550 p[2] = m->m_pkthdr.len >> 8;
1551 p[3] = m->m_pkthdr.len & 0xff;
1552 p[4] =
priv->conf.peer_id >> 8;
1553 p[5] =
priv->conf.peer_id & 0xff;
1554 p[6] = session_id >> 8;
1555 p[7] = session_id & 0xff;
1563 priv->
stats.xmitOctets += m->m_pkthdr.len;
1575ng_l2tp_seq_check(
struct l2tp_seq *seq)
1577 int self_unack, peer_unack;
1580#define CHECK(p) KASSERT((p), ("%s: not: %s", __func__, #p))
1587 CHECK(seq->
cwnd >= 1);
1589 CHECK(seq->
ssth >= 1);
1592 CHECK(seq->
acks == 0);
1595 CHECK(self_unack >= 0);
1596 CHECK(peer_unack >= 0);
1597 CHECK(peer_unack <= seq->
wmax);
1598 CHECK((self_unack == 0) ^ callout_active(&seq->
xack_timer));
1599 CHECK((peer_unack == 0) ^ callout_active(&seq->
rack_timer));
1600 for (i = 0; i < peer_unack; i++)
1601 CHECK(seq->
xwin[i] != NULL);
1602 for ( ; i < seq->
cwnd; i++)
1603 CHECK(seq->
xwin[i] == NULL);
int ng_fn_eachhook(hook_p hook, void *arg)
#define NG_HOOK_NODE(hook)
#define NG_NODE_FOREACH_HOOK(node, fn, arg, rethook)
int ng_rcvmsg_t(node_p node, item_p item, hook_p lasthook)
int ng_disconnect_t(hook_p hook)
#define NG_NODE_SET_PRIVATE(node, val)
#define NG_RESPOND_MSG(error, here, item, resp)
#define NG_NODE_IS_VALID(node)
#define NG_NODE_UNREF(node)
#define NG_HOOK_SET_PRIVATE(hook, val)
int ng_rmnode_self(node_p here)
#define NG_FWD_NEW_DATA(error, item, hook, m)
#define NG_SEND_DATA_ONLY(error, hook, m)
#define NG_SEND_MSG_ID(error, here, msg, ID, retaddr)
int ng_rcvdata_t(hook_p hook, item_p item)
int ng_shutdown_t(node_p node)
#define NG_FREE_ITEM(item)
#define NG_HOOK_SET_RCVDATA(hook, val)
int ng_constructor_t(node_p node)
#define NG_NODE_NUMHOOKS(node)
#define NGI_GET_MSG(i, m)
#define NG_NODE_PRIVATE(node)
int ng_newhook_t(node_p node, hook_p hook, const char *name)
#define NG_HOOK_PRIVATE(hook)
MALLOC_DEFINE(M_NG_CCATM, "ng_ccatm", "netgraph uni api node")
static int ng_l2tp_seq_set(priv_p priv, const struct ng_l2tp_seq_config *conf)
static ng_constructor_t ng_l2tp_constructor
static const struct ng_cmdlist ng_l2tp_cmdlist[]
static const struct ng_parse_struct_field ng_l2tp_sess_config_type_fields[]
static void ng_l2tp_seq_init(priv_p priv)
static ng_rcvdata_t ng_l2tp_rcvdata
#define L2TP_CONTROL_DSEQ
#define L2TP_MAX_REXMIT_TO
static ng_disconnect_t ng_l2tp_disconnect
static void ng_l2tp_seq_failure(priv_p priv)
static ng_rcvdata_t ng_l2tp_rcvdata_lower
static ng_newhook_t ng_l2tp_newhook
static int ng_l2tp_seq_adjust(priv_p priv, const struct ng_l2tp_config *conf)
static const struct ng_parse_struct_field ng_l2tp_seq_config_fields[]
#define L2TP_SEQ_DIFF(x, y)
static const struct ng_parse_type ng_l2tp_session_stats_type
static const struct ng_parse_type ng_l2tp_seq_config_type
static hookpriv_p ng_l2tp_find_session(priv_p privp, u_int16_t sid)
static const struct ng_parse_struct_field ng_l2tp_config_type_fields[]
#define SEQ_LOCK_ASSERT(seq)
static int ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns)
static const struct ng_parse_struct_field ng_l2tp_stats_type_fields[]
static struct ng_type ng_l2tp_typestruct
static const struct ng_parse_type ng_l2tp_stats_type
static void ng_l2tp_seq_rack_timeout(void *)
static void ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr)
static ng_fn_eachhook ng_l2tp_reset_session
NETGRAPH_INIT(l2tp, &ng_l2tp_typestruct)
static const struct ng_parse_type ng_l2tp_sess_config_type
static const struct ng_parse_type ng_l2tp_config_type
static ng_rcvdata_t ng_l2tp_rcvdata_ctrl
static void ng_l2tp_seq_xack_timeout(void *)
static const struct ng_parse_struct_field ng_l2tp_session_stats_type_fields[]
static void ng_l2tp_seq_reset(priv_p priv)
static ng_shutdown_t ng_l2tp_shutdown
struct ng_l2tp_private * priv_p
struct ng_l2tp_hook_private * hookpriv_p
static ng_rcvmsg_t ng_l2tp_rcvmsg
@ NGM_L2TP_GETCLR_SESSION_STATS
@ NGM_L2TP_GET_SESSION_STATS
@ NGM_L2TP_SET_SESS_CONFIG
@ NGM_L2TP_CLR_SESSION_STATS
@ NGM_L2TP_GET_SESS_CONFIG
#define NG_L2TP_HOOK_SESSION_P
#define NG_L2TP_STATS_TYPE_INFO
#define NG_L2TP_NODE_TYPE
#define NG_L2TP_HOOK_CTRL
#define NG_L2TP_CONFIG_TYPE_INFO
#define NG_L2TP_SESS_CONFIG_TYPE_INFO
#define NG_L2TP_SESSION_STATS_TYPE_INFO
#define NG_L2TP_SEQ_CONFIG_TYPE_INFO
#define NG_L2TP_HOOK_LOWER
#define NG_MKRESPONSE(rsp, msg, len, how)
#define NG_MKMESSAGE(msg, cookie, cmdid, len, how)
const struct ng_parse_type ng_parse_struct_type
const struct ng_parse_type ng_parse_hint16_type
const struct ng_parse_type ng_parse_int16_type
static LIST_HEAD(ngatm_msg)
struct callout xack_timer
struct callout rack_timer
struct mbuf * xwin[L2TP_MAX_XWIN]
struct ng_l2tp_session_stats stats
struct ng_l2tp_sess_config conf
struct ng_l2tp_config conf
struct ng_mesg::ng_msghdr header