summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl/l2tp/l2tp.c
diff options
context:
space:
mode:
authorGuillaume Nault <g.nault@alphalink.fr>2014-04-08 23:06:07 +0200
committerDmitry Kozlov <xeb@mail.ru>2014-04-11 06:47:42 +0400
commit4a16913ad3e0ba957fb0c4d440e8f91ae2df01d7 (patch)
tree5d24d7dd53df745303d4c39bcfa57dd9d085f54b /accel-pppd/ctrl/l2tp/l2tp.c
parent634c92cf8acf9e8a0e915c82bb114a791850348a (diff)
downloadaccel-ppp-4a16913ad3e0ba957fb0c4d440e8f91ae2df01d7.tar.gz
accel-ppp-4a16913ad3e0ba957fb0c4d440e8f91ae2df01d7.zip
l2tp: respect peer's receive window
Store the Receive Window Size AVP sent by peer and respect it when sending messages. That is, stop sending messages from send queue once the number of unacknowledged messages is higher than peer's receive window. Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
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 e7b6955..325cfea 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) {