#!/usr/sbin/dtrace -Cqs
/*
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma D option dynvarsize=64m

#define TH_RST		0x04
#define MAX_RECORDS	10
#define M_CTL		0x0d

#define PRINT_MAIN_HEADER()						\
	(printf("\n%-25s %-6s %-25s %-6s %-10s %-10s %8s %8s\n",	\
	    "LADDR", "LPORT", "RADDR", "RPORT", "ISS", "IRS",		\
	    "SND_CNT", "RCV_CNT"))

#define PRINT_RECORD_HEADER()						\
	(printf("%-20s %-20s %-3s %15s %15s %8s %8s %5s\n",		\
	    "PROBENAME", "TIME", "S/R", "SEQ", "ACK", "DATALEN",	\
	    "WND", "FLAGS"))

#define PRINT_MAIN_HEADER_VALUES()					\
	(printf("%-25s %-6d %-25s %-6d %-10d %-10d %8d %8d\n",		\
	    laddr[self->conn_id], lport[self->conn_id],			\
	    faddr[self->conn_id], fport[self->conn_id],			\
	    iss[self->conn_id], irs[self->conn_id],			\
	    send_count[self->conn_id], recv_count[self->conn_id]))

#define PRINT_HEADER()							\
	PRINT_MAIN_HEADER(); PRINT_MAIN_HEADER_VALUES();		\
	    PRINT_RECORD_HEADER()

#define PRINT_RECORD(i)							\
	(printf("%-20s %-20Y %-3s %15d %15d %8d %8d %2x\n",		\
	    probe_name[self->conn_id, i],				\
	    conn_time[self->conn_id, i],				\
	    send_recv[self->conn_id, i],				\
	    seqno[self->conn_id, i],					\
	    ack[self->conn_id, i],					\
	    datalen[self->conn_id, i],					\
	    wnd[self->conn_id, i],					\
	    flags[self->conn_id, i]))

tcp-trace-*
{
	/* extract connection details */

	this->mp = (mblk_t *)arg0;
	this->mp = (this->mp->b_datap->db_type == M_CTL?
	    this->mp->b_cont : this->mp);
	self->tcpp = (tcp_t *)arg1;
	this->connp = (conn_t *)self->tcpp->tcp_connp;

	self->iph = (ipha_t *)this->mp->b_rptr;
	this->iph_length =
	    (int)(((ipha_t *)self->iph)->ipha_version_and_hdr_length
	    & 0xF) << 2;
	self->tcph = (tcpha_t *)((char *)self->iph + this->iph_length);
	this->tcph_length =
	    (((tcph_t *)self->tcph)->th_offset_and_rsrvd[0] >>2) &(0xF << 2);

	/* ports */
	self->i_lport = ntohs(this->connp->u_port.tcpu_ports.tcpu_lport);
	self->i_fport = ntohs(this->connp->u_port.tcpu_ports.tcpu_fport);

	/* IP addresses */
	this->i_fad = (in6_addr_t *)&this->connp->connua_v6addr.connua_faddr;
	this->i_lad = (in6_addr_t *)&this->connp->connua_v6addr.connua_laddr;

	/* the address would either be IPv6 or IPv4-mapped-IPv6  */
	self->i_faddr = inet_ntop(AF_INET6, (void *)this->i_fad);
	self->i_laddr = inet_ntop(AF_INET6, (void *)this->i_lad);

	/* create connection identifier, so we can track packets by conn */
	self->conn_id = (uint64_t)self->tcpp->tcp_connp;
}

tcp-trace-*
/first[self->conn_id] == 0/
{
	/* initialize counters - this is the first packet for this connection */
	pcount[self->conn_id] = -1;
	rollover[self->conn_id] = 0;
	end_ptr[self->conn_id] = 0;
	num[self->conn_id] = 0;

	first[self->conn_id] = 1;

	/* connection info */
	laddr[self->conn_id] = self->i_laddr;
	faddr[self->conn_id] = self->i_faddr;
	lport[self->conn_id] = self->i_lport;
	fport[self->conn_id] = self->i_fport;
	iss[self->conn_id] = self->tcpp->tcp_iss;
	irs[self->conn_id] = self->tcpp->tcp_irs;

}

tcp-trace-*
{
	/* counters, to keep track of how much info to dump */
	pcount[self->conn_id]++;
	rollover[self->conn_id] |= pcount[self->conn_id]/MAX_RECORDS;
	pcount[self->conn_id] = pcount[self->conn_id]%MAX_RECORDS;
	self->pcount = pcount[self->conn_id];
	end_ptr[self->conn_id] = self->pcount;
	num[self->conn_id] = (rollover[self->conn_id]?
	    MAX_RECORDS : pcount[self->conn_id] + 1);
	conn_time[self->conn_id, self->pcount] = walltimestamp;

	/* tcp state info */
	seqno[self->conn_id, self->pcount] = ntohl(self->tcph->tha_seq);
	ack[self->conn_id, self->pcount] = ntohl(self->tcph->tha_ack);
	datalen[self->conn_id, self->pcount] =  ntohs(self->iph->ipha_length);
	wnd[self->conn_id, self->pcount] =  ntohs(self->tcph->tha_win);
	probe_name[self->conn_id, self->pcount] = probename;

	/* flag 0x04 indicates a RST packet */
	flags[self->conn_id, self->pcount] = self->tcph->tha_flags;
	self->flags = self->tcph->tha_flags;
}

tcp-trace-send
{
	send_count[self->conn_id]++;
	send_recv[self->conn_id, self->pcount] = "S";
}

tcp-trace-recv
{
	recv_count[self->conn_id]++;
	send_recv[self->conn_id, self->pcount] = "R";
}

tcp-trace-*
/(self->flags & TH_RST)/
{
	PRINT_HEADER();

	self->i = (end_ptr[self->conn_id] + MAX_RECORDS - num[self->conn_id]
	    + 1)%MAX_RECORDS;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 10)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 9)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 8)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 7)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 6)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 5)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 4)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 3)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 2)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
}

tcp-trace-*
/(self->flags & TH_RST) && (num[self->conn_id] >= 1)/
{
	PRINT_RECORD(self->i);
	self->i = (self->i + 1)%MAX_RECORDS;

	num[self->conn_id]--;
	self->reset = self->conn_id;
}

tcp-trace-*
/self->reset/
{
	pcount[self->reset] = -1;
	rollover[self->reset] = 0;
	end_ptr[self->reset] = 0;
	num[self->reset] = 0;

	self->reset = 0;
}

conn-destroy
{
	/* clear old connection state */
	this->conn_id = (uint64_t)arg0;

	pcount[this->conn_id] = -1;
	rollover[this->conn_id] = 0;
	end_ptr[this->conn_id] = 0;
	num[this->conn_id] = 0;
	first[this->conn_id] = 0;

	laddr[this->conn_id] = 0;
	faddr[this->conn_id] = 0;
	lport[this->conn_id] = 0;
	fport[this->conn_id] = 0;
	iss[this->conn_id] = 0;
	irs[this->conn_id] = 0;
}