/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */

#pragma D depends_on module unix
#pragma D depends_on provider dccp

inline int32_t DCCP_STATE_CLOSED = @DCCPS_CLOSED@;
#pragma D binding "1.10" DCCP_STATE_CLOSED
inline int32_t DCCP_STATE_BOUND = @DCCPS_BOUND@;
#pragma D binding "1.10" DCCP_STATE_BOUND
inline int32_t DCCP_STATE_REQUEST = @DCCPS_REQUEST@;
#pragma D binding "1.10" DCCP_STATE_REQUEST
inline int32_t DCCP_STATE_LISTEN = @DCCPS_LISTEN@;
#pragma D binding "1.10" DCCP_STATE_LISTEN
inline int32_t DCCP_STATE_PARTOPEN = @DCCPS_PARTOPEN@;
#pragma D binding "1.10" DCCP_STATE_PARTOPEN
inline int32_t DCCP_STATE_RESPOND = @DCCPS_RESPOND@;
#pragma D binding "1.10" DCCP_STATE_RESPOND
inline int32_t DCCP_STATE_OPEN = @DCCPS_OPEN@;
#pragma D binding "1.10" DCCP_STATE_OPEN
inline int32_t DCCP_STATE_CLOSING = @DCCPS_CLOSING@;
#pragma D binding "1.10" DCCP_STATE_CLOSING
inline int32_t DCCP_STATE_CLOSEREQ = @DCCPS_CLOSEREQ@;
#pragma D binding "1.10" DCCP_STATE_CLOSEREQ
inline int32_t DCCP_STATE_TIMEWAIT = @DCCPS_TIMEWAIT@;
#pragma D binding "1.10" DCCP_STATE_TIMEWAIT

/*
 * Convert a DCCP state value to a string.
 */
inline string dccp_state_string[int32_t state] =
	state == DCCP_STATE_CLOSED ? "state-closed" :
	state == DCCP_STATE_BOUND ? "state-bound" :
	state == DCCP_STATE_REQUEST ? "state-request" :
	state == DCCP_STATE_LISTEN ? "state-listen" :
	state == DCCP_STATE_PARTOPEN ? "state-partopen" :
	state == DCCP_STATE_RESPOND ? "state-respond" :
	state == DCCP_STATE_OPEN ? "state-open" :
	state == DCCP_STATE_CLOSING ? "state-closing" :
	state == DCCP_STATE_CLOSEREQ ? "state-closereq" :
	state == DCCP_STATE_TIMEWAIT ? "state-timewait" :
	"<unknown>";
#pragma D binding "1.10" dccp_state_string

/*
 * dccpinfo is the DCCP header fields.
 */
typedef struct dccpinfo {
	uint16_t dccp_sport;		/* source port */
	uint16_t dccp_dport;		/* destination port */
	uint32_t dccp_seq;		/* sequence number */
	uint8_t dccp_offset;		/* data offset, in bytes */
	uint16_t dccp_checksum;		/* checksum */
	dccph_t *dccp_hdr;		/* raw DCCP header */
} dccpinfo_t;

/*
 * dccpsinfo contains stable DCCP details from dccp_t.
 */
typedef struct dccpsinfo {
	uintptr_t dccps_addr;
	int dccps_local;		/* Is delivered locally, boolean */
	int dccps_active;		/* Active open (from here), boolean */
	uint16_t dccps_lport;		/* Local port */
	uint16_t dccps_rport;		/* Remote port */
	string dccps_laddr;		/* Local address, as a string */
	string dccps_raddr;		/* Remote address, as a string */
	int32_t dccps_state;		/* DCCP state */
	uint32_t dccps_iss;		/* Initial sequence # sent */
} dccpsinfo_t;

/*
 * dccplsinfo provides the old dccp state for state changes.
 */
typedef struct dccplsinfo {
	int32_t dccps_state;		/* Previous DCCP state */
} dccplsinfo_t;

