summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl/l2tp/l2tp.c
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/ctrl/l2tp/l2tp.c')
-rw-r--r--accel-pppd/ctrl/l2tp/l2tp.c66
1 files changed, 64 insertions, 2 deletions
diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c
index e7b6955c..325cfeab 100644
--- a/accel-pppd/ctrl/l2tp/l2tp.c
+++ b/accel-pppd/ctrl/l2tp/l2tp.c
@@ -53,6 +53,20 @@
#define APSTATE_STARTED 3
#define APSTATE_FINISHING 4
+/* Default size of receive window for peer not sending the
+ * Receive Window Size AVP (defined in RFC 2661 section 4.4.3).
+ */
+#define DEFAULT_PEER_RECV_WINDOW_SIZE 4
+
+/* Maximum value of the Receive Window Size AVP.
+ * The Ns field value of received messages must lie in the range of the tunnel
+ * Nr field and the following 32768 values inclusive. Other values mean that
+ * the message is a duplicate (see comment in nsnr_cmp()).
+ * So it wouldn't make sense to have a receive window larger than 32768, as
+ * messages that could fill the 32768+ slots would be rejected as duplicates.
+ */
+#define RECV_WINDOW_SIZE_MAX 32768
+
#define DEFAULT_PPP_MAX_MTU 1420
int conf_verbose = 0;
@@ -145,6 +159,8 @@ struct l2tp_conn_t
uint16_t peer_Nr;
struct list_head send_queue;
struct list_head rtms_queue;
+ unsigned int send_queue_len;
+ uint16_t peer_rcv_wnd_sz;
unsigned int ref_count;
int state;
@@ -446,6 +462,7 @@ static void l2tp_tunnel_clear_sendqueue(struct l2tp_conn_t *conn)
list_del(&pack->entry);
l2tp_packet_free(pack);
}
+ conn->send_queue_len = 0;
}
static void l2tp_session_clear_sendqueue(struct l2tp_sess_t *sess)
@@ -457,6 +474,7 @@ static void l2tp_session_clear_sendqueue(struct l2tp_sess_t *sess)
sess_entry);
list_del(&pack->sess_entry);
list_del(&pack->entry);
+ --sess->paren_conn->send_queue_len;
l2tp_packet_free(pack);
}
}
@@ -549,11 +567,14 @@ static int l2tp_tunnel_clean_rtmsqueue(struct l2tp_conn_t *conn)
static int l2tp_tunnel_push_sendqueue(struct l2tp_conn_t *conn)
{
struct l2tp_packet_t *pack;
+ uint16_t Nr_max = conn->peer_Nr + conn->peer_rcv_wnd_sz;
unsigned int pkt_sent = 0;
while (!list_empty(&conn->send_queue)) {
pack = list_first_entry(&conn->send_queue, typeof(*pack),
entry);
+ if (nsnr_cmp(conn->Ns, Nr_max) >= 0)
+ break;
pack->hdr.Ns = htons(conn->Ns);
@@ -571,6 +592,7 @@ static int l2tp_tunnel_push_sendqueue(struct l2tp_conn_t *conn)
pack->sess_entry.prev = NULL;
}
list_move_tail(&pack->entry, &conn->rtms_queue);
+ --conn->send_queue_len;
++conn->Ns;
++pkt_sent;
}
@@ -578,8 +600,15 @@ static int l2tp_tunnel_push_sendqueue(struct l2tp_conn_t *conn)
log_tunnel(log_debug, conn, "%u message%s sent from send queue\n",
pkt_sent, pkt_sent > 1 ? "s" : "");
- if (pkt_sent == 0)
+ if (pkt_sent == 0) {
+ if (!list_empty(&conn->send_queue))
+ log_tunnel(log_info2, conn,
+ "no message sent while processing the send queue (%u outstanding messages):"
+ " peer's receive window is full (%hu messages)\n",
+ conn->send_queue_len, conn->peer_rcv_wnd_sz);
+
return 0;
+ }
/* At least one message sent, restart retransmission timer if necessary
* (timer may be stopped, e.g. because there was no message left in the
@@ -612,6 +641,7 @@ static int l2tp_tunnel_send(struct l2tp_conn_t *conn,
pack->hdr.tid = htons(conn->peer_tid);
list_add_tail(&pack->entry, &conn->send_queue);
+ ++conn->send_queue_len;
return 0;
}
@@ -1607,6 +1637,7 @@ static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer,
conn->lns_mode = lns_mode;
conn->port_set = port_set;
conn->hide_avps = hide_avps;
+ conn->peer_rcv_wnd_sz = DEFAULT_PEER_RECV_WINDOW_SIZE;
tunnel_hold(conn);
__sync_add_and_fetch(&stat_conn_starting, 1);
@@ -2503,6 +2534,7 @@ static int l2tp_recv_SCCRQ(const struct l2tp_serv_t *serv,
const struct l2tp_attr_t *assigned_cid = NULL;
const struct l2tp_attr_t *framing_cap = NULL;
const struct l2tp_attr_t *router_id = NULL;
+ const struct l2tp_attr_t *recv_window_size = NULL;
const struct l2tp_attr_t *challenge = NULL;
struct l2tp_conn_t *conn = NULL;
struct sockaddr_in host_addr = { 0 };
@@ -2539,6 +2571,9 @@ static int l2tp_recv_SCCRQ(const struct l2tp_serv_t *serv,
case Assigned_Tunnel_ID:
assigned_tid = attr;
break;
+ case Recv_Window_Size:
+ recv_window_size = attr;
+ break;
case Challenge:
challenge = attr;
break;
@@ -2594,6 +2629,18 @@ static int l2tp_recv_SCCRQ(const struct l2tp_serv_t *serv,
}
tid = conn->tid;
+ if (recv_window_size) {
+ conn->peer_rcv_wnd_sz = recv_window_size->val.uint16;
+ if (conn->peer_rcv_wnd_sz == 0 ||
+ conn->peer_rcv_wnd_sz > RECV_WINDOW_SIZE_MAX) {
+ log_error("l2tp: impossible to handle SCCRQ from %s:"
+ " invalid Receive Window Size %hu\n",
+ src_addr, conn->peer_rcv_wnd_sz);
+ l2tp_tunnel_free(conn);
+ return -1;
+ }
+ }
+
if (conf_secret) {
conn->secret = _strdup(conf_secret);
if (conn->secret == NULL) {
@@ -2647,6 +2694,7 @@ static int l2tp_recv_SCCRP(struct l2tp_conn_t *conn,
const struct l2tp_attr_t *protocol_version = NULL;
const struct l2tp_attr_t *assigned_tid = NULL;
const struct l2tp_attr_t *framing_cap = NULL;
+ const struct l2tp_attr_t *recv_window_size = NULL;
const struct l2tp_attr_t *challenge = NULL;
const struct l2tp_attr_t *challenge_resp = NULL;
const struct l2tp_attr_t *unknown_attr = NULL;
@@ -2668,7 +2716,6 @@ static int l2tp_recv_SCCRP(struct l2tp_conn_t *conn,
case Bearer_Capabilities:
case Firmware_Revision:
case Vendor_Name:
- case Recv_Window_Size:
break;
case Protocol_Version:
protocol_version = attr;
@@ -2679,6 +2726,9 @@ static int l2tp_recv_SCCRP(struct l2tp_conn_t *conn,
case Assigned_Tunnel_ID:
assigned_tid = attr;
break;
+ case Recv_Window_Size:
+ recv_window_size = attr;
+ break;
case Challenge:
challenge = attr;
break;
@@ -2741,6 +2791,18 @@ static int l2tp_recv_SCCRP(struct l2tp_conn_t *conn,
l2tp_tunnel_disconnect(conn, 5, 0);
return -1;
}
+ if (recv_window_size) {
+ conn->peer_rcv_wnd_sz = recv_window_size->val.uint16;
+ if (conn->peer_rcv_wnd_sz == 0 ||
+ conn->peer_rcv_wnd_sz > RECV_WINDOW_SIZE_MAX) {
+ log_error("impossible to handle SCCRP:"
+ " invalid Receive Window Size %hu\n",
+ conn->peer_rcv_wnd_sz);
+ conn->peer_rcv_wnd_sz = DEFAULT_PEER_RECV_WINDOW_SIZE;
+ l2tp_tunnel_disconnect(conn, 2, 3);
+ return -1;
+ }
+ }
if (l2tp_tunnel_checkchallresp(Message_Type_Start_Ctrl_Conn_Reply,
conn, challenge_resp) < 0) {