Skip to content

Commit 31b02e1

Browse files
author
Vlad Yasevich
committed
sctp: Failover transmitted list on transport delete
Add-IP feature allows users to delete an active transport. If that transport has chunks in flight, those chunks need to be moved to another transport or association may get into unrecoverable state. Reported-by: Rafael Laufer <[email protected]> Signed-off-by: Vlad Yasevich <[email protected]>
1 parent f68b2e0 commit 31b02e1

File tree

3 files changed

+67
-13
lines changed

3 files changed

+67
-13
lines changed

net/sctp/associola.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,33 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
584584
asoc->addip_last_asconf->transport == peer)
585585
asoc->addip_last_asconf->transport = NULL;
586586

587+
/* If we have something on the transmitted list, we have to
588+
* save it off. The best place is the active path.
589+
*/
590+
if (!list_empty(&peer->transmitted)) {
591+
struct sctp_transport *active = asoc->peer.active_path;
592+
struct sctp_chunk *ch;
593+
594+
/* Reset the transport of each chunk on this list */
595+
list_for_each_entry(ch, &peer->transmitted,
596+
transmitted_list) {
597+
ch->transport = NULL;
598+
ch->rtt_in_progress = 0;
599+
}
600+
601+
list_splice_tail_init(&peer->transmitted,
602+
&active->transmitted);
603+
604+
/* Start a T3 timer here in case it wasn't running so
605+
* that these migrated packets have a chance to get
606+
* retrnasmitted.
607+
*/
608+
if (!timer_pending(&active->T3_rtx_timer))
609+
if (!mod_timer(&active->T3_rtx_timer,
610+
jiffies + active->rto))
611+
sctp_transport_hold(active);
612+
}
613+
587614
asoc->peer.transport_count--;
588615

589616
sctp_transport_free(peer);

