diff options
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r-- | sys/netinet/sctp_output.c | 301 |
1 files changed, 207 insertions, 94 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 85824fd..28b39d1 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -2606,7 +2606,9 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn, uint8_t dest_is_loop, uint8_t dest_is_priv, int addr_wanted, - sa_family_t fam) + sa_family_t fam, + sctp_route_t * ro +) { struct sctp_ifa *ifa, *sifa; int num_eligible_addr = 0; @@ -2619,6 +2621,27 @@ sctp_select_nth_preferred_addr_from_ifn_boundall(struct sctp_ifn *ifn, dest_is_priv, fam); if (sifa == NULL) continue; + /* + * Check if the IPv6 address matches to next-hop. In the + * mobile case, old IPv6 address may be not deleted from the + * interface. Then, the interface has previous and new + * addresses. We should use one corresponding to the + * next-hop. (by micchie) + */ + if (stcb && fam == AF_INET6 && + sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_BASE)) { + if (sctp_v6src_match_nexthop(&sifa->address.sin6, ro) + == 0) { + continue; + } + } + /* Avoid topologically incorrect IPv4 address */ + if (stcb && fam == AF_INET && + sctp_is_mobility_feature_on(stcb->sctp_ep, SCTP_MOBILITY_BASE)) { + if (sctp_v4src_match_nexthop(sifa, ro) == 0) { + continue; + } + } if (stcb) { if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, sifa)) { @@ -2753,7 +2776,7 @@ sctp_choose_boundall(struct sctp_inpcb *inp, SCTPDBG(SCTP_DEBUG_OUTPUT2, "cur_addr_num:%d\n", cur_addr_num); sctp_ifa = sctp_select_nth_preferred_addr_from_ifn_boundall(sctp_ifn, stcb, non_asoc_addr_ok, dest_is_loop, - dest_is_priv, cur_addr_num, fam); + dest_is_priv, cur_addr_num, fam, ro); /* if sctp_ifa is NULL something changed??, fall to plan b. */ if (sctp_ifa) { @@ -2806,7 +2829,7 @@ bound_all_plan_b: cur_addr_num = 0; } sifa = sctp_select_nth_preferred_addr_from_ifn_boundall(sctp_ifn, stcb, non_asoc_addr_ok, dest_is_loop, - dest_is_priv, cur_addr_num, fam); + dest_is_priv, cur_addr_num, fam, ro); if (sifa == NULL) continue; if (net) { @@ -3018,6 +3041,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp, } SCTPDBG(SCTP_DEBUG_OUTPUT2, "Select source addr for:"); SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, (struct sockaddr *)to); + SCTP_IPI_ADDR_LOCK(); if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* * Bound all case @@ -3025,6 +3049,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp, answer = sctp_choose_boundall(inp, stcb, net, ro, vrf_id, dest_is_priv, dest_is_loop, non_asoc_addr_ok, fam); + SCTP_IPI_ADDR_UNLOCK(); return (answer); } /* @@ -3041,6 +3066,7 @@ sctp_source_address_selection(struct sctp_inpcb *inp, dest_is_priv, dest_is_loop, fam); } + SCTP_IPI_ADDR_UNLOCK(); return (answer); } @@ -3528,7 +3554,8 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } } else { /* PMTU check versus smallest asoc MTU goes here */ - if (ro->ro_rt != NULL) { + if ((ro->ro_rt != NULL) && + (net->ro._s_addr)) { uint32_t mtu; mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); @@ -3541,7 +3568,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, sctp_mtu_size_reset(inp, &stcb->asoc, mtu); net->mtu = mtu; } - } else { + } else if (ro->ro_rt == NULL) { /* route was freed */ if (net->ro._s_addr && net->src_addr_selected) { @@ -3772,7 +3799,8 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } net->src_addr_selected = 0; } - if (ro->ro_rt != NULL) { + if ((ro->ro_rt != NULL) && + (net->ro._s_addr)) { uint32_t mtu; mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); @@ -7893,7 +7921,6 @@ sctp_send_shutdown_ack(struct sctp_tcb *stcb, struct sctp_nets *net) return; } chk->copy_by_ref = 0; - chk->send_size = sizeof(struct sctp_chunkhdr); chk->rec.chunk_id.id = SCTP_SHUTDOWN_ACK; chk->rec.chunk_id.can_take_data = 1; @@ -8000,79 +8027,81 @@ sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net) } void -sctp_send_asconf_ack(struct sctp_tcb *stcb, uint32_t retrans) +sctp_send_asconf_ack(struct sctp_tcb *stcb) { /* * formulate and queue a asconf-ack back to sender. the asconf-ack * must be stored in the tcb. */ struct sctp_tmit_chunk *chk; + struct sctp_asconf_ack *ack, *latest_ack; struct mbuf *m_ack, *m; + struct sctp_nets *net = NULL; SCTP_TCB_LOCK_ASSERT(stcb); - /* is there a asconf-ack mbuf chain to send? */ - if (stcb->asoc.last_asconf_ack_sent == NULL) { + /* Get the latest ASCONF-ACK */ + latest_ack = TAILQ_LAST(&stcb->asoc.asconf_ack_sent, sctp_asconf_ackhead); + if (latest_ack == NULL) { return; } - /* copy the asconf_ack */ - m_ack = SCTP_M_COPYM(stcb->asoc.last_asconf_ack_sent, 0, M_COPYALL, - M_DONTWAIT); - if (m_ack == NULL) { - /* couldn't copy it */ - return; - } - sctp_alloc_a_chunk(stcb, chk); - if (chk == NULL) { - /* no memory */ - if (m_ack) - sctp_m_freem(m_ack); - return; - } - chk->copy_by_ref = 0; - /* figure out where it goes to */ - if (retrans) { + if (latest_ack->last_sent_to != NULL && + latest_ack->last_sent_to == stcb->asoc.last_control_chunk_from) { /* we're doing a retransmission */ - if (stcb->asoc.used_alt_asconfack > 2) { - /* tried alternate nets already, go back */ - chk->whoTo = NULL; - } else { - /* need to try and alternate net */ - chk->whoTo = sctp_find_alternate_net(stcb, stcb->asoc.last_control_chunk_from, 0); - stcb->asoc.used_alt_asconfack++; - } - if (chk->whoTo == NULL) { + net = sctp_find_alternate_net(stcb, stcb->asoc.last_control_chunk_from, 0); + if (net == NULL) { /* no alternate */ if (stcb->asoc.last_control_chunk_from == NULL) - chk->whoTo = stcb->asoc.primary_destination; + net = stcb->asoc.primary_destination; else - chk->whoTo = stcb->asoc.last_control_chunk_from; - stcb->asoc.used_alt_asconfack = 0; + net = stcb->asoc.last_control_chunk_from; } } else { /* normal case */ if (stcb->asoc.last_control_chunk_from == NULL) - chk->whoTo = stcb->asoc.primary_destination; + net = stcb->asoc.primary_destination; else - chk->whoTo = stcb->asoc.last_control_chunk_from; - stcb->asoc.used_alt_asconfack = 0; + net = stcb->asoc.last_control_chunk_from; } - chk->data = m_ack; - chk->send_size = 0; - /* Get size */ - m = m_ack; - while (m) { - chk->send_size += SCTP_BUF_LEN(m); - m = SCTP_BUF_NEXT(m); + latest_ack->last_sent_to = net; + + atomic_add_int(&latest_ack->last_sent_to->ref_count, 1); + + TAILQ_FOREACH(ack, &stcb->asoc.asconf_ack_sent, next) { + if (ack->data == NULL) { + continue; + } + /* copy the asconf_ack */ + m_ack = SCTP_M_COPYM(ack->data, 0, M_COPYALL, M_DONTWAIT); + if (m_ack == NULL) { + /* couldn't copy it */ + return; + } + sctp_alloc_a_chunk(stcb, chk); + if (chk == NULL) { + /* no memory */ + if (m_ack) + sctp_m_freem(m_ack); + return; + } + chk->copy_by_ref = 0; + + chk->whoTo = net; + chk->data = m_ack; + chk->send_size = 0; + /* Get size */ + m = m_ack; + chk->send_size = ack->len; + chk->rec.chunk_id.id = SCTP_ASCONF_ACK; + chk->rec.chunk_id.can_take_data = 1; + chk->sent = SCTP_DATAGRAM_UNSENT; + chk->snd_count = 0; + chk->flags |= CHUNK_FLAGS_FRAGMENT_OK; /* XXX */ + chk->asoc = &stcb->asoc; + atomic_add_int(&chk->whoTo->ref_count, 1); + + TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next); + chk->asoc->ctrl_queue_cnt++; } - chk->rec.chunk_id.id = SCTP_ASCONF_ACK; - chk->rec.chunk_id.can_take_data = 1; - chk->sent = SCTP_DATAGRAM_UNSENT; - chk->snd_count = 0; - chk->flags = 0; - chk->asoc = &stcb->asoc; - atomic_add_int(&chk->whoTo->ref_count, 1); - TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next); - chk->asoc->ctrl_queue_cnt++; return; } @@ -9516,11 +9545,7 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh, comp_cp->shut_cmp.ch.chunk_length = htons(sizeof(struct sctp_shutdown_complete_chunk)); /* add checksum */ - if ((sctp_no_csum_on_loopback) && SCTP_IS_IT_LOOPBACK(mout)) { - comp_cp->sh.checksum = 0; - } else { - comp_cp->sh.checksum = sctp_calculate_sum(mout, NULL, offset_out); - } + comp_cp->sh.checksum = sctp_calculate_sum(mout, NULL, offset_out); if (iph_out != NULL) { sctp_route_t ro; int ret; @@ -9779,8 +9804,7 @@ sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net) sctp_free_remote_addr(chk->whoTo); chk->whoTo = NULL; } - SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk); - SCTP_DECR_CHK_COUNT(); + sctp_free_a_chunk((struct sctp_tcb *)NULL, chk); return (-1); } } @@ -9905,8 +9929,13 @@ sctp_send_packet_dropped(struct sctp_tcb *stcb, struct sctp_nets *net, break; } switch (ch->chunk_type) { + case SCTP_PACKET_DROPPED: case SCTP_ABORT_ASSOCIATION: - /* we don't respond with an PKT-DROP to an ABORT */ + /*- + * we don't respond with an PKT-DROP to an ABORT + * or PKT-DROP + */ + sctp_free_a_chunk(stcb, chk); return; default: break; @@ -10395,6 +10424,9 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, abm = (struct sctp_abort_msg *)((caddr_t)ip6_out + iphlen_out); } else { /* Currently not supported */ + if (err_cause) + sctp_m_freem(err_cause); + sctp_m_freem(mout); return; } @@ -10436,11 +10468,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, } /* add checksum */ - if ((sctp_no_csum_on_loopback) && SCTP_IS_IT_LOOPBACK(m)) { - abm->sh.checksum = 0; - } else { - abm->sh.checksum = sctp_calculate_sum(mout, NULL, iphlen_out); - } + abm->sh.checksum = sctp_calculate_sum(mout, NULL, iphlen_out); if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { /* no mbuf's */ sctp_m_freem(mout); @@ -10546,11 +10574,7 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag, m_copyback(scm, len, padlen, (caddr_t)&cpthis); len += padlen; } - if ((sctp_no_csum_on_loopback) && SCTP_IS_IT_LOOPBACK(m)) { - val = 0; - } else { - val = sctp_calculate_sum(scm, NULL, 0); - } + val = sctp_calculate_sum(scm, NULL, 0); mout = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA); if (mout == NULL) { sctp_m_freem(scm); @@ -10714,14 +10738,6 @@ sctp_copy_it_in(struct sctp_tcb *stcb, int resv_in_first; *error = 0; - /* Unless E_EOR mode is on, we must make a send FIT in one call. */ - 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; - } /* Now can we send this? */ if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) || (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) || @@ -10847,6 +10863,7 @@ sctp_lower_sosend(struct socket *so, struct sctp_nets *net; struct sctp_association *asoc; struct sctp_inpcb *t_inp; + int user_marks_eor; int create_lock_applied = 0; int nagle_applies = 0; int some_on_control = 0; @@ -10854,6 +10871,7 @@ sctp_lower_sosend(struct socket *so, int hold_tcblock = 0; int non_blocking = 0; int temp_flags = 0; + uint32_t local_add_more; error = 0; net = NULL; @@ -10869,6 +10887,7 @@ sctp_lower_sosend(struct socket *so, SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL); return (EINVAL); } + user_marks_eor = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR); atomic_add_int(&inp->total_sends, 1); if (uio) { if (uio->uio_resid < 0) { @@ -11068,12 +11087,7 @@ 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) { + if (addr == NULL) { SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, ENOENT); error = ENOENT; goto out_unlocked; @@ -11226,6 +11240,7 @@ sctp_lower_sosend(struct socket *so, if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NO_FRAGMENT)) { if (sndlen > asoc->smallest_mtu) { + SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EMSGSIZE); error = EMSGSIZE; goto out_unlocked; } @@ -11444,11 +11459,32 @@ sctp_lower_sosend(struct socket *so, error = EFAULT; goto out_unlocked; } + /* Unless E_EOR mode is on, we must make a send FIT in one call. */ + if ((user_marks_eor == 0) && + (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_unlocked; + } + if (user_marks_eor) { + local_add_more = sctp_add_more_threshold; + } else { + /*- + * For non-eeor the whole message must fit in + * the socket send buffer. + */ + local_add_more = uio->uio_resid; + } len = 0; - if ((max_len < sctp_add_more_threshold) && (SCTP_SB_LIMIT_SND(so) > sctp_add_more_threshold)) { + if (((max_len < local_add_more) && + (SCTP_SB_LIMIT_SND(so) > local_add_more)) || + ((stcb->asoc.chunks_on_out_queue + stcb->asoc.stream_queue_cnt) > sctp_max_chunks_on_queue)) { /* 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)) { + while ((SCTP_SB_LIMIT_SND(so) < (stcb->asoc.total_output_queue_size + sctp_add_more_threshold)) || + ((stcb->asoc.stream_queue_cnt + stcb->asoc.chunks_on_out_queue) > sctp_max_chunks_on_queue)) { + if (sctp_logging_level & SCTP_BLK_LOGGING_ENABLE) { sctp_log_block(SCTP_BLOCK_LOG_INTO_BLKA, so, asoc, uio->uio_resid); @@ -11505,7 +11541,6 @@ sctp_lower_sosend(struct socket *so, struct sctp_stream_queue_pending *sp; struct sctp_stream_out *strm; uint32_t sndout, initial_out; - int user_marks_eor; initial_out = uio->uio_resid; @@ -11520,7 +11555,6 @@ sctp_lower_sosend(struct socket *so, SCTP_TCB_SEND_UNLOCK(stcb); strm = &stcb->asoc.strmout[srcv->sinfo_stream]; - user_marks_eor = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR); if (strm->last_msg_incomplete == 0) { do_a_copy_in: sp = sctp_copy_it_in(stcb, asoc, srcv, uio, net, max_len, user_marks_eor, &error, non_blocking); @@ -11937,6 +11971,7 @@ dataless_eof: } sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, asoc->primary_destination); + sctp_feature_off(inp, SCTP_PCB_FLAGS_NODELAY); } } } @@ -12152,3 +12187,81 @@ sctp_add_auth_chunk(struct mbuf *m, struct mbuf **m_end, return (m); } + +int +sctp_v6src_match_nexthop(struct sockaddr_in6 *src6, sctp_route_t * ro) +{ + struct nd_prefix *pfx = NULL; + struct nd_pfxrouter *pfxrtr = NULL; + struct sockaddr_in6 gw6; + + if (ro == NULL || ro->ro_rt == NULL || src6->sin6_family != AF_INET6) + return (0); + + /* get prefix entry of address */ + LIST_FOREACH(pfx, &nd_prefix, ndpr_entry) { + if (pfx->ndpr_stateflags & NDPRF_DETACHED) + continue; + if (IN6_ARE_MASKED_ADDR_EQUAL(&pfx->ndpr_prefix.sin6_addr, + &src6->sin6_addr, &pfx->ndpr_mask)) + break; + } + /* no prefix entry in the prefix list */ + if (pfx == NULL) { + SCTPDBG(SCTP_DEBUG_OUTPUT2, "No prefix entry for "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, (struct sockaddr *)src6); + return (0); + } + SCTPDBG(SCTP_DEBUG_OUTPUT2, "v6src_match_nexthop(), Prefix entry is "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, (struct sockaddr *)src6); + + /* search installed gateway from prefix entry */ + for (pfxrtr = pfx->ndpr_advrtrs.lh_first; pfxrtr; pfxrtr = + pfxrtr->pfr_next) { + memset(&gw6, 0, sizeof(struct sockaddr_in6)); + gw6.sin6_family = AF_INET6; + gw6.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&gw6.sin6_addr, &pfxrtr->router->rtaddr, + sizeof(struct in6_addr)); + SCTPDBG(SCTP_DEBUG_OUTPUT2, "prefix router is "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, (struct sockaddr *)&gw6); + SCTPDBG(SCTP_DEBUG_OUTPUT2, "installed router is "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, ro->ro_rt->rt_gateway); + if (sctp_cmpaddr((struct sockaddr *)&gw6, + ro->ro_rt->rt_gateway)) { + SCTPDBG(SCTP_DEBUG_OUTPUT2, "pfxrouter is installed\n"); + return (1); + } + } + SCTPDBG(SCTP_DEBUG_OUTPUT2, "pfxrouter is not installed\n"); + return (0); +} +int +sctp_v4src_match_nexthop(struct sctp_ifa *sifa, sctp_route_t * ro) +{ + struct sockaddr_in *sin, *mask; + struct ifaddr *ifa; + struct in_addr srcnetaddr, gwnetaddr; + + if (ro == NULL || ro->ro_rt == NULL || + sifa->address.sa.sa_family != AF_INET) { + return (0); + } + ifa = (struct ifaddr *)sifa->ifa; + mask = (struct sockaddr_in *)(ifa->ifa_netmask); + sin = (struct sockaddr_in *)&sifa->address.sin; + srcnetaddr.s_addr = (sin->sin_addr.s_addr & mask->sin_addr.s_addr); + SCTPDBG(SCTP_DEBUG_OUTPUT1, "match_nexthop4: src address is "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &sifa->address.sa); + SCTPDBG(SCTP_DEBUG_OUTPUT1, "network address is %x\n", srcnetaddr.s_addr); + + sin = (struct sockaddr_in *)ro->ro_rt->rt_gateway; + gwnetaddr.s_addr = (sin->sin_addr.s_addr & mask->sin_addr.s_addr); + SCTPDBG(SCTP_DEBUG_OUTPUT1, "match_nexthop4: nexthop is "); + SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, ro->ro_rt->rt_gateway); + SCTPDBG(SCTP_DEBUG_OUTPUT1, "network address is %x\n", gwnetaddr.s_addr); + if (srcnetaddr.s_addr == gwnetaddr.s_addr) { + return (1); + } + return (0); +} |