summaryrefslogtreecommitdiff
path: root/net/sctp/associola.c
diff options
context:
space:
mode:
authorVlad Yasevich <vladislav.yasevich@hp.com>2007-12-20 14:11:47 -0800
committerDavid S. Miller <davem@davemloft.net>2008-01-28 14:59:23 -0800
commita08de64d074b36a56ee3bb985cd171281db78e96 (patch)
tree15b3c77a7078b5dc0510a23cc7d95922f7ad420a /net/sctp/associola.c
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/associola.c')
-rw-r--r--net/sctp/associola.c58
1 files changed, 56 insertions, 2 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 61bebb9b96e..a016e78061f 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -61,6 +61,7 @@
/* Forward declarations for internal functions. */
static void sctp_assoc_bh_rcv(struct work_struct *work);
+static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc);
/* 1st Level Abstractions. */
@@ -242,6 +243,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->addip_serial = asoc->c.initial_tsn;
INIT_LIST_HEAD(&asoc->addip_chunk_list);
+ INIT_LIST_HEAD(&asoc->asconf_ack_list);
/* Make an empty list of remote transport addresses. */
INIT_LIST_HEAD(&asoc->peer.transport_addr_list);
@@ -431,8 +433,7 @@ void sctp_association_free(struct sctp_association *asoc)
asoc->peer.transport_count = 0;
/* Free any cached ASCONF_ACK chunk. */
- if (asoc->addip_last_asconf_ack)
- sctp_chunk_free(asoc->addip_last_asconf_ack);
+ sctp_assoc_free_asconf_acks(asoc);
/* Free any cached ASCONF chunk. */
if (asoc->addip_last_asconf)
@@ -1485,3 +1486,56 @@ retry:
asoc->assoc_id = (sctp_assoc_t) assoc_id;
return error;
}
+
+/* Free asconf_ack cache */
+static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc)
+{
+ struct sctp_chunk *ack;
+ struct sctp_chunk *tmp;
+
+ list_for_each_entry_safe(ack, tmp, &asoc->asconf_ack_list,
+ transmitted_list) {
+ list_del_init(&ack->transmitted_list);
+ sctp_chunk_free(ack);
+ }
+}
+
+/* Clean up the ASCONF_ACK queue */
+void sctp_assoc_clean_asconf_ack_cache(const struct sctp_association *asoc)
+{
+ struct sctp_chunk *ack;
+ struct sctp_chunk *tmp;
+
+ /* We can remove all the entries from the queue upto
+ * the "Peer-Sequence-Number".
+ */
+ list_for_each_entry_safe(ack, tmp, &asoc->asconf_ack_list,
+ transmitted_list) {
+ if (ack->subh.addip_hdr->serial ==
+ htonl(asoc->peer.addip_serial))
+ break;
+
+ list_del_init(&ack->transmitted_list);
+ sctp_chunk_free(ack);
+ }
+}
+
+/* Find the ASCONF_ACK whose serial number matches ASCONF */
+struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
+ const struct sctp_association *asoc,
+ __be32 serial)
+{
+ struct sctp_chunk *ack = NULL;
+
+ /* Walk through the list of cached ASCONF-ACKs and find the
+ * ack chunk whose serial number matches that of the request.
+ */
+ list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
+ if (ack->subh.addip_hdr->serial == serial) {
+ sctp_chunk_hold(ack);
+ break;
+ }
+ }
+
+ return ack;
+}