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 * 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 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 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 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 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 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 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 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 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 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 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 * 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