xref: /titanic_41/usr/src/uts/common/inet/tcp/tcp_misc.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
1721fffe3SKacheong Poon /*
2721fffe3SKacheong Poon  * CDDL HEADER START
3721fffe3SKacheong Poon  *
4721fffe3SKacheong Poon  * The contents of this file are subject to the terms of the
5721fffe3SKacheong Poon  * Common Development and Distribution License (the "License").
6721fffe3SKacheong Poon  * You may not use this file except in compliance with the License.
7721fffe3SKacheong Poon  *
8721fffe3SKacheong Poon  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9721fffe3SKacheong Poon  * or http://www.opensolaris.org/os/licensing.
10721fffe3SKacheong Poon  * See the License for the specific language governing permissions
11721fffe3SKacheong Poon  * and limitations under the License.
12721fffe3SKacheong Poon  *
13721fffe3SKacheong Poon  * When distributing Covered Code, include this CDDL HEADER in each
14721fffe3SKacheong Poon  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15721fffe3SKacheong Poon  * If applicable, add the following below this CDDL HEADER, with the
16721fffe3SKacheong Poon  * fields enclosed by brackets "[]" replaced with your own identifying
17721fffe3SKacheong Poon  * information: Portions Copyright [yyyy] [name of copyright owner]
18721fffe3SKacheong Poon  *
19721fffe3SKacheong Poon  * CDDL HEADER END
20721fffe3SKacheong Poon  */
21721fffe3SKacheong Poon 
22721fffe3SKacheong Poon /*
23*5dd46ab5SKacheong Poon  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24721fffe3SKacheong Poon  */
25721fffe3SKacheong Poon 
26721fffe3SKacheong Poon #include <sys/types.h>
27721fffe3SKacheong Poon #include <sys/strlog.h>
28721fffe3SKacheong Poon #include <sys/policy.h>
29721fffe3SKacheong Poon #include <sys/strsun.h>
30721fffe3SKacheong Poon #include <sys/squeue_impl.h>
31721fffe3SKacheong Poon #include <sys/squeue.h>
32721fffe3SKacheong Poon 
33721fffe3SKacheong Poon #include <inet/common.h>
34721fffe3SKacheong Poon #include <inet/ip.h>
35721fffe3SKacheong Poon #include <inet/tcp.h>
36721fffe3SKacheong Poon #include <inet/tcp_impl.h>
37721fffe3SKacheong Poon 
38721fffe3SKacheong Poon /* Control whether TCP can enter defensive mode when under memory pressure. */
39721fffe3SKacheong Poon static boolean_t tcp_do_reclaim = B_TRUE;
40721fffe3SKacheong Poon 
41721fffe3SKacheong Poon /*
42721fffe3SKacheong Poon  * Routines related to the TCP_IOC_ABORT_CONN ioctl command.
43721fffe3SKacheong Poon  *
44721fffe3SKacheong Poon  * TCP_IOC_ABORT_CONN is a non-transparent ioctl command used for aborting
45721fffe3SKacheong Poon  * TCP connections. To invoke this ioctl, a tcp_ioc_abort_conn_t structure
46721fffe3SKacheong Poon  * (defined in tcp.h) needs to be filled in and passed into the kernel
47721fffe3SKacheong Poon  * via an I_STR ioctl command (see streamio(7I)). The tcp_ioc_abort_conn_t
48721fffe3SKacheong Poon  * structure contains the four-tuple of a TCP connection and a range of TCP
49721fffe3SKacheong Poon  * states (specified by ac_start and ac_end). The use of wildcard addresses
50721fffe3SKacheong Poon  * and ports is allowed. Connections with a matching four tuple and a state
51721fffe3SKacheong Poon  * within the specified range will be aborted. The valid states for the
52721fffe3SKacheong Poon  * ac_start and ac_end fields are in the range TCPS_SYN_SENT to TCPS_TIME_WAIT,
53721fffe3SKacheong Poon  * inclusive.
54721fffe3SKacheong Poon  *
55721fffe3SKacheong Poon  * An application which has its connection aborted by this ioctl will receive
56721fffe3SKacheong Poon  * an error that is dependent on the connection state at the time of the abort.
57721fffe3SKacheong Poon  * If the connection state is < TCPS_TIME_WAIT, an application should behave as
58721fffe3SKacheong Poon  * though a RST packet has been received.  If the connection state is equal to
59721fffe3SKacheong Poon  * TCPS_TIME_WAIT, the 2MSL timeout will immediately be canceled by the kernel
60721fffe3SKacheong Poon  * and all resources associated with the connection will be freed.
61721fffe3SKacheong Poon  */
62721fffe3SKacheong Poon static mblk_t	*tcp_ioctl_abort_build_msg(tcp_ioc_abort_conn_t *, tcp_t *);
63721fffe3SKacheong Poon static void	tcp_ioctl_abort_dump(tcp_ioc_abort_conn_t *);
64721fffe3SKacheong Poon static void	tcp_ioctl_abort_handler(void *arg, mblk_t *mp, void *arg2,
65721fffe3SKacheong Poon     ip_recv_attr_t *dummy);
66721fffe3SKacheong Poon static int	tcp_ioctl_abort(tcp_ioc_abort_conn_t *, tcp_stack_t *tcps);
67721fffe3SKacheong Poon void	tcp_ioctl_abort_conn(queue_t *, mblk_t *);
68721fffe3SKacheong Poon static int	tcp_ioctl_abort_bucket(tcp_ioc_abort_conn_t *, int, int *,
69721fffe3SKacheong Poon     boolean_t, tcp_stack_t *);
70721fffe3SKacheong Poon 
71721fffe3SKacheong Poon /*
72721fffe3SKacheong Poon  * Macros used for accessing the different types of sockaddr
73721fffe3SKacheong Poon  * structures inside a tcp_ioc_abort_conn_t.
74721fffe3SKacheong Poon  */
75721fffe3SKacheong Poon #define	TCP_AC_V4LADDR(acp) ((sin_t *)&(acp)->ac_local)
76721fffe3SKacheong Poon #define	TCP_AC_V4RADDR(acp) ((sin_t *)&(acp)->ac_remote)
77721fffe3SKacheong Poon #define	TCP_AC_V4LOCAL(acp) (TCP_AC_V4LADDR(acp)->sin_addr.s_addr)
78721fffe3SKacheong Poon #define	TCP_AC_V4REMOTE(acp) (TCP_AC_V4RADDR(acp)->sin_addr.s_addr)
79721fffe3SKacheong Poon #define	TCP_AC_V4LPORT(acp) (TCP_AC_V4LADDR(acp)->sin_port)
80721fffe3SKacheong Poon #define	TCP_AC_V4RPORT(acp) (TCP_AC_V4RADDR(acp)->sin_port)
81721fffe3SKacheong Poon #define	TCP_AC_V6LADDR(acp) ((sin6_t *)&(acp)->ac_local)
82721fffe3SKacheong Poon #define	TCP_AC_V6RADDR(acp) ((sin6_t *)&(acp)->ac_remote)
83721fffe3SKacheong Poon #define	TCP_AC_V6LOCAL(acp) (TCP_AC_V6LADDR(acp)->sin6_addr)
84721fffe3SKacheong Poon #define	TCP_AC_V6REMOTE(acp) (TCP_AC_V6RADDR(acp)->sin6_addr)
85721fffe3SKacheong Poon #define	TCP_AC_V6LPORT(acp) (TCP_AC_V6LADDR(acp)->sin6_port)
86721fffe3SKacheong Poon #define	TCP_AC_V6RPORT(acp) (TCP_AC_V6RADDR(acp)->sin6_port)
87721fffe3SKacheong Poon 
88721fffe3SKacheong Poon /*
89721fffe3SKacheong Poon  * Return the correct error code to mimic the behavior
90721fffe3SKacheong Poon  * of a connection reset.
91721fffe3SKacheong Poon  */
92721fffe3SKacheong Poon #define	TCP_AC_GET_ERRCODE(state, err) {	\
93721fffe3SKacheong Poon 		switch ((state)) {		\
94721fffe3SKacheong Poon 		case TCPS_SYN_SENT:		\
95721fffe3SKacheong Poon 		case TCPS_SYN_RCVD:		\
96721fffe3SKacheong Poon 			(err) = ECONNREFUSED;	\
97721fffe3SKacheong Poon 			break;			\
98721fffe3SKacheong Poon 		case TCPS_ESTABLISHED:		\
99721fffe3SKacheong Poon 		case TCPS_FIN_WAIT_1:		\
100721fffe3SKacheong Poon 		case TCPS_FIN_WAIT_2:		\
101721fffe3SKacheong Poon 		case TCPS_CLOSE_WAIT:		\
102721fffe3SKacheong Poon 			(err) = ECONNRESET;	\
103721fffe3SKacheong Poon 			break;			\
104721fffe3SKacheong Poon 		case TCPS_CLOSING:		\
105721fffe3SKacheong Poon 		case TCPS_LAST_ACK:		\
106721fffe3SKacheong Poon 		case TCPS_TIME_WAIT:		\
107721fffe3SKacheong Poon 			(err) = 0;		\
108721fffe3SKacheong Poon 			break;			\
109721fffe3SKacheong Poon 		default:			\
110721fffe3SKacheong Poon 			(err) = ENXIO;		\
111721fffe3SKacheong Poon 		}				\
112721fffe3SKacheong Poon 	}
113721fffe3SKacheong Poon 
114721fffe3SKacheong Poon /*
115721fffe3SKacheong Poon  * Check if a tcp structure matches the info in acp.
116721fffe3SKacheong Poon  */
117721fffe3SKacheong Poon #define	TCP_AC_ADDR_MATCH(acp, connp, tcp)			\
118721fffe3SKacheong Poon 	(((acp)->ac_local.ss_family == AF_INET) ?		\
119721fffe3SKacheong Poon 	((TCP_AC_V4LOCAL((acp)) == INADDR_ANY ||		\
120721fffe3SKacheong Poon 	TCP_AC_V4LOCAL((acp)) == (connp)->conn_laddr_v4) &&	\
121721fffe3SKacheong Poon 	(TCP_AC_V4REMOTE((acp)) == INADDR_ANY ||		\
122721fffe3SKacheong Poon 	TCP_AC_V4REMOTE((acp)) == (connp)->conn_faddr_v4) &&	\
123721fffe3SKacheong Poon 	(TCP_AC_V4LPORT((acp)) == 0 ||				\
124721fffe3SKacheong Poon 	TCP_AC_V4LPORT((acp)) == (connp)->conn_lport) &&	\
125721fffe3SKacheong Poon 	(TCP_AC_V4RPORT((acp)) == 0 ||				\
126721fffe3SKacheong Poon 	TCP_AC_V4RPORT((acp)) == (connp)->conn_fport) &&	\
127721fffe3SKacheong Poon 	(acp)->ac_start <= (tcp)->tcp_state &&			\
128721fffe3SKacheong Poon 	(acp)->ac_end >= (tcp)->tcp_state) :			\
129721fffe3SKacheong Poon 	((IN6_IS_ADDR_UNSPECIFIED(&TCP_AC_V6LOCAL((acp))) ||	\
130721fffe3SKacheong Poon 	IN6_ARE_ADDR_EQUAL(&TCP_AC_V6LOCAL((acp)),		\
131721fffe3SKacheong Poon 	&(connp)->conn_laddr_v6)) &&				\
132721fffe3SKacheong Poon 	(IN6_IS_ADDR_UNSPECIFIED(&TCP_AC_V6REMOTE((acp))) ||	\
133721fffe3SKacheong Poon 	IN6_ARE_ADDR_EQUAL(&TCP_AC_V6REMOTE((acp)),		\
134721fffe3SKacheong Poon 	&(connp)->conn_faddr_v6)) &&				\
135721fffe3SKacheong Poon 	(TCP_AC_V6LPORT((acp)) == 0 ||				\
136721fffe3SKacheong Poon 	TCP_AC_V6LPORT((acp)) == (connp)->conn_lport) &&	\
137721fffe3SKacheong Poon 	(TCP_AC_V6RPORT((acp)) == 0 ||				\
138721fffe3SKacheong Poon 	TCP_AC_V6RPORT((acp)) == (connp)->conn_fport) &&	\
139721fffe3SKacheong Poon 	(acp)->ac_start <= (tcp)->tcp_state &&			\
140721fffe3SKacheong Poon 	(acp)->ac_end >= (tcp)->tcp_state))
141721fffe3SKacheong Poon 
142721fffe3SKacheong Poon #define	TCP_AC_MATCH(acp, connp, tcp)				\
143721fffe3SKacheong Poon 	(((acp)->ac_zoneid == ALL_ZONES ||			\
144721fffe3SKacheong Poon 	(acp)->ac_zoneid == (connp)->conn_zoneid) ?		\
145721fffe3SKacheong Poon 	TCP_AC_ADDR_MATCH(acp, connp, tcp) : 0)
146721fffe3SKacheong Poon 
147721fffe3SKacheong Poon /*
148721fffe3SKacheong Poon  * Build a message containing a tcp_ioc_abort_conn_t structure
149721fffe3SKacheong Poon  * which is filled in with information from acp and tp.
150721fffe3SKacheong Poon  */
151721fffe3SKacheong Poon static mblk_t *
tcp_ioctl_abort_build_msg(tcp_ioc_abort_conn_t * acp,tcp_t * tp)152721fffe3SKacheong Poon tcp_ioctl_abort_build_msg(tcp_ioc_abort_conn_t *acp, tcp_t *tp)
153721fffe3SKacheong Poon {
154721fffe3SKacheong Poon 	mblk_t *mp;
155721fffe3SKacheong Poon 	tcp_ioc_abort_conn_t *tacp;
156721fffe3SKacheong Poon 
157721fffe3SKacheong Poon 	mp = allocb(sizeof (uint32_t) + sizeof (*acp), BPRI_LO);
158721fffe3SKacheong Poon 	if (mp == NULL)
159721fffe3SKacheong Poon 		return (NULL);
160721fffe3SKacheong Poon 
161721fffe3SKacheong Poon 	*((uint32_t *)mp->b_rptr) = TCP_IOC_ABORT_CONN;
162721fffe3SKacheong Poon 	tacp = (tcp_ioc_abort_conn_t *)((uchar_t *)mp->b_rptr +
163721fffe3SKacheong Poon 	    sizeof (uint32_t));
164721fffe3SKacheong Poon 
165721fffe3SKacheong Poon 	tacp->ac_start = acp->ac_start;
166721fffe3SKacheong Poon 	tacp->ac_end = acp->ac_end;
167721fffe3SKacheong Poon 	tacp->ac_zoneid = acp->ac_zoneid;
168721fffe3SKacheong Poon 
169721fffe3SKacheong Poon 	if (acp->ac_local.ss_family == AF_INET) {
170721fffe3SKacheong Poon 		tacp->ac_local.ss_family = AF_INET;
171721fffe3SKacheong Poon 		tacp->ac_remote.ss_family = AF_INET;
172721fffe3SKacheong Poon 		TCP_AC_V4LOCAL(tacp) = tp->tcp_connp->conn_laddr_v4;
173721fffe3SKacheong Poon 		TCP_AC_V4REMOTE(tacp) = tp->tcp_connp->conn_faddr_v4;
174721fffe3SKacheong Poon 		TCP_AC_V4LPORT(tacp) = tp->tcp_connp->conn_lport;
175721fffe3SKacheong Poon 		TCP_AC_V4RPORT(tacp) = tp->tcp_connp->conn_fport;
176721fffe3SKacheong Poon 	} else {
177721fffe3SKacheong Poon 		tacp->ac_local.ss_family = AF_INET6;
178721fffe3SKacheong Poon 		tacp->ac_remote.ss_family = AF_INET6;
179721fffe3SKacheong Poon 		TCP_AC_V6LOCAL(tacp) = tp->tcp_connp->conn_laddr_v6;
180721fffe3SKacheong Poon 		TCP_AC_V6REMOTE(tacp) = tp->tcp_connp->conn_faddr_v6;
181721fffe3SKacheong Poon 		TCP_AC_V6LPORT(tacp) = tp->tcp_connp->conn_lport;
182721fffe3SKacheong Poon 		TCP_AC_V6RPORT(tacp) = tp->tcp_connp->conn_fport;
183721fffe3SKacheong Poon 	}
184721fffe3SKacheong Poon 	mp->b_wptr = (uchar_t *)mp->b_rptr + sizeof (uint32_t) + sizeof (*acp);
185721fffe3SKacheong Poon 	return (mp);
186721fffe3SKacheong Poon }
187721fffe3SKacheong Poon 
188721fffe3SKacheong Poon /*
189721fffe3SKacheong Poon  * Print a tcp_ioc_abort_conn_t structure.
190721fffe3SKacheong Poon  */
191721fffe3SKacheong Poon static void
tcp_ioctl_abort_dump(tcp_ioc_abort_conn_t * acp)192721fffe3SKacheong Poon tcp_ioctl_abort_dump(tcp_ioc_abort_conn_t *acp)
193721fffe3SKacheong Poon {
194721fffe3SKacheong Poon 	char lbuf[128];
195721fffe3SKacheong Poon 	char rbuf[128];
196721fffe3SKacheong Poon 	sa_family_t af;
197721fffe3SKacheong Poon 	in_port_t lport, rport;
198721fffe3SKacheong Poon 	ushort_t logflags;
199721fffe3SKacheong Poon 
200721fffe3SKacheong Poon 	af = acp->ac_local.ss_family;
201721fffe3SKacheong Poon 
202721fffe3SKacheong Poon 	if (af == AF_INET) {
203721fffe3SKacheong Poon 		(void) inet_ntop(af, (const void *)&TCP_AC_V4LOCAL(acp),
204721fffe3SKacheong Poon 		    lbuf, 128);
205721fffe3SKacheong Poon 		(void) inet_ntop(af, (const void *)&TCP_AC_V4REMOTE(acp),
206721fffe3SKacheong Poon 		    rbuf, 128);
207721fffe3SKacheong Poon 		lport = ntohs(TCP_AC_V4LPORT(acp));
208721fffe3SKacheong Poon 		rport = ntohs(TCP_AC_V4RPORT(acp));
209721fffe3SKacheong Poon 	} else {
210721fffe3SKacheong Poon 		(void) inet_ntop(af, (const void *)&TCP_AC_V6LOCAL(acp),
211721fffe3SKacheong Poon 		    lbuf, 128);
212721fffe3SKacheong Poon 		(void) inet_ntop(af, (const void *)&TCP_AC_V6REMOTE(acp),
213721fffe3SKacheong Poon 		    rbuf, 128);
214721fffe3SKacheong Poon 		lport = ntohs(TCP_AC_V6LPORT(acp));
215721fffe3SKacheong Poon 		rport = ntohs(TCP_AC_V6RPORT(acp));
216721fffe3SKacheong Poon 	}
217721fffe3SKacheong Poon 
218721fffe3SKacheong Poon 	logflags = SL_TRACE | SL_NOTE;
219721fffe3SKacheong Poon 	/*
220721fffe3SKacheong Poon 	 * Don't print this message to the console if the operation was done
221721fffe3SKacheong Poon 	 * to a non-global zone.
222721fffe3SKacheong Poon 	 */
223721fffe3SKacheong Poon 	if (acp->ac_zoneid == GLOBAL_ZONEID || acp->ac_zoneid == ALL_ZONES)
224721fffe3SKacheong Poon 		logflags |= SL_CONSOLE;
225721fffe3SKacheong Poon 	(void) strlog(TCP_MOD_ID, 0, 1, logflags,
226721fffe3SKacheong Poon 	    "TCP_IOC_ABORT_CONN: local = %s:%d, remote = %s:%d, "
227721fffe3SKacheong Poon 	    "start = %d, end = %d\n", lbuf, lport, rbuf, rport,
228721fffe3SKacheong Poon 	    acp->ac_start, acp->ac_end);
229721fffe3SKacheong Poon }
230721fffe3SKacheong Poon 
231721fffe3SKacheong Poon /*
232721fffe3SKacheong Poon  * Called using SQ_FILL when a message built using
233721fffe3SKacheong Poon  * tcp_ioctl_abort_build_msg is put into a queue.
234721fffe3SKacheong Poon  * Note that when we get here there is no wildcard in acp any more.
235721fffe3SKacheong Poon  */
236721fffe3SKacheong Poon /* ARGSUSED2 */
237721fffe3SKacheong Poon static void
tcp_ioctl_abort_handler(void * arg,mblk_t * mp,void * arg2,ip_recv_attr_t * dummy)238721fffe3SKacheong Poon tcp_ioctl_abort_handler(void *arg, mblk_t *mp, void *arg2,
239721fffe3SKacheong Poon     ip_recv_attr_t *dummy)
240721fffe3SKacheong Poon {
241721fffe3SKacheong Poon 	conn_t			*connp = (conn_t *)arg;
242721fffe3SKacheong Poon 	tcp_t			*tcp = connp->conn_tcp;
243721fffe3SKacheong Poon 	tcp_ioc_abort_conn_t	*acp;
244721fffe3SKacheong Poon 
245721fffe3SKacheong Poon 	/*
246721fffe3SKacheong Poon 	 * Don't accept any input on a closed tcp as this TCP logically does
247721fffe3SKacheong Poon 	 * not exist on the system. Don't proceed further with this TCP.
248721fffe3SKacheong Poon 	 * For eg. this packet could trigger another close of this tcp
249721fffe3SKacheong Poon 	 * which would be disastrous for tcp_refcnt. tcp_close_detached /
250721fffe3SKacheong Poon 	 * tcp_clean_death / tcp_closei_local must be called at most once
251721fffe3SKacheong Poon 	 * on a TCP.
252721fffe3SKacheong Poon 	 */
253721fffe3SKacheong Poon 	if (tcp->tcp_state == TCPS_CLOSED ||
254721fffe3SKacheong Poon 	    tcp->tcp_state == TCPS_BOUND) {
255721fffe3SKacheong Poon 		freemsg(mp);
256721fffe3SKacheong Poon 		return;
257721fffe3SKacheong Poon 	}
258721fffe3SKacheong Poon 
259721fffe3SKacheong Poon 	acp = (tcp_ioc_abort_conn_t *)(mp->b_rptr + sizeof (uint32_t));
260721fffe3SKacheong Poon 	if (tcp->tcp_state <= acp->ac_end) {
261721fffe3SKacheong Poon 		/*
262721fffe3SKacheong Poon 		 * If we get here, we are already on the correct
263721fffe3SKacheong Poon 		 * squeue. This ioctl follows the following path
264721fffe3SKacheong Poon 		 * tcp_wput -> tcp_wput_ioctl -> tcp_ioctl_abort_conn
265721fffe3SKacheong Poon 		 * ->tcp_ioctl_abort->squeue_enter (if on a
266721fffe3SKacheong Poon 		 * different squeue)
267721fffe3SKacheong Poon 		 */
268721fffe3SKacheong Poon 		int errcode;
269721fffe3SKacheong Poon 
270721fffe3SKacheong Poon 		TCP_AC_GET_ERRCODE(tcp->tcp_state, errcode);
271721fffe3SKacheong Poon 		(void) tcp_clean_death(tcp, errcode);
272721fffe3SKacheong Poon 	}
273721fffe3SKacheong Poon 	freemsg(mp);
274721fffe3SKacheong Poon }
275721fffe3SKacheong Poon 
276721fffe3SKacheong Poon /*
277721fffe3SKacheong Poon  * Abort all matching connections on a hash chain.
278721fffe3SKacheong Poon  */
279721fffe3SKacheong Poon static int
tcp_ioctl_abort_bucket(tcp_ioc_abort_conn_t * acp,int index,int * count,boolean_t exact,tcp_stack_t * tcps)280721fffe3SKacheong Poon tcp_ioctl_abort_bucket(tcp_ioc_abort_conn_t *acp, int index, int *count,
281721fffe3SKacheong Poon     boolean_t exact, tcp_stack_t *tcps)
282721fffe3SKacheong Poon {
283721fffe3SKacheong Poon 	int nmatch, err = 0;
284721fffe3SKacheong Poon 	tcp_t *tcp;
285721fffe3SKacheong Poon 	MBLKP mp, last, listhead = NULL;
286721fffe3SKacheong Poon 	conn_t	*tconnp;
287721fffe3SKacheong Poon 	connf_t	*connfp;
288721fffe3SKacheong Poon 	ip_stack_t *ipst = tcps->tcps_netstack->netstack_ip;
289721fffe3SKacheong Poon 
290721fffe3SKacheong Poon 	connfp = &ipst->ips_ipcl_conn_fanout[index];
291721fffe3SKacheong Poon 
292721fffe3SKacheong Poon startover:
293721fffe3SKacheong Poon 	nmatch = 0;
294721fffe3SKacheong Poon 
295721fffe3SKacheong Poon 	mutex_enter(&connfp->connf_lock);
296721fffe3SKacheong Poon 	for (tconnp = connfp->connf_head; tconnp != NULL;
297721fffe3SKacheong Poon 	    tconnp = tconnp->conn_next) {
298721fffe3SKacheong Poon 		tcp = tconnp->conn_tcp;
299721fffe3SKacheong Poon 		/*
300721fffe3SKacheong Poon 		 * We are missing a check on sin6_scope_id for linklocals here,
301721fffe3SKacheong Poon 		 * but current usage is just for aborting based on zoneid
302721fffe3SKacheong Poon 		 * for shared-IP zones.
303721fffe3SKacheong Poon 		 */
304721fffe3SKacheong Poon 		if (TCP_AC_MATCH(acp, tconnp, tcp)) {
305721fffe3SKacheong Poon 			CONN_INC_REF(tconnp);
306721fffe3SKacheong Poon 			mp = tcp_ioctl_abort_build_msg(acp, tcp);
307721fffe3SKacheong Poon 			if (mp == NULL) {
308721fffe3SKacheong Poon 				err = ENOMEM;
309721fffe3SKacheong Poon 				CONN_DEC_REF(tconnp);
310721fffe3SKacheong Poon 				break;
311721fffe3SKacheong Poon 			}
312721fffe3SKacheong Poon 			mp->b_prev = (mblk_t *)tcp;
313721fffe3SKacheong Poon 
314721fffe3SKacheong Poon 			if (listhead == NULL) {
315721fffe3SKacheong Poon 				listhead = mp;
316721fffe3SKacheong Poon 				last = mp;
317721fffe3SKacheong Poon 			} else {
318721fffe3SKacheong Poon 				last->b_next = mp;
319721fffe3SKacheong Poon 				last = mp;
320721fffe3SKacheong Poon 			}
321721fffe3SKacheong Poon 			nmatch++;
322721fffe3SKacheong Poon 			if (exact)
323721fffe3SKacheong Poon 				break;
324721fffe3SKacheong Poon 		}
325721fffe3SKacheong Poon 
326721fffe3SKacheong Poon 		/* Avoid holding lock for too long. */
327721fffe3SKacheong Poon 		if (nmatch >= 500)
328721fffe3SKacheong Poon 			break;
329721fffe3SKacheong Poon 	}
330721fffe3SKacheong Poon 	mutex_exit(&connfp->connf_lock);
331721fffe3SKacheong Poon 
332721fffe3SKacheong Poon 	/* Pass mp into the correct tcp */
333721fffe3SKacheong Poon 	while ((mp = listhead) != NULL) {
334721fffe3SKacheong Poon 		listhead = listhead->b_next;
335721fffe3SKacheong Poon 		tcp = (tcp_t *)mp->b_prev;
336721fffe3SKacheong Poon 		mp->b_next = mp->b_prev = NULL;
337721fffe3SKacheong Poon 		SQUEUE_ENTER_ONE(tcp->tcp_connp->conn_sqp, mp,
338721fffe3SKacheong Poon 		    tcp_ioctl_abort_handler, tcp->tcp_connp, NULL,
339721fffe3SKacheong Poon 		    SQ_FILL, SQTAG_TCP_ABORT_BUCKET);
340721fffe3SKacheong Poon 	}
341721fffe3SKacheong Poon 
342721fffe3SKacheong Poon 	*count += nmatch;
343721fffe3SKacheong Poon 	if (nmatch >= 500 && err == 0)
344721fffe3SKacheong Poon 		goto startover;
345721fffe3SKacheong Poon 	return (err);
346721fffe3SKacheong Poon }
347721fffe3SKacheong Poon 
348721fffe3SKacheong Poon /*
349721fffe3SKacheong Poon  * Abort all connections that matches the attributes specified in acp.
350721fffe3SKacheong Poon  */
351721fffe3SKacheong Poon static int
tcp_ioctl_abort(tcp_ioc_abort_conn_t * acp,tcp_stack_t * tcps)352721fffe3SKacheong Poon tcp_ioctl_abort(tcp_ioc_abort_conn_t *acp, tcp_stack_t *tcps)
353721fffe3SKacheong Poon {
354721fffe3SKacheong Poon 	sa_family_t af;
355721fffe3SKacheong Poon 	uint32_t  ports;
356721fffe3SKacheong Poon 	uint16_t *pports;
357721fffe3SKacheong Poon 	int err = 0, count = 0;
358721fffe3SKacheong Poon 	boolean_t exact = B_FALSE; /* set when there is no wildcard */
359721fffe3SKacheong Poon 	int index = -1;
360721fffe3SKacheong Poon 	ushort_t logflags;
361721fffe3SKacheong Poon 	ip_stack_t	*ipst = tcps->tcps_netstack->netstack_ip;
362721fffe3SKacheong Poon 
363721fffe3SKacheong Poon 	af = acp->ac_local.ss_family;
364721fffe3SKacheong Poon 
365721fffe3SKacheong Poon 	if (af == AF_INET) {
366721fffe3SKacheong Poon 		if (TCP_AC_V4REMOTE(acp) != INADDR_ANY &&
367721fffe3SKacheong Poon 		    TCP_AC_V4LPORT(acp) != 0 && TCP_AC_V4RPORT(acp) != 0) {
368721fffe3SKacheong Poon 			pports = (uint16_t *)&ports;
369721fffe3SKacheong Poon 			pports[1] = TCP_AC_V4LPORT(acp);
370721fffe3SKacheong Poon 			pports[0] = TCP_AC_V4RPORT(acp);
371721fffe3SKacheong Poon 			exact = (TCP_AC_V4LOCAL(acp) != INADDR_ANY);
372721fffe3SKacheong Poon 		}
373721fffe3SKacheong Poon 	} else {
374721fffe3SKacheong Poon 		if (!IN6_IS_ADDR_UNSPECIFIED(&TCP_AC_V6REMOTE(acp)) &&
375721fffe3SKacheong Poon 		    TCP_AC_V6LPORT(acp) != 0 && TCP_AC_V6RPORT(acp) != 0) {
376721fffe3SKacheong Poon 			pports = (uint16_t *)&ports;
377721fffe3SKacheong Poon 			pports[1] = TCP_AC_V6LPORT(acp);
378721fffe3SKacheong Poon 			pports[0] = TCP_AC_V6RPORT(acp);
379721fffe3SKacheong Poon 			exact = !IN6_IS_ADDR_UNSPECIFIED(&TCP_AC_V6LOCAL(acp));
380721fffe3SKacheong Poon 		}
381721fffe3SKacheong Poon 	}
382721fffe3SKacheong Poon 
383721fffe3SKacheong Poon 	/*
384721fffe3SKacheong Poon 	 * For cases where remote addr, local port, and remote port are non-
385721fffe3SKacheong Poon 	 * wildcards, tcp_ioctl_abort_bucket will only be called once.
386721fffe3SKacheong Poon 	 */
387721fffe3SKacheong Poon 	if (index != -1) {
388721fffe3SKacheong Poon 		err = tcp_ioctl_abort_bucket(acp, index,
389721fffe3SKacheong Poon 		    &count, exact, tcps);
390721fffe3SKacheong Poon 	} else {
391721fffe3SKacheong Poon 		/*
392721fffe3SKacheong Poon 		 * loop through all entries for wildcard case
393721fffe3SKacheong Poon 		 */
394721fffe3SKacheong Poon 		for (index = 0;
395721fffe3SKacheong Poon 		    index < ipst->ips_ipcl_conn_fanout_size;
396721fffe3SKacheong Poon 		    index++) {
397721fffe3SKacheong Poon 			err = tcp_ioctl_abort_bucket(acp, index,
398721fffe3SKacheong Poon 			    &count, exact, tcps);
399721fffe3SKacheong Poon 			if (err != 0)
400721fffe3SKacheong Poon 				break;
401721fffe3SKacheong Poon 		}
402721fffe3SKacheong Poon 	}
403721fffe3SKacheong Poon 
404721fffe3SKacheong Poon 	logflags = SL_TRACE | SL_NOTE;
405721fffe3SKacheong Poon 	/*
406721fffe3SKacheong Poon 	 * Don't print this message to the console if the operation was done
407721fffe3SKacheong Poon 	 * to a non-global zone.
408721fffe3SKacheong Poon 	 */
409721fffe3SKacheong Poon 	if (acp->ac_zoneid == GLOBAL_ZONEID || acp->ac_zoneid == ALL_ZONES)
410721fffe3SKacheong Poon 		logflags |= SL_CONSOLE;
411721fffe3SKacheong Poon 	(void) strlog(TCP_MOD_ID, 0, 1, logflags, "TCP_IOC_ABORT_CONN: "
412721fffe3SKacheong Poon 	    "aborted %d connection%c\n", count, ((count > 1) ? 's' : ' '));
413721fffe3SKacheong Poon 	if (err == 0 && count == 0)
414721fffe3SKacheong Poon 		err = ENOENT;
415721fffe3SKacheong Poon 	return (err);
416721fffe3SKacheong Poon }
417721fffe3SKacheong Poon 
418721fffe3SKacheong Poon /*
419721fffe3SKacheong Poon  * Process the TCP_IOC_ABORT_CONN ioctl request.
420721fffe3SKacheong Poon  */
421721fffe3SKacheong Poon void
tcp_ioctl_abort_conn(queue_t * q,mblk_t * mp)422721fffe3SKacheong Poon tcp_ioctl_abort_conn(queue_t *q, mblk_t *mp)
423721fffe3SKacheong Poon {
424721fffe3SKacheong Poon 	int	err;
425721fffe3SKacheong Poon 	IOCP    iocp;
426721fffe3SKacheong Poon 	MBLKP   mp1;
427721fffe3SKacheong Poon 	sa_family_t laf, raf;
428721fffe3SKacheong Poon 	tcp_ioc_abort_conn_t *acp;
429721fffe3SKacheong Poon 	zone_t		*zptr;
430721fffe3SKacheong Poon 	conn_t		*connp = Q_TO_CONN(q);
431721fffe3SKacheong Poon 	zoneid_t	zoneid = connp->conn_zoneid;
432721fffe3SKacheong Poon 	tcp_t		*tcp = connp->conn_tcp;
433721fffe3SKacheong Poon 	tcp_stack_t	*tcps = tcp->tcp_tcps;
434721fffe3SKacheong Poon 
435721fffe3SKacheong Poon 	iocp = (IOCP)mp->b_rptr;
436721fffe3SKacheong Poon 
437721fffe3SKacheong Poon 	if ((mp1 = mp->b_cont) == NULL ||
438721fffe3SKacheong Poon 	    iocp->ioc_count != sizeof (tcp_ioc_abort_conn_t)) {
439721fffe3SKacheong Poon 		err = EINVAL;
440721fffe3SKacheong Poon 		goto out;
441721fffe3SKacheong Poon 	}
442721fffe3SKacheong Poon 
443721fffe3SKacheong Poon 	/* check permissions */
444721fffe3SKacheong Poon 	if (secpolicy_ip_config(iocp->ioc_cr, B_FALSE) != 0) {
445721fffe3SKacheong Poon 		err = EPERM;
446721fffe3SKacheong Poon 		goto out;
447721fffe3SKacheong Poon 	}
448721fffe3SKacheong Poon 
449721fffe3SKacheong Poon 	if (mp1->b_cont != NULL) {
450721fffe3SKacheong Poon 		freemsg(mp1->b_cont);
451721fffe3SKacheong Poon 		mp1->b_cont = NULL;
452721fffe3SKacheong Poon 	}
453721fffe3SKacheong Poon 
454721fffe3SKacheong Poon 	acp = (tcp_ioc_abort_conn_t *)mp1->b_rptr;
455721fffe3SKacheong Poon 	laf = acp->ac_local.ss_family;
456721fffe3SKacheong Poon 	raf = acp->ac_remote.ss_family;
457721fffe3SKacheong Poon 
458721fffe3SKacheong Poon 	/* check that a zone with the supplied zoneid exists */
459721fffe3SKacheong Poon 	if (acp->ac_zoneid != GLOBAL_ZONEID && acp->ac_zoneid != ALL_ZONES) {
460721fffe3SKacheong Poon 		zptr = zone_find_by_id(zoneid);
461721fffe3SKacheong Poon 		if (zptr != NULL) {
462721fffe3SKacheong Poon 			zone_rele(zptr);
463721fffe3SKacheong Poon 		} else {
464721fffe3SKacheong Poon 			err = EINVAL;
465721fffe3SKacheong Poon 			goto out;
466721fffe3SKacheong Poon 		}
467721fffe3SKacheong Poon 	}
468721fffe3SKacheong Poon 
469721fffe3SKacheong Poon 	/*
470721fffe3SKacheong Poon 	 * For exclusive stacks we set the zoneid to zero
471721fffe3SKacheong Poon 	 * to make TCP operate as if in the global zone.
472721fffe3SKacheong Poon 	 */
473721fffe3SKacheong Poon 	if (tcps->tcps_netstack->netstack_stackid != GLOBAL_NETSTACKID)
474721fffe3SKacheong Poon 		acp->ac_zoneid = GLOBAL_ZONEID;
475721fffe3SKacheong Poon 
476721fffe3SKacheong Poon 	if (acp->ac_start < TCPS_SYN_SENT || acp->ac_end > TCPS_TIME_WAIT ||
477721fffe3SKacheong Poon 	    acp->ac_start > acp->ac_end || laf != raf ||
478721fffe3SKacheong Poon 	    (laf != AF_INET && laf != AF_INET6)) {
479721fffe3SKacheong Poon 		err = EINVAL;
480721fffe3SKacheong Poon 		goto out;
481721fffe3SKacheong Poon 	}
482721fffe3SKacheong Poon 
483721fffe3SKacheong Poon 	tcp_ioctl_abort_dump(acp);
484721fffe3SKacheong Poon 	err = tcp_ioctl_abort(acp, tcps);
485721fffe3SKacheong Poon 
486721fffe3SKacheong Poon out:
487721fffe3SKacheong Poon 	if (mp1 != NULL) {
488721fffe3SKacheong Poon 		freemsg(mp1);
489721fffe3SKacheong Poon 		mp->b_cont = NULL;
490721fffe3SKacheong Poon 	}
491721fffe3SKacheong Poon 
492721fffe3SKacheong Poon 	if (err != 0)
493721fffe3SKacheong Poon 		miocnak(q, mp, 0, err);
494721fffe3SKacheong Poon 	else
495721fffe3SKacheong Poon 		miocack(q, mp, 0, 0);
496721fffe3SKacheong Poon }
497721fffe3SKacheong Poon 
498721fffe3SKacheong Poon /*
499721fffe3SKacheong Poon  * Timeout function to reset the TCP stack variable tcps_reclaim to false.
500721fffe3SKacheong Poon  */
501721fffe3SKacheong Poon void
tcp_reclaim_timer(void * arg)502721fffe3SKacheong Poon tcp_reclaim_timer(void *arg)
503721fffe3SKacheong Poon {
504721fffe3SKacheong Poon 	tcp_stack_t *tcps = (tcp_stack_t *)arg;
505721fffe3SKacheong Poon 	int64_t tot_conn = 0;
506721fffe3SKacheong Poon 	int i;
507721fffe3SKacheong Poon 	extern pgcnt_t lotsfree, needfree;
508721fffe3SKacheong Poon 
509721fffe3SKacheong Poon 	for (i = 0; i < tcps->tcps_sc_cnt; i++)
510721fffe3SKacheong Poon 		tot_conn += tcps->tcps_sc[i]->tcp_sc_conn_cnt;
511721fffe3SKacheong Poon 
512721fffe3SKacheong Poon 	/*
513721fffe3SKacheong Poon 	 * This happens only when a stack is going away.  tcps_reclaim_tid
514721fffe3SKacheong Poon 	 * should not be reset to 0 when returning in this case.
515721fffe3SKacheong Poon 	 */
516721fffe3SKacheong Poon 	mutex_enter(&tcps->tcps_reclaim_lock);
517721fffe3SKacheong Poon 	if (!tcps->tcps_reclaim) {
518721fffe3SKacheong Poon 		mutex_exit(&tcps->tcps_reclaim_lock);
519721fffe3SKacheong Poon 		return;
520721fffe3SKacheong Poon 	}
521721fffe3SKacheong Poon 
522721fffe3SKacheong Poon 	if ((freemem >= lotsfree + needfree) || tot_conn < maxusers) {
523721fffe3SKacheong Poon 		tcps->tcps_reclaim = B_FALSE;
524721fffe3SKacheong Poon 		tcps->tcps_reclaim_tid = 0;
525721fffe3SKacheong Poon 	} else {
526721fffe3SKacheong Poon 		/* Stay in defensive mode and restart the timer */
527721fffe3SKacheong Poon 		tcps->tcps_reclaim_tid = timeout(tcp_reclaim_timer,
528721fffe3SKacheong Poon 		    tcps, MSEC_TO_TICK(tcps->tcps_reclaim_period));
529721fffe3SKacheong Poon 	}
530721fffe3SKacheong Poon 	mutex_exit(&tcps->tcps_reclaim_lock);
531721fffe3SKacheong Poon }
532721fffe3SKacheong Poon 
533721fffe3SKacheong Poon /*
534721fffe3SKacheong Poon  * Kmem reclaim call back function.  When the system is under memory
535721fffe3SKacheong Poon  * pressure, we set the TCP stack variable tcps_reclaim to true.  This
536721fffe3SKacheong Poon  * variable is reset to false after tcps_reclaim_period msecs.  During this
537721fffe3SKacheong Poon  * period, TCP will be more aggressive in aborting connections not making
538721fffe3SKacheong Poon  * progress, meaning retransmitting for some time (tcp_early_abort seconds).
539721fffe3SKacheong Poon  * TCP will also not accept new connection request for those listeners whose
540721fffe3SKacheong Poon  * q or q0 is not empty.
541721fffe3SKacheong Poon  */
542721fffe3SKacheong Poon /* ARGSUSED */
543721fffe3SKacheong Poon void
tcp_conn_reclaim(void * arg)544721fffe3SKacheong Poon tcp_conn_reclaim(void *arg)
545721fffe3SKacheong Poon {
546721fffe3SKacheong Poon 	netstack_handle_t nh;
547721fffe3SKacheong Poon 	netstack_t *ns;
548721fffe3SKacheong Poon 	tcp_stack_t *tcps;
549721fffe3SKacheong Poon 	extern pgcnt_t lotsfree, needfree;
550721fffe3SKacheong Poon 
551721fffe3SKacheong Poon 	if (!tcp_do_reclaim)
552721fffe3SKacheong Poon 		return;
553721fffe3SKacheong Poon 
554721fffe3SKacheong Poon 	/*
555721fffe3SKacheong Poon 	 * The reclaim function may be called even when the system is not
556721fffe3SKacheong Poon 	 * really under memory pressure.
557721fffe3SKacheong Poon 	 */
558721fffe3SKacheong Poon 	if (freemem >= lotsfree + needfree)
559721fffe3SKacheong Poon 		return;
560721fffe3SKacheong Poon 
561721fffe3SKacheong Poon 	netstack_next_init(&nh);
562721fffe3SKacheong Poon 	while ((ns = netstack_next(&nh)) != NULL) {
563721fffe3SKacheong Poon 		int i;
564721fffe3SKacheong Poon 		int64_t tot_conn = 0;
565721fffe3SKacheong Poon 
5664ba231ceSKacheong Poon 		/*
5674ba231ceSKacheong Poon 		 * During boot time, the first netstack_t is created and
5684ba231ceSKacheong Poon 		 * initialized before TCP has registered with the netstack
5694ba231ceSKacheong Poon 		 * framework.  If this reclaim function is called before TCP
5704ba231ceSKacheong Poon 		 * has finished its initialization, netstack_next() will
5714ba231ceSKacheong Poon 		 * return the first netstack_t (since its netstack_flags is
5724ba231ceSKacheong Poon 		 * not NSF_UNINIT).  And its netstack_tcp will be NULL.  We
5734ba231ceSKacheong Poon 		 * need to catch it.
5744ba231ceSKacheong Poon 		 *
5754ba231ceSKacheong Poon 		 * All subsequent netstack_t creation will not have this
5764ba231ceSKacheong Poon 		 * problem since the initialization is not finished until TCP
5774ba231ceSKacheong Poon 		 * has finished its own tcp_stack_t initialization.  Hence
5784ba231ceSKacheong Poon 		 * netstack_next() will not return one with NULL netstack_tcp.
5794ba231ceSKacheong Poon 		 */
5804ba231ceSKacheong Poon 		if ((tcps = ns->netstack_tcp) == NULL) {
5814ba231ceSKacheong Poon 			netstack_rele(ns);
5824ba231ceSKacheong Poon 			continue;
5834ba231ceSKacheong Poon 		}
584721fffe3SKacheong Poon 
585721fffe3SKacheong Poon 		/*
586721fffe3SKacheong Poon 		 * Even if the system is under memory pressure, the reason may
587721fffe3SKacheong Poon 		 * not be because of TCP activity.  Check the number of
588721fffe3SKacheong Poon 		 * connections in each stack.  If the number exceeds the
589721fffe3SKacheong Poon 		 * threshold (maxusers), turn on defensive mode.
590721fffe3SKacheong Poon 		 */
591721fffe3SKacheong Poon 		for (i = 0; i < tcps->tcps_sc_cnt; i++)
592721fffe3SKacheong Poon 			tot_conn += tcps->tcps_sc[i]->tcp_sc_conn_cnt;
593721fffe3SKacheong Poon 		if (tot_conn < maxusers) {
594721fffe3SKacheong Poon 			netstack_rele(ns);
595721fffe3SKacheong Poon 			continue;
596721fffe3SKacheong Poon 		}
597721fffe3SKacheong Poon 
598721fffe3SKacheong Poon 		mutex_enter(&tcps->tcps_reclaim_lock);
599721fffe3SKacheong Poon 		if (!tcps->tcps_reclaim) {
600721fffe3SKacheong Poon 			tcps->tcps_reclaim = B_TRUE;
601721fffe3SKacheong Poon 			tcps->tcps_reclaim_tid = timeout(tcp_reclaim_timer,
602721fffe3SKacheong Poon 			    tcps, MSEC_TO_TICK(tcps->tcps_reclaim_period));
603721fffe3SKacheong Poon 			TCP_STAT(tcps, tcp_reclaim_cnt);
604721fffe3SKacheong Poon 		}
605721fffe3SKacheong Poon 		mutex_exit(&tcps->tcps_reclaim_lock);
606721fffe3SKacheong Poon 		netstack_rele(ns);
607721fffe3SKacheong Poon 	}
608721fffe3SKacheong Poon 	netstack_next_fini(&nh);
609721fffe3SKacheong Poon }
610721fffe3SKacheong Poon 
611721fffe3SKacheong Poon /*
612721fffe3SKacheong Poon  * Given a tcp_stack_t and a port (in host byte order), find a listener
613721fffe3SKacheong Poon  * configuration for that port and return the ratio.
614721fffe3SKacheong Poon  */
615721fffe3SKacheong Poon uint32_t
tcp_find_listener_conf(tcp_stack_t * tcps,in_port_t port)616721fffe3SKacheong Poon tcp_find_listener_conf(tcp_stack_t *tcps, in_port_t port)
617721fffe3SKacheong Poon {
618721fffe3SKacheong Poon 	tcp_listener_t	*tl;
619721fffe3SKacheong Poon 	uint32_t ratio = 0;
620721fffe3SKacheong Poon 
621721fffe3SKacheong Poon 	mutex_enter(&tcps->tcps_listener_conf_lock);
622721fffe3SKacheong Poon 	for (tl = list_head(&tcps->tcps_listener_conf); tl != NULL;
623721fffe3SKacheong Poon 	    tl = list_next(&tcps->tcps_listener_conf, tl)) {
624721fffe3SKacheong Poon 		if (tl->tl_port == port) {
625721fffe3SKacheong Poon 			ratio = tl->tl_ratio;
626721fffe3SKacheong Poon 			break;
627721fffe3SKacheong Poon 		}
628721fffe3SKacheong Poon 	}
629721fffe3SKacheong Poon 	mutex_exit(&tcps->tcps_listener_conf_lock);
630721fffe3SKacheong Poon 	return (ratio);
631721fffe3SKacheong Poon }
632721fffe3SKacheong Poon 
633721fffe3SKacheong Poon /*
634721fffe3SKacheong Poon  * To remove all listener limit configuration in a tcp_stack_t.
635721fffe3SKacheong Poon  */
636721fffe3SKacheong Poon void
tcp_listener_conf_cleanup(tcp_stack_t * tcps)637721fffe3SKacheong Poon tcp_listener_conf_cleanup(tcp_stack_t *tcps)
638721fffe3SKacheong Poon {
639721fffe3SKacheong Poon 	tcp_listener_t	*tl;
640721fffe3SKacheong Poon 
641721fffe3SKacheong Poon 	mutex_enter(&tcps->tcps_listener_conf_lock);
642721fffe3SKacheong Poon 	while ((tl = list_head(&tcps->tcps_listener_conf)) != NULL) {
643721fffe3SKacheong Poon 		list_remove(&tcps->tcps_listener_conf, tl);
644721fffe3SKacheong Poon 		kmem_free(tl, sizeof (tcp_listener_t));
645721fffe3SKacheong Poon 	}
646721fffe3SKacheong Poon 	mutex_destroy(&tcps->tcps_listener_conf_lock);
647721fffe3SKacheong Poon 	list_destroy(&tcps->tcps_listener_conf);
648721fffe3SKacheong Poon }
649721fffe3SKacheong Poon 
650721fffe3SKacheong Poon /*
651*5dd46ab5SKacheong Poon  * When a CPU is added, we need to allocate the per CPU stats struct.
652721fffe3SKacheong Poon  */
653*5dd46ab5SKacheong Poon void
tcp_stack_cpu_add(tcp_stack_t * tcps,processorid_t cpu_seqid)654*5dd46ab5SKacheong Poon tcp_stack_cpu_add(tcp_stack_t *tcps, processorid_t cpu_seqid)
655721fffe3SKacheong Poon {
656721fffe3SKacheong Poon 	int i;
657721fffe3SKacheong Poon 
658*5dd46ab5SKacheong Poon 	if (cpu_seqid < tcps->tcps_sc_cnt)
659*5dd46ab5SKacheong Poon 		return;
660*5dd46ab5SKacheong Poon 	for (i = tcps->tcps_sc_cnt; i <= cpu_seqid; i++) {
661721fffe3SKacheong Poon 		ASSERT(tcps->tcps_sc[i] == NULL);
662*5dd46ab5SKacheong Poon 		tcps->tcps_sc[i] = kmem_zalloc(sizeof (tcp_stats_cpu_t),
663*5dd46ab5SKacheong Poon 		    KM_SLEEP);
664721fffe3SKacheong Poon 	}
665721fffe3SKacheong Poon 	membar_producer();
666*5dd46ab5SKacheong Poon 	tcps->tcps_sc_cnt = cpu_seqid + 1;
667721fffe3SKacheong Poon }
668721fffe3SKacheong Poon 
669721fffe3SKacheong Poon /*
670721fffe3SKacheong Poon  * Diagnostic routine used to return a string associated with the tcp state.
671721fffe3SKacheong Poon  * Note that if the caller does not supply a buffer, it will use an internal
672721fffe3SKacheong Poon  * static string.  This means that if multiple threads call this function at
673721fffe3SKacheong Poon  * the same time, output can be corrupted...  Note also that this function
674721fffe3SKacheong Poon  * does not check the size of the supplied buffer.  The caller has to make
675721fffe3SKacheong Poon  * sure that it is big enough.
676721fffe3SKacheong Poon  */
677721fffe3SKacheong Poon char *
tcp_display(tcp_t * tcp,char * sup_buf,char format)678721fffe3SKacheong Poon tcp_display(tcp_t *tcp, char *sup_buf, char format)
679721fffe3SKacheong Poon {
680721fffe3SKacheong Poon 	char		buf1[30];
681721fffe3SKacheong Poon 	static char	priv_buf[INET6_ADDRSTRLEN * 2 + 80];
682721fffe3SKacheong Poon 	char		*buf;
683721fffe3SKacheong Poon 	char		*cp;
684721fffe3SKacheong Poon 	in6_addr_t	local, remote;
685721fffe3SKacheong Poon 	char		local_addrbuf[INET6_ADDRSTRLEN];
686721fffe3SKacheong Poon 	char		remote_addrbuf[INET6_ADDRSTRLEN];
687721fffe3SKacheong Poon 	conn_t		*connp;
688721fffe3SKacheong Poon 
689721fffe3SKacheong Poon 	if (sup_buf != NULL)
690721fffe3SKacheong Poon 		buf = sup_buf;
691721fffe3SKacheong Poon 	else
692721fffe3SKacheong Poon 		buf = priv_buf;
693721fffe3SKacheong Poon 
694721fffe3SKacheong Poon 	if (tcp == NULL)
695721fffe3SKacheong Poon 		return ("NULL_TCP");
696721fffe3SKacheong Poon 
697721fffe3SKacheong Poon 	connp = tcp->tcp_connp;
698721fffe3SKacheong Poon 	switch (tcp->tcp_state) {
699721fffe3SKacheong Poon 	case TCPS_CLOSED:
700721fffe3SKacheong Poon 		cp = "TCP_CLOSED";
701721fffe3SKacheong Poon 		break;
702721fffe3SKacheong Poon 	case TCPS_IDLE:
703721fffe3SKacheong Poon 		cp = "TCP_IDLE";
704721fffe3SKacheong Poon 		break;
705721fffe3SKacheong Poon 	case TCPS_BOUND:
706721fffe3SKacheong Poon 		cp = "TCP_BOUND";
707721fffe3SKacheong Poon 		break;
708721fffe3SKacheong Poon 	case TCPS_LISTEN:
709721fffe3SKacheong Poon 		cp = "TCP_LISTEN";
710721fffe3SKacheong Poon 		break;
711721fffe3SKacheong Poon 	case TCPS_SYN_SENT:
712721fffe3SKacheong Poon 		cp = "TCP_SYN_SENT";
713721fffe3SKacheong Poon 		break;
714721fffe3SKacheong Poon 	case TCPS_SYN_RCVD:
715721fffe3SKacheong Poon 		cp = "TCP_SYN_RCVD";
716721fffe3SKacheong Poon 		break;
717721fffe3SKacheong Poon 	case TCPS_ESTABLISHED:
718721fffe3SKacheong Poon 		cp = "TCP_ESTABLISHED";
719721fffe3SKacheong Poon 		break;
720721fffe3SKacheong Poon 	case TCPS_CLOSE_WAIT:
721721fffe3SKacheong Poon 		cp = "TCP_CLOSE_WAIT";
722721fffe3SKacheong Poon 		break;
723721fffe3SKacheong Poon 	case TCPS_FIN_WAIT_1:
724721fffe3SKacheong Poon 		cp = "TCP_FIN_WAIT_1";
725721fffe3SKacheong Poon 		break;
726721fffe3SKacheong Poon 	case TCPS_CLOSING:
727721fffe3SKacheong Poon 		cp = "TCP_CLOSING";
728721fffe3SKacheong Poon 		break;
729721fffe3SKacheong Poon 	case TCPS_LAST_ACK:
730721fffe3SKacheong Poon 		cp = "TCP_LAST_ACK";
731721fffe3SKacheong Poon 		break;
732721fffe3SKacheong Poon 	case TCPS_FIN_WAIT_2:
733721fffe3SKacheong Poon 		cp = "TCP_FIN_WAIT_2";
734721fffe3SKacheong Poon 		break;
735721fffe3SKacheong Poon 	case TCPS_TIME_WAIT:
736721fffe3SKacheong Poon 		cp = "TCP_TIME_WAIT";
737721fffe3SKacheong Poon 		break;
738721fffe3SKacheong Poon 	default:
739721fffe3SKacheong Poon 		(void) mi_sprintf(buf1, "TCPUnkState(%d)", tcp->tcp_state);
740721fffe3SKacheong Poon 		cp = buf1;
741721fffe3SKacheong Poon 		break;
742721fffe3SKacheong Poon 	}
743721fffe3SKacheong Poon 	switch (format) {
744721fffe3SKacheong Poon 	case DISP_ADDR_AND_PORT:
745721fffe3SKacheong Poon 		if (connp->conn_ipversion == IPV4_VERSION) {
746721fffe3SKacheong Poon 			/*
747721fffe3SKacheong Poon 			 * Note that we use the remote address in the tcp_b
748721fffe3SKacheong Poon 			 * structure.  This means that it will print out
749721fffe3SKacheong Poon 			 * the real destination address, not the next hop's
750721fffe3SKacheong Poon 			 * address if source routing is used.
751721fffe3SKacheong Poon 			 */
752721fffe3SKacheong Poon 			IN6_IPADDR_TO_V4MAPPED(connp->conn_laddr_v4, &local);
753721fffe3SKacheong Poon 			IN6_IPADDR_TO_V4MAPPED(connp->conn_faddr_v4, &remote);
754721fffe3SKacheong Poon 
755721fffe3SKacheong Poon 		} else {
756721fffe3SKacheong Poon 			local = connp->conn_laddr_v6;
757721fffe3SKacheong Poon 			remote = connp->conn_faddr_v6;
758721fffe3SKacheong Poon 		}
759721fffe3SKacheong Poon 		(void) inet_ntop(AF_INET6, &local, local_addrbuf,
760721fffe3SKacheong Poon 		    sizeof (local_addrbuf));
761721fffe3SKacheong Poon 		(void) inet_ntop(AF_INET6, &remote, remote_addrbuf,
762721fffe3SKacheong Poon 		    sizeof (remote_addrbuf));
763721fffe3SKacheong Poon 		(void) mi_sprintf(buf, "[%s.%u, %s.%u] %s",
764721fffe3SKacheong Poon 		    local_addrbuf, ntohs(connp->conn_lport), remote_addrbuf,
765721fffe3SKacheong Poon 		    ntohs(connp->conn_fport), cp);
766721fffe3SKacheong Poon 		break;
767721fffe3SKacheong Poon 	case DISP_PORT_ONLY:
768721fffe3SKacheong Poon 	default:
769721fffe3SKacheong Poon 		(void) mi_sprintf(buf, "[%u, %u] %s",
770721fffe3SKacheong Poon 		    ntohs(connp->conn_lport), ntohs(connp->conn_fport), cp);
771721fffe3SKacheong Poon 		break;
772721fffe3SKacheong Poon 	}
773721fffe3SKacheong Poon 
774721fffe3SKacheong Poon 	return (buf);
775721fffe3SKacheong Poon }
776