/*
 * __dtrace_tcp_tcph_t is used by the tcpinfo_t * translator to take either
 * the non-NULL tcph_t * passed in or, if it is NULL, uses arg3 (tcp_t *)
 * from the tcp:::send and tcp:::recieve probes and translates the tcp_t *
 * into the tcpinfo_t.  When no headers are available - as is the case for
 * TCP fusion tcp:::send and tcp:::receive - this allows us to present the
 * consumer with header data based on tcp_t * content and hide TCP fusion
 * implementation details.
 */
typedef dccph_t * __dtrace_dccp_dccph_t;

#pragma D binding "1.10" translator
translator dccpinfo_t < dccph_t *T > {
	dccp_sport = ntohs(*(uint16_t *)T->dh_lport);
	dccp_dport = ntohs(*(uint16_t *)T->dh_fport);
	dccp_seq = ntohl(*(uint32_t *)T->dh_seq); /* XXX */
	dccp_offset = (*(uint8_t *)T->dh_offset & 0xf0) >> 2;
	dccp_checksum = ntohs(*(uint16_t *)T->dh_sum);
	dccp_hdr = T;
};

#pragma D binding "1.10" translator
translator dccpinfo_t < __dtrace_dccp_dccph_t *T > {
	dccp_sport =
	    T != NULL ? ntohs(*(uint16_t *)((dccph_t *)T)->dh_lport) :
	    arg3 != NULL && probename == "send" ?
	    ntohs(((dccp_t *)arg3)->dccp_connp->u_port.connu_ports.connu_lport) :
	    arg3 != NULL && probename == "receive" ?
	    ntohs(((dccp_t *)arg3)->dccp_connp->u_port.connu_ports.connu_fport) :
	    0;
	dccp_dport =
	    T != NULL ? ntohs(*(uint16_t *)((dccph_t *)T)->dh_fport) :
	    arg3 != NULL && probename == "send" ?
	    ntohs(((dccp_t *)arg3)->dccp_connp->u_port.connu_ports.connu_fport) :
	    arg3 != NULL && probename == "receive" ?
	    ntohs(((dccp_t *)arg3)->dccp_connp->u_port.connu_ports.connu_lport) :
	    0;
	dccp_seq =
	    T != NULL ? ntohl(*(uint32_t *)((dccph_t *)T)->dh_seq) :
	    0;
	dccp_offset = T != NULL ?
	    (*(uint8_t *)((dccph_t *)T)->dh_offset & 0xf0) >> 2 :
	    @DCCP_MIN_HEADER_LENGTH@;
	dccp_checksum = T != NULL ? ntohs(*(uint16_t *)((dccph_t *)T)->dh_sum) :
	    0;
	dccp_hdr = NULL;
};

#pragma D binding "1.10" translator
translator dccpsinfo_t < dccp_t *T > {
	dccps_addr = (uintptr_t)T;
	/*
	 * The following two members should just use tcp_t->tcp_loopback
	 * and tcp_t->tcp_active_open, however these are bit fields and
	 * can't be used until CR 6876830 is fixed.  Meanwhile we source
	 * them a different way.
	 */
	dccps_local = T ? T->dccp_ipha ?
	    T->dccp_ipha->ipha_src == T->dccp_ipha->ipha_dst : 1 : 0;
	dccps_active = T ? !T->dccp_saved_listener : 0;
	dccps_lport = T ?
	    ntohs(T->dccp_connp->u_port.connu_ports.connu_lport) : 0;
	dccps_rport = T ?
	    ntohs(T->dccp_connp->u_port.connu_ports.connu_fport) : 0;
	dccps_laddr = T ?
	    inet_ntoa6(&T->dccp_connp->connua_v6addr.connua_laddr) : "<unknown>";
	dccps_raddr = T ?
	    inet_ntoa6(&T->dccp_connp->connua_v6addr.connua_faddr) : "<unknown>";
	dccps_state = T ? T->dccp_state : DCCP_STATE_CLOSED;
	dccps_iss = T ? T->dccp_iss : 0;
};

/*
 * Note: although we specify that the old state argument used as the
 * input to the tcplsinfo_t translator is an int32_t, it reaches us as an
 * int64_t (since it is a probe argument) so explicitly cast it back to
 * interpret the negatively-valued states correctly.
 */
#pragma D binding "1.10" translator
translator dccplsinfo_t < int64_t I > {
	dccps_state = (int32_t) I;
};
