xref: /freebsd/sys/netinet6/sctp6_usrreq.c (revision b9dd6a90b68f6c0d79c0e9bf49c5c10b9a6393aa)
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