diff options
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r-- | sys/netinet/sctp_output.c | 465 |
1 files changed, 292 insertions, 173 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 96d9495..b13e9c4 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -2880,7 +2880,9 @@ bound_all_plan_b: printf("Plan C no preferred for Dest, acceptable for?\n"); } #endif - + if (emit_ifn == NULL) { + goto plan_d; + } LIST_FOREACH(sctp_ifa, &emit_ifn->ifalist, next_ifa) { if ((sctp_ifa->localifa_flags & SCTP_ADDR_DEFER_USE) && (non_asoc_addr_ok == 0)) @@ -2902,7 +2904,7 @@ bound_all_plan_b: atomic_add_int(&sifa->refcount, 1); return (sifa); } - +plan_d: /* * plan_d: We are in trouble. No preferred address on the emit * interface. And not even a perfered address on all interfaces. Go @@ -2969,7 +2971,6 @@ sctp_source_address_selection(struct sctp_inpcb *inp, struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&ro->ro_dst; struct sctp_ifa *answer; uint8_t dest_is_priv, dest_is_loop; - int did_rtalloc = 0; sa_family_t fam; /* @@ -3024,11 +3025,17 @@ sctp_source_address_selection(struct sctp_inpcb *inp, * we must use rotation amongst the bound addresses.. */ if (ro->ro_rt == NULL) { + uint32_t table_id = 0; + /* * Need a route to cache. */ - rtalloc_ign(ro, 0UL); - did_rtalloc = 1; + if (stcb) { + table_id = stcb->asoc.table_id; + } else { + table_id = SCTP_VRF_DEFAULT_TABLEID(vrf_id); + } + SCTP_RTALLOC(ro, vrf_id, table_id); } if (ro->ro_rt == NULL) { return (NULL); @@ -3327,25 +3334,22 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, */ /* Will need ifdefs around this */ struct mbuf *o_pak; - + struct mbuf *newm; struct sctphdr *sctphdr; int packet_length; - int o_flgs; uint32_t csum; int ret; - unsigned int have_mtu; uint32_t vrf_id; - sctp_route_t *ro; - + sctp_route_t *ro = NULL; if ((net) && (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)) { sctp_m_freem(m); return (EFAULT); } - if (stcb == NULL) { - vrf_id = inp->def_vrf_id; - } else { + if (stcb) { vrf_id = stcb->asoc.vrf_id; + } else { + vrf_id = inp->def_vrf_id; } /* fill in the HMAC digest for any AUTH chunk in the packet */ @@ -3354,7 +3358,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } /* Calculate the csum and fill in the length of the packet */ sctphdr = mtod(m, struct sctphdr *); - have_mtu = 0; if (sctp_no_csum_on_loopback && (stcb) && (stcb->asoc.loopback_scope)) { @@ -3376,17 +3379,17 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, sctp_route_t iproute; uint8_t tos_value; - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip)); - if (o_pak == NULL) { - /* failed to prepend data, give up */ + newm = sctp_get_mbuf_for_msg(sizeof(struct ip), 1, M_DONTWAIT, 1, MT_DATA); + if (newm == NULL) { sctp_m_freem(m); return (ENOMEM); } - SCTP_ALIGN_TO_END(o_pak, sizeof(struct ip)); - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip); + SCTP_ALIGN_TO_END(newm, sizeof(struct ip)); + SCTP_BUF_LEN(newm) = sizeof(struct ip); packet_length += sizeof(struct ip); - SCTP_ATTACH_CHAIN(o_pak, m, packet_length); - ip = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip *); + SCTP_BUF_NEXT(newm) = m; + m = newm; + ip = mtod(m, struct ip *); ip->ip_v = IPVERSION; ip->ip_hl = (sizeof(struct ip) >> 2); if (net) { @@ -3403,12 +3406,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } else ip->ip_off = 0; - /* FreeBSD has a function for ip_id's */ ip->ip_id = ip_newid(); ip->ip_ttl = inp->ip_inp.inp.inp_ip_ttl; - ip->ip_len = SCTP_HEADER_LEN(o_pak); + ip->ip_len = packet_length; if (stcb) { if ((stcb->asoc.ecn_allowed) && ecn_ok) { /* Enable ECN */ @@ -3435,10 +3437,16 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, /* call the routine to select the src address */ if (net) { + if (net->ro._s_addr && (net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED)) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + net->src_addr_selected = 0; + } if (net->src_addr_selected == 0) { /* Cache the source address */ net->ro._s_addr = sctp_source_address_selection(inp, stcb, - ro, net, out_of_asoc_ok, vrf_id); + ro, net, out_of_asoc_ok, + vrf_id); if (net->ro._s_addr == NULL) { /* No route to host */ goto no_route; @@ -3514,15 +3522,10 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } } } - sctp_m_freem(o_pak); return (EHOSTUNREACH); - } else { - have_mtu = ro->ro_rt->rt_ifp->if_mtu; } - if (inp->sctp_socket) { - o_flgs = (IP_RAWOUTPUT | (inp->sctp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST))); - } else { - o_flgs = IP_RAWOUTPUT; + if (ro != &iproute) { + memcpy(&iproute, ro, sizeof(*ro)); } #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) { @@ -3532,23 +3535,17 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, printf("RTP route is %p through\n", ro->ro_rt); } #endif -#ifdef _WHY_THIS_CODE - if ((have_mtu) && (net) && (have_mtu > net->mtu)) { - ro->ro_rt->rt_ifp->if_mtu = net->mtu; - } -#endif - if (ro != &iproute) { - memcpy(&iproute, ro, sizeof(*ro)); - } - ret = ip_output(o_pak, inp->ip_inp.inp.inp_options, - ro, o_flgs, inp->ip_inp.inp.inp_moptions - ,(struct inpcb *)NULL - ); -#ifdef _WHY_THIS_CODE - if ((ro->ro_rt) && (have_mtu) && (net) && (have_mtu > net->mtu)) { - ro->ro_rt->rt_ifp->if_mtu = have_mtu; + + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + /* failed to prepend data, give up */ + sctp_m_freem(m); + return (ENOMEM); } -#endif + SCTP_ATTACH_CHAIN(o_pak, m, packet_length); + + /* send it out. table id is taken from stcb */ + SCTP_IP_OUTPUT(ret, o_pak, ro, stcb, vrf_id, 0); + SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); if (ret) @@ -3568,10 +3565,17 @@ 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->rt_rmx.rmx_mtu && - (stcb->asoc.smallest_mtu > ro->ro_rt->rt_rmx.rmx_mtu)) { - sctp_mtu_size_reset(inp, &stcb->asoc, - ro->ro_rt->rt_rmx.rmx_mtu); + uint32_t mtu; + + mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); + if (mtu && + (stcb->asoc.smallest_mtu > mtu)) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_mtu_size_reset called after ip_output mtu-change:%d\n", + mtu); +#endif + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + net->mtu = mtu; } } else { /* route was freed */ @@ -3589,15 +3593,14 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, else if (to->sa_family == AF_INET6) { uint32_t flowlabel; struct ip6_hdr *ip6h; - struct route_in6 ip6route; struct ifnet *ifp; u_char flowTop; uint16_t flowBottom; u_char tosBottom, tosTop; struct sockaddr_in6 *sin6, tmp, *lsa6, lsa6_tmp; - struct sockaddr_in6 lsa6_storage; int prev_scope = 0; + struct sockaddr_in6 lsa6_storage; int error; u_short prev_port = 0; @@ -3606,18 +3609,19 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } else { flowlabel = ((struct in6pcb *)inp)->in6p_flowinfo; } - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip6_hdr)); - if (o_pak == NULL) { - /* failed to prepend data, give up */ + + newm = sctp_get_mbuf_for_msg(sizeof(struct ip6_hdr), 1, M_DONTWAIT, 1, MT_DATA); + if (newm == NULL) { sctp_m_freem(m); return (ENOMEM); } - SCTP_ALIGN_TO_END(o_pak, sizeof(struct ip6_hdr)); - - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip6_hdr); + SCTP_ALIGN_TO_END(newm, sizeof(struct ip6_hdr)); + SCTP_BUF_LEN(newm) = sizeof(struct ip6_hdr); packet_length += sizeof(struct ip6_hdr); - SCTP_ATTACH_CHAIN(o_pak, m, packet_length); - ip6h = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip6_hdr *); + SCTP_BUF_NEXT(newm) = m; + m = newm; + + ip6h = mtod(m, struct ip6_hdr *); /* * We assume here that inp_flow is in host byte order within * the TCB! @@ -3654,7 +3658,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } ip6h->ip6_flow = htonl(((tosTop << 24) | ((tosBottom | flowTop) << 16) | flowBottom)); ip6h->ip6_nxt = IPPROTO_SCTP; - ip6h->ip6_plen = (SCTP_HEADER_LEN(o_pak) - sizeof(struct ip6_hdr)); + ip6h->ip6_plen = (packet_length - sizeof(struct ip6_hdr)); ip6h->ip6_dst = sin6->sin6_addr; /* @@ -3667,6 +3671,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, lsa6_tmp.sin6_len = sizeof(lsa6_tmp); lsa6 = &lsa6_tmp; if (net) { + if (net->ro._s_addr && net->ro._s_addr->localifa_flags & SCTP_BEING_DELETED) { + sctp_free_ifa(net->ro._s_addr); + net->ro._s_addr = NULL; + net->src_addr_selected = 0; + } if (net->src_addr_selected == 0) { /* Cache the source address */ net->ro._s_addr = sctp_source_address_selection(inp, @@ -3712,7 +3721,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) { - sctp_m_freem(o_pak); + sctp_m_freem(m); return (error); } /* XXX */ @@ -3725,12 +3734,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, * We set the hop limit now since there is a good chance * that our ro pointer is now filled */ - ip6h->ip6_hlim = in6_selecthlim((struct in6pcb *)&inp->ip_inp.inp, - (ro ? - (ro->ro_rt ? (ro->ro_rt->rt_ifp) : (NULL)) : - (NULL))); - o_flgs = 0; - ifp = ro->ro_rt->rt_ifp; + ip6h->ip6_hlim = SCTP_GET_HLIM(inp, ro); + ifp = SCTP_GET_IFN_VOID_FROM_ROUTE(ro); + #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) { /* Copy to be sure something bad is not happening */ @@ -3743,20 +3749,24 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, printf("dst: "); sctp_print_address((struct sockaddr *)sin6); } -#endif /* SCTP_DEBUG */ +#endif if (net) { sin6 = (struct sockaddr_in6 *)&net->ro._l_addr; /* preserve the port and scope for link local send */ prev_scope = sin6->sin6_scope_id; prev_port = sin6->sin6_port; } - ret = ip6_output(o_pak, ((struct in6pcb *)inp)->in6p_outputopts, - (struct route_in6 *)ro, - o_flgs, - ((struct in6pcb *)inp)->in6p_moptions, - &ifp - ,NULL - ); + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + /* failed to prepend data, give up */ + sctp_m_freem(m); + return (ENOMEM); + } + SCTP_ATTACH_CHAIN(o_pak, m, packet_length); + + /* send it out. table id is taken from stcb */ + SCTP_IP6_OUTPUT(ret, o_pak, (struct route_in6 *)ro, &ifp, + stcb, vrf_id, 0); + if (net) { /* for link local this must be done */ sin6->sin6_scope_id = prev_scope; @@ -3766,7 +3776,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) { printf("return from send is %d\n", ret); } -#endif /* SCTP_DEBUG_OUTPUT */ +#endif SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); if (ret) { @@ -3781,7 +3791,6 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, /* PMTU check versus smallest asoc MTU goes here */ if (ro->ro_rt == NULL) { /* Route was freed */ - if (net->ro._s_addr && net->src_addr_selected) { sctp_free_ifa(net->ro._s_addr); @@ -3790,15 +3799,25 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, net->src_addr_selected = 0; } if (ro->ro_rt != NULL) { - if (ro->ro_rt->rt_rmx.rmx_mtu && - (stcb->asoc.smallest_mtu > ro->ro_rt->rt_rmx.rmx_mtu)) { - sctp_mtu_size_reset(inp, - &stcb->asoc, - ro->ro_rt->rt_rmx.rmx_mtu); + uint32_t mtu; + + mtu = SCTP_GATHER_MTU_FROM_ROUTE(net->ro._s_addr, &net->ro._l_addr.sa, ro->ro_rt); + if (mtu && + (stcb->asoc.smallest_mtu > mtu)) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_mtu_size_reset called after ip6_output mtu-change:%d\n", + mtu); +#endif + sctp_mtu_size_reset(inp, &stcb->asoc, mtu); + net->mtu = mtu; } } else if (ifp) { if (ND_IFINFO(ifp)->linkmtu && (stcb->asoc.smallest_mtu > ND_IFINFO(ifp)->linkmtu)) { +#ifdef SCTP_PRINT_FOR_B_AND_M + printf("sctp_mtu_size_reset called via ifp ND_IFINFO() linkmtu:%d\n", + ND_IFINFO(ifp)->linkmtu); +#endif sctp_mtu_size_reset(inp, &stcb->asoc, ND_IFINFO(ifp)->linkmtu); @@ -4544,7 +4563,7 @@ sctp_are_there_new_addresses(struct sctp_association *asoc, void sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct mbuf *init_pkt, int iphlen, int offset, struct sctphdr *sh, - struct sctp_init_chunk *init_chk) + struct sctp_init_chunk *init_chk, uint32_t vrf_id, uint32_t table_id) { struct sctp_association *asoc; struct mbuf *m, *m_at, *m_tmp, *m_cookie, *op_err, *mp_last; @@ -4567,15 +4586,11 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int abort_flag, padval, sz_of; int num_ext; int p_len; - uint32_t vrf_id; - if (stcb) { + if (stcb) asoc = &stcb->asoc; - vrf_id = asoc->vrf_id; - } else { - vrf_id = inp->def_vrf_id; + else asoc = NULL; - } mp_last = NULL; if ((asoc != NULL) && (SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_WAIT) && @@ -4586,7 +4601,8 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, * though we even set the T bit and copy in the 0 tag.. this * looks no different than if no listener was present. */ - sctp_send_abort(init_pkt, iphlen, sh, 0, NULL); + sctp_send_abort(init_pkt, iphlen, sh, 0, NULL, vrf_id, + table_id); return; } abort_flag = 0; @@ -4594,7 +4610,9 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, (offset + sizeof(struct sctp_init_chunk)), &abort_flag, (struct sctp_chunkhdr *)init_chk); if (abort_flag) { - sctp_send_abort(init_pkt, iphlen, sh, init_chk->init.initiate_tag, op_err); + sctp_send_abort(init_pkt, iphlen, sh, + init_chk->init.initiate_tag, op_err, vrf_id, + table_id); return; } m = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA); @@ -4682,7 +4700,8 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, ro = &iproute; memcpy(&ro->ro_dst, sin, sizeof(*sin)); addr = sctp_source_address_selection(inp, NULL, - ro, NULL, 0, vrf_id); + ro, NULL, 0, + vrf_id); if (addr == NULL) return; @@ -4711,9 +4730,9 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, stc.site_scope = 1; stc.local_scope = 0; } + sctp_free_ifa(addr); } else if (iph->ip_v == (IPV6_VERSION >> 4)) { struct sctp_ifa *addr; - struct route_in6 iproute6; ip6 = mtod(init_pkt, struct ip6_hdr *); @@ -4780,6 +4799,7 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, } memcpy(&stc.laddress, &addr->address.sin6.sin6_addr, sizeof(struct in6_addr)); stc.laddr_type = SCTP_IPV6_ADDRESS; + sctp_free_ifa(addr); } } else { /* set the scope per the existing tcb */ @@ -5446,6 +5466,7 @@ sctp_msg_append(struct sctp_tcb *stcb, SCTP_GETTIME_TIMEVAL(&sp->ts); sp->stream = srcv->sinfo_stream; sp->msg_is_complete = 1; + sp->sender_all_done = 1; sp->some_taken = 0; sp->data = m; sp->tail_mbuf = NULL; @@ -5509,7 +5530,9 @@ error_out: if (can_take_mbuf) { appendchain = clonechain; } else { - if (!copy_by_ref && (sizeofcpy <= ((((sctp_mbuf_threshold_count - 1) * MLEN) + MHLEN)))) { + if (!copy_by_ref && + (sizeofcpy <= ((((sctp_mbuf_threshold_count - 1) * MLEN) + MHLEN))) + ) { /* Its not in a cluster */ if (*endofchain == NULL) { /* lets get a mbuf cluster */ @@ -8025,7 +8048,8 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp, #ifdef SCTP_AUDITING_ENABLED sctp_audit_log(0xC3, 1); #endif - if (TAILQ_EMPTY(&asoc->sent_queue)) { + if ((TAILQ_EMPTY(&asoc->sent_queue)) && + (TAILQ_EMPTY(&asoc->control_send_queue))) { #ifdef SCTP_DEBUG if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) { printf("SCTP hits empty queue with cnt set to %d?\n", @@ -8780,7 +8804,6 @@ sctp_output(inp, m, addr, control, p, flags) struct mbuf *m; struct sockaddr *addr; struct mbuf *control; - struct thread *p; int flags; { @@ -9338,25 +9361,26 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb, } int -sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh) +sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh, + uint32_t vrf_id, uint32_t table_id) { /* formulate and SEND a SHUTDOWN-COMPLETE */ struct mbuf *o_pak; struct mbuf *mout; struct ip *iph, *iph_out; struct ip6_hdr *ip6, *ip6_out; - int offset_out, len; + int offset_out, len, mlen; struct sctp_shutdown_complete_msg *comp_cp; /* Get room for the largest message */ len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_shutdown_complete_msg)); - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(len); - if (o_pak == NULL) { - /* no mbuf's */ + + mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA); + if (mout == NULL) { return (-1); } - mout = SCTP_HEADER_TO_CHAIN(o_pak); + SCTP_BUF_LEN(mout) = len; iph = mtod(m, struct ip *); iph_out = NULL; ip6_out = NULL; @@ -9408,8 +9432,11 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh) /* Currently not supported. */ return (-1); } - - SCTP_HEADER_LEN(o_pak) = SCTP_BUF_LEN(mout); + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + /* no mbuf's */ + sctp_m_freem(mout); + return (-1); + } /* Now copy in and fill in the ABORT tags etc. */ comp_cp->sh.src_port = sh->dest_port; comp_cp->sh.dest_port = sh->src_port; @@ -9420,31 +9447,39 @@ 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(o_pak)) { + 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); } if (iph_out != NULL) { sctp_route_t ro; + int ret; + struct sctp_tcb *stcb = NULL; + mlen = SCTP_BUF_LEN(mout); bzero(&ro, sizeof ro); /* set IPv4 length */ - iph_out->ip_len = SCTP_HEADER_LEN(o_pak); + iph_out->ip_len = mlen; + SCTP_ATTACH_CHAIN(o_pak, mout, mlen); + /* out it goes */ - ip_output(o_pak, 0, &ro, IP_RAWOUTPUT, NULL - ,NULL - ); + SCTP_IP_OUTPUT(ret, o_pak, &ro, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); } else if (ip6_out != NULL) { struct route_in6 ro; + int ret; + struct sctp_tcb *stcb = NULL; + struct ifnet *ifp = NULL; bzero(&ro, sizeof(ro)); - ip6_output(o_pak, NULL, &ro, 0, NULL, NULL - ,NULL - ); + mlen = SCTP_BUF_LEN(mout); + SCTP_ATTACH_CHAIN(o_pak, mout, mlen); + SCTP_IP6_OUTPUT(ret, o_pak, &ro, &ifp, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); @@ -9453,6 +9488,7 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh) SCTP_STAT_INCR_COUNTER64(sctps_outpackets); SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks); return (0); + } static struct sctp_nets * @@ -10166,7 +10202,7 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, void sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, - struct mbuf *err_cause) + struct mbuf *err_cause, uint32_t vrf_id, uint32_t table_id) { /*- * Formulate the abort message, and send it back down. @@ -10176,7 +10212,7 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, struct sctp_abort_msg *abm; struct ip *iph, *iph_out; struct ip6_hdr *ip6, *ip6_out; - int iphlen_out; + int iphlen_out, len; /* don't respond to ABORT with ABORT */ if (sctp_is_there_an_abort_here(m, iphlen, &vtag)) { @@ -10184,13 +10220,14 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, sctp_m_freem(err_cause); return; } - o_pak = SCTP_GET_HEADER_FOR_OUTPUT((sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg))); - if (o_pak == NULL) { + len = (sizeof(struct ip6_hdr) + sizeof(struct sctp_abort_msg)); + + mout = sctp_get_mbuf_for_msg(len, 1, M_DONTWAIT, 1, MT_DATA); + if (mout == NULL) { if (err_cause) sctp_m_freem(err_cause); return; } - mout = SCTP_HEADER_TO_CHAIN(o_pak); iph = mtod(m, struct ip *); iph_out = NULL; ip6_out = NULL; @@ -10255,19 +10292,19 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, err_len += SCTP_BUF_LEN(m_tmp); m_tmp = SCTP_BUF_NEXT(m_tmp); } - SCTP_HEADER_LEN(o_pak) = SCTP_BUF_LEN(mout) + err_len; + len = SCTP_BUF_LEN(mout) + err_len; if (err_len % 4) { /* need pad at end of chunk */ uint32_t cpthis = 0; int padlen; - padlen = 4 - (SCTP_HEADER_LEN(o_pak) % 4); - m_copyback(mout, SCTP_HEADER_LEN(o_pak), padlen, (caddr_t)&cpthis); - SCTP_HEADER_LEN(o_pak) += padlen; + padlen = 4 - (len % 4); + m_copyback(mout, len, padlen, (caddr_t)&cpthis); + len += padlen; } abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch) + err_len); } else { - SCTP_HEADER_LEN(mout) = SCTP_BUF_LEN(mout); + len = SCTP_BUF_LEN(mout); abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch)); } @@ -10277,8 +10314,15 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, } else { 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); + return; + } if (iph_out != NULL) { sctp_route_t ro; + struct sctp_tcb *stcb = NULL; + int ret; /* zap the stack pointer to the route */ bzero(&ro, sizeof ro); @@ -10289,16 +10333,19 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, } #endif /* set IPv4 length */ - iph_out->ip_len = SCTP_HEADER_LEN(o_pak); + iph_out->ip_len = len; /* out it goes */ - (void)ip_output(o_pak, 0, &ro, IP_RAWOUTPUT, NULL - ,NULL - ); + SCTP_ATTACH_CHAIN(o_pak, mout, len); + SCTP_IP_OUTPUT(ret, o_pak, &ro, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); } else if (ip6_out != NULL) { struct route_in6 ro; + int ret; + struct sctp_tcb *stcb = NULL; + struct ifnet *ifp = NULL; /* zap the stack pointer to the route */ bzero(&ro, sizeof(ro)); @@ -10308,10 +10355,10 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, sctp_print_address_pkt((struct ip *)ip6_out, &abm->sh); } #endif - ip6_out->ip6_plen = SCTP_HEADER_LEN(o_pak) - sizeof(*ip6_out); - ip6_output(o_pak, NULL, &ro, 0, NULL, NULL - ,NULL - ); + ip6_out->ip6_plen = len - sizeof(*ip6_out); + SCTP_ATTACH_CHAIN(o_pak, mout, len); + SCTP_IP6_OUTPUT(ret, o_pak, &ro, &ifp, stcb, vrf_id, table_id); + /* Free the route if we got one back */ if (ro.ro_rt) RTFREE(ro.ro_rt); @@ -10321,17 +10368,16 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, } void -sctp_send_operr_to(struct mbuf *m, int iphlen, - struct mbuf *scm, - uint32_t vtag) +sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag, + uint32_t vrf_id, uint32_t table_id) { struct mbuf *o_pak; struct sctphdr *ihdr; int retcode; struct sctphdr *ohdr; struct sctp_chunkhdr *ophdr; - struct ip *iph; + struct mbuf *mout; #ifdef SCTP_DEBUG struct sockaddr_in6 lsa6, fsa6; @@ -10363,7 +10409,6 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, len += SCTP_BUF_LEN(at); at = SCTP_BUF_NEXT(at); } - ophdr->chunk_length = htons(len - sizeof(struct sctphdr)); if (len % 4) { /* need padding */ @@ -10379,22 +10424,28 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, } else { 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); + return; + } + SCTP_BUF_NEXT(mout) = scm; + if (SCTP_GET_HEADER_FOR_OUTPUT(o_pak)) { + sctp_m_freem(mout); + return; + } ohdr->checksum = val; if (iph->ip_v == IPVERSION) { /* V4 */ struct ip *out; sctp_route_t ro; + struct sctp_tcb *stcb = NULL; - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip)); - if (o_pak == NULL) { - sctp_m_freem(scm); - return; - } - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip); + SCTP_BUF_LEN(mout) = sizeof(struct ip); len += sizeof(struct ip); - SCTP_ATTACH_CHAIN(o_pak, scm, len); + bzero(&ro, sizeof ro); - out = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip *); + out = mtod(mout, struct ip *); out->ip_v = iph->ip_v; out->ip_hl = (sizeof(struct ip) / 4); out->ip_tos = iph->ip_tos; @@ -10405,10 +10456,11 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, out->ip_sum = 0; out->ip_src = iph->ip_dst; out->ip_dst = iph->ip_src; - out->ip_len = SCTP_HEADER_LEN(o_pak); - retcode = ip_output(o_pak, 0, &ro, IP_RAWOUTPUT, NULL - ,NULL - ); + out->ip_len = len; + SCTP_ATTACH_CHAIN(o_pak, mout, len); + + SCTP_IP_OUTPUT(retcode, o_pak, &ro, stcb, vrf_id, table_id); + SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); /* Free the route if we got one back */ @@ -10417,20 +10469,16 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, } else { /* V6 */ struct route_in6 ro; + int ret; + struct sctp_tcb *stcb = NULL; + struct ifnet *ifp = NULL; struct ip6_hdr *out6, *in6; - o_pak = SCTP_GET_HEADER_FOR_OUTPUT(sizeof(struct ip6_hdr)); - if (o_pak == NULL) { - sctp_m_freem(scm); - return; - } - SCTP_BUF_LEN(SCTP_HEADER_TO_CHAIN(o_pak)) = sizeof(struct ip6_hdr); + SCTP_BUF_LEN(mout) = sizeof(struct ip6_hdr); len += sizeof(struct ip6_hdr); - SCTP_ATTACH_CHAIN(o_pak, scm, len); - bzero(&ro, sizeof ro); in6 = mtod(m, struct ip6_hdr *); - out6 = mtod(SCTP_HEADER_TO_CHAIN(o_pak), struct ip6_hdr *); + out6 = mtod(mout, struct ip6_hdr *); out6->ip6_flow = in6->ip6_flow; out6->ip6_hlim = ip6_defhlim; out6->ip6_nxt = IPPROTO_SCTP; @@ -10454,9 +10502,10 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, sctp_print_address((struct sockaddr *)&fsa6); } #endif /* SCTP_DEBUG */ - ip6_output(o_pak, NULL, &ro, 0, NULL, NULL - ,NULL - ); + + SCTP_ATTACH_CHAIN(o_pak, mout, len); + SCTP_IP6_OUTPUT(ret, o_pak, &ro, &ifp, stcb, vrf_id, table_id); + SCTP_STAT_INCR(sctps_sendpackets); SCTP_STAT_INCR_COUNTER64(sctps_outpackets); /* Free the route if we got one back */ @@ -10496,7 +10545,7 @@ sctp_copy_one(struct sctp_stream_queue_pending *sp, left = sp->length; sp->data = m_uiotombuf(uio, M_WAITOK, sp->length, - resv_upfront, M_PKTHDR); + resv_upfront, 0); if (sp->data == NULL) return (ENOMEM); @@ -10596,8 +10645,6 @@ out_now: } - - int sctp_sosend(struct socket *so, struct sockaddr *addr, @@ -10640,7 +10687,8 @@ sctp_lower_sosend(struct socket *so, struct mbuf *control, int flags, int use_rcvinfo, - struct sctp_sndrcvinfo *srcv, + struct sctp_sndrcvinfo *srcv + , struct thread *p ) { @@ -10669,6 +10717,7 @@ sctp_lower_sosend(struct socket *so, int got_all_of_the_send = 0; int hold_tcblock = 0; int non_blocking = 0; + int temp_flags = 0; error = 0; net = NULL; @@ -10721,12 +10770,50 @@ sctp_lower_sosend(struct socket *so, } hold_tcblock = 0; SCTP_INP_RUNLOCK(inp); - if (addr) + if (addr) { /* Must locate the net structure if addr given */ net = sctp_findnet(stcb, addr); - else + if (net) { + /* validate port was 0 or correct */ + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)addr; + if ((sin->sin_port != 0) && + (sin->sin_port != stcb->rport)) { + net = NULL; + } + } + temp_flags |= SCTP_ADDR_OVER; + } else net = stcb->asoc.primary_destination; + if (addr && (net == NULL)) { + /* Could not find address, was it legal */ + if (addr->sa_family == AF_INET) { + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)addr; + if (sin->sin_addr.s_addr == 0) { + if ((sin->sin_port == 0) || + (sin->sin_port == stcb->rport)) { + net = stcb->asoc.primary_destination; + } + } + } else { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)addr; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + if ((sin6->sin6_port == 0) || + (sin6->sin6_port == stcb->rport)) { + net = stcb->asoc.primary_destination; + } + } + } + } + if (net == NULL) { + error = EINVAL; + goto out_unlocked; + } } else if (use_rcvinfo && srcv && srcv->sinfo_assoc_id) { stcb = sctp_findassociation_ep_asocid(inp, srcv->sinfo_assoc_id, 0); if (stcb) { @@ -10738,6 +10825,21 @@ sctp_lower_sosend(struct socket *so, net = sctp_findnet(stcb, addr); else net = stcb->asoc.primary_destination; + if ((srcv->sinfo_flags & SCTP_ADDR_OVER) && + ((net == NULL) || (addr == NULL))) { + struct sockaddr_in *sin; + + if (addr == NULL) { + error = EINVAL; + goto out_unlocked; + } + sin = (struct sockaddr_in *)addr; + /* Validate port is 0 or correct */ + if ((sin->sin_port != 0) && + (sin->sin_port != stcb->rport)) { + net = NULL; + } + } } hold_tcblock = 0; } else if (addr) { @@ -11010,13 +11112,14 @@ sctp_lower_sosend(struct socket *so, p->td_proc->p_stats->p_ru.ru_msgsnd++; } if (stcb) { - if (net && ((srcv->sinfo_flags & SCTP_ADDR_OVER))) { - /* we take the override or the unconfirmed */ - ; - } else { + if (((srcv->sinfo_flags | temp_flags) & SCTP_ADDR_OVER) == 0) { net = stcb->asoc.primary_destination; } } + if (net == NULL) { + error = EINVAL; + goto out_unlocked; + } if ((net->flight_size > net->cwnd) && (sctp_cmt_on_off == 0)) { /*- * CMT: Added check for CMT above. net above is the primary @@ -11389,7 +11492,15 @@ sctp_lower_sosend(struct socket *so, un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + ((stcb->asoc.chunks_on_out_queue - stcb->asoc.total_flight_count) * sizeof(struct sctp_data_chunk))); - queue_only = 0; + if (net->flight_size > (net->mtu * stcb->asoc.max_burst)) { + 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 { + queue_only = 0; + } } if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && @@ -11646,7 +11757,15 @@ skip_out_eof: un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) + ((stcb->asoc.chunks_on_out_queue - stcb->asoc.total_flight_count) * sizeof(struct sctp_data_chunk))); - queue_only = 0; + if (net->flight_size > (net->mtu * stcb->asoc.max_burst)) { + 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 { + queue_only = 0; + } } if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) && (stcb->asoc.total_flight > 0) && |