sctp: Fix oops when INIT-ACK indicates that peer doesn't support AUTH
Vlad Yasevich [Thu, 18 Sep 2008 23:28:27 +0000 (16:28 -0700)]
If INIT-ACK is received with SupportedExtensions parameter which
indicates that the peer does not support AUTH, the packet will be
silently ignore, and sctp_process_init() do cleanup all of the
transports in the association.
When T1-Init timer is expires, OOPS happen while we try to choose
a different init transport.

The solution is to only clean up the non-active transports, i.e
the ones that the peer added.  However, that introduces a problem
with sctp_connectx(), because we don't mark the proper state for
the transports provided by the user.  So, we'll simply mark
user-provided transports as ACTIVE.  That will allow INIT
retransmissions to work properly in the sctp_connectx() context
and prevent the crash.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

net/sctp/associola.c
net/sctp/sm_make_chunk.c

index 8472b8b..abd51ce 100644 (file)
@@ -599,11 +599,12 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
        /* Check to see if this is a duplicate. */
        peer = sctp_assoc_lookup_paddr(asoc, addr);
        if (peer) {
+               /* An UNKNOWN state is only set on transports added by
+                * user in sctp_connectx() call.  Such transports should be
+                * considered CONFIRMED per RFC 4960, Section 5.4.
+                */
                if (peer->state == SCTP_UNKNOWN) {
-                       if (peer_state == SCTP_ACTIVE)
-                               peer->state = SCTP_ACTIVE;
-                       if (peer_state == SCTP_UNCONFIRMED)
-                               peer->state = SCTP_UNCONFIRMED;
+                       peer->state = SCTP_ACTIVE;
                }
                return peer;
        }
index fe94f42..b599cbb 100644 (file)
@@ -2321,12 +2321,10 @@ clean_up:
        /* Release the transport structures. */
        list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
                transport = list_entry(pos, struct sctp_transport, transports);
-               list_del_init(pos);
-               sctp_transport_free(transport);
+               if (transport->state != SCTP_ACTIVE)
+                       sctp_assoc_rm_peer(asoc, transport);
        }
 
-       asoc->peer.transport_count = 0;
-
 nomem:
        return 0;
 }