diff options
| author | Alexander Motin <mav@FreeBSD.org> | 2007-10-19 13:09:01 +0000 |
|---|---|---|
| committer | Alexander Motin <mav@FreeBSD.org> | 2007-10-19 13:09:01 +0000 |
| commit | 104e8616c553a75c0d5ba1f0f3a8aef6ffe508cd (patch) | |
| tree | 880766575e2b3c919d389db8524e44bef8ae60a7 /sys/netgraph | |
| parent | b475eb93eb42bd5a09b1236d77d7f8aff299d8bb (diff) | |
Notes
Diffstat (limited to 'sys/netgraph')
| -rw-r--r-- | sys/netgraph/ng_l2tp.c | 257 |
1 files changed, 151 insertions, 106 deletions
diff --git a/sys/netgraph/ng_l2tp.c b/sys/netgraph/ng_l2tp.c index 3838514750a2..88adde2351d0 100644 --- a/sys/netgraph/ng_l2tp.c +++ b/sys/netgraph/ng_l2tp.c @@ -125,11 +125,10 @@ struct l2tp_seq { u_int16_t ssth; /* slow start threshold */ u_int16_t acks; /* # consecutive acks rec'd */ u_int16_t rexmits; /* # retransmits sent */ - u_int16_t max_rexmits; /* max # retransmits sent */ - u_int16_t max_rexmit_to; /* max retransmit timeout */ struct callout rack_timer; /* retransmit timer */ struct callout xack_timer; /* delayed ack timer */ struct mbuf *xwin[L2TP_MAX_XWIN]; /* transmit window */ + struct mtx mtx; /* seq mutex */ }; /* Node private data */ @@ -159,13 +158,11 @@ static ng_rcvmsg_t ng_l2tp_rcvmsg; static ng_shutdown_t ng_l2tp_shutdown; static ng_newhook_t ng_l2tp_newhook; static ng_rcvdata_t ng_l2tp_rcvdata; +static ng_rcvdata_t ng_l2tp_rcvdata_lower; +static ng_rcvdata_t ng_l2tp_rcvdata_ctrl; static ng_disconnect_t ng_l2tp_disconnect; /* Internal functions */ -static int ng_l2tp_recv_lower(node_p node, item_p item); -static int ng_l2tp_recv_ctrl(node_p node, item_p item); -static int ng_l2tp_recv_data(node_p node, item_p item, hookpriv_p hpriv); - static int ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns); static void ng_l2tp_seq_init(priv_p priv); @@ -345,6 +342,8 @@ NETGRAPH_INIT(l2tp, &ng_l2tp_typestruct); /* Whether to use m_copypacket() or m_dup() */ #define L2TP_COPY_MBUF m_copypacket +#define ERROUT(x) do { error = (x); goto done; } while (0) + /************************************************************************ NETGRAPH NODE STUFF ************************************************************************/ @@ -389,10 +388,12 @@ ng_l2tp_newhook(node_p node, hook_p hook, const char *name) if (priv->ctrl != NULL) return (EISCONN); priv->ctrl = hook; + NG_HOOK_SET_RCVDATA(hook, ng_l2tp_rcvdata_ctrl); } else if (strcmp(name, NG_L2TP_HOOK_LOWER) == 0) { if (priv->lower != NULL) return (EISCONN); priv->lower = hook; + NG_HOOK_SET_RCVDATA(hook, ng_l2tp_rcvdata_lower); } else { static const char hexdig[16] = "0123456789abcdef"; u_int16_t session_id; @@ -657,46 +658,6 @@ ng_l2tp_rcvmsg(node_p node, item_p item, hook_p lasthook) } /* - * Receive incoming data on a hook. - */ -static int -ng_l2tp_rcvdata(hook_p hook, item_p item) -{ - const node_p node = NG_HOOK_NODE(hook); - const priv_p priv = NG_NODE_PRIVATE(node); - int error; - - /* Sanity check */ - L2TP_SEQ_CHECK(&priv->seq); - - /* If not configured, reject */ - if (!priv->conf.enabled) { - NG_FREE_ITEM(item); - return (ENXIO); - } - - /* Handle incoming frame from below */ - if (hook == priv->lower) { - error = ng_l2tp_recv_lower(node, item); - goto done; - } - - /* Handle outgoing control frame */ - if (hook == priv->ctrl) { - error = ng_l2tp_recv_ctrl(node, item); - goto done; - } - - /* Handle outgoing data frame */ - error = ng_l2tp_recv_data(node, item, NG_HOOK_PRIVATE(hook)); - -done: - /* Done */ - L2TP_SEQ_CHECK(&priv->seq); - return (error); -} - -/* * Destroy node */ static int @@ -715,6 +676,8 @@ ng_l2tp_shutdown(node_p node) ng_uncallout(&seq->rack_timer, node); ng_uncallout(&seq->xack_timer, node); + mtx_destroy(&seq->mtx); + FREE(priv, M_NETGRAPH_L2TP); /* Unref node */ @@ -787,12 +750,13 @@ ng_l2tp_reset_session(hook_p hook, void *arg) * Handle an incoming frame from below. */ static int -ng_l2tp_recv_lower(node_p node, item_p item) +ng_l2tp_rcvdata_lower(hook_p h, item_p item) { static const u_int16_t req_bits[2][2] = { { L2TP_DATA_0BITS, L2TP_DATA_1BITS }, { L2TP_CTRL_0BITS, L2TP_CTRL_1BITS }, }; + const node_p node = NG_HOOK_NODE(h); const priv_p priv = NG_NODE_PRIVATE(node); hookpriv_p hpriv = NULL; hook_p hook = NULL; @@ -805,6 +769,15 @@ ng_l2tp_recv_lower(node_p node, item_p item) int error; int len, plen; + /* Sanity check */ + L2TP_SEQ_CHECK(&priv->seq); + + /* If not configured, reject */ + if (!priv->conf.enabled) { + NG_FREE_ITEM(item); + ERROUT(ENXIO); + } + /* Grab mbuf */ NGI_GET_M(item, m); @@ -820,12 +793,12 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvRunts++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (EINVAL); + ERROUT(EINVAL); } if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { priv->stats.memoryFailures++; NG_FREE_ITEM(item); - return (EINVAL); + ERROUT(EINVAL); } hdr = ntohs(*mtod(m, u_int16_t *)); m_adj(m, 2); @@ -837,7 +810,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvInvalid++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (EINVAL); + ERROUT(EINVAL); } if (m->m_pkthdr.len < 4 /* tunnel, session id */ + (2 * ((hdr & L2TP_HDR_LEN) != 0)) /* length field */ @@ -846,7 +819,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvRunts++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (EINVAL); + ERROUT(EINVAL); } /* Get and validate length field if present */ @@ -854,7 +827,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { priv->stats.memoryFailures++; NG_FREE_ITEM(item); - return (EINVAL); + ERROUT(EINVAL); } len = (u_int16_t)ntohs(*mtod(m, u_int16_t *)) - 4; m_adj(m, 2); @@ -862,7 +835,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvInvalid++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (EINVAL); + ERROUT(EINVAL); } if (len < m->m_pkthdr.len) /* trim extra bytes */ m_adj(m, -(m->m_pkthdr.len - len)); @@ -872,7 +845,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { priv->stats.memoryFailures++; NG_FREE_ITEM(item); - return (EINVAL); + ERROUT(EINVAL); } memcpy(ids, mtod(m, u_int16_t *), 4); m_adj(m, 4); @@ -883,7 +856,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvWrongTunnel++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (EADDRNOTAVAIL); + ERROUT(EADDRNOTAVAIL); } /* Check session ID (for data packets only) */ @@ -894,7 +867,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvUnknownSID++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (ENOTCONN); + ERROUT(ENOTCONN); } hpriv = NG_HOOK_PRIVATE(hook); } @@ -904,7 +877,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL) { priv->stats.memoryFailures++; NG_FREE_ITEM(item); - return (EINVAL); + ERROUT(EINVAL); } memcpy(&ns, &mtod(m, u_int16_t *)[0], 2); ns = ntohs(ns); @@ -921,7 +894,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) if (m->m_len < 2 && (m = m_pullup(m, 2)) == NULL) { priv->stats.memoryFailures++; NG_FREE_ITEM(item); - return (EINVAL); + ERROUT(EINVAL); } memcpy(&offset, mtod(m, u_int16_t *), 2); offset = ntohs(offset); @@ -931,7 +904,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvInvalid++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (EINVAL); + ERROUT(EINVAL); } m_adj(m, 2+offset); } @@ -947,7 +920,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) priv->stats.recvZLBs++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (0); + ERROUT(0); } /* @@ -960,7 +933,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) if (m == NULL) { priv->stats.memoryFailures++; NG_FREE_ITEM(item); - return (ENOBUFS); + ERROUT(ENOBUFS); } memcpy(mtod(m, u_int16_t *), &ids[1], 2); @@ -968,12 +941,12 @@ ng_l2tp_recv_lower(node_p node, item_p item) if (ng_l2tp_seq_recv_ns(priv, ns) == -1) { NG_FREE_ITEM(item); NG_FREE_M(m); - return (0); + ERROUT(0); } /* Deliver packet to upper layers */ NG_FWD_NEW_DATA(error, item, priv->ctrl, m); - return (error); + ERROUT(error); } /* Per session packet, account it. */ @@ -991,7 +964,7 @@ ng_l2tp_recv_lower(node_p node, item_p item) NG_FREE_ITEM(item); /* duplicate or out of order */ NG_FREE_M(m); priv->stats.recvDataDrops++; - return (0); + ERROUT(0); } hpriv->nr = ns + 1; } @@ -1000,11 +973,14 @@ ng_l2tp_recv_lower(node_p node, item_p item) if (m->m_pkthdr.len == 0) { NG_FREE_ITEM(item); NG_FREE_M(m); - return (0); + ERROUT(0); } /* Deliver data */ NG_FWD_NEW_DATA(error, item, hook, m); +done: + /* Done */ + L2TP_SEQ_CHECK(&priv->seq); return (error); } @@ -1012,12 +988,24 @@ ng_l2tp_recv_lower(node_p node, item_p item) * Handle an outgoing control frame. */ static int -ng_l2tp_recv_ctrl(node_p node, item_p item) +ng_l2tp_rcvdata_ctrl(hook_p hook, item_p item) { + const node_p node = NG_HOOK_NODE(hook); const priv_p priv = NG_NODE_PRIVATE(node); struct l2tp_seq *const seq = &priv->seq; struct mbuf *m; + int error; int i; + u_int16_t ns; + + /* Sanity check */ + L2TP_SEQ_CHECK(&priv->seq); + + /* If not configured, reject */ + if (!priv->conf.enabled) { + NG_FREE_ITEM(item); + ERROUT(ENXIO); + } /* Grab mbuf and discard other stuff XXX */ NGI_GET_M(item, m); @@ -1027,61 +1015,79 @@ ng_l2tp_recv_ctrl(node_p node, item_p item) if (m->m_pkthdr.len < 2) { priv->stats.xmitInvalid++; m_freem(m); - return (EINVAL); + ERROUT(EINVAL); } /* Check max length */ if (m->m_pkthdr.len >= 0x10000 - 14) { priv->stats.xmitTooBig++; m_freem(m); - return (EOVERFLOW); + ERROUT(EOVERFLOW); } + mtx_lock(&seq->mtx); + /* Find next empty slot in transmit queue */ for (i = 0; i < L2TP_MAX_XWIN && seq->xwin[i] != NULL; i++); if (i == L2TP_MAX_XWIN) { + mtx_unlock(&seq->mtx); priv->stats.xmitDrops++; m_freem(m); - return (ENOBUFS); + ERROUT(ENOBUFS); } seq->xwin[i] = m; - /* Sanity check receive ack timer state */ - KASSERT((i == 0) ^ callout_active(&seq->rack_timer), - ("%s: xwin %d full but rack timer %s running", - __func__, i, callout_active(&seq->rack_timer) ? "" : "not ")); - /* If peer's receive window is already full, nothing else to do */ - if (i >= seq->cwnd) - return (0); + if (i >= seq->cwnd) { + mtx_unlock(&seq->mtx); + ERROUT(0); + } /* Start retransmit timer if not already running */ if (!callout_active(&seq->rack_timer)) ng_callout(&seq->rack_timer, node, NULL, hz, ng_l2tp_seq_rack_timeout, NULL, 0); + + ns = seq->ns++; + + mtx_unlock(&seq->mtx); /* Copy packet */ - if ((m = L2TP_COPY_MBUF(seq->xwin[i], M_DONTWAIT)) == NULL) { + if ((m = L2TP_COPY_MBUF(m, M_DONTWAIT)) == NULL) { priv->stats.memoryFailures++; - return (ENOBUFS); + ERROUT(ENOBUFS); } /* Send packet and increment xmit sequence number */ - return (ng_l2tp_xmit_ctrl(priv, m, seq->ns++)); + error = ng_l2tp_xmit_ctrl(priv, m, ns); +done: + /* Done */ + L2TP_SEQ_CHECK(&priv->seq); + return (error); } /* * Handle an outgoing data frame. */ static int -ng_l2tp_recv_data(node_p node, item_p item, hookpriv_p hpriv) +ng_l2tp_rcvdata(hook_p hook, item_p item) { - const priv_p priv = NG_NODE_PRIVATE(node); + const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + const hookpriv_p hpriv = NG_HOOK_PRIVATE(hook); struct mbuf *m; u_int16_t hdr; int error; int i = 1; + /* Sanity check */ + L2TP_SEQ_CHECK(&priv->seq); + + /* If not configured, reject */ + if (!priv->conf.enabled) { + NG_FREE_ITEM(item); + ERROUT(ENXIO); + } + /* Get mbuf */ NGI_GET_M(item, m); @@ -1090,7 +1096,7 @@ ng_l2tp_recv_data(node_p node, item_p item, hookpriv_p hpriv) priv->stats.xmitDataTooBig++; NG_FREE_ITEM(item); NG_FREE_M(m); - return (EOVERFLOW); + ERROUT(EOVERFLOW); } /* Prepend L2TP header */ @@ -1101,7 +1107,7 @@ ng_l2tp_recv_data(node_p node, item_p item, hookpriv_p hpriv) if (m == NULL) { priv->stats.memoryFailures++; NG_FREE_ITEM(item); - return (ENOBUFS); + ERROUT(ENOBUFS); } hdr = L2TP_DATA_HDR; if (hpriv->conf.include_length) { @@ -1128,6 +1134,9 @@ ng_l2tp_recv_data(node_p node, item_p item, hookpriv_p hpriv) /* Send packet */ NG_FWD_NEW_DATA(error, item, priv->lower, m); +done: + /* Done */ + L2TP_SEQ_CHECK(&priv->seq); return (error); } @@ -1166,10 +1175,9 @@ ng_l2tp_seq_init(priv_p priv) if (seq->wmax > L2TP_MAX_XWIN) seq->wmax = L2TP_MAX_XWIN; seq->ssth = seq->wmax; - seq->max_rexmits = priv->conf.rexmit_max; - seq->max_rexmit_to = priv->conf.rexmit_max_to; ng_callout_init(&seq->rack_timer); ng_callout_init(&seq->xack_timer); + mtx_init(&seq->mtx, "ng_l2tp", NULL, MTX_DEF); L2TP_SEQ_CHECK(seq); } @@ -1223,10 +1231,6 @@ ng_l2tp_seq_adjust(priv_p priv, const struct ng_l2tp_config *conf) return (EBUSY); seq->wmax = new_wmax; - /* Update retransmit parameters */ - seq->max_rexmits = conf->rexmit_max; - seq->max_rexmit_to = conf->rexmit_max_to; - /* Done */ return (0); } @@ -1259,10 +1263,16 @@ ng_l2tp_seq_reset(priv_p priv) NG_NODE_FOREACH_HOOK(priv->node, ng_l2tp_reset_session, NULL, hook); /* Reset node's sequence number state */ - memset(seq, 0, sizeof(*seq)); - seq->cwnd = 1; + seq->ns = 0; + seq->nr = 0; + seq->rack = 0; + seq->xack = 0; seq->wmax = L2TP_MAX_XWIN; + seq->cwnd = 1; seq->ssth = seq->wmax; + seq->acks = 0; + seq->rexmits = 0; + bzero(seq->xwin, sizeof(seq->xwin)); /* Done */ L2TP_SEQ_CHECK(seq); @@ -1275,14 +1285,20 @@ static void ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr) { struct l2tp_seq *const seq = &priv->seq; - struct mbuf *m; - int nack; - int i; + struct mbuf *xwin[L2TP_MAX_XWIN]; /* partial local copy */ + int nack; + int i, j; + uint16_t ns; + + mtx_lock(&seq->mtx); /* Verify peer's ACK is in range */ - if ((nack = L2TP_SEQ_DIFF(nr, seq->rack)) <= 0) + if ((nack = L2TP_SEQ_DIFF(nr, seq->rack)) <= 0) { + mtx_unlock(&seq->mtx); return; /* duplicate ack */ + } if (L2TP_SEQ_DIFF(nr, seq->ns) > 0) { + mtx_unlock(&seq->mtx); priv->stats.recvBadAcks++; /* ack for packet not sent */ return; } @@ -1334,8 +1350,10 @@ ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr) ng_uncallout(&seq->rack_timer, priv->node); /* If transmit queue is empty, we're done for now */ - if (seq->xwin[0] == NULL) + if (seq->xwin[0] == NULL) { + mtx_unlock(&seq->mtx); return; + } /* Start restransmit timer again */ ng_callout(&seq->rack_timer, priv->node, NULL, @@ -1343,16 +1361,30 @@ ng_l2tp_seq_recv_nr(priv_p priv, u_int16_t nr) /* * Send more packets, trying to keep peer's receive window full. - * If there is a memory error, pretend packet was sent, as it - * will get retransmitted later anyway. + * Make copy of everything we need before lock release. */ + ns = seq->ns; + j = 0; while ((i = L2TP_SEQ_DIFF(seq->ns, seq->rack)) < seq->cwnd && seq->xwin[i] != NULL) { - if ((m = L2TP_COPY_MBUF(seq->xwin[i], M_DONTWAIT)) == NULL) + xwin[j++] = seq->xwin[i]; + seq->ns++; + } + + mtx_unlock(&seq->mtx); + + /* + * Send prepared. + * If there is a memory error, pretend packet was sent, as it + * will get retransmitted later anyway. + */ + for (i = 0; i < j; i++) { + struct mbuf *m; + if ((m = L2TP_COPY_MBUF(xwin[i], M_DONTWAIT)) == NULL) priv->stats.memoryFailures++; else - ng_l2tp_xmit_ctrl(priv, m, seq->ns); - seq->ns++; + ng_l2tp_xmit_ctrl(priv, m, ns); + ns++; } } @@ -1381,6 +1413,8 @@ ng_l2tp_seq_recv_ns(priv_p priv, u_int16_t ns) return (-1); } + mtx_lock(&seq->mtx); + /* Update recv sequence number */ seq->nr++; @@ -1389,6 +1423,8 @@ ng_l2tp_seq_recv_ns(priv_p priv, u_int16_t ns) ng_callout(&seq->xack_timer, priv->node, NULL, L2TP_DELAYED_ACK, ng_l2tp_seq_xack_timeout, NULL, 0); + mtx_unlock(&seq->mtx); + /* Accept packet */ return (0); } @@ -1444,13 +1480,13 @@ ng_l2tp_seq_rack_timeout(node_p node, hook_p hook, void *arg1, int arg2) priv->stats.xmitRetransmits++; /* Have we reached the retransmit limit? If so, notify owner. */ - if (seq->rexmits++ >= seq->max_rexmits) + if (seq->rexmits++ >= priv->conf.rexmit_max) ng_l2tp_seq_failure(priv); /* Restart timer, this time with an increased delay */ delay = (seq->rexmits > 12) ? (1 << 12) : (1 << seq->rexmits); - if (delay > seq->max_rexmit_to) - delay = seq->max_rexmit_to; + if (delay > priv->conf.rexmit_max_to) + delay = priv->conf.rexmit_max_to; ng_callout(&seq->rack_timer, node, NULL, hz * delay, ng_l2tp_seq_rack_timeout, NULL, 0); @@ -1483,6 +1519,8 @@ ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns) u_int16_t session_id = 0; int error; + mtx_lock(&seq->mtx); + /* Stop ack timer: we're sending an ack with this packet. Doing this before to keep state predictable after error. */ if (callout_active(&seq->xack_timer)) @@ -1490,6 +1528,8 @@ ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns) seq->xack = seq->nr; + mtx_unlock(&seq->mtx); + /* If no mbuf passed, send an empty packet (ZLB) */ if (m == NULL) { @@ -1544,12 +1584,15 @@ ng_l2tp_xmit_ctrl(priv_p priv, struct mbuf *m, u_int16_t ns) static void ng_l2tp_seq_check(struct l2tp_seq *seq) { - const int self_unack = L2TP_SEQ_DIFF(seq->nr, seq->xack); - const int peer_unack = L2TP_SEQ_DIFF(seq->ns, seq->rack); + int self_unack, peer_unack; int i; #define CHECK(p) KASSERT((p), ("%s: not: %s", __func__, #p)) + mtx_lock(&seq->mtx); + + self_unack = L2TP_SEQ_DIFF(seq->nr, seq->xack); + peer_unack = L2TP_SEQ_DIFF(seq->ns, seq->rack); CHECK(seq->wmax <= L2TP_MAX_XWIN); CHECK(seq->cwnd >= 1); CHECK(seq->cwnd <= seq->wmax); @@ -1569,6 +1612,8 @@ ng_l2tp_seq_check(struct l2tp_seq *seq) for ( ; i < seq->cwnd; i++) /* verify peer's recv window full */ CHECK(seq->xwin[i] == NULL); + mtx_unlock(&seq->mtx); + #undef CHECK } #endif /* INVARIANTS */ |
