sctp: alloc stream info when initializing asoc

When sending a msg without asoc established, sctp will send INIT packet
first and then enqueue chunks.

Before receiving INIT_ACK, stream info is not yet alloced. But enqueuing
chunks needs to access stream info, like out stream state and out stream
cnt.

This patch is to fix it by allocing out stream info when initializing an
asoc, allocing in stream and re-allocing out stream when processing init.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Xin Long 2017-03-30 01:00:53 +08:00 committed by David S. Miller
parent bcc5364bdc
commit 3dbcc105d5
4 changed files with 45 additions and 17 deletions

View File

@ -377,7 +377,8 @@ typedef struct sctp_sender_hb_info {
__u64 hb_nonce; __u64 hb_nonce;
} sctp_sender_hb_info_t; } sctp_sender_hb_info_t;
struct sctp_stream *sctp_stream_new(__u16 incnt, __u16 outcnt, gfp_t gfp); int sctp_stream_new(struct sctp_association *asoc, gfp_t gfp);
int sctp_stream_init(struct sctp_association *asoc, gfp_t gfp);
void sctp_stream_free(struct sctp_stream *stream); void sctp_stream_free(struct sctp_stream *stream);
void sctp_stream_clear(struct sctp_stream *stream); void sctp_stream_clear(struct sctp_stream *stream);

View File

@ -246,6 +246,9 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
if (!sctp_ulpq_init(&asoc->ulpq, asoc)) if (!sctp_ulpq_init(&asoc->ulpq, asoc))
goto fail_init; goto fail_init;
if (sctp_stream_new(asoc, gfp))
goto fail_init;
/* Assume that peer would support both address types unless we are /* Assume that peer would support both address types unless we are
* told otherwise. * told otherwise.
*/ */
@ -264,7 +267,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
/* AUTH related initializations */ /* AUTH related initializations */
INIT_LIST_HEAD(&asoc->endpoint_shared_keys); INIT_LIST_HEAD(&asoc->endpoint_shared_keys);
if (sctp_auth_asoc_copy_shkeys(ep, asoc, gfp)) if (sctp_auth_asoc_copy_shkeys(ep, asoc, gfp))
goto fail_init; goto stream_free;
asoc->active_key_id = ep->active_key_id; asoc->active_key_id = ep->active_key_id;
asoc->prsctp_enable = ep->prsctp_enable; asoc->prsctp_enable = ep->prsctp_enable;
@ -287,6 +290,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
return asoc; return asoc;
stream_free:
sctp_stream_free(asoc->stream);
fail_init: fail_init:
sock_put(asoc->base.sk); sock_put(asoc->base.sk);
sctp_endpoint_put(asoc->ep); sctp_endpoint_put(asoc->ep);

View File

@ -2460,15 +2460,10 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk,
* association. * association.
*/ */
if (!asoc->temp) { if (!asoc->temp) {
int error; if (sctp_stream_init(asoc, gfp))
asoc->stream = sctp_stream_new(asoc->c.sinit_max_instreams,
asoc->c.sinit_num_ostreams, gfp);
if (!asoc->stream)
goto clean_up; goto clean_up;
error = sctp_assoc_set_id(asoc, gfp); if (sctp_assoc_set_id(asoc, gfp))
if (error)
goto clean_up; goto clean_up;
} }

View File

@ -35,33 +35,60 @@
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
struct sctp_stream *sctp_stream_new(__u16 incnt, __u16 outcnt, gfp_t gfp) int sctp_stream_new(struct sctp_association *asoc, gfp_t gfp)
{ {
struct sctp_stream *stream; struct sctp_stream *stream;
int i; int i;
stream = kzalloc(sizeof(*stream), gfp); stream = kzalloc(sizeof(*stream), gfp);
if (!stream) if (!stream)
return NULL; return -ENOMEM;
stream->outcnt = outcnt; stream->outcnt = asoc->c.sinit_num_ostreams;
stream->out = kcalloc(stream->outcnt, sizeof(*stream->out), gfp); stream->out = kcalloc(stream->outcnt, sizeof(*stream->out), gfp);
if (!stream->out) { if (!stream->out) {
kfree(stream); kfree(stream);
return NULL; return -ENOMEM;
} }
for (i = 0; i < stream->outcnt; i++) for (i = 0; i < stream->outcnt; i++)
stream->out[i].state = SCTP_STREAM_OPEN; stream->out[i].state = SCTP_STREAM_OPEN;
stream->incnt = incnt; asoc->stream = stream;
return 0;
}
int sctp_stream_init(struct sctp_association *asoc, gfp_t gfp)
{
struct sctp_stream *stream = asoc->stream;
int i;
/* Initial stream->out size may be very big, so free it and alloc
* a new one with new outcnt to save memory.
*/
kfree(stream->out);
stream->outcnt = asoc->c.sinit_num_ostreams;
stream->out = kcalloc(stream->outcnt, sizeof(*stream->out), gfp);
if (!stream->out)
goto nomem;
for (i = 0; i < stream->outcnt; i++)
stream->out[i].state = SCTP_STREAM_OPEN;
stream->incnt = asoc->c.sinit_max_instreams;
stream->in = kcalloc(stream->incnt, sizeof(*stream->in), gfp); stream->in = kcalloc(stream->incnt, sizeof(*stream->in), gfp);
if (!stream->in) { if (!stream->in) {
kfree(stream->out); kfree(stream->out);
kfree(stream); goto nomem;
return NULL;
} }
return stream; return 0;
nomem:
asoc->stream = NULL;
kfree(stream);
return -ENOMEM;
} }
void sctp_stream_free(struct sctp_stream *stream) void sctp_stream_free(struct sctp_stream *stream)