#include <sys/types.h>
#include <sys/strlog.h>
#include <sys/policy.h>
#include <sys/sockio.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/squeue_impl.h>
#include <sys/squeue.h>
#define _SUN_TPI_VERSION 2
#include <sys/tihdr.h>
#include <sys/timod.h>
#include <sys/tpicommon.h>
#include <sys/socketvar.h>
#include <inet/common.h>
#include <inet/proto_set.h>
#include <inet/ip.h>
#include <inet/tcp.h>
#include <inet/tcp_impl.h>
static void tcp_activate(sock_lower_handle_t, sock_upper_handle_t,
sock_upcalls_t *, int, cred_t *);
static int tcp_accept(sock_lower_handle_t, sock_lower_handle_t,
sock_upper_handle_t, cred_t *);
static int tcp_bind(sock_lower_handle_t, struct sockaddr *,
socklen_t, cred_t *);
static int tcp_listen(sock_lower_handle_t, int, cred_t *);
static int tcp_connect(sock_lower_handle_t, const struct sockaddr *,
socklen_t, sock_connid_t *, cred_t *);
static int tcp_getpeername(sock_lower_handle_t, struct sockaddr *,
socklen_t *, cred_t *);
static int tcp_getsockname(sock_lower_handle_t, struct sockaddr *,
socklen_t *, cred_t *);
static int tcp_getsockopt(sock_lower_handle_t, int, int, void *,
socklen_t *, cred_t *);
static int tcp_setsockopt(sock_lower_handle_t, int, int, const void *,
socklen_t, cred_t *);
static int tcp_sendmsg(sock_lower_handle_t, mblk_t *, struct nmsghdr *,
cred_t *);
static int tcp_shutdown(sock_lower_handle_t, int, cred_t *);
static void tcp_clr_flowctrl(sock_lower_handle_t);
static int tcp_ioctl(sock_lower_handle_t, int, intptr_t, int, int32_t *,
cred_t *);
static int tcp_close(sock_lower_handle_t, int, cred_t *);
sock_downcalls_t sock_tcp_downcalls = {
tcp_activate,
tcp_accept,
tcp_bind,
tcp_listen,
tcp_connect,
tcp_getpeername,
tcp_getsockname,
tcp_getsockopt,
tcp_setsockopt,
tcp_sendmsg,
NULL,
NULL,
NULL,
tcp_shutdown,
tcp_clr_flowctrl,
tcp_ioctl,
tcp_close,
};
static void
tcp_activate(sock_lower_handle_t proto_handle, sock_upper_handle_t sock_handle,
sock_upcalls_t *sock_upcalls, int flags, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
struct sock_proto_props sopp;
extern struct module_info tcp_rinfo;
ASSERT(connp->conn_upper_handle == NULL);
ASSERT(cr != NULL);
sopp.sopp_flags = SOCKOPT_RCVHIWAT | SOCKOPT_RCVLOWAT |
SOCKOPT_MAXPSZ | SOCKOPT_MAXBLK | SOCKOPT_RCVTIMER |
SOCKOPT_RCVTHRESH | SOCKOPT_MAXADDRLEN | SOCKOPT_MINPSZ;
sopp.sopp_rxhiwat = SOCKET_RECVHIWATER;
sopp.sopp_rxlowat = SOCKET_RECVLOWATER;
sopp.sopp_maxpsz = INFPSZ;
sopp.sopp_maxblk = INFPSZ;
sopp.sopp_rcvtimer = SOCKET_TIMER_INTERVAL;
sopp.sopp_rcvthresh = SOCKET_RECVHIWATER >> 3;
sopp.sopp_maxaddrlen = sizeof (sin6_t);
sopp.sopp_minpsz = (tcp_rinfo.mi_minpsz == 1) ? 0 :
tcp_rinfo.mi_minpsz;
connp->conn_upcalls = sock_upcalls;
connp->conn_upper_handle = sock_handle;
ASSERT(connp->conn_rcvbuf != 0 &&
connp->conn_rcvbuf == connp->conn_tcp->tcp_rwnd);
(*sock_upcalls->su_set_proto_props)(sock_handle, &sopp);
}
static int
tcp_accept(sock_lower_handle_t lproto_handle,
sock_lower_handle_t eproto_handle, sock_upper_handle_t sock_handle,
cred_t *cr)
{
conn_t *lconnp, *econnp;
tcp_t *listener, *eager;
econnp = (conn_t *)eproto_handle;
eager = econnp->conn_tcp;
ASSERT(IPCL_IS_NONSTR(econnp));
ASSERT(eager->tcp_listener != NULL);
listener = eager->tcp_listener;
lconnp = (conn_t *)listener->tcp_connp;
ASSERT(listener->tcp_state == TCPS_LISTEN);
ASSERT(lconnp->conn_upper_handle != NULL);
ASSERT(econnp->conn_upper_handle == NULL ||
econnp->conn_upper_handle == sock_handle);
ASSERT(econnp->conn_upcalls == NULL ||
econnp->conn_upcalls == lconnp->conn_upcalls);
econnp->conn_upper_handle = sock_handle;
econnp->conn_upcalls = lconnp->conn_upcalls;
ASSERT(econnp->conn_netstack ==
listener->tcp_connp->conn_netstack);
ASSERT(eager->tcp_tcps == listener->tcp_tcps);
ASSERT(econnp->conn_ref >= 2);
mutex_enter(&listener->tcp_eager_lock);
ASSERT(!listener->tcp_eager_prev_q0->tcp_conn_def_q0);
tcp_eager_unlink(eager);
mutex_exit(&listener->tcp_eager_lock);
CONN_DEC_REF(listener->tcp_connp);
return ((eager->tcp_state < TCPS_ESTABLISHED) ? ECONNABORTED : 0);
}
static int
tcp_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa,
socklen_t len, cred_t *cr)
{
int error;
conn_t *connp = (conn_t *)proto_handle;
ASSERT(cr != NULL);
ASSERT(connp->conn_upper_handle != NULL);
error = squeue_synch_enter(connp, NULL);
if (error != 0) {
return (ENOSR);
}
if (sa == NULL) {
if (connp->conn_tcp->tcp_state < TCPS_LISTEN)
error = tcp_do_unbind(connp);
else
error = EINVAL;
} else {
error = tcp_do_bind(connp, sa, len, cr, B_TRUE);
}
squeue_synch_exit(connp, SQ_NODRAIN);
if (error < 0) {
if (error == -TOUTSTATE)
error = EINVAL;
else
error = proto_tlitosyserr(-error);
}
return (error);
}
static int
tcp_listen(sock_lower_handle_t proto_handle, int backlog, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
tcp_t *tcp = connp->conn_tcp;
int error;
ASSERT(connp->conn_upper_handle != NULL);
ASSERT(cr != NULL);
error = squeue_synch_enter(connp, NULL);
if (error != 0) {
return (ENOBUFS);
}
error = tcp_do_listen(connp, NULL, 0, backlog, cr, B_FALSE);
if (error == 0) {
(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
SOCK_OPCTL_ENAB_ACCEPT,
(uintptr_t)(tcp->tcp_conn_req_max +
tcp->tcp_tcps->tcps_conn_req_max_q0));
} else if (error < 0) {
if (error == -TOUTSTATE)
error = EINVAL;
else
error = proto_tlitosyserr(-error);
}
squeue_synch_exit(connp, SQ_NODRAIN);
return (error);
}
static int
tcp_connect(sock_lower_handle_t proto_handle, const struct sockaddr *sa,
socklen_t len, sock_connid_t *id, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
int error;
ASSERT(connp->conn_upper_handle != NULL);
ASSERT(cr != NULL);
error = proto_verify_ip_addr(connp->conn_family, sa, len);
if (error != 0) {
return (error);
}
error = squeue_synch_enter(connp, NULL);
if (error != 0) {
return (ENOSR);
}
error = tcp_do_connect(connp, sa, len, cr, curproc->p_pid);
if (error == 0) {
*id = connp->conn_tcp->tcp_connid;
} else if (error < 0) {
if (error == -TOUTSTATE) {
switch (connp->conn_tcp->tcp_state) {
case TCPS_SYN_SENT:
error = EALREADY;
break;
case TCPS_ESTABLISHED:
error = EISCONN;
break;
case TCPS_LISTEN:
error = EOPNOTSUPP;
break;
default:
error = EINVAL;
break;
}
} else {
error = proto_tlitosyserr(-error);
}
}
if (connp->conn_tcp->tcp_loopback) {
struct sock_proto_props sopp;
sopp.sopp_flags = SOCKOPT_LOOPBACK;
sopp.sopp_loopback = B_TRUE;
(*connp->conn_upcalls->su_set_proto_props)(
connp->conn_upper_handle, &sopp);
}
squeue_synch_exit(connp, SQ_PROCESS);
return ((error == 0) ? EINPROGRESS : error);
}
static int
tcp_getpeername(sock_lower_handle_t proto_handle, struct sockaddr *addr,
socklen_t *addrlenp, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
tcp_t *tcp = connp->conn_tcp;
ASSERT(cr != NULL);
ASSERT(tcp != NULL);
if (tcp->tcp_state < TCPS_SYN_RCVD)
return (ENOTCONN);
return (conn_getpeername(connp, addr, addrlenp));
}
static int
tcp_getsockname(sock_lower_handle_t proto_handle, struct sockaddr *addr,
socklen_t *addrlenp, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
ASSERT(cr != NULL);
return (conn_getsockname(connp, addr, addrlenp));
}
static int
tcp_getsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
void *optvalp, socklen_t *optlen, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
int error;
t_uscalar_t max_optbuf_len;
void *optvalp_buf;
int len;
ASSERT(connp->conn_upper_handle != NULL);
error = proto_opt_check(level, option_name, *optlen, &max_optbuf_len,
tcp_opt_obj.odb_opt_des_arr,
tcp_opt_obj.odb_opt_arr_cnt,
B_FALSE, B_TRUE, cr);
if (error != 0) {
if (error < 0) {
error = proto_tlitosyserr(-error);
}
return (error);
}
optvalp_buf = kmem_alloc(max_optbuf_len, KM_SLEEP);
error = squeue_synch_enter(connp, NULL);
if (error == ENOMEM) {
kmem_free(optvalp_buf, max_optbuf_len);
return (ENOMEM);
}
len = tcp_opt_get(connp, level, option_name, optvalp_buf);
squeue_synch_exit(connp, SQ_NODRAIN);
if (len == -1) {
kmem_free(optvalp_buf, max_optbuf_len);
return (EINVAL);
}
t_uscalar_t size = MIN(len, *optlen);
bcopy(optvalp_buf, optvalp, size);
bcopy(&size, optlen, sizeof (size));
kmem_free(optvalp_buf, max_optbuf_len);
return (0);
}
static int
tcp_setsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
const void *optvalp, socklen_t optlen, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
int error;
ASSERT(connp->conn_upper_handle != NULL);
if (level == IPPROTO_TCP) {
switch (option_name) {
case TCP_NODELAY:
if (optlen != sizeof (int32_t))
return (EINVAL);
mutex_enter(&connp->conn_tcp->tcp_non_sq_lock);
connp->conn_tcp->tcp_naglim = *(int *)optvalp ? 1 :
connp->conn_tcp->tcp_mss;
mutex_exit(&connp->conn_tcp->tcp_non_sq_lock);
return (0);
default:
break;
}
}
error = squeue_synch_enter(connp, NULL);
if (error == ENOMEM) {
return (ENOMEM);
}
error = proto_opt_check(level, option_name, optlen, NULL,
tcp_opt_obj.odb_opt_des_arr,
tcp_opt_obj.odb_opt_arr_cnt,
B_TRUE, B_FALSE, cr);
if (error != 0) {
if (error < 0) {
error = proto_tlitosyserr(-error);
}
squeue_synch_exit(connp, SQ_NODRAIN);
return (error);
}
error = tcp_opt_set(connp, SETFN_OPTCOM_NEGOTIATE, level, option_name,
optlen, (uchar_t *)optvalp, (uint_t *)&optlen, (uchar_t *)optvalp,
NULL, cr);
squeue_synch_exit(connp, SQ_NODRAIN);
ASSERT(error >= 0);
return (error);
}
static int
tcp_sendmsg(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg,
cred_t *cr)
{
tcp_t *tcp;
uint32_t msize;
conn_t *connp = (conn_t *)proto_handle;
int32_t tcpstate;
ASSERT(cr != NULL);
ASSERT(connp->conn_ref >= 2);
ASSERT(connp->conn_upper_handle != NULL);
if (msg->msg_controllen != 0) {
freemsg(mp);
return (EOPNOTSUPP);
}
switch (DB_TYPE(mp)) {
case M_DATA:
tcp = connp->conn_tcp;
ASSERT(tcp != NULL);
tcpstate = tcp->tcp_state;
if (tcpstate < TCPS_ESTABLISHED) {
freemsg(mp);
return ((tcpstate == TCPS_SYN_SENT) ? ENOTCONN :
((tcp->tcp_connid > 0) ? EPIPE : ENOTCONN));
} else if (tcpstate > TCPS_CLOSE_WAIT) {
freemsg(mp);
return (EPIPE);
}
msize = msgdsize(mp);
mutex_enter(&tcp->tcp_non_sq_lock);
tcp->tcp_squeue_bytes += msize;
if (TCP_UNSENT_BYTES(tcp) > connp->conn_sndbuf) {
tcp_setqfull(tcp);
}
mutex_exit(&tcp->tcp_non_sq_lock);
CONN_INC_REF(connp);
if (msg->msg_flags & MSG_OOB) {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, tcp_output_urgent,
connp, NULL, tcp_squeue_flag, SQTAG_TCP_OUTPUT);
} else {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, tcp_output,
connp, NULL, tcp_squeue_flag, SQTAG_TCP_OUTPUT);
}
return (0);
default:
ASSERT(0);
}
freemsg(mp);
return (0);
}
static int
tcp_shutdown(sock_lower_handle_t proto_handle, int how, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
tcp_t *tcp = connp->conn_tcp;
ASSERT(connp->conn_upper_handle != NULL);
ASSERT(cr != NULL);
if (tcp->tcp_state < TCPS_SYN_SENT)
return (ENOTCONN);
if (how != SHUT_RD) {
mblk_t *bp;
bp = allocb_wait(0, BPRI_HI, STR_NOSIG, NULL);
CONN_INC_REF(connp);
SQUEUE_ENTER_ONE(connp->conn_sqp, bp, tcp_shutdown_output,
connp, NULL, SQ_NODRAIN, SQTAG_TCP_SHUTDOWN_OUTPUT);
(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
SOCK_OPCTL_SHUT_SEND, 0);
}
if (how != SHUT_WR)
(*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
SOCK_OPCTL_SHUT_RECV, 0);
return (0);
}
static void
tcp_clr_flowctrl(sock_lower_handle_t proto_handle)
{
conn_t *connp = (conn_t *)proto_handle;
tcp_t *tcp = connp->conn_tcp;
mblk_t *mp;
int error;
ASSERT(connp->conn_upper_handle != NULL);
mutex_enter(&tcp->tcp_rsrv_mp_lock);
if ((mp = tcp->tcp_rsrv_mp) == NULL) {
mutex_exit(&tcp->tcp_rsrv_mp_lock);
return;
}
tcp->tcp_rsrv_mp = NULL;
mutex_exit(&tcp->tcp_rsrv_mp_lock);
error = squeue_synch_enter(connp, mp);
ASSERT(error == 0);
mutex_enter(&tcp->tcp_rsrv_mp_lock);
tcp->tcp_rsrv_mp = mp;
mutex_exit(&tcp->tcp_rsrv_mp_lock);
if (tcp->tcp_fused) {
tcp_fuse_backenable(tcp);
} else {
tcp->tcp_rwnd = connp->conn_rcvbuf;
if (tcp->tcp_state >= TCPS_ESTABLISHED &&
tcp_rwnd_reopen(tcp) == TH_ACK_NEEDED) {
tcp_xmit_ctl(NULL, tcp,
(tcp->tcp_swnd == 0) ? tcp->tcp_suna :
tcp->tcp_snxt, tcp->tcp_rnxt, TH_ACK);
}
}
squeue_synch_exit(connp, SQ_NODRAIN);
}
static int
tcp_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg,
int mode, int32_t *rvalp, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
int error;
ASSERT(connp->conn_upper_handle != NULL);
ASSERT(cr != NULL);
if (connp->conn_helper_info == NULL) {
tcp_stack_t *tcps = connp->conn_tcp->tcp_tcps;
error = ip_create_helper_stream(connp, tcps->tcps_ldi_ident);
if (error != 0) {
ip0dbg(("tcp_ioctl: create of IP helper stream "
"failed %d\n", error));
return (error);
}
}
switch (cmd) {
case ND_SET:
case ND_GET:
case _SIOCSOCKFALLBACK:
case TCP_IOC_ABORT_CONN:
case TI_GETPEERNAME:
case TI_GETMYNAME:
ip1dbg(("tcp_ioctl: cmd 0x%x on non streams socket",
cmd));
error = EINVAL;
break;
default:
mutex_enter(&connp->conn_lock);
if (connp->conn_state_flags & (CONN_CLOSING)) {
mutex_exit(&connp->conn_lock);
error = EINVAL;
break;
}
CONN_INC_IOCTLREF_LOCKED(connp);
error = ldi_ioctl(connp->conn_helper_info->iphs_handle,
cmd, arg, mode, cr, rvalp);
CONN_DEC_IOCTLREF(connp);
break;
}
return (error);
}
static int
tcp_close(sock_lower_handle_t proto_handle, int flags, cred_t *cr)
{
conn_t *connp = (conn_t *)proto_handle;
ASSERT(connp->conn_upper_handle != NULL);
ASSERT(cr != NULL);
tcp_close_common(connp, flags);
ip_free_helper_stream(connp);
CONN_DEC_REF(connp);
return (EINPROGRESS);
}
sock_lower_handle_t
tcp_create(int family, int type, int proto, sock_downcalls_t **sock_downcalls,
uint_t *smodep, int *errorp, int flags, cred_t *credp)
{
conn_t *connp;
boolean_t isv6 = family == AF_INET6;
if (type != SOCK_STREAM || (family != AF_INET && family != AF_INET6) ||
(proto != 0 && proto != IPPROTO_TCP)) {
*errorp = EPROTONOSUPPORT;
return (NULL);
}
connp = tcp_create_common(credp, isv6, B_TRUE, errorp);
if (connp == NULL) {
return (NULL);
}
mutex_enter(&connp->conn_lock);
CONN_INC_REF_LOCKED(connp);
ASSERT(connp->conn_ref == 2);
connp->conn_state_flags &= ~CONN_INCIPIENT;
connp->conn_flags |= IPCL_NONSTR;
mutex_exit(&connp->conn_lock);
ASSERT(errorp != NULL);
*errorp = 0;
*sock_downcalls = &sock_tcp_downcalls;
*smodep = SM_CONNREQUIRED | SM_EXDATA | SM_ACCEPTSUPP |
SM_SENDFILESUPP;
return ((sock_lower_handle_t)connp);
}
static void
tcp_fallback_noneager(tcp_t *tcp, mblk_t *stropt_mp, queue_t *q,
boolean_t issocket, so_proto_quiesced_cb_t quiesced_cb,
sock_quiesce_arg_t *arg)
{
conn_t *connp = tcp->tcp_connp;
struct stroptions *stropt;
struct T_capability_ack tca;
struct sockaddr_in6 laddr, faddr;
socklen_t laddrlen, faddrlen;
short opts;
int error;
mblk_t *mp, *mpnext;
connp->conn_dev = (dev_t)RD(q)->q_ptr;
connp->conn_minor_arena = WR(q)->q_ptr;
RD(q)->q_ptr = WR(q)->q_ptr = connp;
connp->conn_rq = RD(q);
connp->conn_wq = WR(q);
WR(q)->q_qinfo = &tcp_sock_winit;
if (!issocket)
tcp_use_pure_tpi(tcp);
ip_free_helper_stream(connp);
DB_TYPE(stropt_mp) = M_SETOPTS;
stropt = (struct stroptions *)stropt_mp->b_rptr;
stropt_mp->b_wptr += sizeof (struct stroptions);
stropt->so_flags = SO_HIWAT | SO_WROFF | SO_MAXBLK;
stropt->so_wroff = connp->conn_ht_iphc_len + (tcp->tcp_loopback ? 0 :
tcp->tcp_tcps->tcps_wroff_xtra);
if (tcp->tcp_snd_sack_ok)
stropt->so_wroff += TCPOPT_MAX_SACK_LEN;
stropt->so_hiwat = connp->conn_rcvbuf;
stropt->so_maxblk = tcp_maxpsz_set(tcp, B_FALSE);
putnext(RD(q), stropt_mp);
tcp_do_capability_ack(tcp, &tca, TC1_INFO|TC1_ACCEPTOR_ID);
laddrlen = faddrlen = sizeof (sin6_t);
(void) tcp_getsockname((sock_lower_handle_t)connp,
(struct sockaddr *)&laddr, &laddrlen, CRED());
error = tcp_getpeername((sock_lower_handle_t)connp,
(struct sockaddr *)&faddr, &faddrlen, CRED());
if (error != 0)
faddrlen = 0;
opts = 0;
if (connp->conn_oobinline)
opts |= SO_OOBINLINE;
if (connp->conn_ixa->ixa_flags & IXAF_DONTROUTE)
opts |= SO_DONTROUTE;
mp = (*quiesced_cb)(connp->conn_upper_handle, arg, &tca,
(struct sockaddr *)&laddr, laddrlen,
(struct sockaddr *)&faddr, faddrlen, opts);
while (mp != NULL) {
mpnext = mp->b_next;
tcp->tcp_rcv_list = mp->b_next;
mp->b_next = NULL;
putnext(q, mp);
mp = mpnext;
}
ASSERT(tcp->tcp_rcv_last_head == NULL);
ASSERT(tcp->tcp_rcv_last_tail == NULL);
ASSERT(tcp->tcp_rcv_cnt == 0);
if (tcp->tcp_conn_req_cnt_q0 != 0)
tcp_eager_cleanup(tcp, B_TRUE);
}
static void
tcp_fallback_eager(tcp_t *eager, boolean_t issocket,
so_proto_quiesced_cb_t quiesced_cb, sock_quiesce_arg_t *arg)
{
conn_t *connp = eager->tcp_connp;
tcp_t *listener = eager->tcp_listener;
mblk_t *mp;
ASSERT(listener != NULL);
mp = (*quiesced_cb)(connp->conn_upper_handle, arg, NULL, NULL, 0,
NULL, 0, 0);
if (mp != NULL) {
ASSERT(eager->tcp_rcv_cnt == 0);
eager->tcp_rcv_list = mp;
eager->tcp_rcv_cnt = msgdsize(mp);
while (mp->b_next != NULL) {
mp = mp->b_next;
eager->tcp_rcv_cnt += msgdsize(mp);
}
eager->tcp_rcv_last_head = mp;
while (mp->b_cont)
mp = mp->b_cont;
eager->tcp_rcv_last_tail = mp;
if (eager->tcp_rcv_cnt > eager->tcp_rwnd)
eager->tcp_rwnd = 0;
else
eager->tcp_rwnd -= eager->tcp_rcv_cnt;
}
if (!issocket)
eager->tcp_issocket = B_FALSE;
eager->tcp_detached = B_TRUE;
eager->tcp_hard_binding = B_TRUE;
connp->conn_rq = listener->tcp_connp->conn_rq;
connp->conn_wq = listener->tcp_connp->conn_wq;
mp = eager->tcp_conn.tcp_eager_conn_ind;
ASSERT(mp != NULL);
eager->tcp_conn.tcp_eager_conn_ind = NULL;
if (!issocket) {
struct T_conn_ind *conn_ind;
conn_ind = (struct T_conn_ind *)mp->b_rptr;
conn_ind->OPT_length = 0;
conn_ind->OPT_offset = 0;
}
putnext(listener->tcp_connp->conn_rq, mp);
}
int
tcp_fallback(sock_lower_handle_t proto_handle, queue_t *q,
boolean_t direct_sockfs, so_proto_quiesced_cb_t quiesced_cb,
sock_quiesce_arg_t *arg)
{
tcp_t *tcp;
conn_t *connp = (conn_t *)proto_handle;
int error;
mblk_t *stropt_mp;
mblk_t *ordrel_mp;
tcp = connp->conn_tcp;
stropt_mp = allocb_wait(sizeof (struct stroptions), BPRI_HI, STR_NOSIG,
NULL);
ASSERT(tcp->tcp_ordrel_mp == NULL);
ordrel_mp = allocb_wait(sizeof (struct T_ordrel_ind), BPRI_HI,
STR_NOSIG, NULL);
ordrel_mp->b_datap->db_type = M_PROTO;
((struct T_ordrel_ind *)ordrel_mp->b_rptr)->PRIM_type = T_ORDREL_IND;
ordrel_mp->b_wptr += sizeof (struct T_ordrel_ind);
error = squeue_synch_enter(connp, NULL);
if (error != 0) {
freeb(stropt_mp);
freeb(ordrel_mp);
return (ENOMEM);
}
if (tcp->tcp_fused)
tcp_unfuse(tcp);
if (tcp->tcp_listener != NULL) {
freeb(stropt_mp);
tcp_fallback_eager(tcp, direct_sockfs, quiesced_cb, arg);
} else {
tcp_fallback_noneager(tcp, stropt_mp, q, direct_sockfs,
quiesced_cb, arg);
}
connp->conn_flags &= ~IPCL_NONSTR;
tcp->tcp_ordrel_mp = ordrel_mp;
ASSERT(connp->conn_ref >= 2);
squeue_synch_exit(connp, SQ_NODRAIN);
return (0);
}
boolean_t
tcp_newconn_notify(tcp_t *tcp, ip_recv_attr_t *ira)
{
tcp_t *listener = tcp->tcp_listener;
conn_t *lconnp = listener->tcp_connp;
conn_t *econnp = tcp->tcp_connp;
tcp_t *tail;
ipaddr_t *addr_cache;
sock_upper_handle_t upper;
struct sock_proto_props sopp;
mutex_enter(&listener->tcp_eager_lock);
MAKE_UNDROPPABLE(tcp);
ASSERT(listener->tcp_conn_req_cnt_q0 > 0);
listener->tcp_conn_req_cnt_q0--;
listener->tcp_conn_req_cnt_q++;
tcp->tcp_eager_next_q0->tcp_eager_prev_q0 = tcp->tcp_eager_prev_q0;
tcp->tcp_eager_prev_q0->tcp_eager_next_q0 = tcp->tcp_eager_next_q0;
tcp->tcp_eager_prev_q0 = NULL;
tcp->tcp_eager_next_q0 = NULL;
tail = listener->tcp_eager_last_q;
if (tail != NULL)
tail->tcp_eager_next_q = tcp;
else
listener->tcp_eager_next_q = tcp;
listener->tcp_eager_last_q = tcp;
tcp->tcp_eager_next_q = NULL;
if (tcp->tcp_syn_rcvd_timeout != 0) {
tcp->tcp_syn_rcvd_timeout = 0;
listener->tcp_syn_rcvd_timeout--;
if (listener->tcp_syn_defense &&
listener->tcp_syn_rcvd_timeout <=
(listener->tcp_tcps->tcps_conn_req_max_q0 >> 5) &&
10*MINUTES < TICK_TO_MSEC(ddi_get_lbolt64() -
listener->tcp_last_rcv_lbolt)) {
listener->tcp_syn_defense = B_FALSE;
if (listener->tcp_ip_addr_cache) {
kmem_free((void *)listener->tcp_ip_addr_cache,
IP_ADDR_CACHE_SIZE * sizeof (ipaddr_t));
listener->tcp_ip_addr_cache = NULL;
}
}
}
addr_cache = (ipaddr_t *)(listener->tcp_ip_addr_cache);
if (addr_cache != NULL) {
addr_cache[IP_ADDR_CACHE_HASH(tcp->tcp_connp->conn_faddr_v4)] =
tcp->tcp_connp->conn_faddr_v4;
}
mutex_exit(&listener->tcp_eager_lock);
if ((upper = (*lconnp->conn_upcalls->su_newconn)
(lconnp->conn_upper_handle, (sock_lower_handle_t)econnp,
&sock_tcp_downcalls, ira->ira_cred, ira->ira_cpid,
&econnp->conn_upcalls)) == NULL) {
return (B_FALSE);
}
econnp->conn_upper_handle = upper;
tcp->tcp_detached = B_FALSE;
tcp->tcp_hard_binding = B_FALSE;
tcp->tcp_tconnind_started = B_TRUE;
if (econnp->conn_keepalive) {
tcp->tcp_ka_last_intrvl = 0;
tcp->tcp_ka_tid = TCP_TIMER(tcp, tcp_keepalive_timer,
tcp->tcp_ka_interval);
}
tcp_get_proto_props(tcp, &sopp);
(*econnp->conn_upcalls->su_set_proto_props)
(econnp->conn_upper_handle, &sopp);
return (B_TRUE);
}