aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp
diff options
context:
space:
mode:
authorVlad Yasevich <vladislav.yasevich@hp.com>2007-12-20 17:11:47 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:59:23 -0500
commita08de64d074b36a56ee3bb985cd171281db78e96 (patch)
tree15b3c77a7078b5dc0510a23cc7d95922f7ad420a /net/sctp
parentba8a06daed7d7c8785c92c343da9e202e6988fda (diff)
[SCTP]: Update ASCONF processing to conform to spec.
The processing of the ASCONF chunks has changed a lot in the spec. New items are: 1. A list of ASCONF-ACK chunks is now cached 2. The source of the packet is used in response. 3. New handling for unexpect ASCONF chunks. Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/associola.c58
-rw-r--r--net/sctp/outqueue.c29
-rw-r--r--net/sctp/sm_make_chunk.c12
-rw-r--r--net/sctp/sm_statefuns.c64
4 files changed, 130 insertions, 33 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 61bebb9b96e6..a016e78061f4 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -61,6 +61,7 @@
61 61
62/* Forward declarations for internal functions. */ 62/* Forward declarations for internal functions. */
63static void sctp_assoc_bh_rcv(struct work_struct *work); 63static void sctp_assoc_bh_rcv(struct work_struct *work);
64static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc);
64 65
65 66
66/* 1st Level Abstractions. */ 67/* 1st Level Abstractions. */
@@ -242,6 +243,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
242 asoc->addip_serial = asoc->c.initial_tsn; 243 asoc->addip_serial = asoc->c.initial_tsn;
243 244
244 INIT_LIST_HEAD(&asoc->addip_chunk_list); 245 INIT_LIST_HEAD(&asoc->addip_chunk_list);
246 INIT_LIST_HEAD(&asoc->asconf_ack_list);
245 247
246 /* Make an empty list of remote transport addresses. */ 248 /* Make an empty list of remote transport addresses. */
247 INIT_LIST_HEAD(&asoc->peer.transport_addr_list); 249 INIT_LIST_HEAD(&asoc->peer.transport_addr_list);
@@ -431,8 +433,7 @@ void sctp_association_free(struct sctp_association *asoc)
431 asoc->peer.transport_count = 0; 433 asoc->peer.transport_count = 0;
432 434
433 /* Free any cached ASCONF_ACK chunk. */ 435 /* Free any cached ASCONF_ACK chunk. */
434 if (asoc->addip_last_asconf_ack) 436 sctp_assoc_free_asconf_acks(asoc);
435 sctp_chunk_free(asoc->addip_last_asconf_ack);
436 437
437 /* Free any cached ASCONF chunk. */ 438 /* Free any cached ASCONF chunk. */
438 if (asoc->addip_last_asconf) 439 if (asoc->addip_last_asconf)
@@ -1485,3 +1486,56 @@ retry:
1485 asoc->assoc_id = (sctp_assoc_t) assoc_id; 1486 asoc->assoc_id = (sctp_assoc_t) assoc_id;
1486 return error; 1487 return error;
1487} 1488}
1489
1490/* Free asconf_ack cache */
1491static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc)
1492{
1493 struct sctp_chunk *ack;
1494 struct sctp_chunk *tmp;
1495
1496 list_for_each_entry_safe(ack, tmp, &asoc->asconf_ack_list,
1497 transmitted_list) {
1498 list_del_init(&ack->transmitted_list);
1499 sctp_chunk_free(ack);
1500 }
1501}
1502
1503/* Clean up the ASCONF_ACK queue */
1504void sctp_assoc_clean_asconf_ack_cache(const struct sctp_association *asoc)
1505{
1506 struct sctp_chunk *ack;
1507 struct sctp_chunk *tmp;
1508
1509 /* We can remove all the entries from the queue upto
1510 * the "Peer-Sequence-Number".
1511 */
1512 list_for_each_entry_safe(ack, tmp, &asoc->asconf_ack_list,
1513 transmitted_list) {
1514 if (ack->subh.addip_hdr->serial ==
1515 htonl(asoc->peer.addip_serial))
1516 break;
1517
1518 list_del_init(&ack->transmitted_list);
1519 sctp_chunk_free(ack);
1520 }
1521}
1522
1523/* Find the ASCONF_ACK whose serial number matches ASCONF */
1524struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
1525 const struct sctp_association *asoc,
1526 __be32 serial)
1527{
1528 struct sctp_chunk *ack = NULL;
1529
1530 /* Walk through the list of cached ASCONF-ACKs and find the
1531 * ack chunk whose serial number matches that of the request.
1532 */
1533 list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
1534 if (ack->subh.addip_hdr->serial == serial) {
1535 sctp_chunk_hold(ack);
1536 break;
1537 }
1538 }
1539
1540 return ack;
1541}
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index fa76f235169b..a42af865c2ef 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -716,7 +716,29 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
716 new_transport = chunk->transport; 716 new_transport = chunk->transport;
717 717
718 if (!new_transport) { 718 if (!new_transport) {
719 new_transport = asoc->peer.active_path; 719 /*
720 * If we have a prior transport pointer, see if
721 * the destination address of the chunk
722 * matches the destination address of the
723 * current transport. If not a match, then
724 * try to look up the transport with a given
725 * destination address. We do this because
726 * after processing ASCONFs, we may have new
727 * transports created.
728 */
729 if (transport &&
730 sctp_cmp_addr_exact(&chunk->dest,
731 &transport->ipaddr))
732 new_transport = transport;
733 else
734 new_transport = sctp_assoc_lookup_paddr(asoc,
735 &chunk->dest);
736
737 /* if we still don't have a new transport, then
738 * use the current active path.
739 */
740 if (!new_transport)
741 new_transport = asoc->peer.active_path;
720 } else if ((new_transport->state == SCTP_INACTIVE) || 742 } else if ((new_transport->state == SCTP_INACTIVE) ||
721 (new_transport->state == SCTP_UNCONFIRMED)) { 743 (new_transport->state == SCTP_UNCONFIRMED)) {
722 /* If the chunk is Heartbeat or Heartbeat Ack, 744 /* If the chunk is Heartbeat or Heartbeat Ack,
@@ -729,9 +751,12 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
729 * address of the IP datagram containing the 751 * address of the IP datagram containing the
730 * HEARTBEAT chunk to which this ack is responding. 752 * HEARTBEAT chunk to which this ack is responding.
731 * ... 753 * ...
754 *
755 * ASCONF_ACKs also must be sent to the source.
732 */ 756 */
733 if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT && 757 if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT &&
734 chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT_ACK) 758 chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT_ACK &&
759 chunk->chunk_hdr->type != SCTP_CID_ASCONF_ACK)
735 new_transport = asoc->peer.active_path; 760 new_transport = asoc->peer.active_path;
736 } 761 }
737 762
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 8138bbd93210..7fd6a6b68614 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1275,6 +1275,9 @@ nodata:
1275/* Release the memory occupied by a chunk. */ 1275/* Release the memory occupied by a chunk. */
1276static void sctp_chunk_destroy(struct sctp_chunk *chunk) 1276static void sctp_chunk_destroy(struct sctp_chunk *chunk)
1277{ 1277{
1278 BUG_ON(!list_empty(&chunk->list));
1279 list_del_init(&chunk->transmitted_list);
1280
1278 /* Free the chunk skb data and the SCTP_chunk stub itself. */ 1281 /* Free the chunk skb data and the SCTP_chunk stub itself. */
1279 dev_kfree_skb(chunk->skb); 1282 dev_kfree_skb(chunk->skb);
1280 1283
@@ -1285,9 +1288,6 @@ static void sctp_chunk_destroy(struct sctp_chunk *chunk)
1285/* Possibly, free the chunk. */ 1288/* Possibly, free the chunk. */
1286void sctp_chunk_free(struct sctp_chunk *chunk) 1289void sctp_chunk_free(struct sctp_chunk *chunk)
1287{ 1290{
1288 BUG_ON(!list_empty(&chunk->list));
1289 list_del_init(&chunk->transmitted_list);
1290
1291 /* Release our reference on the message tracker. */ 1291 /* Release our reference on the message tracker. */
1292 if (chunk->msg) 1292 if (chunk->msg)
1293 sctp_datamsg_put(chunk->msg); 1293 sctp_datamsg_put(chunk->msg);
@@ -2980,11 +2980,9 @@ done:
2980 * after freeing the reference to old asconf ack if any. 2980 * after freeing the reference to old asconf ack if any.
2981 */ 2981 */
2982 if (asconf_ack) { 2982 if (asconf_ack) {
2983 if (asoc->addip_last_asconf_ack)
2984 sctp_chunk_free(asoc->addip_last_asconf_ack);
2985
2986 sctp_chunk_hold(asconf_ack); 2983 sctp_chunk_hold(asconf_ack);
2987 asoc->addip_last_asconf_ack = asconf_ack; 2984 list_add_tail(&asconf_ack->transmitted_list,
2985 &asoc->asconf_ack_list);
2988 } 2986 }
2989 2987
2990 return asconf_ack; 2988 return asconf_ack;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index b6aaa7e97d82..a1be9d93f1a8 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -3402,48 +3402,68 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
3402 3402
3403 /* Verify the ASCONF chunk before processing it. */ 3403 /* Verify the ASCONF chunk before processing it. */
3404 if (!sctp_verify_asconf(asoc, 3404 if (!sctp_verify_asconf(asoc,
3405 (sctp_paramhdr_t *)((void *)addr_param + length), 3405 (sctp_paramhdr_t *)((void *)addr_param + length),
3406 (void *)chunk->chunk_end, 3406 (void *)chunk->chunk_end,
3407 &err_param)) 3407 &err_param))
3408 return sctp_sf_violation_paramlen(ep, asoc, type, 3408 return sctp_sf_violation_paramlen(ep, asoc, type,
3409 (void *)&err_param, commands); 3409 (void *)&err_param, commands);
3410 3410
3411 /* ADDIP 4.2 C1) Compare the value of the serial number to the value 3411 /* ADDIP 5.2 E1) Compare the value of the serial number to the value
3412 * the endpoint stored in a new association variable 3412 * the endpoint stored in a new association variable
3413 * 'Peer-Serial-Number'. 3413 * 'Peer-Serial-Number'.
3414 */ 3414 */
3415 if (serial == asoc->peer.addip_serial + 1) { 3415 if (serial == asoc->peer.addip_serial + 1) {
3416 /* ADDIP 4.2 C2) If the value found in the serial number is 3416 /* If this is the first instance of ASCONF in the packet,
3417 * equal to the ('Peer-Serial-Number' + 1), the endpoint MUST 3417 * we can clean our old ASCONF-ACKs.
3418 * do V1-V5. 3418 */
3419 if (!chunk->has_asconf)
3420 sctp_assoc_clean_asconf_ack_cache(asoc);
3421
3422 /* ADDIP 5.2 E4) When the Sequence Number matches the next one
3423 * expected, process the ASCONF as described below and after
3424 * processing the ASCONF Chunk, append an ASCONF-ACK Chunk to
3425 * the response packet and cache a copy of it (in the event it
3426 * later needs to be retransmitted).
3427 *
3428 * Essentially, do V1-V5.
3419 */ 3429 */
3420 asconf_ack = sctp_process_asconf((struct sctp_association *) 3430 asconf_ack = sctp_process_asconf((struct sctp_association *)
3421 asoc, chunk); 3431 asoc, chunk);
3422 if (!asconf_ack) 3432 if (!asconf_ack)
3423 return SCTP_DISPOSITION_NOMEM; 3433 return SCTP_DISPOSITION_NOMEM;
3424 } else if (serial == asoc->peer.addip_serial) { 3434 } else if (serial < asoc->peer.addip_serial + 1) {
3425 /* ADDIP 4.2 C3) If the value found in the serial number is 3435 /* ADDIP 5.2 E2)
3426 * equal to the value stored in the 'Peer-Serial-Number' 3436 * If the value found in the Sequence Number is less than the
3427 * IMPLEMENTATION NOTE: As an optimization a receiver may wish 3437 * ('Peer- Sequence-Number' + 1), simply skip to the next
3428 * to save the last ASCONF-ACK for some predetermined period of 3438 * ASCONF, and include in the outbound response packet
3429 * time and instead of re-processing the ASCONF (with the same 3439 * any previously cached ASCONF-ACK response that was
3430 * serial number) it may just re-transmit the ASCONF-ACK. 3440 * sent and saved that matches the Sequence Number of the
3441 * ASCONF. Note: It is possible that no cached ASCONF-ACK
3442 * Chunk exists. This will occur when an older ASCONF
3443 * arrives out of order. In such a case, the receiver
3444 * should skip the ASCONF Chunk and not include ASCONF-ACK
3445 * Chunk for that chunk.
3431 */ 3446 */
3432 if (asoc->addip_last_asconf_ack) 3447 asconf_ack = sctp_assoc_lookup_asconf_ack(asoc, hdr->serial);
3433 asconf_ack = asoc->addip_last_asconf_ack; 3448 if (!asconf_ack)
3434 else
3435 return SCTP_DISPOSITION_DISCARD; 3449 return SCTP_DISPOSITION_DISCARD;
3436 } else { 3450 } else {
3437 /* ADDIP 4.2 C4) Otherwise, the ASCONF Chunk is discarded since 3451 /* ADDIP 5.2 E5) Otherwise, the ASCONF Chunk is discarded since
3438 * it must be either a stale packet or from an attacker. 3452 * it must be either a stale packet or from an attacker.
3439 */ 3453 */
3440 return SCTP_DISPOSITION_DISCARD; 3454 return SCTP_DISPOSITION_DISCARD;
3441 } 3455 }
3442 3456
3443 /* ADDIP 4.2 C5) In both cases C2 and C3 the ASCONF-ACK MUST be sent 3457 /* ADDIP 5.2 E6) The destination address of the SCTP packet
3444 * back to the source address contained in the IP header of the ASCONF 3458 * containing the ASCONF-ACK Chunks MUST be the source address of
3445 * being responded to. 3459 * the SCTP packet that held the ASCONF Chunks.
3460 *
3461 * To do this properly, we'll set the destination address of the chunk
3462 * and at the transmit time, will try look up the transport to use.
3463 * Since ASCONFs may be bundled, the correct transport may not be
3464 * created untill we process the entire packet, thus this workaround.
3446 */ 3465 */
3466 asconf_ack->dest = chunk->source;
3447 sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(asconf_ack)); 3467 sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(asconf_ack));
3448 3468
3449 return SCTP_DISPOSITION_CONSUME; 3469 return SCTP_DISPOSITION_CONSUME;