net/sctp/outqueue.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,9 @@ void sctp_retransmit_mark(struct sctp_outq *q,
406406
* not be retransmitted
407407
*/
408408
if (!chunk->tsn_gap_acked) {
409-
chunk->transport->flight_size -=
410-
sctp_data_size(chunk);
409+
if (chunk->transport)
410+
chunk->transport->flight_size -=
411+
sctp_data_size(chunk);
411412
q->outstanding_bytes -= sctp_data_size(chunk);
412413
q->asoc->peer.rwnd += (sctp_data_size(chunk) +
413414
sizeof(struct sk_buff));
@@ -443,7 +444,8 @@ void sctp_retransmit_mark(struct sctp_outq *q,
443444
q->asoc->peer.rwnd += (sctp_data_size(chunk) +
444445
sizeof(struct sk_buff));
445446
q->outstanding_bytes -= sctp_data_size(chunk);
446-
transport->flight_size -= sctp_data_size(chunk);
447+
if (chunk->transport)
448+
transport->flight_size -= sctp_data_size(chunk);
447449

448450
/* sctpimpguide-05 Section 2.8.2
449451
* M5) If a T3-rtx timer expires, the
@@ -1310,6 +1312,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
13101312
__u32 rtt;
13111313
__u8 restart_timer = 0;
13121314
int bytes_acked = 0;
1315+
int migrate_bytes = 0;
13131316

13141317
/* These state variables are for coherent debug output. --xguo */
13151318

@@ -1343,8 +1346,9 @@ static void sctp_check_transmitted(struct sctp_outq *q,
13431346
* considering it as 'outstanding'.
13441347
*/
13451348
if (!tchunk->tsn_gap_acked) {
1346-
tchunk->transport->flight_size -=
1347-
sctp_data_size(tchunk);
1349+
if (tchunk->transport)
1350+
tchunk->transport->flight_size -=
1351+
sctp_data_size(tchunk);
13481352
q->outstanding_bytes -= sctp_data_size(tchunk);
13491353
}
13501354
continue;
@@ -1378,6 +1382,20 @@ static void sctp_check_transmitted(struct sctp_outq *q,
13781382
rtt);
13791383
}
13801384
}
1385+
1386+
/* If the chunk hasn't been marked as ACKED,
1387+
* mark it and account bytes_acked if the
1388+
* chunk had a valid transport (it will not
1389+
* have a transport if ASCONF had deleted it
1390+
* while DATA was outstanding).
1391+
*/
1392+
if (!tchunk->tsn_gap_acked) {
1393+
tchunk->tsn_gap_acked = 1;
1394+
bytes_acked += sctp_data_size(tchunk);
1395+
if (!tchunk->transport)
1396+
migrate_bytes += sctp_data_size(tchunk);
1397+
}
1398+
13811399
if (TSN_lte(tsn, sack_ctsn)) {
13821400
/* RFC 2960 6.3.2 Retransmission Timer Rules
13831401
*
@@ -1391,8 +1409,6 @@ static void sctp_check_transmitted(struct sctp_outq *q,
13911409
restart_timer = 1;
13921410

13931411
if (!tchunk->tsn_gap_acked) {
1394-
tchunk->tsn_gap_acked = 1;
1395-
bytes_acked += sctp_data_size(tchunk);
13961412
/*
13971413
* SFR-CACC algorithm:
13981414
* 2) If the SACK contains gap acks
@@ -1432,10 +1448,6 @@ static void sctp_check_transmitted(struct sctp_outq *q,
14321448
* older than that newly acknowledged DATA
14331449
* chunk, are qualified as 'Stray DATA chunks'.
14341450
*/
1435-
if (!tchunk->tsn_gap_acked) {
1436-
tchunk->tsn_gap_acked = 1;
1437-
bytes_acked += sctp_data_size(tchunk);
1438-
}
14391451
list_add_tail(lchunk, &tlist);
14401452
}
14411453

@@ -1491,7 +1503,8 @@ static void sctp_check_transmitted(struct sctp_outq *q,
14911503
tsn);
14921504
tchunk->tsn_gap_acked = 0;
14931505

1494-
bytes_acked -= sctp_data_size(tchunk);
1506+
if (tchunk->transport)
1507+
bytes_acked -= sctp_data_size(tchunk);
14951508

14961509
/* RFC 2960 6.3.2 Retransmission Timer Rules
14971510
*
@@ -1561,6 +1574,14 @@ static void sctp_check_transmitted(struct sctp_outq *q,
15611574
#endif /* SCTP_DEBUG */
15621575
if (transport) {
15631576
if (bytes_acked) {
1577+
/* We may have counted DATA that was migrated
1578+
* to this transport due to DEL-IP operation.
1579+
* Subtract those bytes, since the were never
1580+
* send on this transport and shouldn't be
1581+
* credited to this transport.
1582+
*/
1583+
bytes_acked -= migrate_bytes;
1584+
15641585
/* 8.2. When an outstanding TSN is acknowledged,
15651586
* the endpoint shall clear the error counter of
15661587
* the destination transport address to which the
@@ -1589,7 +1610,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
15891610
transport->flight_size -= bytes_acked;
15901611
if (transport->flight_size == 0)
15911612
transport->partial_bytes_acked = 0;
1592-
q->outstanding_bytes -= bytes_acked;
1613+
q->outstanding_bytes -= bytes_acked + migrate_bytes;
15931614
} else {
15941615
/* RFC 2960 6.1, sctpimpguide-06 2.15.2
15951616
* When a sender is doing zero window probing, it

net/sctp/sm_statefuns.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,6 +3543,12 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
35433543
asconf_ack = sctp_assoc_lookup_asconf_ack(asoc, hdr->serial);
35443544
if (!asconf_ack)
35453545
return SCTP_DISPOSITION_DISCARD;
3546+
3547+
/* Reset the transport so that we select the correct one
3548+
* this time around. This is to make sure that we don't
3549+
* accidentally use a stale transport that's been removed.
3550+
*/
3551+
asconf_ack->transport = NULL;
35463552
} else {
35473553
/* ADDIP 5.2 E5) Otherwise, the ASCONF Chunk is discarded since
35483554
* it must be either a stale packet or from an attacker.

0 commit comments

Comments
 (0)