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