static int tcpip_sock_print(struct sonode *socknode) { switch (socknode->so_family) { case AF_INET: { conn_t conn_t; in_port_t port; if (mdb_vread(&conn_t, sizeof (conn_t), (uintptr_t)socknode->so_proto_handle) == -1) { mdb_warn("failed to read conn_t V4"); return (-1); } mdb_printf("socket: "); mdb_nhconvert(&port, &conn_t.conn_lport, sizeof (port)); mdb_printf("AF_INET %I %d ", conn_t.conn_laddr_v4, port); /* * If this is a listening socket, we don't print * the remote address. */ if (IPCL_IS_TCP(&conn_t) && IPCL_IS_BOUND(&conn_t) == 0 || IPCL_IS_UDP(&conn_t) && IPCL_IS_CONNECTED(&conn_t)) { mdb_printf("remote: "); mdb_nhconvert(&port, &conn_t.conn_fport, sizeof (port)); mdb_printf("AF_INET %I %d ", conn_t.conn_faddr_v4, port); } break; } case AF_INET6: { conn_t conn_t; in_port_t port; if (mdb_vread(&conn_t, sizeof (conn_t), (uintptr_t)socknode->so_proto_handle) == -1) { mdb_warn("failed to read conn_t V6"); return (-1); } mdb_printf("socket: "); mdb_nhconvert(&port, &conn_t.conn_lport, sizeof (port)); mdb_printf("AF_INET6 %N %d ", &conn_t.conn_laddr_v4, port); /* * If this is a listening socket, we don't print * the remote address. */ if (IPCL_IS_TCP(&conn_t) && IPCL_IS_BOUND(&conn_t) == 0 || IPCL_IS_UDP(&conn_t) && IPCL_IS_CONNECTED(&conn_t)) { mdb_printf("remote: "); mdb_nhconvert(&port, &conn_t.conn_fport, sizeof (port)); mdb_printf("AF_INET6 %N %d ", &conn_t.conn_faddr_v6, port); } break; } default: mdb_printf("AF_?? (%d)", socknode->so_family); break; } return (0); }
/* * This routine gets called by the eager tcp upon changing state from * SYN_RCVD to ESTABLISHED. It fuses a direct path between itself * and the active connect tcp such that the regular tcp processings * may be bypassed under allowable circumstances. Because the fusion * requires both endpoints to be in the same squeue, it does not work * for simultaneous active connects because there is no easy way to * switch from one squeue to another once the connection is created. * This is different from the eager tcp case where we assign it the * same squeue as the one given to the active connect tcp during open. */ void tcp_fuse(tcp_t *tcp, uchar_t *iphdr, tcph_t *tcph) { conn_t *peer_connp, *connp = tcp->tcp_connp; tcp_t *peer_tcp; ASSERT(!tcp->tcp_fused); ASSERT(tcp->tcp_loopback); ASSERT(tcp->tcp_loopback_peer == NULL); /* * We need to inherit q_hiwat of the listener tcp, but we can't * really use tcp_listener since we get here after sending up * T_CONN_IND and tcp_wput_accept() may be called independently, * at which point tcp_listener is cleared; this is why we use * tcp_saved_listener. The listener itself is guaranteed to be * around until tcp_accept_finish() is called on this eager -- * this won't happen until we're done since we're inside the * eager's perimeter now. */ ASSERT(tcp->tcp_saved_listener != NULL); /* * Lookup peer endpoint; search for the remote endpoint having * the reversed address-port quadruplet in ESTABLISHED state, * which is guaranteed to be unique in the system. Zone check * is applied accordingly for loopback address, but not for * local address since we want fusion to happen across Zones. */ if (tcp->tcp_ipversion == IPV4_VERSION) { peer_connp = ipcl_conn_tcp_lookup_reversed_ipv4(connp, (ipha_t *)iphdr, tcph); } else { peer_connp = ipcl_conn_tcp_lookup_reversed_ipv6(connp, (ip6_t *)iphdr, tcph); } /* * We can only proceed if peer exists, resides in the same squeue * as our conn and is not raw-socket. The squeue assignment of * this eager tcp was done earlier at the time of SYN processing * in ip_fanout_tcp{_v6}. Note that similar squeues by itself * doesn't guarantee a safe condition to fuse, hence we perform * additional tests below. */ ASSERT(peer_connp == NULL || peer_connp != connp); if (peer_connp == NULL || peer_connp->conn_sqp != connp->conn_sqp || !IPCL_IS_TCP(peer_connp)) { if (peer_connp != NULL) { TCP_STAT(tcp_fusion_unqualified); CONN_DEC_REF(peer_connp); } return; } peer_tcp = peer_connp->conn_tcp; /* active connect tcp */ ASSERT(peer_tcp != NULL && peer_tcp != tcp && !peer_tcp->tcp_fused); ASSERT(peer_tcp->tcp_loopback && peer_tcp->tcp_loopback_peer == NULL); ASSERT(peer_connp->conn_sqp == connp->conn_sqp); /* * Fuse the endpoints; we perform further checks against both * tcp endpoints to ensure that a fusion is allowed to happen. * In particular we bail out for non-simple TCP/IP or if IPsec/ * IPQoS policy/kernel SSL exists. */ if (!tcp->tcp_unfusable && !peer_tcp->tcp_unfusable && !TCP_LOOPBACK_IP(tcp) && !TCP_LOOPBACK_IP(peer_tcp) && tcp->tcp_kssl_ent == NULL && !IPP_ENABLED(IPP_LOCAL_OUT|IPP_LOCAL_IN)) { mblk_t *mp; struct stroptions *stropt; queue_t *peer_rq = peer_tcp->tcp_rq; ASSERT(!TCP_IS_DETACHED(peer_tcp) && peer_rq != NULL); ASSERT(tcp->tcp_fused_sigurg_mp == NULL); ASSERT(peer_tcp->tcp_fused_sigurg_mp == NULL); ASSERT(tcp->tcp_kssl_ctx == NULL); /* * We need to drain data on both endpoints during unfuse. * If we need to send up SIGURG at the time of draining, * we want to be sure that an mblk is readily available. * This is why we pre-allocate the M_PCSIG mblks for both * endpoints which will only be used during/after unfuse. */ if ((mp = allocb(1, BPRI_HI)) == NULL) goto failed; tcp->tcp_fused_sigurg_mp = mp; if ((mp = allocb(1, BPRI_HI)) == NULL) goto failed; peer_tcp->tcp_fused_sigurg_mp = mp; /* Allocate M_SETOPTS mblk */ if ((mp = allocb(sizeof (*stropt), BPRI_HI)) == NULL) goto failed; /* Fuse both endpoints */ peer_tcp->tcp_loopback_peer = tcp; tcp->tcp_loopback_peer = peer_tcp; peer_tcp->tcp_fused = tcp->tcp_fused = B_TRUE; /* * We never use regular tcp paths in fusion and should * therefore clear tcp_unsent on both endpoints. Having * them set to non-zero values means asking for trouble * especially after unfuse, where we may end up sending * through regular tcp paths which expect xmit_list and * friends to be correctly setup. */ peer_tcp->tcp_unsent = tcp->tcp_unsent = 0; tcp_timers_stop(tcp); tcp_timers_stop(peer_tcp); /* * At this point we are a detached eager tcp and therefore * don't have a queue assigned to us until accept happens. * In the mean time the peer endpoint may immediately send * us data as soon as fusion is finished, and we need to be * able to flow control it in case it sends down huge amount * of data while we're still detached. To prevent that we * inherit the listener's q_hiwat value; this is temporary * since we'll repeat the process in tcp_accept_finish(). */ (void) tcp_fuse_set_rcv_hiwat(tcp, tcp->tcp_saved_listener->tcp_rq->q_hiwat); /* * Set the stream head's write offset value to zero since we * won't be needing any room for TCP/IP headers; tell it to * not break up the writes (this would reduce the amount of * work done by kmem); and configure our receive buffer. * Note that we can only do this for the active connect tcp * since our eager is still detached; it will be dealt with * later in tcp_accept_finish(). */ DB_TYPE(mp) = M_SETOPTS; mp->b_wptr += sizeof (*stropt); stropt = (struct stroptions *)mp->b_rptr; stropt->so_flags = SO_MAXBLK | SO_WROFF | SO_HIWAT; stropt->so_maxblk = tcp_maxpsz_set(peer_tcp, B_FALSE); stropt->so_wroff = 0; /* * Record the stream head's high water mark for * peer endpoint; this is used for flow-control * purposes in tcp_fuse_output(). */ stropt->so_hiwat = tcp_fuse_set_rcv_hiwat(peer_tcp, peer_rq->q_hiwat); /* Send the options up */ putnext(peer_rq, mp); } else { TCP_STAT(tcp_fusion_unqualified); } CONN_DEC_REF(peer_connp); return; failed: if (tcp->tcp_fused_sigurg_mp != NULL) { freeb(tcp->tcp_fused_sigurg_mp); tcp->tcp_fused_sigurg_mp = NULL; } if (peer_tcp->tcp_fused_sigurg_mp != NULL) { freeb(peer_tcp->tcp_fused_sigurg_mp); peer_tcp->tcp_fused_sigurg_mp = NULL; } CONN_DEC_REF(peer_connp); }