Add the add-stream capability. Still needs more

testing..

MFC after:	1 month
This commit is contained in:
Randall Stewart 2009-02-20 15:03:54 +00:00
parent d9d641af77
commit ea44232b3a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=188854
10 changed files with 281 additions and 36 deletions

View File

@ -418,11 +418,12 @@ __FBSDID("$FreeBSD$");
#define SCTP_HOSTNAME_ADDRESS 0x000b
#define SCTP_SUPPORTED_ADDRTYPE 0x000c
/* draft-ietf-stewart-strreset-xxx */
/* draft-ietf-stewart-tsvwg-strreset-xxx */
#define SCTP_STR_RESET_OUT_REQUEST 0x000d
#define SCTP_STR_RESET_IN_REQUEST 0x000e
#define SCTP_STR_RESET_TSN_REQUEST 0x000f
#define SCTP_STR_RESET_RESPONSE 0x0010
#define SCTP_STR_RESET_ADD_STREAMS 0x0011
#define SCTP_MAX_RESET_PARAMS 2
#define SCTP_STREAM_RESET_TSN_DELTA 0x1000
@ -794,7 +795,11 @@ __FBSDID("$FreeBSD$");
#define SCTP_NOTIFY_SPECIAL_SP_FAIL 27
#define SCTP_NOTIFY_NO_PEER_AUTH 28
#define SCTP_NOTIFY_SENDER_DRY 29
#define SCTP_NOTIFY_MAX 29
#define SCTP_NOTIFY_STR_RESET_ADD_OK 30
#define SCTP_NOTIFY_STR_RESET_ADD_FAIL 31
#define SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK 32
#define SCTP_NOTIFY_MAX 32
/* This is the value for messages that are NOT completely
* copied down where we will start to split the message.

View File

@ -498,7 +498,12 @@ struct sctp_stream_reset_response_tsn {
uint32_t receivers_next_tsn;
} SCTP_PACKED;
struct sctp_stream_reset_add_strm {
struct sctp_paramhdr ph;
uint32_t request_seq;
uint16_t number_of_streams;
uint16_t reserved;
};
#define SCTP_STREAM_RESET_NOTHING 0x00000000 /* Nothing for me to do */
#define SCTP_STREAM_RESET_PERFORMED 0x00000001 /* Did it */

View File

