summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r--sys/netinet/sctp_output.c263
1 files changed, 196 insertions, 67 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 9e4f0f0..85824fd 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -2712,12 +2712,15 @@ sctp_choose_boundall(struct sctp_inpcb *inp,
ifn = SCTP_GET_IFN_VOID_FROM_ROUTE(ro);
ifn_index = SCTP_GET_IF_INDEX_FROM_ROUTE(ro);
-
emit_ifn = looked_at = sctp_ifn = sctp_find_ifn(ifn, ifn_index);
if (sctp_ifn == NULL) {
/* ?? We don't have this guy ?? */
+ SCTPDBG(SCTP_DEBUG_OUTPUT2, "No ifn emit interface?\n");
goto bound_all_plan_b;
}
+ SCTPDBG(SCTP_DEBUG_OUTPUT2, "ifn_index:%d name:%s is emit interface\n",
+ ifn_index, sctp_ifn->ifn_name);
+
if (net) {
cur_addr_num = net->indx_of_eligible_next_to_use;
}
@@ -2726,8 +2729,8 @@ sctp_choose_boundall(struct sctp_inpcb *inp,
non_asoc_addr_ok,
dest_is_loop,
dest_is_priv, fam);
- SCTPDBG(SCTP_DEBUG_OUTPUT2, "Found %d preferred source addresses\n",
- num_preferred);
+ SCTPDBG(SCTP_DEBUG_OUTPUT2, "Found %d preferred source addresses for intf:%s\n",
+ num_preferred, sctp_ifn->ifn_name);
if (num_preferred == 0) {
/*
* no eligible addresses, we must use some other interface
@@ -2768,13 +2771,18 @@ sctp_choose_boundall(struct sctp_inpcb *inp,
bound_all_plan_b:
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Trying Plan B\n");
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
+ SCTPDBG(SCTP_DEBUG_OUTPUT2, "Examine interface %s\n",
+ sctp_ifn->ifn_name);
if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) {
/* wrong base scope */
+ SCTPDBG(SCTP_DEBUG_OUTPUT2, "skip\n");
continue;
}
- if ((sctp_ifn == looked_at) && looked_at)
+ if ((sctp_ifn == looked_at) && looked_at) {
/* already looked at this guy */
+ SCTPDBG(SCTP_DEBUG_OUTPUT2, "already seen\n");
continue;
+ }
num_preferred = sctp_count_num_preferred_boundall(sctp_ifn, stcb, non_asoc_addr_ok,
dest_is_loop, dest_is_priv, fam);
SCTPDBG(SCTP_DEBUG_OUTPUT2,
@@ -2782,6 +2790,7 @@ bound_all_plan_b:
ifn, num_preferred);
if (num_preferred == 0) {
/* None on this interface. */
+ SCTPDBG(SCTP_DEBUG_OUTPUT2, "No prefered -- skipping to next\n");
continue;
}
SCTPDBG(SCTP_DEBUG_OUTPUT2,
@@ -3259,6 +3268,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
sctp_route_t *ro = NULL;
if ((net) && (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EFAULT);
sctp_m_freem(m);
return (EFAULT);
}
@@ -3276,6 +3286,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
sctphdr = mtod(m, struct sctphdr *);
if (sctp_no_csum_on_loopback &&
(stcb) &&
+ (to->sa_family == AF_INET) &&
(stcb->asoc.loopback_scope)) {
sctphdr->checksum = 0;
/*
@@ -3298,7 +3309,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
newm = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA);
if (newm == NULL) {
sctp_m_freem(m);
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
SCTP_ALIGN_TO_END(newm, sizeof(struct ip));
@@ -3474,6 +3485,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
}
}
}
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EHOSTUNREACH);
sctp_m_freem(m);
return (EHOSTUNREACH);
}
@@ -3489,8 +3501,8 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) {
/* failed to prepend data, give up */
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
sctp_m_freem(m);
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
return (ENOMEM);
}
#ifdef SCTP_PACKET_LOGGING
@@ -3565,7 +3577,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA);
if (newm == NULL) {
sctp_m_freem(m);
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr));
@@ -3588,8 +3600,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
sin6 = &tmp;
/* KAME hack: embed scopeid */
- if (sa6_embedscope(sin6, ip6_use_defzone) != 0)
+ if (sa6_embedscope(sin6, ip6_use_defzone) != 0) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL);
+ }
if (net == NULL) {
memset(&ip6route, 0, sizeof(ip6route));
ro = (sctp_route_t *) & ip6route;
@@ -3681,6 +3695,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
lsa6_storage.sin6_family = AF_INET6;
lsa6_storage.sin6_len = sizeof(lsa6_storage);
if ((error = sa6_recoverscope(&lsa6_storage)) != 0) {
+ SCTPDBG(SCTP_DEBUG_OUTPUT3, "recover scope fails error %d\n", error);
sctp_m_freem(m);
return (error);
}
@@ -3717,7 +3732,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) {
/* failed to prepend data, give up */
sctp_m_freem(m);
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
#ifdef SCTP_PACKET_LOGGING
@@ -3790,6 +3805,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
SCTPDBG(SCTP_DEBUG_OUTPUT1, "Unknown protocol (TSNH) type %d\n",
((struct sockaddr *)to)->sa_family);
sctp_m_freem(m);
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EFAULT);
return (EFAULT);
}
}
@@ -3841,6 +3857,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
}
if (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
/* This case should not happen */
+ SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT - failed timer?\n");
return;
}
/* start the INIT timer */
@@ -3849,6 +3866,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
m = sctp_get_mbuf_for_msg(MCLBYTES, 1, M_DONTWAIT, 1, MT_DATA);
if (m == NULL) {
/* No memory, INIT timer will re-attempt. */
+ SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT - mbuf?\n");
return;
}
SCTP_BUF_LEN(m) = sizeof(struct sctp_init_msg);
@@ -4059,9 +4077,11 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
}
p_len += padval;
}
+ SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT - calls lowlevel_output\n");
ret = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr,
m, 0, NULL, 0, 0, NULL, 0);
+ SCTPDBG(SCTP_DEBUG_OUTPUT4, "lowlevel_output - %d\n", ret);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
(void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
@@ -5382,11 +5402,13 @@ sctp_msg_append(struct sctp_tcb *stcb,
holds_lock = hold_stcb_lock;
if (srcv->sinfo_stream >= stcb->asoc.streamoutcnt) {
/* Invalid stream number */
+ SCTP_LTRACE_ERR_RET_PKT(m, NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_now;
}
if ((stcb->asoc.stream_locked) &&
(stcb->asoc.stream_locked_on != srcv->sinfo_stream)) {
+ SCTP_LTRACE_ERR_RET_PKT(m, NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EAGAIN);
error = EAGAIN;
goto out_now;
}
@@ -5397,12 +5419,13 @@ sctp_msg_append(struct sctp_tcb *stcb,
(SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
(stcb->asoc.state & SCTP_STATE_SHUTDOWN_PENDING)) {
/* got data while shutting down */
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET);
error = ECONNRESET;
goto out_now;
}
sctp_alloc_a_strmoq(stcb, sp);
if (sp == NULL) {
- SCTP_LTRACE_ERR(stcb->sctp_ep, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
error = ENOMEM;
goto out_now;
}
@@ -5720,7 +5743,7 @@ sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr,
if (SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) {
SCTP_STAT_DECR_GAUGE32(sctps_currestab);
}
- asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+ SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb,
asoc->primary_destination);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb,
@@ -5882,7 +5905,7 @@ sctp_sendall(struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m,
SCTP_M_COPYAL);
if (ca == NULL) {
sctp_m_freem(m);
- SCTP_LTRACE_ERR(inp, NULL, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
memset(ca, 0, sizeof(struct sctp_copy_all));
@@ -5900,7 +5923,7 @@ sctp_sendall(struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m,
ca->m = sctp_copy_out_all(uio, ca->sndlen);
if (ca->m == NULL) {
SCTP_FREE(ca, SCTP_M_COPYAL);
- SCTP_LTRACE_ERR(inp, NULL, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
} else {
@@ -5923,6 +5946,7 @@ sctp_sendall(struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m,
if (ret) {
SCTP_PRINTF("Failed to initiate iterator for sendall\n");
SCTP_FREE(ca, SCTP_M_COPYAL);
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EFAULT);
return (EFAULT);
}
return (0);
@@ -6136,6 +6160,14 @@ sctp_can_we_split_this(struct sctp_tcb *stcb,
return (goal_mtu);
}
}
+ /*-
+ * For those strange folk that make the send buffer
+ * smaller than our fragmentation point, we can't
+ * get a full msg in so we have to allow splitting.
+ */
+ if (SCTP_SB_LIMIT_SND(stcb->sctp_socket) < frag_point) {
+ return (sp->length);
+ }
if ((sp->length <= goal_mtu) ||
((sp->length - goal_mtu) < sctp_min_residual)) {
/* Sub-optimial residual don't split in non-eeor mode. */
@@ -6341,7 +6373,7 @@ out_gu:
rcv_flags |= SCTP_DATA_UNORDERED;
}
/* clear out the chunk before setting up */
- memset(chk, sizeof(*chk), 0);
+ memset(chk, 0, sizeof(*chk));
chk->rec.data.rcv_flags = rcv_flags;
if (SCTP_BUF_IS_EXTENDED(sp->data)) {
chk->copy_by_ref = 1;
@@ -7098,7 +7130,7 @@ again_one_more_time:
chk->send_size, chk->copy_by_ref);
if (outchain == NULL) {
*reason_code = 8;
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
@@ -7195,6 +7227,7 @@ again_one_more_time:
SCTP_BUF_PREPEND(outchain, sizeof(struct sctphdr), M_DONTWAIT);
if (outchain == NULL) {
/* no memory */
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
error = ENOBUFS;
goto error_out_again;
}
@@ -7361,7 +7394,7 @@ again_one_more_time:
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
}
*reason_code = 3;
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
/* upate our MTU size */
@@ -7467,6 +7500,7 @@ again_one_more_time:
SCTP_BUF_PREPEND(outchain, sizeof(struct sctphdr), M_DONTWAIT);
if (outchain == NULL) {
/* out of mbufs */
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
error = ENOBUFS;
goto errored_send;
}
@@ -8066,7 +8100,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
struct mbuf *m, *endofchain;
struct sctphdr *shdr;
int asconf;
- struct sctp_nets *net;
+ struct sctp_nets *net = NULL;
uint32_t tsns_sent = 0;
int no_fragmentflg, bundle_at, cnt_thru;
unsigned int mtu;
@@ -8147,6 +8181,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
SCTP_BUF_PREPEND(m, sizeof(struct sctphdr), M_DONTWAIT);
if (m == NULL) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
return (ENOBUFS);
}
shdr = mtod(m, struct sctphdr *);
@@ -8207,8 +8242,10 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp,
SCTP_PRINTF("Gak, chk->snd_count:%d >= max:%d - send abort\n",
chk->snd_count,
sctp_max_retran_chunk);
- sctp_send_abort_tcb(stcb, NULL);
- sctp_timer_start(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL);
+ atomic_add_int(&stcb->asoc.refcnt, 1);
+ sctp_abort_an_association(stcb->sctp_ep, stcb, 0, NULL);
+ SCTP_TCB_LOCK(stcb);
+ atomic_subtract_int(&stcb->asoc.refcnt, 1);
return (SCTP_RETRAN_EXIT);
}
/* pick up the net */
@@ -8292,7 +8329,7 @@ one_chunk_around:
}
m = sctp_copy_mbufchain(chk->data, m, &endofchain, 0, chk->send_size, chk->copy_by_ref);
if (m == NULL) {
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
/* Do clear IP_DF ? */
@@ -8344,7 +8381,7 @@ one_chunk_around:
}
m = sctp_copy_mbufchain(fwd->data, m, &endofchain, 0, fwd->send_size, fwd->copy_by_ref);
if (m == NULL) {
- SCTP_LTRACE_ERR(inp, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
/* Do clear IP_DF ? */
@@ -8383,6 +8420,7 @@ one_chunk_around:
}
SCTP_BUF_PREPEND(m, sizeof(struct sctphdr), M_DONTWAIT);
if (m == NULL) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
return (ENOBUFS);
}
shdr = mtod(m, struct sctphdr *);
@@ -8831,9 +8869,11 @@ sctp_output(inp, m, addr, control, p, flags)
int flags;
{
if (inp == NULL) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL);
}
if (inp->sctp_socket == NULL) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL);
}
return (sctp_sosend(inp->sctp_socket,
@@ -9240,8 +9280,15 @@ sctp_send_sack(struct sctp_tcb *stcb)
offset += 8;
}
if (num_gap_blocks == 0) {
- /* reneged all chunks */
- asoc->highest_tsn_inside_map = asoc->cumulative_tsn;
+ /*
+ * slide not yet happened, and somehow we got called
+ * to send a sack. Cumack needs to move up.
+ */
+ int abort_flag = 0;
+
+ asoc->cumulative_tsn = asoc->highest_tsn_inside_map;
+ sack->sack.cum_tsn_ack = htonl(asoc->cumulative_tsn);
+ sctp_sack_check(stcb, 0, 0, &abort_flag);
}
}
/* now we must add any dups we are going to report. */
@@ -9333,6 +9380,7 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr)
abort->ch.chunk_flags = 0;
abort->ch.chunk_length = htons(sizeof(*abort) + sz);
+
/* prepend and fill in the SCTP header */
SCTP_BUF_PREPEND(m_out, sizeof(struct sctphdr), M_DONTWAIT);
if (m_out == NULL) {
@@ -9811,6 +9859,9 @@ sctp_send_packet_dropped(struct sctp_tcb *stcb, struct sctp_nets *net,
struct ip *iph;
int fullsz = 0, extra = 0;
long spc;
+ int offset;
+ struct sctp_chunkhdr *ch, chunk_buf;
+ unsigned int chk_length;
asoc = &stcb->asoc;
SCTP_TCB_LOCK_ASSERT(stcb);
@@ -9843,6 +9894,28 @@ sctp_send_packet_dropped(struct sctp_tcb *stcb, struct sctp_nets *net,
ip6h = mtod(m, struct ip6_hdr *);
len = chk->send_size = htons(ip6h->ip6_plen);
}
+ /* Validate that we do not have an ABORT in here. */
+ offset = iphlen + sizeof(struct sctphdr);
+ ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
+ sizeof(*ch), (uint8_t *) & chunk_buf);
+ while (ch != NULL) {
+ chk_length = ntohs(ch->chunk_length);
+ if (chk_length < sizeof(*ch)) {
+ /* break to abort land */
+ break;
+ }
+ switch (ch->chunk_type) {
+ case SCTP_ABORT_ASSOCIATION:
+ /* we don't respond with an PKT-DROP to an ABORT */
+ return;
+ default:
+ break;
+ }
+ offset += SCTP_SIZE32(chk_length);
+ ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
+ sizeof(*ch), (uint8_t *) & chunk_buf);
+ }
+
if ((len + SCTP_MAX_OVERHEAD + sizeof(struct sctp_pktdrop_chunk)) >
min(stcb->asoc.smallest_mtu, MCLBYTES)) {
/*
@@ -10181,19 +10254,22 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
/*-
* Already one pending, must get ACK back to clear the flag.
*/
+ 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)) {
/* nothing to do */
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL);
}
if (send_tsn_req && (send_out_req || send_in_req)) {
/* error, can't do that */
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL);
}
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
- SCTP_LTRACE_ERR(stcb->sctp_ep, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
chk->copy_by_ref = 0;
@@ -10207,7 +10283,7 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
chk->data = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA);
if (chk->data == NULL) {
sctp_free_a_chunk(stcb, chk);
- SCTP_LTRACE_ERR(stcb->sctp_ep, stcb, ENOMEM, 0);
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
SCTP_BUF_RESV_UF(chk->data, SCTP_MIN_OVERHEAD);
@@ -10586,9 +10662,10 @@ sctp_copy_resume(struct sctp_stream_queue_pending *sp,
m = m_uiotombuf(uio, M_WAITOK, max_send_len, 0,
(M_PKTHDR | (user_marks_eor ? M_EOR : 0)));
- if (m == NULL)
+ if (m == NULL) {
+ SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
*error = ENOMEM;
- else {
+ } else {
*sndout = m_length(m, NULL);
*new_tail = m_last(m);
}
@@ -10605,9 +10682,10 @@ sctp_copy_one(struct sctp_stream_queue_pending *sp,
left = sp->length;
sp->data = m_uiotombuf(uio, M_WAITOK, sp->length,
resv_upfront, 0);
- if (sp->data == NULL)
+ if (sp->data == NULL) {
+ SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
-
+ }
sp->tail_mbuf = m_last(sp->data);
return (0);
}
@@ -10640,6 +10718,7 @@ sctp_copy_it_in(struct sctp_tcb *stcb,
if (((user_marks_eor == 0) && non_blocking) &&
(uio->uio_resid > (int)SCTP_SB_LIMIT_SND(stcb->sctp_socket))) {
/* It will NEVER fit */
+ SCTP_LTRACE_ERR_RET(NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EMSGSIZE);
*error = EMSGSIZE;
goto out_now;
}
@@ -10649,11 +10728,13 @@ sctp_copy_it_in(struct sctp_tcb *stcb,
(SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
(asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) {
/* got data while shutting down */
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET);
*error = ECONNRESET;
goto out_now;
}
sctp_alloc_a_strmoq(stcb, sp);
if (sp == NULL) {
+ SCTP_LTRACE_ERR_RET(NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
*error = ENOMEM;
goto out_now;
}
@@ -10707,8 +10788,7 @@ sctp_sosend(struct socket *so,
struct uio *uio,
struct mbuf *top,
struct mbuf *control,
- int flags
- ,
+ int flags,
struct thread *p
)
{
@@ -10781,30 +10861,41 @@ sctp_lower_sosend(struct socket *so,
asoc = NULL;
t_inp = inp = (struct sctp_inpcb *)so->so_pcb;
if (inp == NULL) {
+ SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EFAULT);
error = EFAULT;
goto out_unlocked;
}
if ((uio == NULL) && (i_pak == NULL)) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL);
}
atomic_add_int(&inp->total_sends, 1);
- if (uio)
+ if (uio) {
+ if (uio->uio_resid < 0) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
+ return (EINVAL);
+ }
sndlen = uio->uio_resid;
- else {
- sndlen = SCTP_HEADER_LEN(i_pak);
+ } else {
top = SCTP_HEADER_TO_CHAIN(i_pak);
+ sndlen = SCTP_HEADER_LEN(i_pak);
}
- /*
- * Pre-screen address, if one is given the sin-len must be set
- * correctly!
+ SCTPDBG(SCTP_DEBUG_OUTPUT1, "Send called addr:%p send length %d\n",
+ addr,
+ sndlen);
+ /*-
+ * Pre-screen address, if one is given the sin-len
+ * must be set correctly!
*/
if (addr) {
if ((addr->sa_family == AF_INET) &&
(addr->sa_len != sizeof(struct sockaddr_in))) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
} else if ((addr->sa_family == AF_INET6) &&
(addr->sa_len != sizeof(struct sockaddr_in6))) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
@@ -10814,12 +10905,14 @@ sctp_lower_sosend(struct socket *so,
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
(inp->sctp_socket->so_qlimit)) {
/* The listener can NOT send */
+ SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EFAULT);
error = EFAULT;
goto out_unlocked;
}
if ((use_rcvinfo) && srcv) {
if (INVALID_SINFO_FLAG(srcv->sinfo_flags) ||
PR_SCTP_INVALID_POLICY(srcv->sinfo_flags)) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
@@ -10840,6 +10933,7 @@ sctp_lower_sosend(struct socket *so,
stcb = LIST_FIRST(&inp->sctp_asoc_list);
if (stcb == NULL) {
SCTP_INP_RUNLOCK(inp);
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOTCONN);
error = ENOTCONN;
goto out_unlocked;
}
@@ -10886,6 +10980,7 @@ sctp_lower_sosend(struct socket *so,
}
}
if (net == NULL) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
@@ -10905,6 +11000,7 @@ sctp_lower_sosend(struct socket *so,
struct sockaddr_in *sin;
if (addr == NULL) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
@@ -10942,12 +11038,14 @@ sctp_lower_sosend(struct socket *so,
if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
/* Should I really unlock ? */
+ SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EFAULT);
error = EFAULT;
goto out_unlocked;
}
if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) &&
(addr->sa_family == AF_INET6)) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
@@ -10964,6 +11062,7 @@ sctp_lower_sosend(struct socket *so,
hold_tcblock = 1;
}
if (t_inp != inp) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOTCONN);
error = ENOTCONN;
goto out_unlocked;
}
@@ -10971,9 +11070,11 @@ sctp_lower_sosend(struct socket *so,
if (stcb == NULL) {
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOTCONN);
error = ENOTCONN;
goto out_unlocked;
} else if (addr == NULL) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOENT);
error = ENOENT;
goto out_unlocked;
} else {
@@ -10992,6 +11093,7 @@ sctp_lower_sosend(struct socket *so,
* User asks to abort a non-existant assoc,
* or EOF a non-existant assoc with no data
*/
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOENT);
error = ENOENT;
goto out_unlocked;
}
@@ -11016,7 +11118,7 @@ sctp_lower_sosend(struct socket *so,
*/
queue_only = 1;
asoc = &stcb->asoc;
- asoc->state = SCTP_STATE_COOKIE_WAIT;
+ SCTP_SET_STATE(asoc, SCTP_STATE_COOKIE_WAIT);
(void)SCTP_GETTIME_TIMEVAL(&asoc->time_entered);
/* initialize authentication params for the assoc */
@@ -11134,6 +11236,7 @@ sctp_lower_sosend(struct socket *so,
(sndlen + stcb->asoc.total_output_queue_size)) ||
(stcb->asoc.chunks_on_out_queue >
sctp_max_chunks_on_queue)) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EWOULDBLOCK);
error = EWOULDBLOCK;
atomic_add_int(&stcb->sctp_ep->total_nospaces, 1);
goto out_unlocked;
@@ -11146,6 +11249,7 @@ sctp_lower_sosend(struct socket *so,
free_cnt_applied = 1;
if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET);
error = ECONNRESET;
goto out_unlocked;
}
@@ -11157,6 +11261,7 @@ sctp_lower_sosend(struct socket *so,
/*
* Can't queue any data while stream reset is underway.
*/
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EAGAIN);
error = EAGAIN;
goto out_unlocked;
}
@@ -11181,6 +11286,7 @@ sctp_lower_sosend(struct socket *so,
(srcv->sinfo_flags & SCTP_ABORT)) {
;
} else {
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET);
error = ECONNRESET;
goto out_unlocked;
}
@@ -11195,6 +11301,7 @@ sctp_lower_sosend(struct socket *so,
}
}
if (net == NULL) {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
@@ -11228,6 +11335,7 @@ sctp_lower_sosend(struct socket *so,
(SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED)) {
/* It has to be up before we abort */
/* how big is the user initiated abort? */
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out;
}
@@ -11236,14 +11344,15 @@ sctp_lower_sosend(struct socket *so,
hold_tcblock = 0;
}
if (top) {
- struct mbuf *cntm;
+ struct mbuf *cntm = NULL;
mm = sctp_get_mbuf_for_msg(1, 0, M_WAIT, 1, MT_DATA);
-
- cntm = top;
- while (cntm) {
- tot_out += SCTP_BUF_LEN(cntm);
- cntm = SCTP_BUF_NEXT(cntm);
+ if (sndlen != 0) {
+ cntm = top;
+ while (cntm) {
+ tot_out += SCTP_BUF_LEN(cntm);
+ cntm = SCTP_BUF_NEXT(cntm);
+ }
}
tot_demand = (tot_out + sizeof(struct sctp_paramhdr));
} else {
@@ -11254,6 +11363,7 @@ sctp_lower_sosend(struct socket *so,
mm = sctp_get_mbuf_for_msg(tot_demand, 0, M_WAIT, 1, MT_DATA);
}
if (mm == NULL) {
+ SCTP_LTRACE_ERR_RET(NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
error = ENOMEM;
goto out;
}
@@ -11283,7 +11393,9 @@ sctp_lower_sosend(struct socket *so,
mm = NULL;
}
} else {
- SCTP_BUF_NEXT(mm) = top;
+ if (sndlen != 0) {
+ SCTP_BUF_NEXT(mm) = top;
+ }
}
}
if (hold_tcblock == 0) {
@@ -11299,6 +11411,14 @@ sctp_lower_sosend(struct socket *so,
/* now relock the stcb so everything is sane */
hold_tcblock = 0;
stcb = NULL;
+ /*
+ * In this case top is already chained to mm avoid double
+ * free, since we free it below if top != NULL and driver
+ * would free it after sending the packet out
+ */
+ if (sndlen != 0) {
+ top = NULL;
+ }
goto out_unlocked;
}
/* Calculate the maximum we can send */
@@ -11314,16 +11434,18 @@ sctp_lower_sosend(struct socket *so,
/* Is the stream no. valid? */
if (srcv->sinfo_stream >= asoc->streamoutcnt) {
/* Invalid stream number */
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL;
goto out_unlocked;
}
if (asoc->strmout == NULL) {
/* huh? software error */
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EFAULT);
error = EFAULT;
goto out_unlocked;
}
len = 0;
- if (max_len < sctp_add_more_threshold) {
+ if ((max_len < sctp_add_more_threshold) && (SCTP_SB_LIMIT_SND(so) > sctp_add_more_threshold)) {
/* No room right no ! */
SOCKBUF_LOCK(&so->so_snd);
while (SCTP_SB_LIMIT_SND(so) < (stcb->asoc.total_output_queue_size + sctp_add_more_threshold)) {
@@ -11365,27 +11487,33 @@ sctp_lower_sosend(struct socket *so,
goto out_unlocked;
}
atomic_add_int(&stcb->total_sends, 1);
+ /*
+ * sndlen covers for mbuf case uio_resid covers for the non-mbuf
+ * case NOTE: uio will be null when top/mbuf is passed
+ */
+ if ((sndlen == 0) || ((uio) && (uio->uio_resid == 0))) {
+ if (srcv->sinfo_flags & SCTP_EOF) {
+ got_all_of_the_send = 1;
+ goto dataless_eof;
+ } else {
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
+ error = EINVAL;
+ goto out;
+ }
+ }
if (top == NULL) {
struct sctp_stream_queue_pending *sp;
struct sctp_stream_out *strm;
uint32_t sndout, initial_out;
int user_marks_eor;
- if (uio->uio_resid == 0) {
- if (srcv->sinfo_flags & SCTP_EOF) {
- got_all_of_the_send = 1;
- goto dataless_eof;
- } else {
- error = EINVAL;
- goto out;
- }
- }
initial_out = uio->uio_resid;
SCTP_TCB_SEND_LOCK(stcb);
if ((asoc->stream_locked) &&
(asoc->stream_locked_on != srcv->sinfo_stream)) {
SCTP_TCB_SEND_UNLOCK(stcb);
+ SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EAGAIN);
error = EAGAIN;
goto out;
}
@@ -11459,8 +11587,9 @@ sctp_lower_sosend(struct socket *so,
max_len = 0;
if ((max_len > sctp_add_more_threshold) ||
+ (max_len && (SCTP_SB_LIMIT_SND(so) < sctp_add_more_threshold)) ||
(uio->uio_resid &&
- (uio->uio_resid < (int)max_len))) {
+ (uio->uio_resid <= (int)max_len))) {
sndout = 0;
new_tail = NULL;
if (hold_tcblock) {
@@ -11482,8 +11611,10 @@ sctp_lower_sosend(struct socket *so,
* aborted.
*/
sctp_m_freem(mm);
- if (stcb->asoc.state & SCTP_PCB_FLAGS_WAS_ABORTED)
+ if (stcb->asoc.state & SCTP_PCB_FLAGS_WAS_ABORTED) {
+ SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ECONNRESET);
error = ECONNRESET;
+ }
SCTP_TCB_SEND_UNLOCK(stcb);
goto out;
}
@@ -11613,7 +11744,7 @@ sctp_lower_sosend(struct socket *so,
queue_only = 0;
} else {
sctp_send_initiate(inp, stcb);
- stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
+ SCTP_SET_STATE(asoc, SCTP_STATE_COOKIE_WAIT);
queue_only_for_init = 0;
queue_only = 1;
}
@@ -11659,7 +11790,9 @@ sctp_lower_sosend(struct socket *so,
* size we KNOW we will get to sleep safely with the
* wakeup flag in place.
*/
- if (SCTP_SB_LIMIT_SND(so) < (stcb->asoc.total_output_queue_size + sctp_add_more_threshold)) {
+ if (SCTP_SB_LIMIT_SND(so) <= (stcb->asoc.total_output_queue_size +
+ min(sctp_add_more_threshold, SCTP_SB_LIMIT_SND(so)))
+ ) {
if (sctp_logging_level & SCTP_BLK_LOGGING_ENABLE) {
sctp_log_block(SCTP_BLOCK_LOG_INTO_BLK,
so, asoc, uio->uio_resid);
@@ -11748,7 +11881,7 @@ dataless_eof:
if (SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) {
SCTP_STAT_DECR_GAUGE32(sctps_currestab);
}
- asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+ SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb,
asoc->primary_destination);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb,
@@ -11872,11 +12005,7 @@ skip_out_eof:
queue_only = 0;
} else {
sctp_send_initiate(inp, stcb);
- if (stcb->asoc.state & SCTP_STATE_SHUTDOWN_PENDING)
- stcb->asoc.state = SCTP_STATE_COOKIE_WAIT |
- SCTP_STATE_SHUTDOWN_PENDING;
- else
- stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
+ SCTP_SET_STATE(&stcb->asoc, SCTP_STATE_COOKIE_WAIT);
queue_only_for_init = 0;
queue_only = 1;
}
@@ -11916,10 +12045,10 @@ skip_out_eof:
(void)sctp_med_chunk_output(inp, stcb, &stcb->asoc, &num_out,
&reason, 1, &cwnd_full, 1, &now, &now_filled, frag_point);
}
- SCTPDBG(SCTP_DEBUG_OUTPUT1, "USR Send complete qo:%d prw:%d unsent:%d tf:%d cooq:%d toqs:%d \n",
+ SCTPDBG(SCTP_DEBUG_OUTPUT1, "USR Send complete qo:%d prw:%d unsent:%d tf:%d cooq:%d toqs:%d err:%d",
queue_only, stcb->asoc.peers_rwnd, un_sent,
stcb->asoc.total_flight, stcb->asoc.chunks_on_out_queue,
- stcb->asoc.total_output_queue_size);
+ stcb->asoc.total_output_queue_size, error);
out:
out_unlocked:
OpenPOWER on IntegriCloud