1/*	$KAME: sctp_peeloff.c,v 1.13 2005/03/06 16:04:18 itojun Exp $	*/
2/*	$NetBSD: sctp_peeloff.c,v 1.2 2016/04/25 21:21:02 rjs Exp $ */
3
4/*
5 * Copyright (C) 2002, 2003 Cisco Systems Inc,
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: sctp_peeloff.c,v 1.2 2016/04/25 21:21:02 rjs Exp $");
34
35#ifdef _KERNEL_OPT
36#include "opt_inet.h"
37#include "opt_ipsec.h"
38#include "opt_sctp.h"
39#endif /* _KERNEL_OPT */
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/malloc.h>
45#include <sys/mbuf.h>
46#include <sys/domain.h>
47#include <sys/proc.h>
48#include <sys/protosw.h>
49#include <sys/socket.h>
50#include <sys/socketvar.h>
51#include <sys/sysctl.h>
52#include <sys/syslog.h>
53#include <net/if.h>
54#include <net/route.h>
55#include <netinet/in.h>
56#include <netinet/in_systm.h>
57#include <netinet/ip.h>
58#ifdef INET6
59#include <netinet/ip6.h>
60#endif
61#include <netinet/in_pcb.h>
62#include <netinet/in_var.h>
63#include <netinet/ip_var.h>
64#ifdef INET6
65#include <netinet6/ip6_var.h>
66#endif
67#include <netinet/ip_icmp.h>
68#include <netinet/icmp_var.h>
69#include <netinet/sctp_pcb.h>
70#include <netinet/sctp.h>
71#include <netinet/sctp_uio.h>
72#include <netinet/sctp_var.h>
73#include <netinet/sctp_peeloff.h>
74#include <netinet/sctputil.h>
75
76#ifdef IPSEC
77#include <netipsec/ipsec.h>
78#include <netipsec/key.h>
79#endif /*IPSEC*/
80
81#ifdef SCTP_DEBUG
82extern u_int32_t sctp_debug_on;
83#endif /* SCTP_DEBUG */
84
85
86int
87sctp_can_peel_off(struct socket *head, u_int32_t assoc_id)
88{
89	struct sctp_inpcb *inp;
90	struct sctp_tcb *stcb;
91	inp = (struct sctp_inpcb *)head->so_pcb;
92	if (inp == NULL) {
93		return (EFAULT);
94	}
95	stcb = sctp_findassociation_ep_asocid(inp, assoc_id);
96	if (stcb == NULL) {
97		return (ENOTCONN);
98	}
99	/* We are clear to peel this one off */
100	return (0);
101}
102
103int
104sctp_do_peeloff(struct socket *head, struct socket *so, u_int32_t assoc_id)
105{
106	struct sctp_inpcb *inp, *n_inp;
107	struct sctp_tcb *stcb;
108
109	inp = (struct sctp_inpcb *)head->so_pcb;
110	if (inp == NULL)
111		return (EFAULT);
112	stcb = sctp_findassociation_ep_asocid(inp, assoc_id);
113	if (stcb == NULL)
114		return (ENOTCONN);
115
116	n_inp = (struct sctp_inpcb *)so->so_pcb;
117	n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
118	    SCTP_PCB_FLAGS_CONNECTED |
119	    SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */
120	    (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
121	n_inp->sctp_socket = so;
122
123	/*
124	 * Now we must move it from one hash table to another and get
125	 * the stcb in the right place.
126	 */
127	sctp_move_pcb_and_assoc(inp, n_inp, stcb);
128	/*
129	 * And now the final hack. We move data in the
130	 * pending side i.e. head to the new socket
131	 * buffer. Let the GRUBBING begin :-0
132	 */
133	sctp_grub_through_socket_buffer(inp, head, so, stcb);
134	return (0);
135}
136
137struct socket *
138sctp_get_peeloff(struct socket *head, u_int32_t assoc_id, int *error)
139{
140	struct socket *newso;
141	struct sctp_inpcb *inp, *n_inp;
142	struct sctp_tcb *stcb;
143
144#ifdef SCTP_DEBUG
145	if (sctp_debug_on & SCTP_DEBUG_PEEL1) {
146		printf("SCTP peel-off called\n");
147	}
148#endif /* SCTP_DEBUG */
149
150	inp = (struct sctp_inpcb *)head->so_pcb;
151	if (inp == NULL) {
152		*error = EFAULT;
153		return (NULL);
154	}
155	stcb = sctp_findassociation_ep_asocid(inp, assoc_id);
156	if (stcb == NULL) {
157		*error = ENOTCONN;
158		return (NULL);
159	}
160	newso = sonewconn(head, SS_ISCONNECTED);
161	if (newso == NULL) {
162#ifdef SCTP_DEBUG
163		if (sctp_debug_on & SCTP_DEBUG_PEEL1) {
164			printf("sctp_peeloff:sonewconn failed err\n");
165		}
166#endif /* SCTP_DEBUG */
167		*error = ENOMEM;
168		SCTP_TCB_UNLOCK(stcb);
169		return (NULL);
170	}
171	n_inp = (struct sctp_inpcb *)newso->so_pcb;
172	SCTP_INP_WLOCK(n_inp);
173	n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
174	    SCTP_PCB_FLAGS_CONNECTED |
175	    SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */
176	    (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
177	n_inp->sctp_socket = newso;
178	/* Turn off any non-blocking symantic. */
179	newso->so_state &= ~SS_NBIO;
180	newso->so_state |= SS_ISCONNECTED;
181	/* We remove it right away */
182	newso = TAILQ_FIRST(&head->so_q);
183	if (soqremque(newso, 1) == 0)
184		panic("sctp_peeloff");
185
186	/*
187	 * Now we must move it from one hash table to another and get
188	 * the stcb in the right place.
189	 */
190	SCTP_INP_WUNLOCK(n_inp);
191	sctp_move_pcb_and_assoc(inp, n_inp, stcb);
192	/*
193	 * And now the final hack. We move data in the
194	 * pending side i.e. head to the new socket
195	 * buffer. Let the GRUBBING begin :-0
196	 */
197	sctp_grub_through_socket_buffer(inp, head, newso, stcb);
198	SCTP_TCB_UNLOCK(stcb);
199	return (newso);
200}
201