diff options
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r-- | sys/netinet/sctp_output.c | 263 |
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: |