@ -314,7 +314,7 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
asoc->pre_open_streams = newcnt;
}
SCTP_TCB_SEND_UNLOCK(stcb);
asoc->streamoutcnt = asoc->pre_open_streams;
asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams;
/* init tsn's */
asoc->highest_tsn_inside_map = asoc->asconf_seq_in = ntohl(init->initial_tsn) - 1;
/* EY - nr_sack: initialize highest tsn in nr_mapping_array */
@ -3440,6 +3440,17 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb,
if (action != SCTP_STREAM_RESET_PERFORMED) {
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_FAILED_IN, stcb, number_entries, srparam->list_of_streams, SCTP_SO_NOT_LOCKED);
}
} else if (type == SCTP_STR_RESET_ADD_STREAMS) {
/* Ok we now may have more streams */
if (action == SCTP_STREAM_RESET_PERFORMED) {
/* Put the new streams into effect */
stcb->asoc.streamoutcnt = stcb->asoc.strm_realoutsize;
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_ADD_OK, stcb,
(uint32_t) stcb->asoc.streamoutcnt, NULL, SCTP_SO_NOT_LOCKED);
} else {
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_ADD_FAIL, stcb,
(uint32_t) stcb->asoc.streamoutcnt, NULL, SCTP_SO_NOT_LOCKED);
}
} else if (type == SCTP_STR_RESET_TSN_REQUEST) {
/**
* a) Adopt the new in tsn.
@ -3709,6 +3720,63 @@ sctp_handle_str_reset_request_out(struct sctp_tcb *stcb,
}
}
static void
sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk,
struct sctp_stream_reset_add_strm *str_add)
{
/*
* Peer is requesting to add more streams. If its within our
* max-streams we will allow it.
*/
uint16_t num_stream, i;
uint32_t seq;
/* Get the number. */
seq = ntohl(str_add->request_seq);
num_stream = ntohs(str_add->number_of_streams);
/* Now what would be the new total? */
num_stream += stcb->asoc.streamincnt;
if (num_stream > stcb->asoc.max_inbound_streams) {
/* We must reject it they ask for to many */
denied:
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED);
stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_DENIED;
} else {
/* Ok, we can do that :-) */
struct sctp_stream_in *oldstrm;
/* save off the old */
oldstrm = stcb->asoc.strmin;
SCTP_MALLOC(stcb->asoc.strmin, struct sctp_stream_in *,
(num_stream * sizeof(struct sctp_stream_in)),
SCTP_M_STRMI);
if (stcb->asoc.strmin == NULL) {
stcb->asoc.strmin = oldstrm;
goto denied;
}
/* copy off the old data */
memcpy(stcb->asoc.strmin, oldstrm,
(stcb->asoc.streamincnt * sizeof(struct sctp_stream_in)));
/* Init the new streams */
for (i = stcb->asoc.streamincnt; i < num_stream; i++) {
TAILQ_INIT(&stcb->asoc.strmin[i].inqueue);
stcb->asoc.strmin[i].stream_no = i;
stcb->asoc.strmin[i].last_sequence_delivered = 0xffff;
stcb->asoc.strmin[i].delivery_started = 0;
}
SCTP_FREE(oldstrm, SCTP_M_STRMI);
/* update the size */
stcb->asoc.streamincnt = num_stream;
/* Send the ack */
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED);
stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED;
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK, stcb,
(uint32_t) stcb->asoc.streamincnt, NULL, SCTP_SO_NOT_LOCKED);
}
}
#ifdef __GNUC__
__attribute__((noinline))
#endif
@ -3803,6 +3871,12 @@ __attribute__((noinline))
}
}
sctp_handle_str_reset_request_out(stcb, chk, req_out, trunc);
} else if (ptype == SCTP_STR_RESET_ADD_STREAMS) {
struct sctp_stream_reset_add_strm *str_add;
str_add = (struct sctp_stream_reset_add_strm *)ph;
num_req++;
sctp_handle_str_reset_add_strm(stcb, chk, str_add);
} else if (ptype == SCTP_STR_RESET_IN_REQUEST) {
struct sctp_stream_reset_in_request *req_in;

View File

@ -8620,13 +8620,12 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net,
sp->some_taken = 1;
}
} else {
to_move = sctp_can_we_split_this(stcb, length, goal_mtu,
frag_point, eeor_mode);
to_move = sctp_can_we_split_this(stcb, length, goal_mtu, frag_point, eeor_mode);
if (to_move) {
/*-
* We use a snapshot of length in case it
* is expanding during the compare.
*/
* We use a snapshot of length in case it
* is expanding during the compare.
*/
uint32_t llen;
llen = length;
@ -8634,9 +8633,9 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net,
to_move = llen;
if (send_lock_up == 0) {
/*-
* We are taking all of an incomplete msg
* thus we need a send lock.
*/
* We are taking all of an incomplete msg
* thus we need a send lock.
*/
SCTP_TCB_SEND_LOCK(stcb);
send_lock_up = 1;
if (sp->msg_is_complete) {
@ -8836,8 +8835,7 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net,
goto out_of;
}
sctp_snd_sb_alloc(stcb, sizeof(struct sctp_data_chunk));
chk->book_size = chk->send_size = (to_move +
sizeof(struct sctp_data_chunk));
chk->book_size = chk->send_size = (to_move + sizeof(struct sctp_data_chunk));
chk->book_size_scale = 0;
chk->sent = SCTP_DATAGRAM_UNSENT;
@ -13339,13 +13337,49 @@ sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *chk,
return;
}
static void
sctp_add_a_stream(struct sctp_tmit_chunk *chk,
uint32_t seq,
uint16_t adding)
{
int len, old_len;
struct sctp_chunkhdr *ch;
struct sctp_stream_reset_add_strm *addstr;
ch = mtod(chk->data, struct sctp_chunkhdr *);
old_len = len = SCTP_SIZE32(ntohs(ch->chunk_length));
/* get to new offset for the param. */
addstr = (struct sctp_stream_reset_add_strm *)((caddr_t)ch + len);
/* now how long will this param be? */
len = sizeof(struct sctp_stream_reset_add_strm);
/* Fill it out. */
addstr->ph.param_type = htons(SCTP_STR_RESET_ADD_STREAMS);
addstr->ph.param_length = htons(len);
addstr->request_seq = htonl(seq);
addstr->number_of_streams = htons(adding);
addstr->reserved = 0;
/* now fix the chunk length */
ch->chunk_length = htons(len + old_len);
chk->send_size = len + old_len;
chk->book_size = SCTP_SIZE32(chk->send_size);
chk->book_size_scale = 0;
SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
return;
}
int
sctp_send_str_reset_req(struct sctp_tcb *stcb,
int number_entries, uint16_t * list,
uint8_t send_out_req, uint32_t resp_seq,
uint8_t send_out_req,
uint32_t resp_seq,
uint8_t send_in_req,
uint8_t send_tsn_req)
uint8_t send_tsn_req,
uint8_t add_stream,
uint16_t adding
)
{
struct sctp_association *asoc;
@ -13361,7 +13395,8 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EBUSY);
return (EBUSY);
}
if ((send_out_req == 0) && (send_in_req == 0) && (send_tsn_req == 0)) {
if ((send_out_req == 0) && (send_in_req == 0) && (send_tsn_req == 0) &&
(add_stream == 0)) {
/* nothing to do */
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL);
@ -13412,6 +13447,11 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
seq++;
asoc->stream_reset_outstanding++;
}
if (add_stream) {
sctp_add_a_stream(chk, seq, adding);
seq++;
asoc->stream_reset_outstanding++;
}
if (send_in_req) {
sctp_add_stream_reset_in(chk, number_entries, list, seq);
asoc->stream_reset_outstanding++;
@ -14432,7 +14472,7 @@ sctp_lower_sosend(struct socket *so,
if (tmp_str != NULL) {
SCTP_FREE(asoc->strmout, SCTP_M_STRMO);
asoc->strmout = tmp_str;
asoc->streamoutcnt = asoc->pre_open_streams;
asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams;
} else {
asoc->pre_open_streams = asoc->streamoutcnt;
}
@ -15015,9 +15055,6 @@ sctp_lower_sosend(struct socket *so,
un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) +
(stcb->asoc.stream_queue_cnt * sizeof(struct sctp_data_chunk)));
if (net->flight_size > net->cwnd) {
queue_only = 1;
SCTP_STAT_INCR(sctps_send_burst_avoid);
} else if (net->flight_size > net->cwnd) {
queue_only = 1;
SCTP_STAT_INCR(sctps_send_cwnd_avoid);
} else {
@ -15292,9 +15329,6 @@ sctp_lower_sosend(struct socket *so,
un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) +
(stcb->asoc.stream_queue_cnt * sizeof(struct sctp_data_chunk)));
if (net->flight_size > net->cwnd) {
queue_only = 1;
SCTP_STAT_INCR(sctps_send_burst_avoid);
} else if (net->flight_size > net->cwnd) {
queue_only = 1;
SCTP_STAT_INCR(sctps_send_cwnd_avoid);
} else {

View File

@ -192,10 +192,14 @@ sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *chk,
int
sctp_send_str_reset_req(struct sctp_tcb *stcb,
int number_entries, uint16_t * list,
uint8_t send_out_req, uint32_t resp_seq,
int number_entries,
uint16_t * list,
uint8_t send_out_req,
uint32_t resp_seq,
uint8_t send_in_req,
uint8_t send_tsn_req);
uint8_t send_tsn_req,
uint8_t add_str,
uint16_t adding);
void

View File

@ -4885,7 +4885,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
SCTP_FREE(asoc->strmout, SCTP_M_STRMO);
asoc->strmout = NULL;
}
asoc->streamoutcnt = 0;
asoc->strm_realoutsize = asoc->streamoutcnt = 0;
if (asoc->strmin) {
struct sctp_queued_to_read *ctl;

View File

@ -935,7 +935,7 @@ struct sctp_association {
/* could re-arrange to optimize space here. */
uint16_t streamincnt;
uint16_t streamoutcnt;
uint16_t strm_realoutsize;
/* my maximum number of retrans of INIT and SEND */
/* copied from SCTP but should be individually setable */
uint16_t max_init_times;

View File

@ -377,7 +377,7 @@ struct sctp_stream_reset_event {
#define SCTP_STRRESET_ALL_STREAMS 0x0004
#define SCTP_STRRESET_STREAM_LIST 0x0008
#define SCTP_STRRESET_FAILED 0x0010
#define SCTP_STRRESET_ADD_STREAM 0x0020
/* SCTP notification event */
struct sctp_tlv {
@ -596,6 +596,7 @@ struct sctp_blk_args {
#define SCTP_RESET_LOCAL_SEND 0x0002
#define SCTP_RESET_BOTH 0x0003
#define SCTP_RESET_TSN 0x0004
#define SCTP_RESET_ADD_STREAMS 0x0005
struct sctp_stream_reset {
sctp_assoc_t strrst_assoc_id;
@ -941,9 +942,7 @@ struct sctpstat {
uint32_t sctps_cached_strmoq; /* Number of cached stream oq's used */
uint32_t sctps_left_abandon; /* Number of unread message abandonded
* by close */
uint32_t sctps_send_burst_avoid; /* Send burst avoidance,
* already max burst inflight
* to net */
uint32_t sctps_send_burst_avoid; /* Unused */
uint32_t sctps_send_cwnd_avoid; /* Send cwnd full avoidance, already
* max burst inflight to net */
uint32_t sctps_fwdtsn_map_over; /* number of map array over-runs via

View File

@ -3263,7 +3263,9 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
case SCTP_RESET_STREAMS:
{
struct sctp_stream_reset *strrst;
uint8_t send_in = 0, send_tsn = 0, send_out = 0;
uint8_t send_in = 0, send_tsn = 0, send_out = 0,
addstream = 0;
uint16_t addstrmcnt = 0;
int i;
SCTP_CHECK_AND_CAST(strrst, optval, struct sctp_stream_reset, optsize);
@ -3301,6 +3303,60 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
send_out = 1;
} else if (strrst->strrst_flags == SCTP_RESET_TSN) {
send_tsn = 1;
} else if (strrst->strrst_flags == SCTP_RESET_ADD_STREAMS) {
if (send_tsn ||
send_in ||
send_out) {
/* We can't do that and add streams */
error = EINVAL;
goto skip_stuff;
}
if (stcb->asoc.stream_reset_outstanding) {
error = EBUSY;
goto skip_stuff;
}
addstream = 1;
/* We allocate here */
addstrmcnt = strrst->strrst_num_streams;
if ((int)(addstrmcnt + stcb->asoc.streamoutcnt) > 0xffff) {
/* You can't have more than 64k */
error = EINVAL;
goto skip_stuff;
}
if ((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < addstrmcnt) {
/* Need to allocate more */
struct sctp_stream_out *oldstream;
oldstream = stcb->asoc.strmout;
/* get some more */
SCTP_MALLOC(stcb->asoc.strmout, struct sctp_stream_out *,
((stcb->asoc.streamoutcnt + addstrmcnt) * sizeof(struct sctp_stream_out)),
SCTP_M_STRMO);
if (stcb->asoc.strmout == NULL) {
stcb->asoc.strmout = oldstream;
error = ENOMEM;
goto skip_stuff;
}
/*
* Ok now we proceed with copying
* the old out stuff and
* initializing the new stuff.
*/
memcpy(stcb->asoc.strmout, oldstream,
(stcb->asoc.streamoutcnt * sizeof(struct sctp_stream_out)));
/* now the new streams */
for (i = stcb->asoc.streamoutcnt; i < (stcb->asoc.streamoutcnt + addstrmcnt); i++) {
stcb->asoc.strmout[i].next_sequence_sent = 0x0;
TAILQ_INIT(&stcb->asoc.strmout[i].outqueue);
stcb->asoc.strmout[i].stream_no = i;
stcb->asoc.strmout[i].last_msg_incomplete = 0;
stcb->asoc.strmout[i].next_spoke.tqe_next = 0;
stcb->asoc.strmout[i].next_spoke.tqe_prev = 0;
}
stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt + addstrmcnt;
SCTP_FREE(oldstream, SCTP_M_STRMO);
}
goto skip_stuff;
} else {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
error = EINVAL;
@ -3322,6 +3378,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
goto get_out;
}
}
skip_stuff:
if (error) {
get_out:
SCTP_TCB_UNLOCK(stcb);
@ -3330,7 +3387,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
error = sctp_send_str_reset_req(stcb, strrst->strrst_num_streams,
strrst->strrst_list,
send_out, (stcb->asoc.str_reset_seq_in - 3),
send_in, send_tsn);
send_in, send_tsn, addstream, addstrmcnt);
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED);
SCTP_TCB_UNLOCK(stcb);

View File

@ -1119,7 +1119,7 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb,
* Now the stream parameters, here we allocate space for all streams
* that we request by default.
*/
asoc->streamoutcnt = asoc->pre_open_streams =
asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams =
m->sctp_ep.pre_open_stream_count;
SCTP_MALLOC(asoc->strmout, struct sctp_stream_out *,
asoc->streamoutcnt * sizeof(struct sctp_stream_out),
@ -3351,6 +3351,63 @@ sctp_notify_sender_dry_event(struct sctp_tcb *stcb,
&stcb->sctp_socket->so_rcv, 1, so_locked);
}
static void
sctp_notify_stream_reset_add(struct sctp_tcb *stcb, int number_entries, int flag)
{
struct mbuf *m_notify;
struct sctp_queued_to_read *control;
struct sctp_stream_reset_event *strreset;
int len;
if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_STREAM_RESETEVNT)) {
/* event not enabled */
return;
}
m_notify = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA);
if (m_notify == NULL)
/* no space left */
return;
SCTP_BUF_LEN(m_notify) = 0;
len = sizeof(struct sctp_stream_reset_event) + (number_entries * sizeof(uint16_t));
if (len > M_TRAILINGSPACE(m_notify)) {
/* never enough room */
sctp_m_freem(m_notify);
return;
}
strreset = mtod(m_notify, struct sctp_stream_reset_event *);
strreset->strreset_type = SCTP_STREAM_RESET_EVENT;
strreset->strreset_flags = SCTP_STRRESET_ADD_STREAM | flag;
strreset->strreset_length = len;
strreset->strreset_assoc_id = sctp_get_associd(stcb);
strreset->strreset_list[0] = number_entries;
SCTP_BUF_LEN(m_notify) = len;
SCTP_BUF_NEXT(m_notify) = NULL;
if (sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) {
/* no space */
sctp_m_freem(m_notify);
return;
}
/* append to socket */
control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination,
0, 0, 0, 0, 0, 0,
m_notify);
if (control == NULL) {
/* no memory */
sctp_m_freem(m_notify);
return;
}
control->spec_flags = M_NOTIFICATION;
control->length = SCTP_BUF_LEN(m_notify);
/* not that we need this */
control->tail_mbuf = m_notify;
sctp_add_to_readq(stcb->sctp_ep, stcb,
control,
&stcb->sctp_socket->so_rcv, 1, SCTP_SO_NOT_LOCKED);
}
static void
sctp_notify_stream_reset(struct sctp_tcb *stcb,
int number_entries, uint16_t * list, int flag)
@ -3528,6 +3585,16 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb,
break;
case SCTP_NOTIFY_HB_RESP:
break;
case SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK:
sctp_notify_stream_reset_add(stcb, error, SCTP_STRRESET_INBOUND_STR);
break;
case SCTP_NOTIFY_STR_RESET_ADD_OK:
sctp_notify_stream_reset_add(stcb, error, SCTP_STRRESET_OUTBOUND_STR);
break;
case SCTP_NOTIFY_STR_RESET_ADD_FAIL:
sctp_notify_stream_reset_add(stcb, error, (SCTP_STRRESET_FAILED | SCTP_STRRESET_OUTBOUND_STR));
break;
case SCTP_NOTIFY_STR_RESET_SEND:
sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), SCTP_STRRESET_OUTBOUND_STR);
break;