1f8829a4aSRandall Stewart /*- 2b1006367SRandall Stewart * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. 3807aad63SMichael Tuexen * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. 4807aad63SMichael Tuexen * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. 5f8829a4aSRandall Stewart * 6f8829a4aSRandall Stewart * Redistribution and use in source and binary forms, with or without 7f8829a4aSRandall Stewart * modification, are permitted provided that the following conditions are met: 8f8829a4aSRandall Stewart * 9f8829a4aSRandall Stewart * a) Redistributions of source code must retain the above copyright notice, 10f8829a4aSRandall Stewart * this list of conditions and the following disclaimer. 11f8829a4aSRandall Stewart * 12f8829a4aSRandall Stewart * b) Redistributions in binary form must reproduce the above copyright 13f8829a4aSRandall Stewart * notice, this list of conditions and the following disclaimer in 14f8829a4aSRandall Stewart * the documentation and/or other materials provided with the distribution. 15f8829a4aSRandall Stewart * 16f8829a4aSRandall Stewart * c) Neither the name of Cisco Systems, Inc. nor the names of its 17f8829a4aSRandall Stewart * contributors may be used to endorse or promote products derived 18f8829a4aSRandall Stewart * from this software without specific prior written permission. 19f8829a4aSRandall Stewart * 20f8829a4aSRandall Stewart * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21f8829a4aSRandall Stewart * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22f8829a4aSRandall Stewart * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23f8829a4aSRandall Stewart * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24f8829a4aSRandall Stewart * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25f8829a4aSRandall Stewart * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26f8829a4aSRandall Stewart * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27f8829a4aSRandall Stewart * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28f8829a4aSRandall Stewart * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29f8829a4aSRandall Stewart * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30f8829a4aSRandall Stewart * THE POSSIBILITY OF SUCH DAMAGE. 31f8829a4aSRandall Stewart */ 32b48287a3SDavid E. O'Brien 3373932c69SRandall Stewart #include <sys/cdefs.h> 34f8829a4aSRandall Stewart __FBSDID("$FreeBSD$"); 35f8829a4aSRandall Stewart 36f8829a4aSRandall Stewart #include <netinet/sctp_os.h> 375e20b91dSMichael Tuexen #ifdef INET6 3893164cf9SRandall Stewart #include <sys/proc.h> 39f8829a4aSRandall Stewart #include <netinet/sctp_pcb.h> 40f8829a4aSRandall Stewart #include <netinet/sctp_header.h> 41f8829a4aSRandall Stewart #include <netinet/sctp_var.h> 42c105859eSRandall Stewart #include <netinet6/sctp6_var.h> 4342551e99SRandall Stewart #include <netinet/sctp_sysctl.h> 44f8829a4aSRandall Stewart #include <netinet/sctp_output.h> 4593164cf9SRandall Stewart #include <netinet/sctp_uio.h> 4693164cf9SRandall Stewart #include <netinet/sctp_asconf.h> 4793164cf9SRandall Stewart #include <netinet/sctputil.h> 4893164cf9SRandall Stewart #include <netinet/sctp_indata.h> 4993164cf9SRandall Stewart #include <netinet/sctp_timer.h> 5093164cf9SRandall Stewart #include <netinet/sctp_auth.h> 51c105859eSRandall Stewart #include <netinet/sctp_input.h> 52c105859eSRandall Stewart #include <netinet/sctp_output.h> 53207304d4SRandall Stewart #include <netinet/sctp_bsd_addr.h> 54a99b6783SRandall Stewart #include <netinet/sctp_crc32.h> 554ff815b7SMichael Tuexen #include <netinet/icmp6.h> 56b3f1ea41SRandall Stewart #include <netinet/udp.h> 57f8829a4aSRandall Stewart 58b2630c29SGeorge V. Neville-Neil #ifdef IPSEC 592cb64cb2SGeorge V. Neville-Neil #include <netipsec/ipsec.h> 602cb64cb2SGeorge V. Neville-Neil #include <netipsec/ipsec6.h> 61b2630c29SGeorge V. Neville-Neil #endif /* IPSEC */ 62f8829a4aSRandall Stewart 6393164cf9SRandall Stewart extern struct protosw inetsw[]; 64f8829a4aSRandall Stewart 65f8829a4aSRandall Stewart int 663a51a264SMichael Tuexen sctp6_input_with_port(struct mbuf **i_pak, int *offp, uint16_t port) 67f8829a4aSRandall Stewart { 68139bc87fSRandall Stewart struct mbuf *m; 696dc5aabcSMichael Tuexen int iphlen; 70a8775ad9SMichael Tuexen uint32_t vrf_id; 716dc5aabcSMichael Tuexen uint8_t ecn_bits; 72b1754ad1SMichael Tuexen struct sockaddr_in6 src, dst; 73f8829a4aSRandall Stewart struct ip6_hdr *ip6; 74f8829a4aSRandall Stewart struct sctphdr *sh; 75f8829a4aSRandall Stewart struct sctp_chunkhdr *ch; 766dc5aabcSMichael Tuexen int length, offset; 779c7635e1SMichael Tuexen 789c7635e1SMichael Tuexen #if !defined(SCTP_WITH_NO_CSUM) 79a8775ad9SMichael Tuexen uint8_t compute_crc; 809c7635e1SMichael Tuexen 819c7635e1SMichael Tuexen #endif 82a8775ad9SMichael Tuexen uint32_t mflowid; 83457b4b88SMichael Tuexen uint8_t mflowtype; 84d089f9b9SMichael Tuexen uint16_t fibnum; 85f8829a4aSRandall Stewart 866dc5aabcSMichael Tuexen iphlen = *offp; 8717205eccSRandall Stewart if (SCTP_GET_PKT_VRFID(*i_pak, vrf_id)) { 8817205eccSRandall Stewart SCTP_RELEASE_PKT(*i_pak); 896dc5aabcSMichael Tuexen return (IPPROTO_DONE); 9017205eccSRandall Stewart } 91c105859eSRandall Stewart m = SCTP_HEADER_TO_CHAIN(*i_pak); 926dc5aabcSMichael Tuexen #ifdef SCTP_MBUF_LOGGING 936dc5aabcSMichael Tuexen /* Log in any input mbufs */ 946dc5aabcSMichael Tuexen if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) { 954be807c4SMichael Tuexen sctp_log_mbc(m, SCTP_MBUF_INPUT); 966dc5aabcSMichael Tuexen } 976dc5aabcSMichael Tuexen #endif 98207304d4SRandall Stewart #ifdef SCTP_PACKET_LOGGING 99f9384252SMichael Tuexen if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING) { 100f9384252SMichael Tuexen sctp_packet_log(m); 101f9384252SMichael Tuexen } 102207304d4SRandall Stewart #endif 103a8775ad9SMichael Tuexen SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, 1041a94cdbeSMichael Tuexen "sctp6_input(): Packet of length %d received on %s with csum_flags 0x%b.\n", 105a8775ad9SMichael Tuexen m->m_pkthdr.len, 106a8775ad9SMichael Tuexen if_name(m->m_pkthdr.rcvif), 1071a94cdbeSMichael Tuexen (int)m->m_pkthdr.csum_flags, CSUM_BITS); 108f30ac432SMichael Tuexen mflowid = m->m_pkthdr.flowid; 109457b4b88SMichael Tuexen mflowtype = M_HASHTYPE_GET(m); 110d089f9b9SMichael Tuexen fibnum = M_GETFIB(m); 1116dc5aabcSMichael Tuexen SCTP_STAT_INCR(sctps_recvpackets); 1126dc5aabcSMichael Tuexen SCTP_STAT_INCR_COUNTER64(sctps_inpackets); 1136dc5aabcSMichael Tuexen /* Get IP, SCTP, and first chunk header together in the first mbuf. */ 1146dc5aabcSMichael Tuexen offset = iphlen + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr); 115a8775ad9SMichael Tuexen ip6 = mtod(m, struct ip6_hdr *); 1166dc5aabcSMichael Tuexen IP6_EXTHDR_GET(sh, struct sctphdr *, m, iphlen, 1176dc5aabcSMichael Tuexen (int)(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr))); 118f8829a4aSRandall Stewart if (sh == NULL) { 119f8829a4aSRandall Stewart SCTP_STAT_INCR(sctps_hdrops); 12060990c0cSMichael Tuexen return (IPPROTO_DONE); 121f8829a4aSRandall Stewart } 122f8829a4aSRandall Stewart ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(struct sctphdr)); 1236dc5aabcSMichael Tuexen offset -= sizeof(struct sctp_chunkhdr); 124b1754ad1SMichael Tuexen memset(&src, 0, sizeof(struct sockaddr_in6)); 125b1754ad1SMichael Tuexen src.sin6_family = AF_INET6; 126b1754ad1SMichael Tuexen src.sin6_len = sizeof(struct sockaddr_in6); 127b1754ad1SMichael Tuexen src.sin6_port = sh->src_port; 128b1754ad1SMichael Tuexen src.sin6_addr = ip6->ip6_src; 129b1754ad1SMichael Tuexen if (in6_setscope(&src.sin6_addr, m->m_pkthdr.rcvif, NULL) != 0) { 130a8775ad9SMichael Tuexen goto out; 131b1754ad1SMichael Tuexen } 132b1754ad1SMichael Tuexen memset(&dst, 0, sizeof(struct sockaddr_in6)); 133b1754ad1SMichael Tuexen dst.sin6_family = AF_INET6; 134b1754ad1SMichael Tuexen dst.sin6_len = sizeof(struct sockaddr_in6); 135b1754ad1SMichael Tuexen dst.sin6_port = sh->dest_port; 136b1754ad1SMichael Tuexen dst.sin6_addr = ip6->ip6_dst; 137b1754ad1SMichael Tuexen if (in6_setscope(&dst.sin6_addr, m->m_pkthdr.rcvif, NULL) != 0) { 138a8775ad9SMichael Tuexen goto out; 139b1754ad1SMichael Tuexen } 1406dc5aabcSMichael Tuexen length = ntohs(ip6->ip6_plen) + iphlen; 1416dc5aabcSMichael Tuexen /* Validate mbuf chain length with IP payload length. */ 142a8775ad9SMichael Tuexen if (SCTP_HEADER_LEN(m) != length) { 1436dc5aabcSMichael Tuexen SCTPDBG(SCTP_DEBUG_INPUT1, 144a8775ad9SMichael Tuexen "sctp6_input() length:%d reported length:%d\n", length, SCTP_HEADER_LEN(m)); 1456dc5aabcSMichael Tuexen SCTP_STAT_INCR(sctps_hdrops); 146a8775ad9SMichael Tuexen goto out; 147f8829a4aSRandall Stewart } 1486dc5aabcSMichael Tuexen if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 149a8775ad9SMichael Tuexen goto out; 1506dc5aabcSMichael Tuexen } 151a8775ad9SMichael Tuexen ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff); 1529c7635e1SMichael Tuexen #if defined(SCTP_WITH_NO_CSUM) 1539c7635e1SMichael Tuexen SCTP_STAT_INCR(sctps_recvnocrc); 1549c7635e1SMichael Tuexen #else 155a99b6783SRandall Stewart if (m->m_pkthdr.csum_flags & CSUM_SCTP_VALID) { 156a99b6783SRandall Stewart SCTP_STAT_INCR(sctps_recvhwcrc); 157a8775ad9SMichael Tuexen compute_crc = 0; 158a8775ad9SMichael Tuexen } else { 159a99b6783SRandall Stewart SCTP_STAT_INCR(sctps_recvswcrc); 160a8775ad9SMichael Tuexen compute_crc = 1; 161f8829a4aSRandall Stewart } 1626dc5aabcSMichael Tuexen #endif 163b1754ad1SMichael Tuexen sctp_common_input_processing(&m, iphlen, offset, length, 164b1754ad1SMichael Tuexen (struct sockaddr *)&src, 165b1754ad1SMichael Tuexen (struct sockaddr *)&dst, 166a8775ad9SMichael Tuexen sh, ch, 167a8775ad9SMichael Tuexen #if !defined(SCTP_WITH_NO_CSUM) 168a8775ad9SMichael Tuexen compute_crc, 169a8775ad9SMichael Tuexen #endif 170a8775ad9SMichael Tuexen ecn_bits, 171d089f9b9SMichael Tuexen mflowtype, mflowid, fibnum, 172f30ac432SMichael Tuexen vrf_id, port); 173a8775ad9SMichael Tuexen out: 1746dc5aabcSMichael Tuexen if (m) { 17517205eccSRandall Stewart sctp_m_freem(m); 1766dc5aabcSMichael Tuexen } 17760990c0cSMichael Tuexen return (IPPROTO_DONE); 178f8829a4aSRandall Stewart } 179f8829a4aSRandall Stewart 180f8829a4aSRandall Stewart 1813a51a264SMichael Tuexen int 1823a51a264SMichael Tuexen sctp6_input(struct mbuf **i_pak, int *offp, int proto SCTP_UNUSED) 1833a51a264SMichael Tuexen { 1843a51a264SMichael Tuexen return (sctp6_input_with_port(i_pak, offp, 0)); 1853a51a264SMichael Tuexen } 1863a51a264SMichael Tuexen 18704ee05e8SRandall Stewart void 188851b7298SRandall Stewart sctp6_notify(struct sctp_inpcb *inp, 189851b7298SRandall Stewart struct sctp_tcb *stcb, 190*b9dd6a90SMichael Tuexen struct sctp_nets *net, 191*b9dd6a90SMichael Tuexen uint8_t icmp6_type, 192*b9dd6a90SMichael Tuexen uint8_t icmp6_code, 193*b9dd6a90SMichael Tuexen uint16_t next_mtu) 194851b7298SRandall Stewart { 195851b7298SRandall Stewart #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) 196851b7298SRandall Stewart struct socket *so; 197851b7298SRandall Stewart 198851b7298SRandall Stewart #endif 199*b9dd6a90SMichael Tuexen int timer_stopped; 200a2b42326SMichael Tuexen 201*b9dd6a90SMichael Tuexen switch (icmp6_type) { 202*b9dd6a90SMichael Tuexen case ICMP6_DST_UNREACH: 203*b9dd6a90SMichael Tuexen if ((icmp6_code == ICMP6_DST_UNREACH_NOROUTE) || 204*b9dd6a90SMichael Tuexen (icmp6_code == ICMP6_DST_UNREACH_ADMIN) || 205*b9dd6a90SMichael Tuexen (icmp6_code == ICMP6_DST_UNREACH_BEYONDSCOPE) || 206*b9dd6a90SMichael Tuexen (icmp6_code == ICMP6_DST_UNREACH_ADDR)) { 207*b9dd6a90SMichael Tuexen /* Mark the net unreachable. */ 208851b7298SRandall Stewart if (net->dest_state & SCTP_ADDR_REACHABLE) { 209*b9dd6a90SMichael Tuexen /* Ok that destination is not reachable */ 210851b7298SRandall Stewart net->dest_state &= ~SCTP_ADDR_REACHABLE; 211851b7298SRandall Stewart net->dest_state &= ~SCTP_ADDR_PF; 212851b7298SRandall Stewart sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, 2131a5b7901SMichael Tuexen stcb, 0, (void *)net, SCTP_SO_NOT_LOCKED); 214851b7298SRandall Stewart } 215*b9dd6a90SMichael Tuexen } 216851b7298SRandall Stewart SCTP_TCB_UNLOCK(stcb); 217*b9dd6a90SMichael Tuexen break; 218*b9dd6a90SMichael Tuexen case ICMP6_PARAM_PROB: 219*b9dd6a90SMichael Tuexen /* Treat it like an ABORT. */ 220*b9dd6a90SMichael Tuexen if (icmp6_code == ICMP6_PARAMPROB_NEXTHEADER) { 221410a3b1eSMichael Tuexen sctp_abort_notification(stcb, 1, 0, NULL, SCTP_SO_NOT_LOCKED); 222851b7298SRandall Stewart #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) 223851b7298SRandall Stewart so = SCTP_INP_SO(inp); 224851b7298SRandall Stewart atomic_add_int(&stcb->asoc.refcnt, 1); 225851b7298SRandall Stewart SCTP_TCB_UNLOCK(stcb); 226851b7298SRandall Stewart SCTP_SOCKET_LOCK(so, 1); 227851b7298SRandall Stewart SCTP_TCB_LOCK(stcb); 228851b7298SRandall Stewart atomic_subtract_int(&stcb->asoc.refcnt, 1); 229851b7298SRandall Stewart #endif 230b7d130beSMichael Tuexen (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, 231*b9dd6a90SMichael Tuexen SCTP_FROM_SCTP_USRREQ + SCTP_LOC_2); 232851b7298SRandall Stewart #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) 233851b7298SRandall Stewart SCTP_SOCKET_UNLOCK(so, 1); 234851b7298SRandall Stewart #endif 235851b7298SRandall Stewart } else { 236851b7298SRandall Stewart SCTP_TCB_UNLOCK(stcb); 237851b7298SRandall Stewart } 238*b9dd6a90SMichael Tuexen break; 239*b9dd6a90SMichael Tuexen case ICMP6_PACKET_TOO_BIG: 240*b9dd6a90SMichael Tuexen if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) { 241*b9dd6a90SMichael Tuexen timer_stopped = 1; 242*b9dd6a90SMichael Tuexen sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net, 243*b9dd6a90SMichael Tuexen SCTP_FROM_SCTP_USRREQ + SCTP_LOC_1); 244*b9dd6a90SMichael Tuexen } else { 245*b9dd6a90SMichael Tuexen timer_stopped = 0; 246851b7298SRandall Stewart } 247*b9dd6a90SMichael Tuexen break; 248*b9dd6a90SMichael Tuexen /* Update the path MTU. */ 249*b9dd6a90SMichael Tuexen if (net->mtu > next_mtu) { 250*b9dd6a90SMichael Tuexen net->mtu = next_mtu; 251*b9dd6a90SMichael Tuexen if (net->port) { 252*b9dd6a90SMichael Tuexen net->mtu -= sizeof(struct udphdr); 253*b9dd6a90SMichael Tuexen } 254*b9dd6a90SMichael Tuexen } 255*b9dd6a90SMichael Tuexen /* Update the association MTU */ 256*b9dd6a90SMichael Tuexen if (stcb->asoc.smallest_mtu > next_mtu) { 257*b9dd6a90SMichael Tuexen sctp_pathmtu_adjustment(stcb, next_mtu); 258*b9dd6a90SMichael Tuexen } 259*b9dd6a90SMichael Tuexen /* Finally, start the PMTU timer if it was running before. */ 260*b9dd6a90SMichael Tuexen if (timer_stopped) { 261*b9dd6a90SMichael Tuexen sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net); 262*b9dd6a90SMichael Tuexen } 263*b9dd6a90SMichael Tuexen SCTP_TCB_UNLOCK(stcb); 264*b9dd6a90SMichael Tuexen break; 265*b9dd6a90SMichael Tuexen default: 266*b9dd6a90SMichael Tuexen SCTP_TCB_UNLOCK(stcb); 267*b9dd6a90SMichael Tuexen break; 268*b9dd6a90SMichael Tuexen } 269*b9dd6a90SMichael Tuexen } 270851b7298SRandall Stewart 271f8829a4aSRandall Stewart void 2721272577eSXin LI sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) 273f8829a4aSRandall Stewart { 274*b9dd6a90SMichael Tuexen struct ip6ctlparam *ip6cp; 275*b9dd6a90SMichael Tuexen struct sctp_inpcb *inp; 276*b9dd6a90SMichael Tuexen struct sctp_tcb *stcb; 277*b9dd6a90SMichael Tuexen struct sctp_nets *net; 278f8829a4aSRandall Stewart struct sctphdr sh; 279*b9dd6a90SMichael Tuexen struct sockaddr_in6 src, dst; 28042551e99SRandall Stewart 281f8829a4aSRandall Stewart if (pktdst->sa_family != AF_INET6 || 282*b9dd6a90SMichael Tuexen pktdst->sa_len != sizeof(struct sockaddr_in6)) { 283f8829a4aSRandall Stewart return; 284*b9dd6a90SMichael Tuexen } 285*b9dd6a90SMichael Tuexen if ((unsigned)cmd >= PRC_NCMDS) { 286f8829a4aSRandall Stewart return; 287*b9dd6a90SMichael Tuexen } 288f8829a4aSRandall Stewart if (PRC_IS_REDIRECT(cmd)) { 289f8829a4aSRandall Stewart d = NULL; 290f8829a4aSRandall Stewart } else if (inet6ctlerrmap[cmd] == 0) { 291f8829a4aSRandall Stewart return; 292f8829a4aSRandall Stewart } 293*b9dd6a90SMichael Tuexen /* If the parameter is from icmp6, decode it. */ 294f8829a4aSRandall Stewart if (d != NULL) { 295f8829a4aSRandall Stewart ip6cp = (struct ip6ctlparam *)d; 296f8829a4aSRandall Stewart } else { 297f8829a4aSRandall Stewart ip6cp = (struct ip6ctlparam *)NULL; 298f8829a4aSRandall Stewart } 299f8829a4aSRandall Stewart 300*b9dd6a90SMichael Tuexen if (ip6cp != NULL) { 301f8829a4aSRandall Stewart /* 302f8829a4aSRandall Stewart * XXX: We assume that when IPV6 is non NULL, M and OFF are 303f8829a4aSRandall Stewart * valid. 304f8829a4aSRandall Stewart */ 305*b9dd6a90SMichael Tuexen if (ip6cp->ip6c_m == NULL) { 306f8829a4aSRandall Stewart return; 307*b9dd6a90SMichael Tuexen } 308*b9dd6a90SMichael Tuexen /* 309*b9dd6a90SMichael Tuexen * Check if we can safely examine the ports and the 310*b9dd6a90SMichael Tuexen * verification tag of the SCTP common header. 311*b9dd6a90SMichael Tuexen */ 312*b9dd6a90SMichael Tuexen if (ip6cp->ip6c_m->m_pkthdr.len < 313*b9dd6a90SMichael Tuexen ip6cp->ip6c_off + offsetof(struct sctphdr, checksum)) { 31447979581SGleb Smirnoff return; 315*b9dd6a90SMichael Tuexen } 316*b9dd6a90SMichael Tuexen /* Copy out the port numbers and the verification tag. */ 317f8829a4aSRandall Stewart bzero(&sh, sizeof(sh)); 318*b9dd6a90SMichael Tuexen m_copydata(ip6cp->ip6c_m, 319*b9dd6a90SMichael Tuexen ip6cp->ip6c_off, 320*b9dd6a90SMichael Tuexen sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t), 321*b9dd6a90SMichael Tuexen (caddr_t)&sh); 322*b9dd6a90SMichael Tuexen memset(&src, 0, sizeof(struct sockaddr_in6)); 323*b9dd6a90SMichael Tuexen src.sin6_family = AF_INET6; 324*b9dd6a90SMichael Tuexen src.sin6_len = sizeof(struct sockaddr_in6); 325*b9dd6a90SMichael Tuexen src.sin6_port = sh.src_port; 326*b9dd6a90SMichael Tuexen src.sin6_addr = ip6cp->ip6c_ip6->ip6_src; 327*b9dd6a90SMichael Tuexen if (in6_setscope(&src.sin6_addr, ip6cp->ip6c_m->m_pkthdr.rcvif, NULL) != 0) { 328*b9dd6a90SMichael Tuexen return; 329*b9dd6a90SMichael Tuexen } 330*b9dd6a90SMichael Tuexen memset(&dst, 0, sizeof(struct sockaddr_in6)); 331*b9dd6a90SMichael Tuexen dst.sin6_family = AF_INET6; 332*b9dd6a90SMichael Tuexen dst.sin6_len = sizeof(struct sockaddr_in6); 333*b9dd6a90SMichael Tuexen dst.sin6_port = sh.dest_port; 334*b9dd6a90SMichael Tuexen dst.sin6_addr = ip6cp->ip6c_ip6->ip6_dst; 335*b9dd6a90SMichael Tuexen if (in6_setscope(&dst.sin6_addr, ip6cp->ip6c_m->m_pkthdr.rcvif, NULL) != 0) { 336*b9dd6a90SMichael Tuexen return; 337*b9dd6a90SMichael Tuexen } 338f8829a4aSRandall Stewart inp = NULL; 339f8829a4aSRandall Stewart net = NULL; 340*b9dd6a90SMichael Tuexen stcb = sctp_findassociation_addr_sa((struct sockaddr *)&dst, 341*b9dd6a90SMichael Tuexen (struct sockaddr *)&src, 342*b9dd6a90SMichael Tuexen &inp, &net, 1, SCTP_DEFAULT_VRFID); 343*b9dd6a90SMichael Tuexen if ((stcb != NULL) && 344*b9dd6a90SMichael Tuexen (net != NULL) && 345*b9dd6a90SMichael Tuexen (inp != NULL) && 346*b9dd6a90SMichael Tuexen (inp->sctp_socket != NULL)) { 347*b9dd6a90SMichael Tuexen /* Check the verification tag */ 348*b9dd6a90SMichael Tuexen if (ntohl(sh.v_tag) != 0) { 349*b9dd6a90SMichael Tuexen /* 350*b9dd6a90SMichael Tuexen * This must be the verification tag used 351*b9dd6a90SMichael Tuexen * for sending out packets. We don't 352*b9dd6a90SMichael Tuexen * consider packets reflecting the 353*b9dd6a90SMichael Tuexen * verification tag. 354*b9dd6a90SMichael Tuexen */ 355*b9dd6a90SMichael Tuexen if (ntohl(sh.v_tag) != stcb->asoc.peer_vtag) { 356*b9dd6a90SMichael Tuexen SCTP_TCB_UNLOCK(stcb); 357*b9dd6a90SMichael Tuexen return; 358f8829a4aSRandall Stewart } 359f8829a4aSRandall Stewart } else { 360*b9dd6a90SMichael Tuexen if (ip6cp->ip6c_m->m_pkthdr.len >= 361*b9dd6a90SMichael Tuexen ip6cp->ip6c_off + sizeof(struct sctphdr) + 362*b9dd6a90SMichael Tuexen sizeof(struct sctp_chunkhdr) + 363*b9dd6a90SMichael Tuexen offsetof(struct sctp_init, a_rwnd)) { 364*b9dd6a90SMichael Tuexen /* 365*b9dd6a90SMichael Tuexen * In this case we can check if we 366*b9dd6a90SMichael Tuexen * got an INIT chunk and if the 367*b9dd6a90SMichael Tuexen * initiate tag matches. 368*b9dd6a90SMichael Tuexen */ 369*b9dd6a90SMichael Tuexen uint32_t initiate_tag; 370*b9dd6a90SMichael Tuexen uint8_t chunk_type; 371*b9dd6a90SMichael Tuexen 372*b9dd6a90SMichael Tuexen m_copydata(ip6cp->ip6c_m, 373*b9dd6a90SMichael Tuexen ip6cp->ip6c_off + 374*b9dd6a90SMichael Tuexen sizeof(struct sctphdr), 375*b9dd6a90SMichael Tuexen sizeof(uint8_t), 376*b9dd6a90SMichael Tuexen (caddr_t)&chunk_type); 377*b9dd6a90SMichael Tuexen m_copydata(ip6cp->ip6c_m, 378*b9dd6a90SMichael Tuexen ip6cp->ip6c_off + 379*b9dd6a90SMichael Tuexen sizeof(struct sctphdr) + 380*b9dd6a90SMichael Tuexen sizeof(struct sctp_chunkhdr), 381*b9dd6a90SMichael Tuexen sizeof(uint32_t), 382*b9dd6a90SMichael Tuexen (caddr_t)&initiate_tag); 383*b9dd6a90SMichael Tuexen if ((chunk_type != SCTP_INITIATION) || 384*b9dd6a90SMichael Tuexen (ntohl(initiate_tag) != stcb->asoc.my_vtag)) { 385*b9dd6a90SMichael Tuexen SCTP_TCB_UNLOCK(stcb); 386*b9dd6a90SMichael Tuexen return; 387f8829a4aSRandall Stewart } 388*b9dd6a90SMichael Tuexen } else { 389*b9dd6a90SMichael Tuexen SCTP_TCB_UNLOCK(stcb); 390*b9dd6a90SMichael Tuexen return; 391*b9dd6a90SMichael Tuexen } 392*b9dd6a90SMichael Tuexen } 393*b9dd6a90SMichael Tuexen sctp6_notify(inp, stcb, net, 394*b9dd6a90SMichael Tuexen ip6cp->ip6c_icmp6->icmp6_type, 395*b9dd6a90SMichael Tuexen ip6cp->ip6c_icmp6->icmp6_code, 396*b9dd6a90SMichael Tuexen (uint16_t) ntohl(ip6cp->ip6c_icmp6->icmp6_mtu)); 397*b9dd6a90SMichael Tuexen } else { 398*b9dd6a90SMichael Tuexen if ((stcb == NULL) && (inp != NULL)) { 399f8829a4aSRandall Stewart /* reduce inp's ref-count */ 400f8829a4aSRandall Stewart SCTP_INP_WLOCK(inp); 401f8829a4aSRandall Stewart SCTP_INP_DECR_REF(inp); 402f8829a4aSRandall Stewart SCTP_INP_WUNLOCK(inp); 403f8829a4aSRandall Stewart } 404*b9dd6a90SMichael Tuexen if (stcb) { 405f8829a4aSRandall Stewart SCTP_TCB_UNLOCK(stcb); 406f8829a4aSRandall Stewart } 407f8829a4aSRandall Stewart } 408f8829a4aSRandall Stewart } 409*b9dd6a90SMichael Tuexen } 410f8829a4aSRandall Stewart 411f8829a4aSRandall Stewart /* 412f8829a4aSRandall Stewart * this routine can probably be collasped into the one in sctp_userreq.c 413f8829a4aSRandall Stewart * since they do the same thing and now we lookup with a sockaddr 414f8829a4aSRandall Stewart */ 415f8829a4aSRandall Stewart static int 416f8829a4aSRandall Stewart sctp6_getcred(SYSCTL_HANDLER_ARGS) 417f8829a4aSRandall Stewart { 41803b0b021SRandall Stewart struct xucred xuc; 419f8829a4aSRandall Stewart struct sockaddr_in6 addrs[2]; 420f8829a4aSRandall Stewart struct sctp_inpcb *inp; 421f8829a4aSRandall Stewart struct sctp_nets *net; 422f8829a4aSRandall Stewart struct sctp_tcb *stcb; 42303b0b021SRandall Stewart int error; 42442551e99SRandall Stewart uint32_t vrf_id; 42542551e99SRandall Stewart 42642551e99SRandall Stewart vrf_id = SCTP_DEFAULT_VRFID; 427f8829a4aSRandall Stewart 42832f9753cSRobert Watson error = priv_check(req->td, PRIV_NETINET_GETCRED); 429f8829a4aSRandall Stewart if (error) 430f8829a4aSRandall Stewart return (error); 431f8829a4aSRandall Stewart 432c4739e2fSRandall Stewart if (req->newlen != sizeof(addrs)) { 433c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 434f8829a4aSRandall Stewart return (EINVAL); 435c4739e2fSRandall Stewart } 436c4739e2fSRandall Stewart if (req->oldlen != sizeof(struct ucred)) { 437c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 438f8829a4aSRandall Stewart return (EINVAL); 439c4739e2fSRandall Stewart } 440f8829a4aSRandall Stewart error = SYSCTL_IN(req, addrs, sizeof(addrs)); 441f8829a4aSRandall Stewart if (error) 442f8829a4aSRandall Stewart return (error); 443f8829a4aSRandall Stewart 444b1754ad1SMichael Tuexen stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[1]), 445b1754ad1SMichael Tuexen sin6tosa(&addrs[0]), 44642551e99SRandall Stewart &inp, &net, 1, vrf_id); 447f8829a4aSRandall Stewart if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) { 44803b0b021SRandall Stewart if ((inp != NULL) && (stcb == NULL)) { 44903b0b021SRandall Stewart /* reduce ref-count */ 450f8829a4aSRandall Stewart SCTP_INP_WLOCK(inp); 451f8829a4aSRandall Stewart SCTP_INP_DECR_REF(inp); 45203b0b021SRandall Stewart goto cred_can_cont; 453f8829a4aSRandall Stewart } 454c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOENT); 45503b0b021SRandall Stewart error = ENOENT; 456f8829a4aSRandall Stewart goto out; 457f8829a4aSRandall Stewart } 458f8829a4aSRandall Stewart SCTP_TCB_UNLOCK(stcb); 45903b0b021SRandall Stewart /* 46003b0b021SRandall Stewart * We use the write lock here, only since in the error leg we need 46103b0b021SRandall Stewart * it. If we used RLOCK, then we would have to 46203b0b021SRandall Stewart * wlock/decr/unlock/rlock. Which in theory could create a hole. 46303b0b021SRandall Stewart * Better to use higher wlock. 46403b0b021SRandall Stewart */ 46503b0b021SRandall Stewart SCTP_INP_WLOCK(inp); 46603b0b021SRandall Stewart cred_can_cont: 46703b0b021SRandall Stewart error = cr_canseesocket(req->td->td_ucred, inp->sctp_socket); 46803b0b021SRandall Stewart if (error) { 46903b0b021SRandall Stewart SCTP_INP_WUNLOCK(inp); 47003b0b021SRandall Stewart goto out; 47103b0b021SRandall Stewart } 47203b0b021SRandall Stewart cru2x(inp->sctp_socket->so_cred, &xuc); 47303b0b021SRandall Stewart SCTP_INP_WUNLOCK(inp); 47403b0b021SRandall Stewart error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred)); 475f8829a4aSRandall Stewart out: 476f8829a4aSRandall Stewart return (error); 477f8829a4aSRandall Stewart } 478f8829a4aSRandall Stewart 479f8829a4aSRandall Stewart SYSCTL_PROC(_net_inet6_sctp6, OID_AUTO, getcred, CTLTYPE_OPAQUE | CTLFLAG_RW, 480f8829a4aSRandall Stewart 0, 0, 481f8829a4aSRandall Stewart sctp6_getcred, "S,ucred", "Get the ucred of a SCTP6 connection"); 482f8829a4aSRandall Stewart 483f8829a4aSRandall Stewart 484f8829a4aSRandall Stewart /* This is the same as the sctp_abort() could be made common */ 485f8829a4aSRandall Stewart static void 486f8829a4aSRandall Stewart sctp6_abort(struct socket *so) 487f8829a4aSRandall Stewart { 488f8829a4aSRandall Stewart struct sctp_inpcb *inp; 489f8829a4aSRandall Stewart uint32_t flags; 490f8829a4aSRandall Stewart 491f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 492dea47f39SMichael Tuexen if (inp == NULL) { 493851b7298SRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 494f8829a4aSRandall Stewart return; 495851b7298SRandall Stewart } 496f8829a4aSRandall Stewart sctp_must_try_again: 497f8829a4aSRandall Stewart flags = inp->sctp_flags; 498f8829a4aSRandall Stewart #ifdef SCTP_LOG_CLOSING 499f8829a4aSRandall Stewart sctp_log_closing(inp, NULL, 17); 500f8829a4aSRandall Stewart #endif 501f8829a4aSRandall Stewart if (((flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) && 502f8829a4aSRandall Stewart (atomic_cmpset_int(&inp->sctp_flags, flags, (flags | SCTP_PCB_FLAGS_SOCKET_GONE | SCTP_PCB_FLAGS_CLOSE_IP)))) { 503f8829a4aSRandall Stewart #ifdef SCTP_LOG_CLOSING 504f8829a4aSRandall Stewart sctp_log_closing(inp, NULL, 16); 505f8829a4aSRandall Stewart #endif 506b0552ae2SRandall Stewart sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT, 507b0552ae2SRandall Stewart SCTP_CALLED_AFTER_CMPSET_OFCLOSE); 508f8829a4aSRandall Stewart SOCK_LOCK(so); 509132dea7dSRandall Stewart SCTP_SB_CLEAR(so->so_snd); 510f8829a4aSRandall Stewart /* 511f8829a4aSRandall Stewart * same for the rcv ones, they are only here for the 512f8829a4aSRandall Stewart * accounting/select. 513f8829a4aSRandall Stewart */ 514132dea7dSRandall Stewart SCTP_SB_CLEAR(so->so_rcv); 515132dea7dSRandall Stewart /* Now null out the reference, we are completely detached. */ 516f8829a4aSRandall Stewart so->so_pcb = NULL; 517f8829a4aSRandall Stewart SOCK_UNLOCK(so); 518f8829a4aSRandall Stewart } else { 519f8829a4aSRandall Stewart flags = inp->sctp_flags; 520f8829a4aSRandall Stewart if ((flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) { 521f8829a4aSRandall Stewart goto sctp_must_try_again; 522f8829a4aSRandall Stewart } 523f8829a4aSRandall Stewart } 524f8829a4aSRandall Stewart return; 525f8829a4aSRandall Stewart } 526f8829a4aSRandall Stewart 527f8829a4aSRandall Stewart static int 5287215cc1bSMichael Tuexen sctp6_attach(struct socket *so, int proto SCTP_UNUSED, struct thread *p SCTP_UNUSED) 529f8829a4aSRandall Stewart { 530f8829a4aSRandall Stewart struct in6pcb *inp6; 53193164cf9SRandall Stewart int error; 532f8829a4aSRandall Stewart struct sctp_inpcb *inp; 533f4c93d24SRandall Stewart uint32_t vrf_id = SCTP_DEFAULT_VRFID; 534f8829a4aSRandall Stewart 535f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 536c4739e2fSRandall Stewart if (inp != NULL) { 537c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 53860990c0cSMichael Tuexen return (EINVAL); 539c4739e2fSRandall Stewart } 540f8829a4aSRandall Stewart if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 541b3f1ea41SRandall Stewart error = SCTP_SORESERVE(so, SCTP_BASE_SYSCTL(sctp_sendspace), SCTP_BASE_SYSCTL(sctp_recvspace)); 542f8829a4aSRandall Stewart if (error) 54360990c0cSMichael Tuexen return (error); 544f8829a4aSRandall Stewart } 545f4c93d24SRandall Stewart error = sctp_inpcb_alloc(so, vrf_id); 546f8829a4aSRandall Stewart if (error) 54760990c0cSMichael Tuexen return (error); 548f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 549f4c93d24SRandall Stewart SCTP_INP_WLOCK(inp); 550f8829a4aSRandall Stewart inp->sctp_flags |= SCTP_PCB_FLAGS_BOUND_V6; /* I'm v6! */ 551f8829a4aSRandall Stewart inp6 = (struct in6pcb *)inp; 552f8829a4aSRandall Stewart 553f8829a4aSRandall Stewart inp6->inp_vflag |= INP_IPV6; 554f8829a4aSRandall Stewart inp6->in6p_hops = -1; /* use kernel default */ 555f8829a4aSRandall Stewart inp6->in6p_cksum = -1; /* just to be sure */ 556f8829a4aSRandall Stewart #ifdef INET 557f8829a4aSRandall Stewart /* 558f8829a4aSRandall Stewart * XXX: ugly!! IPv4 TTL initialization is necessary for an IPv6 559f8829a4aSRandall Stewart * socket as well, because the socket may be bound to an IPv6 560f8829a4aSRandall Stewart * wildcard address, which may match an IPv4-mapped IPv6 address. 561f8829a4aSRandall Stewart */ 562482444b4SRandall Stewart inp6->inp_ip_ttl = MODULE_GLOBAL(ip_defttl); 563f8829a4aSRandall Stewart #endif 564f8829a4aSRandall Stewart /* 565f8829a4aSRandall Stewart * Hmm what about the IPSEC stuff that is missing here but in 566f8829a4aSRandall Stewart * sctp_attach()? 567f8829a4aSRandall Stewart */ 568f4c93d24SRandall Stewart SCTP_INP_WUNLOCK(inp); 56960990c0cSMichael Tuexen return (0); 570f8829a4aSRandall Stewart } 571f8829a4aSRandall Stewart 572f8829a4aSRandall Stewart static int 573f8829a4aSRandall Stewart sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p) 574f8829a4aSRandall Stewart { 575f8829a4aSRandall Stewart struct sctp_inpcb *inp; 576f8829a4aSRandall Stewart struct in6pcb *inp6; 57793164cf9SRandall Stewart int error; 578f8829a4aSRandall Stewart 579f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 580dea47f39SMichael Tuexen if (inp == NULL) { 581c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 58260990c0cSMichael Tuexen return (EINVAL); 583c4739e2fSRandall Stewart } 584d61a0ae0SRandall Stewart if (addr) { 585e6194c2eSMichael Tuexen switch (addr->sa_family) { 586e6194c2eSMichael Tuexen #ifdef INET 587e6194c2eSMichael Tuexen case AF_INET: 588e6194c2eSMichael Tuexen if (addr->sa_len != sizeof(struct sockaddr_in)) { 589c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 59060990c0cSMichael Tuexen return (EINVAL); 591d61a0ae0SRandall Stewart } 592e6194c2eSMichael Tuexen break; 593e6194c2eSMichael Tuexen #endif 594e6194c2eSMichael Tuexen #ifdef INET6 595e6194c2eSMichael Tuexen case AF_INET6: 596e6194c2eSMichael Tuexen if (addr->sa_len != sizeof(struct sockaddr_in6)) { 597e6194c2eSMichael Tuexen SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 59860990c0cSMichael Tuexen return (EINVAL); 599e6194c2eSMichael Tuexen } 600e6194c2eSMichael Tuexen break; 601e6194c2eSMichael Tuexen #endif 602e6194c2eSMichael Tuexen default: 603c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 60460990c0cSMichael Tuexen return (EINVAL); 605d61a0ae0SRandall Stewart } 606d61a0ae0SRandall Stewart } 607f8829a4aSRandall Stewart inp6 = (struct in6pcb *)inp; 608f8829a4aSRandall Stewart inp6->inp_vflag &= ~INP_IPV4; 609f8829a4aSRandall Stewart inp6->inp_vflag |= INP_IPV6; 61044b7479bSRandall Stewart if ((addr != NULL) && (SCTP_IPV6_V6ONLY(inp6) == 0)) { 611e6194c2eSMichael Tuexen switch (addr->sa_family) { 612e6194c2eSMichael Tuexen #ifdef INET 613e6194c2eSMichael Tuexen case AF_INET: 614f8829a4aSRandall Stewart /* binding v4 addr to v6 socket, so reset flags */ 615f8829a4aSRandall Stewart inp6->inp_vflag |= INP_IPV4; 616f8829a4aSRandall Stewart inp6->inp_vflag &= ~INP_IPV6; 617e6194c2eSMichael Tuexen break; 618e6194c2eSMichael Tuexen #endif 619e6194c2eSMichael Tuexen #ifdef INET6 620e6194c2eSMichael Tuexen case AF_INET6: 621e6194c2eSMichael Tuexen { 622f8829a4aSRandall Stewart struct sockaddr_in6 *sin6_p; 623f8829a4aSRandall Stewart 624f8829a4aSRandall Stewart sin6_p = (struct sockaddr_in6 *)addr; 625f8829a4aSRandall Stewart 626f8829a4aSRandall Stewart if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) { 627f8829a4aSRandall Stewart inp6->inp_vflag |= INP_IPV4; 628e6194c2eSMichael Tuexen } 629e6194c2eSMichael Tuexen #ifdef INET 630e6194c2eSMichael Tuexen if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { 631f8829a4aSRandall Stewart struct sockaddr_in sin; 632f8829a4aSRandall Stewart 633f8829a4aSRandall Stewart in6_sin6_2_sin(&sin, sin6_p); 634f8829a4aSRandall Stewart inp6->inp_vflag |= INP_IPV4; 635f8829a4aSRandall Stewart inp6->inp_vflag &= ~INP_IPV6; 6361b649582SRandall Stewart error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, NULL, p); 63760990c0cSMichael Tuexen return (error); 638f8829a4aSRandall Stewart } 639e6194c2eSMichael Tuexen #endif 640e6194c2eSMichael Tuexen break; 641e6194c2eSMichael Tuexen } 642e6194c2eSMichael Tuexen #endif 643e6194c2eSMichael Tuexen default: 644e6194c2eSMichael Tuexen break; 645f8829a4aSRandall Stewart } 646f8829a4aSRandall Stewart } else if (addr != NULL) { 647e6194c2eSMichael Tuexen struct sockaddr_in6 *sin6_p; 648e6194c2eSMichael Tuexen 649f8829a4aSRandall Stewart /* IPV6_V6ONLY socket */ 650e6194c2eSMichael Tuexen #ifdef INET 651f8829a4aSRandall Stewart if (addr->sa_family == AF_INET) { 652f8829a4aSRandall Stewart /* can't bind v4 addr to v6 only socket! */ 653c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 65460990c0cSMichael Tuexen return (EINVAL); 655e6194c2eSMichael Tuexen } 656e6194c2eSMichael Tuexen #endif 657f8829a4aSRandall Stewart sin6_p = (struct sockaddr_in6 *)addr; 658f8829a4aSRandall Stewart 659851b7298SRandall Stewart if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { 660f8829a4aSRandall Stewart /* can't bind v4-mapped addrs either! */ 661f8829a4aSRandall Stewart /* NOTE: we don't support SIIT */ 662c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 66360990c0cSMichael Tuexen return (EINVAL); 664f8829a4aSRandall Stewart } 665f8829a4aSRandall Stewart } 6661b649582SRandall Stewart error = sctp_inpcb_bind(so, addr, NULL, p); 66760990c0cSMichael Tuexen return (error); 668f8829a4aSRandall Stewart } 669f8829a4aSRandall Stewart 670f8829a4aSRandall Stewart 671f8829a4aSRandall Stewart static void 672f8829a4aSRandall Stewart sctp6_close(struct socket *so) 673f8829a4aSRandall Stewart { 6742afb3e84SRandall Stewart sctp_close(so); 675f8829a4aSRandall Stewart } 676f8829a4aSRandall Stewart 67742551e99SRandall Stewart /* This could be made common with sctp_detach() since they are identical */ 678f8829a4aSRandall Stewart 679c105859eSRandall Stewart static 680c105859eSRandall Stewart int 681f8829a4aSRandall Stewart sctp6_disconnect(struct socket *so) 682f8829a4aSRandall Stewart { 6832afb3e84SRandall Stewart return (sctp_disconnect(so)); 684f8829a4aSRandall Stewart } 685f8829a4aSRandall Stewart 686c105859eSRandall Stewart 687f8829a4aSRandall Stewart int 688f8829a4aSRandall Stewart sctp_sendm(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, 689f8829a4aSRandall Stewart struct mbuf *control, struct thread *p); 690f8829a4aSRandall Stewart 691f8829a4aSRandall Stewart 692f8829a4aSRandall Stewart static int 693f8829a4aSRandall Stewart sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, 694f8829a4aSRandall Stewart struct mbuf *control, struct thread *p) 695f8829a4aSRandall Stewart { 696f8829a4aSRandall Stewart struct sctp_inpcb *inp; 697f8829a4aSRandall Stewart struct in6pcb *inp6; 698f8829a4aSRandall Stewart 699f8829a4aSRandall Stewart #ifdef INET 700f8829a4aSRandall Stewart struct sockaddr_in6 *sin6; 701f8829a4aSRandall Stewart 702f8829a4aSRandall Stewart #endif /* INET */ 703f8829a4aSRandall Stewart /* No SPL needed since sctp_output does this */ 704f8829a4aSRandall Stewart 705f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 706f8829a4aSRandall Stewart if (inp == NULL) { 707f8829a4aSRandall Stewart if (control) { 70817205eccSRandall Stewart SCTP_RELEASE_PKT(control); 709f8829a4aSRandall Stewart control = NULL; 710f8829a4aSRandall Stewart } 71117205eccSRandall Stewart SCTP_RELEASE_PKT(m); 712c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 71360990c0cSMichael Tuexen return (EINVAL); 714f8829a4aSRandall Stewart } 715f8829a4aSRandall Stewart inp6 = (struct in6pcb *)inp; 716f8829a4aSRandall Stewart /* 717f8829a4aSRandall Stewart * For the TCP model we may get a NULL addr, if we are a connected 718f8829a4aSRandall Stewart * socket thats ok. 719f8829a4aSRandall Stewart */ 720f8829a4aSRandall Stewart if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) && 721f8829a4aSRandall Stewart (addr == NULL)) { 722f8829a4aSRandall Stewart goto connected_type; 723f8829a4aSRandall Stewart } 724f8829a4aSRandall Stewart if (addr == NULL) { 72517205eccSRandall Stewart SCTP_RELEASE_PKT(m); 726f8829a4aSRandall Stewart if (control) { 72717205eccSRandall Stewart SCTP_RELEASE_PKT(control); 728f8829a4aSRandall Stewart control = NULL; 729f8829a4aSRandall Stewart } 730c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EDESTADDRREQ); 731f8829a4aSRandall Stewart return (EDESTADDRREQ); 732f8829a4aSRandall Stewart } 733f8829a4aSRandall Stewart #ifdef INET 734f8829a4aSRandall Stewart sin6 = (struct sockaddr_in6 *)addr; 73544b7479bSRandall Stewart if (SCTP_IPV6_V6ONLY(inp6)) { 736f8829a4aSRandall Stewart /* 737f8829a4aSRandall Stewart * if IPV6_V6ONLY flag, we discard datagrams destined to a 738f8829a4aSRandall Stewart * v4 addr or v4-mapped addr 739f8829a4aSRandall Stewart */ 740f8829a4aSRandall Stewart if (addr->sa_family == AF_INET) { 741c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 74260990c0cSMichael Tuexen return (EINVAL); 743f8829a4aSRandall Stewart } 744f8829a4aSRandall Stewart if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 745c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 74660990c0cSMichael Tuexen return (EINVAL); 747f8829a4aSRandall Stewart } 748f8829a4aSRandall Stewart } 749f8829a4aSRandall Stewart if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 750f8829a4aSRandall Stewart struct sockaddr_in sin; 751f8829a4aSRandall Stewart 752f8829a4aSRandall Stewart /* convert v4-mapped into v4 addr and send */ 753f8829a4aSRandall Stewart in6_sin6_2_sin(&sin, sin6); 7543457ccdaSMichael Tuexen return (sctp_sendm(so, flags, m, (struct sockaddr *)&sin, control, p)); 755f8829a4aSRandall Stewart } 756f8829a4aSRandall Stewart #endif /* INET */ 757f8829a4aSRandall Stewart connected_type: 758f8829a4aSRandall Stewart /* now what about control */ 759f8829a4aSRandall Stewart if (control) { 760f8829a4aSRandall Stewart if (inp->control) { 761ad81507eSRandall Stewart SCTP_PRINTF("huh? control set?\n"); 76217205eccSRandall Stewart SCTP_RELEASE_PKT(inp->control); 763f8829a4aSRandall Stewart inp->control = NULL; 764f8829a4aSRandall Stewart } 765f8829a4aSRandall Stewart inp->control = control; 766f8829a4aSRandall Stewart } 767f8829a4aSRandall Stewart /* Place the data */ 768f8829a4aSRandall Stewart if (inp->pkt) { 769139bc87fSRandall Stewart SCTP_BUF_NEXT(inp->pkt_last) = m; 770f8829a4aSRandall Stewart inp->pkt_last = m; 771f8829a4aSRandall Stewart } else { 772f8829a4aSRandall Stewart inp->pkt_last = inp->pkt = m; 773f8829a4aSRandall Stewart } 774f8829a4aSRandall Stewart if ( 775f8829a4aSRandall Stewart /* FreeBSD and MacOSX uses a flag passed */ 776f8829a4aSRandall Stewart ((flags & PRUS_MORETOCOME) == 0) 777f8829a4aSRandall Stewart ) { 778f8829a4aSRandall Stewart /* 779f8829a4aSRandall Stewart * note with the current version this code will only be used 780f8829a4aSRandall Stewart * by OpenBSD, NetBSD and FreeBSD have methods for 781f8829a4aSRandall Stewart * re-defining sosend() to use sctp_sosend(). One can 782f8829a4aSRandall Stewart * optionaly switch back to this code (by changing back the 783f8829a4aSRandall Stewart * defininitions but this is not advisable. 784f8829a4aSRandall Stewart */ 785f8829a4aSRandall Stewart int ret; 786f8829a4aSRandall Stewart 787f8829a4aSRandall Stewart ret = sctp_output(inp, inp->pkt, addr, inp->control, p, flags); 788f8829a4aSRandall Stewart inp->pkt = NULL; 789f8829a4aSRandall Stewart inp->control = NULL; 790f8829a4aSRandall Stewart return (ret); 791f8829a4aSRandall Stewart } else { 792f8829a4aSRandall Stewart return (0); 793f8829a4aSRandall Stewart } 794f8829a4aSRandall Stewart } 795f8829a4aSRandall Stewart 796f8829a4aSRandall Stewart static int 797f8829a4aSRandall Stewart sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p) 798f8829a4aSRandall Stewart { 79942551e99SRandall Stewart uint32_t vrf_id; 800f8829a4aSRandall Stewart int error = 0; 801f8829a4aSRandall Stewart struct sctp_inpcb *inp; 802f8829a4aSRandall Stewart struct sctp_tcb *stcb; 803f8829a4aSRandall Stewart 804f8829a4aSRandall Stewart #ifdef INET 805b54ddf22SMichael Tuexen struct in6pcb *inp6; 806f8829a4aSRandall Stewart struct sockaddr_in6 *sin6; 80724aaac8dSMichael Tuexen union sctp_sockstore store; 808f8829a4aSRandall Stewart 809e6194c2eSMichael Tuexen #endif 810f8829a4aSRandall Stewart 811b54ddf22SMichael Tuexen #ifdef INET 812f8829a4aSRandall Stewart inp6 = (struct in6pcb *)so->so_pcb; 813b54ddf22SMichael Tuexen #endif 814f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 815dea47f39SMichael Tuexen if (inp == NULL) { 816c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ECONNRESET); 817f8829a4aSRandall Stewart return (ECONNRESET); /* I made the same as TCP since we are 818f8829a4aSRandall Stewart * not setup? */ 819f8829a4aSRandall Stewart } 820d61a0ae0SRandall Stewart if (addr == NULL) { 821c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 822d61a0ae0SRandall Stewart return (EINVAL); 823d61a0ae0SRandall Stewart } 824e6194c2eSMichael Tuexen switch (addr->sa_family) { 825e6194c2eSMichael Tuexen #ifdef INET 826e6194c2eSMichael Tuexen case AF_INET: 827e6194c2eSMichael Tuexen if (addr->sa_len != sizeof(struct sockaddr_in)) { 828c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 829d61a0ae0SRandall Stewart return (EINVAL); 830d61a0ae0SRandall Stewart } 831e6194c2eSMichael Tuexen break; 832e6194c2eSMichael Tuexen #endif 833e6194c2eSMichael Tuexen #ifdef INET6 834e6194c2eSMichael Tuexen case AF_INET6: 835e6194c2eSMichael Tuexen if (addr->sa_len != sizeof(struct sockaddr_in6)) { 836c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 837d61a0ae0SRandall Stewart return (EINVAL); 838d61a0ae0SRandall Stewart } 839e6194c2eSMichael Tuexen break; 840e6194c2eSMichael Tuexen #endif 841e6194c2eSMichael Tuexen default: 842e6194c2eSMichael Tuexen SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 843e6194c2eSMichael Tuexen return (EINVAL); 844e6194c2eSMichael Tuexen } 845e6194c2eSMichael Tuexen 846bff64a4dSRandall Stewart vrf_id = inp->def_vrf_id; 847f8829a4aSRandall Stewart SCTP_ASOC_CREATE_LOCK(inp); 848f8829a4aSRandall Stewart SCTP_INP_RLOCK(inp); 849f8829a4aSRandall Stewart if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) == 850f8829a4aSRandall Stewart SCTP_PCB_FLAGS_UNBOUND) { 851f8829a4aSRandall Stewart /* Bind a ephemeral port */ 852f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 853f8829a4aSRandall Stewart error = sctp6_bind(so, NULL, p); 854f8829a4aSRandall Stewart if (error) { 855f8829a4aSRandall Stewart SCTP_ASOC_CREATE_UNLOCK(inp); 856f8829a4aSRandall Stewart 857f8829a4aSRandall Stewart return (error); 858f8829a4aSRandall Stewart } 859f8829a4aSRandall Stewart SCTP_INP_RLOCK(inp); 860f8829a4aSRandall Stewart } 861f8829a4aSRandall Stewart if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && 862f8829a4aSRandall Stewart (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) { 863f8829a4aSRandall Stewart /* We are already connected AND the TCP model */ 864f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 865f8829a4aSRandall Stewart SCTP_ASOC_CREATE_UNLOCK(inp); 866c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EADDRINUSE); 867f8829a4aSRandall Stewart return (EADDRINUSE); 868f8829a4aSRandall Stewart } 869f8829a4aSRandall Stewart #ifdef INET 870f8829a4aSRandall Stewart sin6 = (struct sockaddr_in6 *)addr; 87144b7479bSRandall Stewart if (SCTP_IPV6_V6ONLY(inp6)) { 872f8829a4aSRandall Stewart /* 873f8829a4aSRandall Stewart * if IPV6_V6ONLY flag, ignore connections destined to a v4 874f8829a4aSRandall Stewart * addr or v4-mapped addr 875f8829a4aSRandall Stewart */ 876f8829a4aSRandall Stewart if (addr->sa_family == AF_INET) { 877f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 878f8829a4aSRandall Stewart SCTP_ASOC_CREATE_UNLOCK(inp); 879c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 88060990c0cSMichael Tuexen return (EINVAL); 881f8829a4aSRandall Stewart } 882f8829a4aSRandall Stewart if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 883f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 884f8829a4aSRandall Stewart SCTP_ASOC_CREATE_UNLOCK(inp); 885c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 88660990c0cSMichael Tuexen return (EINVAL); 887f8829a4aSRandall Stewart } 888f8829a4aSRandall Stewart } 889f8829a4aSRandall Stewart if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 890f8829a4aSRandall Stewart /* convert v4-mapped into v4 addr */ 89124aaac8dSMichael Tuexen in6_sin6_2_sin(&store.sin, sin6); 89224aaac8dSMichael Tuexen addr = &store.sa; 893274b0bd5SMichael Tuexen } 894f8829a4aSRandall Stewart #endif /* INET */ 895f8829a4aSRandall Stewart /* Now do we connect? */ 896f8829a4aSRandall Stewart if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) { 897f8829a4aSRandall Stewart stcb = LIST_FIRST(&inp->sctp_asoc_list); 898ad81507eSRandall Stewart if (stcb) { 899f8829a4aSRandall Stewart SCTP_TCB_UNLOCK(stcb); 900ad81507eSRandall Stewart } 901f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 902f8829a4aSRandall Stewart } else { 903f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 904f8829a4aSRandall Stewart SCTP_INP_WLOCK(inp); 905f8829a4aSRandall Stewart SCTP_INP_INCR_REF(inp); 906f8829a4aSRandall Stewart SCTP_INP_WUNLOCK(inp); 907f8829a4aSRandall Stewart stcb = sctp_findassociation_ep_addr(&inp, addr, NULL, NULL, NULL); 908f8829a4aSRandall Stewart if (stcb == NULL) { 909f8829a4aSRandall Stewart SCTP_INP_WLOCK(inp); 910f8829a4aSRandall Stewart SCTP_INP_DECR_REF(inp); 911f8829a4aSRandall Stewart SCTP_INP_WUNLOCK(inp); 912f8829a4aSRandall Stewart } 913f8829a4aSRandall Stewart } 914f8829a4aSRandall Stewart 915f8829a4aSRandall Stewart if (stcb != NULL) { 916f8829a4aSRandall Stewart /* Already have or am bring up an association */ 917f8829a4aSRandall Stewart SCTP_ASOC_CREATE_UNLOCK(inp); 918f8829a4aSRandall Stewart SCTP_TCB_UNLOCK(stcb); 919c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EALREADY); 920f8829a4aSRandall Stewart return (EALREADY); 921f8829a4aSRandall Stewart } 922f8829a4aSRandall Stewart /* We are GOOD to go */ 923c979034bSMichael Tuexen stcb = sctp_aloc_assoc(inp, addr, &error, 0, vrf_id, inp->sctp_ep.pre_open_stream_count, p); 924f8829a4aSRandall Stewart SCTP_ASOC_CREATE_UNLOCK(inp); 925f8829a4aSRandall Stewart if (stcb == NULL) { 926f8829a4aSRandall Stewart /* Gak! no memory */ 927f8829a4aSRandall Stewart return (error); 928f8829a4aSRandall Stewart } 929f8829a4aSRandall Stewart if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) { 930f8829a4aSRandall Stewart stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED; 931f8829a4aSRandall Stewart /* Set the connected flag so we can queue data */ 932f8829a4aSRandall Stewart soisconnecting(so); 933f8829a4aSRandall Stewart } 934f8829a4aSRandall Stewart stcb->asoc.state = SCTP_STATE_COOKIE_WAIT; 935ad81507eSRandall Stewart (void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered); 936f8829a4aSRandall Stewart 937f8829a4aSRandall Stewart /* initialize authentication parameters for the assoc */ 938f8829a4aSRandall Stewart sctp_initialize_auth_params(inp, stcb); 939f8829a4aSRandall Stewart 940ceaad40aSRandall Stewart sctp_send_initiate(inp, stcb, SCTP_SO_LOCKED); 941f8829a4aSRandall Stewart SCTP_TCB_UNLOCK(stcb); 94260990c0cSMichael Tuexen return (error); 943f8829a4aSRandall Stewart } 944f8829a4aSRandall Stewart 945f8829a4aSRandall Stewart static int 946f8829a4aSRandall Stewart sctp6_getaddr(struct socket *so, struct sockaddr **addr) 947f8829a4aSRandall Stewart { 948f8829a4aSRandall Stewart struct sockaddr_in6 *sin6; 949f8829a4aSRandall Stewart struct sctp_inpcb *inp; 95042551e99SRandall Stewart uint32_t vrf_id; 95142551e99SRandall Stewart struct sctp_ifa *sctp_ifa; 952f8829a4aSRandall Stewart 953f8829a4aSRandall Stewart int error; 954f8829a4aSRandall Stewart 955f8829a4aSRandall Stewart /* 956f8829a4aSRandall Stewart * Do the malloc first in case it blocks. 957f8829a4aSRandall Stewart */ 958e6194c2eSMichael Tuexen SCTP_MALLOC_SONAME(sin6, struct sockaddr_in6 *, sizeof(*sin6)); 95920083c2eSMichael Tuexen if (sin6 == NULL) 96060990c0cSMichael Tuexen return (ENOMEM); 961f8829a4aSRandall Stewart sin6->sin6_family = AF_INET6; 962f8829a4aSRandall Stewart sin6->sin6_len = sizeof(*sin6); 963f8829a4aSRandall Stewart 964f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 965f8829a4aSRandall Stewart if (inp == NULL) { 966f8829a4aSRandall Stewart SCTP_FREE_SONAME(sin6); 967c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ECONNRESET); 96860990c0cSMichael Tuexen return (ECONNRESET); 969f8829a4aSRandall Stewart } 970f8829a4aSRandall Stewart SCTP_INP_RLOCK(inp); 971f8829a4aSRandall Stewart sin6->sin6_port = inp->sctp_lport; 972f8829a4aSRandall Stewart if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { 973f8829a4aSRandall Stewart /* For the bound all case you get back 0 */ 974f8829a4aSRandall Stewart if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) { 975f8829a4aSRandall Stewart struct sctp_tcb *stcb; 976f8829a4aSRandall Stewart struct sockaddr_in6 *sin_a6; 977f8829a4aSRandall Stewart struct sctp_nets *net; 978f8829a4aSRandall Stewart int fnd; 979f8829a4aSRandall Stewart 980f8829a4aSRandall Stewart stcb = LIST_FIRST(&inp->sctp_asoc_list); 981f8829a4aSRandall Stewart if (stcb == NULL) { 9826df7c000SMichael Tuexen SCTP_INP_RUNLOCK(inp); 9839a59fb36SMichael Tuexen SCTP_FREE_SONAME(sin6); 9846df7c000SMichael Tuexen SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOENT); 9856df7c000SMichael Tuexen return (ENOENT); 986f8829a4aSRandall Stewart } 987f8829a4aSRandall Stewart fnd = 0; 988f8829a4aSRandall Stewart sin_a6 = NULL; 989f8829a4aSRandall Stewart TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { 990f8829a4aSRandall Stewart sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr; 99103b0b021SRandall Stewart if (sin_a6 == NULL) 99203b0b021SRandall Stewart /* this will make coverity happy */ 99303b0b021SRandall Stewart continue; 99403b0b021SRandall Stewart 995f8829a4aSRandall Stewart if (sin_a6->sin6_family == AF_INET6) { 996f8829a4aSRandall Stewart fnd = 1; 997f8829a4aSRandall Stewart break; 998f8829a4aSRandall Stewart } 999f8829a4aSRandall Stewart } 1000f8829a4aSRandall Stewart if ((!fnd) || (sin_a6 == NULL)) { 1001f8829a4aSRandall Stewart /* punt */ 10026df7c000SMichael Tuexen SCTP_INP_RUNLOCK(inp); 10039a59fb36SMichael Tuexen SCTP_FREE_SONAME(sin6); 10046df7c000SMichael Tuexen SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOENT); 10056df7c000SMichael Tuexen return (ENOENT); 1006f8829a4aSRandall Stewart } 1007bff64a4dSRandall Stewart vrf_id = inp->def_vrf_id; 1008bff64a4dSRandall Stewart sctp_ifa = sctp_source_address_selection(inp, stcb, (sctp_route_t *) & net->ro, net, 0, vrf_id); 100942551e99SRandall Stewart if (sctp_ifa) { 101042551e99SRandall Stewart sin6->sin6_addr = sctp_ifa->address.sin6.sin6_addr; 101142551e99SRandall Stewart } 1012f8829a4aSRandall Stewart } else { 1013f8829a4aSRandall Stewart /* For the bound all case you get back 0 */ 1014f8829a4aSRandall Stewart memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr)); 1015f8829a4aSRandall Stewart } 1016f8829a4aSRandall Stewart } else { 1017f8829a4aSRandall Stewart /* Take the first IPv6 address in the list */ 1018f8829a4aSRandall Stewart struct sctp_laddr *laddr; 1019f8829a4aSRandall Stewart int fnd = 0; 1020f8829a4aSRandall Stewart 1021f8829a4aSRandall Stewart LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { 102242551e99SRandall Stewart if (laddr->ifa->address.sa.sa_family == AF_INET6) { 1023f8829a4aSRandall Stewart struct sockaddr_in6 *sin_a; 1024f8829a4aSRandall Stewart 102524aaac8dSMichael Tuexen sin_a = &laddr->ifa->address.sin6; 1026f8829a4aSRandall Stewart sin6->sin6_addr = sin_a->sin6_addr; 1027f8829a4aSRandall Stewart fnd = 1; 1028f8829a4aSRandall Stewart break; 1029f8829a4aSRandall Stewart } 1030f8829a4aSRandall Stewart } 1031f8829a4aSRandall Stewart if (!fnd) { 1032f8829a4aSRandall Stewart SCTP_FREE_SONAME(sin6); 1033f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 1034c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOENT); 103560990c0cSMichael Tuexen return (ENOENT); 1036f8829a4aSRandall Stewart } 1037f8829a4aSRandall Stewart } 1038f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 1039f8829a4aSRandall Stewart /* Scoping things for v6 */ 104003b0b021SRandall Stewart if ((error = sa6_recoverscope(sin6)) != 0) { 104103b0b021SRandall Stewart SCTP_FREE_SONAME(sin6); 1042f8829a4aSRandall Stewart return (error); 104303b0b021SRandall Stewart } 1044f8829a4aSRandall Stewart (*addr) = (struct sockaddr *)sin6; 1045f8829a4aSRandall Stewart return (0); 1046f8829a4aSRandall Stewart } 1047f8829a4aSRandall Stewart 1048f8829a4aSRandall Stewart static int 1049f8829a4aSRandall Stewart sctp6_peeraddr(struct socket *so, struct sockaddr **addr) 1050f8829a4aSRandall Stewart { 1051310a0277SMichael Tuexen struct sockaddr_in6 *sin6; 1052f8829a4aSRandall Stewart int fnd; 1053f8829a4aSRandall Stewart struct sockaddr_in6 *sin_a6; 1054f8829a4aSRandall Stewart struct sctp_inpcb *inp; 1055f8829a4aSRandall Stewart struct sctp_tcb *stcb; 1056f8829a4aSRandall Stewart struct sctp_nets *net; 1057f8829a4aSRandall Stewart int error; 1058f8829a4aSRandall Stewart 105960990c0cSMichael Tuexen /* Do the malloc first in case it blocks. */ 1060f8829a4aSRandall Stewart SCTP_MALLOC_SONAME(sin6, struct sockaddr_in6 *, sizeof *sin6); 106120083c2eSMichael Tuexen if (sin6 == NULL) 106220083c2eSMichael Tuexen return (ENOMEM); 1063f8829a4aSRandall Stewart sin6->sin6_family = AF_INET6; 1064f8829a4aSRandall Stewart sin6->sin6_len = sizeof(*sin6); 1065f8829a4aSRandall Stewart 1066f8829a4aSRandall Stewart inp = (struct sctp_inpcb *)so->so_pcb; 106760990c0cSMichael Tuexen if ((inp == NULL) || 106860990c0cSMichael Tuexen ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) { 106960990c0cSMichael Tuexen /* UDP type and listeners will drop out here */ 1070f8829a4aSRandall Stewart SCTP_FREE_SONAME(sin6); 107160990c0cSMichael Tuexen SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOTCONN); 107260990c0cSMichael Tuexen return (ENOTCONN); 1073f8829a4aSRandall Stewart } 1074f8829a4aSRandall Stewart SCTP_INP_RLOCK(inp); 1075f8829a4aSRandall Stewart stcb = LIST_FIRST(&inp->sctp_asoc_list); 1076ad81507eSRandall Stewart if (stcb) { 1077f8829a4aSRandall Stewart SCTP_TCB_LOCK(stcb); 1078ad81507eSRandall Stewart } 1079f8829a4aSRandall Stewart SCTP_INP_RUNLOCK(inp); 1080f8829a4aSRandall Stewart if (stcb == NULL) { 1081f8829a4aSRandall Stewart SCTP_FREE_SONAME(sin6); 1082c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ECONNRESET); 108360990c0cSMichael Tuexen return (ECONNRESET); 1084f8829a4aSRandall Stewart } 1085f8829a4aSRandall Stewart fnd = 0; 1086f8829a4aSRandall Stewart TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { 1087f8829a4aSRandall Stewart sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr; 1088f8829a4aSRandall Stewart if (sin_a6->sin6_family == AF_INET6) { 1089f8829a4aSRandall Stewart fnd = 1; 1090f8829a4aSRandall Stewart sin6->sin6_port = stcb->rport; 1091f8829a4aSRandall Stewart sin6->sin6_addr = sin_a6->sin6_addr; 1092f8829a4aSRandall Stewart break; 1093f8829a4aSRandall Stewart } 1094f8829a4aSRandall Stewart } 1095f8829a4aSRandall Stewart SCTP_TCB_UNLOCK(stcb); 1096f8829a4aSRandall Stewart if (!fnd) { 1097f8829a4aSRandall Stewart /* No IPv4 address */ 1098f8829a4aSRandall Stewart SCTP_FREE_SONAME(sin6); 1099c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOENT); 110060990c0cSMichael Tuexen return (ENOENT); 1101f8829a4aSRandall Stewart } 1102caeae63fSMichael Tuexen if ((error = sa6_recoverscope(sin6)) != 0) { 1103caeae63fSMichael Tuexen SCTP_FREE_SONAME(sin6); 1104caeae63fSMichael Tuexen SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, error); 1105f8829a4aSRandall Stewart return (error); 1106caeae63fSMichael Tuexen } 1107f8829a4aSRandall Stewart *addr = (struct sockaddr *)sin6; 1108f8829a4aSRandall Stewart return (0); 1109f8829a4aSRandall Stewart } 1110f8829a4aSRandall Stewart 1111f8829a4aSRandall Stewart static int 1112f8829a4aSRandall Stewart sctp6_in6getaddr(struct socket *so, struct sockaddr **nam) 1113f8829a4aSRandall Stewart { 1114f8829a4aSRandall Stewart struct in6pcb *inp6 = sotoin6pcb(so); 111593164cf9SRandall Stewart int error; 1116f8829a4aSRandall Stewart 1117c4739e2fSRandall Stewart if (inp6 == NULL) { 1118c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 111960990c0cSMichael Tuexen return (EINVAL); 1120c4739e2fSRandall Stewart } 1121f8829a4aSRandall Stewart /* allow v6 addresses precedence */ 1122f8829a4aSRandall Stewart error = sctp6_getaddr(so, nam); 1123e6194c2eSMichael Tuexen #ifdef INET 1124f8829a4aSRandall Stewart if (error) { 11256df7c000SMichael Tuexen struct sockaddr_in6 *sin6; 11266df7c000SMichael Tuexen 1127f8829a4aSRandall Stewart /* try v4 next if v6 failed */ 1128f8829a4aSRandall Stewart error = sctp_ingetaddr(so, nam); 1129f8829a4aSRandall Stewart if (error) { 1130f8829a4aSRandall Stewart return (error); 1131f8829a4aSRandall Stewart } 11326df7c000SMichael Tuexen SCTP_MALLOC_SONAME(sin6, struct sockaddr_in6 *, sizeof *sin6); 11336df7c000SMichael Tuexen if (sin6 == NULL) { 11346df7c000SMichael Tuexen SCTP_FREE_SONAME(*nam); 11356df7c000SMichael Tuexen return (ENOMEM); 1136f8829a4aSRandall Stewart } 11376df7c000SMichael Tuexen in6_sin_2_v4mapsin6((struct sockaddr_in *)*nam, sin6); 11386df7c000SMichael Tuexen SCTP_FREE_SONAME(*nam); 11396df7c000SMichael Tuexen *nam = (struct sockaddr *)sin6; 1140f8829a4aSRandall Stewart } 1141e6194c2eSMichael Tuexen #endif 1142f8829a4aSRandall Stewart return (error); 1143f8829a4aSRandall Stewart } 1144f8829a4aSRandall Stewart 1145f8829a4aSRandall Stewart 1146f8829a4aSRandall Stewart static int 1147f8829a4aSRandall Stewart sctp6_getpeeraddr(struct socket *so, struct sockaddr **nam) 1148f8829a4aSRandall Stewart { 1149f8829a4aSRandall Stewart struct in6pcb *inp6 = sotoin6pcb(so); 115093164cf9SRandall Stewart int error; 1151f8829a4aSRandall Stewart 1152c4739e2fSRandall Stewart if (inp6 == NULL) { 1153c4739e2fSRandall Stewart SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); 115460990c0cSMichael Tuexen return (EINVAL); 1155c4739e2fSRandall Stewart } 1156f8829a4aSRandall Stewart /* allow v6 addresses precedence */ 1157f8829a4aSRandall Stewart error = sctp6_peeraddr(so, nam); 1158e6194c2eSMichael Tuexen #ifdef INET 1159f8829a4aSRandall Stewart if (error) { 11606df7c000SMichael Tuexen struct sockaddr_in6 *sin6; 11616df7c000SMichael Tuexen 1162f8829a4aSRandall Stewart /* try v4 next if v6 failed */ 1163f8829a4aSRandall Stewart error = sctp_peeraddr(so, nam); 1164f8829a4aSRandall Stewart if (error) { 1165f8829a4aSRandall Stewart return (error); 1166f8829a4aSRandall Stewart } 11676df7c000SMichael Tuexen SCTP_MALLOC_SONAME(sin6, struct sockaddr_in6 *, sizeof *sin6); 11686df7c000SMichael Tuexen if (sin6 == NULL) { 11696df7c000SMichael Tuexen SCTP_FREE_SONAME(*nam); 11706df7c000SMichael Tuexen return (ENOMEM); 1171f8829a4aSRandall Stewart } 11726df7c000SMichael Tuexen in6_sin_2_v4mapsin6((struct sockaddr_in *)*nam, sin6); 11736df7c000SMichael Tuexen SCTP_FREE_SONAME(*nam); 11746df7c000SMichael Tuexen *nam = (struct sockaddr *)sin6; 1175f8829a4aSRandall Stewart } 1176e6194c2eSMichael Tuexen #endif 117760990c0cSMichael Tuexen return (error); 1178f8829a4aSRandall Stewart } 1179f8829a4aSRandall Stewart 1180f8829a4aSRandall Stewart struct pr_usrreqs sctp6_usrreqs = { 1181f8829a4aSRandall Stewart .pru_abort = sctp6_abort, 1182f8829a4aSRandall Stewart .pru_accept = sctp_accept, 1183f8829a4aSRandall Stewart .pru_attach = sctp6_attach, 1184f8829a4aSRandall Stewart .pru_bind = sctp6_bind, 1185f8829a4aSRandall Stewart .pru_connect = sctp6_connect, 1186f8829a4aSRandall Stewart .pru_control = in6_control, 1187f8829a4aSRandall Stewart .pru_close = sctp6_close, 1188f8829a4aSRandall Stewart .pru_detach = sctp6_close, 1189f8829a4aSRandall Stewart .pru_sopoll = sopoll_generic, 1190276ca501SRandall Stewart .pru_flush = sctp_flush, 1191f8829a4aSRandall Stewart .pru_disconnect = sctp6_disconnect, 1192f8829a4aSRandall Stewart .pru_listen = sctp_listen, 1193f8829a4aSRandall Stewart .pru_peeraddr = sctp6_getpeeraddr, 1194f8829a4aSRandall Stewart .pru_send = sctp6_send, 1195f8829a4aSRandall Stewart .pru_shutdown = sctp_shutdown, 1196f8829a4aSRandall Stewart .pru_sockaddr = sctp6_in6getaddr, 1197f8829a4aSRandall Stewart .pru_sosend = sctp_sosend, 1198f8829a4aSRandall Stewart .pru_soreceive = sctp_soreceive 1199f8829a4aSRandall Stewart }; 12005e20b91dSMichael Tuexen 12015e20b91dSMichael Tuexen #endif 1202