// TCP tapset
// Copyright (C) 2006 IBM Corp.
// Copyright (C) 2006 Intel Corporation.
// Copyright (C) 2007,2010 Red Hat, Inc.
//
// This file is part of systemtap, and is free software. You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.
//
// This family of probe points is used to probe events that occur in the TCP layer,
//
%{
#include
#include
#include
#include
#include
%}
// Get retransmission timeout in usecs. RTO is initialized from default
// retransmission time, but can be adjusted (increased) each time we
// retransmit. It should always be less than the max value of TCP retransmission
// timeout (TCP_RTO_MAX)
function tcp_get_info_rto:long(sock:long)
%{ /* pure */
struct sock *sk = (struct sock *)(long) THIS->sock;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
struct tcp_opt *tp = tcp_sk(sk);
THIS->__retvalue = (int64_t) jiffies_to_usecs(kread(&(tp->rto)));
#else
const struct inet_connection_sock *icsk = inet_csk(sk);
THIS->__retvalue = (int64_t) jiffies_to_usecs(kread(&(icsk->icsk_rto)));
#endif
CATCH_DEREF_FAULT();
%}
//Get congestion window segment size. Initial value of congestion window size
//typically set to one segment (i.e., slow start algorithm, each segment can be 512 bytes).
//This congestion window size can be dynamically increased based on whether TCP
//is performing slow start or congestion avoidance.
function tcp_get_info_snd_cwnd:long(sock:long)
%{ /* pure */
struct sock *sk = (struct sock *)(long) THIS->sock;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
struct tcp_opt *tp = tcp_sk(sk);
#else
struct tcp_sock *tp = tcp_sk(sk);
#endif
THIS->__retvalue = (int64_t) kread(&(tp->snd_cwnd));
CATCH_DEREF_FAULT();
%}
//
//Definitions of the TCP protocol sk_state field listed below.
//
// TCP_ESTABLISHED = 1, Normal data transfer
// TCP_SYN_SENT = 2, App. has started to open a connection
// TCP_SYN_RECV = 3, A connection request has arrived; wait for ACK
// TCP_FIN_WAIT1 = 4, App. has said it is finished
// TCP_FIN_WAIT2 = 5, The other side has agreed to close
// TCP_TIME_WAIT = 6, Wait for all packets to die off
// TCP_CLOSE = 7, No connection is active or pending
// TCP_CLOSE_WAIT = 8, The other side has initiated a release
// TCP_LAST_ACK = 9, Last ACK, wait for all packets to die off
// TCP_LISTEN = 10, Waiting for incoming call
// TCP_CLOSING = 11, Both sides have tried to close simultaneously
// TCP_MAX_STATES = 12 Max states number
//
function tcp_ts_get_info_state:long(sock:long)
%{ /* pure */
struct sock *sk = (struct sock *)(long) THIS->sock;
THIS->__retvalue = (int64_t) kread(&(sk->sk_state));
CATCH_DEREF_FAULT();
%}
/* return the TCP destination port for a given sock */
function __tcp_sock_dport:long (sock:long)
{
return (@defined(@cast(sock, "inet_sock")->inet_dport)
? @cast(sock, "inet_sock")->inet_dport # kernel >= 2.6.33
: (@defined(@cast(sock, "inet_sock")->dport)
? @cast(sock, "inet_sock", "kernel")->dport # kernel >= 2.6.11
: @cast(sock, "inet_sock", "kernel")->inet->dport))
}
/* returns the TCP header for recent (<2.6.21) kernel */
function __get_skb_tcphdr_new:long(skb:long)
%{ /* pure */
struct sk_buff *skb;
skb = (struct sk_buff *)(long)THIS->skb;
/* as done by skb_transport_header() */
#ifdef NET_SKBUFF_DATA_USES_OFFSET
THIS->__retvalue = (long)(kread(&(skb->head)) + kread(&(skb->transport_header)));
#else
THIS->__retvalue = (long)kread(&(skb->transport_header));
#endif
CATCH_DEREF_FAULT();
%}
/* returns the TCP header for a given sk_buff structure */
function __get_skb_tcphdr:long(skb:long)
{
%( kernel_v < "2.6.21" %?
tcphdr = @cast(skb, "sk_buff")->h->raw
return tcphdr
%:
return __get_skb_tcphdr_new(skb)
%)
}
/* returns TCP URG flag for a given sk_buff structure */
function __tcp_skb_urg:long (tcphdr:long)
{
return @cast(tcphdr, "tcphdr")->urg
}
/* returns TCP ACK flag for a given sk_buff structure */
function __tcp_skb_ack:long (tcphdr:long)
{
return @cast(tcphdr, "tcphdr")->ack
}
/* returns TCP PSH flag for a given sk_buff structure */
function __tcp_skb_psh:long (tcphdr:long)
{
return @cast(tcphdr, "tcphdr")->psh
}
/* returns TCP RST flag for a given sk_buff structure */
function __tcp_skb_rst:long (tcphdr:long)
{
return @cast(tcphdr, "tcphdr")->rst
}
/* returns TCP SYN flag for a given sk_buff structure */
function __tcp_skb_syn:long (tcphdr:long)
{
return @cast(tcphdr, "tcphdr")->syn
}
/* returns TCP FIN flag for a given sk_buff structure */
function __tcp_skb_fin:long (tcphdr:long)
{
return @cast(tcphdr, "tcphdr")->fin
}
/* returns TCP source port for a given sk_buff structure */
function __tcp_skb_sport:long (tcphdr:long)
{
return ntohs(@cast(tcphdr, "tcphdr")->source)
}
/* returns TCP destination port for a given sk_buff structure */
function __tcp_skb_dport:long (tcphdr:long){
return @cast(tcphdr, "tcphdr")->dest
}
/* return the TCP source port for a given sock */
function __tcp_sock_sport:long (sock:long)
{
return (@defined(@cast(sock, "inet_sock")->inet_sport)
? @cast(sock, "inet_sock")->inet_sport # kernel >= 2.6.33
: (@defined(@cast(sock, "inet_sock")->sport)
? @cast(sock, "inet_sock", "kernel")->sport # kernel >= 2.6.11
: @cast(sock, "inet_sock", "kernel")->inet->sport))
}
global sockstate[13], sockstate_init_p
function tcp_sockstate_str:string (state:long) {
if (! sockstate_init_p) {
sockstate_init_p = 1
sockstate[1] = "TCP_ESTABLISHED"
sockstate[2] = "TCP_SYN_SENT"
sockstate[3] = "TCP_SYN_RECV"
sockstate[4] = "TCP_FIN_WAIT1"
sockstate[5] = "TCP_FIN_WAIT2"
sockstate[6] = "TCP_TIME_WAIT"
sockstate[7] = "TCP_CLOSE"
sockstate[8] = "TCP_CLOSE_WAIT"
sockstate[9] = "TCP_LAST_ACK"
sockstate[10] = "TCP_LISTEN"
sockstate[11] = "TCP_CLOSING"
sockstate[12] = "TCP_MAX_STATES"
}
return (state in sockstate ? sockstate[state] : "UNDEF")
}
// Get slow start threshold size. If cwnd size is less than or equal to
// threshold size, then TCP is in slow start; otherwise TCP is in congestion
// avoidance.
function tcp_ts_get_info_snd_ssthresh:long(sock:long)
%{ /* pure */
struct sock *sk = (struct sock *)(long) THIS->sock;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
struct tcp_opt *tp = tcp_sk(sk);
#else
struct tcp_sock *tp = tcp_sk(sk);
#endif
THIS->__retvalue = (int64_t) kread(&(tp->snd_ssthresh));
CATCH_DEREF_FAULT();
%}
// Get receiver's advertised segment size. TCP typically never sends more
// than what receiver can accept.
function tcp_ts_get_info_rcv_mss:long(sock:long)
%{ /* pure */
struct sock *sk = (struct sock *)(long) THIS->sock;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
struct tcp_opt *tp = tcp_sk(sk);
THIS->__retvalue = (int64_t) kread(&(tp->ack.rcv_mss));
#else
const struct inet_connection_sock *icsk = inet_csk(sk);
THIS->__retvalue = (int64_t) kread(&(icsk->icsk_ack.rcv_mss));
#endif
CATCH_DEREF_FAULT();
%}
global sockopt[15], sockopt_init_p
function tcp_sockopt_str:string (optname:long) {
if (!sockopt_init_p) {
sockopt_init_p=1
sockopt[1] = "TCP_NODELAY"
sockopt[2] = "TCP_MAXSEG"
sockopt[3] = "TCP_CORK"
sockopt[4] = "TCP_KEEPIDLE"
sockopt[5] = "TCP_KEEPINTVL"
sockopt[6] = "TCP_KEEPCNT"
sockopt[7] = "TCP_SYNCNT"
sockopt[8] = "TCP_LINGER2"
sockopt[9] = "TCP_DEFER_ACCEPT"
sockopt[10] = "TCP_WINDOW_CLAMP"
sockopt[11] = "TCP_INFO"
sockopt[12] = "TCP_QUICKACK"
sockopt[13] = "TCP_CONGESTION"
sockopt[14] = "TCP_MD5SIG"
}
return (optname in sockopt ? sockopt[optname] : "UNDEF")
}
/**
* probe tcp.sendmsg - Sending a tcp message
* @name: Name of this probe
* @sock: Network socket
* @size: Number of bytes to send
*
* Context:
* The process which sends a tcp message
*/
probe tcp.sendmsg = kernel.function("tcp_sendmsg") {
name = "tcp.sendmsg"
%( kernel_v < "2.6.23" %?
sock = $sk
%:
sock = $sock
%)
size = $size
}
/**
* probe tcp.sendmsg.return - Sending TCP message is done
* @name: Name of this probe
* @size: Number of bytes sent or error code if an error occurred.
*
* Context:
* The process which sends a tcp message
*/
probe tcp.sendmsg.return = kernel.function("tcp_sendmsg").return {
name = "tcp.sendmsg"
size = $return
}
/**
* probe tcp.recvmsg - Receiving TCP message
* @name: Name of this probe
* @sock: Network socket
* @size: Number of bytes to be received
* @saddr: A string representing the source IP address
* @daddr: A string representing the destination IP address
* @sport: TCP source port
* @dport: TCP destination port
* Context:
* The process which receives a tcp message
*/
probe tcp.recvmsg = kernel.function("tcp_recvmsg") {
name = "tcp.recvmsg"
sock = $sk
size = $len
saddr = ip_ntop(__ip_sock_saddr($sk))
daddr = ip_ntop(__ip_sock_daddr($sk))
sport = __tcp_sock_sport($sk)
dport = __tcp_sock_dport($sk)
}
/**
* probe tcp.recvmsg.return - Receiving TCP message complete
* @name: Name of this probe
* @size: Number of bytes received or error code if an error occurred.
* @saddr: A string representing the source IP address
* @daddr: A string representing the destination IP address
* @sport: TCP source port
* @dport: TCP destination port
*
* Context:
* The process which receives a tcp message
*/
probe tcp.recvmsg.return = kernel.function("tcp_recvmsg").return {
name = "tcp.recvmsg"
size = $return
saddr = ip_ntop(__ip_sock_saddr($sk))
daddr = ip_ntop(__ip_sock_daddr($sk))
sport = __tcp_sock_sport($sk)
dport = __tcp_sock_dport($sk)
}
/**
* probe tcp.disconnect - TCP socket disconnection
* @name: Name of this probe
* @sock: Network socket
* @flags: TCP flags (e.g. FIN, etc)
* @saddr: A string representing the source IP address
* @daddr: A string representing the destination IP address
* @sport: TCP source port
* @dport: TCP destination port
*
* Context:
* The process which disconnects tcp
*/
probe tcp.disconnect = kernel.function("tcp_disconnect") {
name = "tcp.disconnect"
sock = $sk
flags = $flags
saddr = ip_ntop(__ip_sock_saddr($sk))
daddr = ip_ntop(__ip_sock_daddr($sk))
sport = __tcp_sock_sport($sk)
dport = __tcp_sock_dport($sk)
}
/**
* probe tcp.disconnect.return - TCP socket disconnection complete
* @name: Name of this probe
* @ret: Error code (0: no error)
*
* Context:
* The process which disconnects tcp
*/
probe tcp.disconnect.return = kernel.function("tcp_disconnect").return {
name = "tcp.disconnect"
ret = $return
}
/**
* probe tcp.setsockopt - Call to setsockopt()
* @name: Name of this probe
* @sock: Network socket
* @level: The level at which the socket options will be manipulated
* @optname: TCP socket options (e.g. TCP_NODELAY, TCP_MAXSEG, etc)
* @optstr: Resolves optname to a human-readable format
* @optlen: Used to access values for setsockopt()
*
* Context:
* The process which calls setsockopt
*/
probe tcp.setsockopt = kernel.function("tcp_setsockopt") {
name = "tcp.setsockopt"
sock = $sk
level = $level
optname = $optname
optstr = tcp_sockopt_str($optname)
optlen = $optlen
}
/**
* probe tcp.setsockopt.return - Return from setsockopt()
* @name: Name of this probe
* @ret: Error code (0: no error)
*
* Context:
* The process which calls setsockopt
*/
probe tcp.setsockopt.return = kernel.function("tcp_setsockopt").return {
name = "tcp.setsockopt"
ret = $return
}
/**
* probe tcp.receive - Called when a TCP packet is received
* @saddr: A string representing the source IP address
* @daddr: A string representing the destination IP address
* @sport: TCP source port
* @dport: TCP destination port
* @urg: TCP URG flag
* @ack: TCP ACK flag
* @psh: TCP PSH flag
* @rst: TCP RST flag
* @syn: TCP SYN flag
* @fin: TCP FIN flag
*/
probe tcp.receive = kernel.function("tcp_v4_rcv") {
iphdr = __get_skb_iphdr($skb)
saddr = ip_ntop(__ip_skb_saddr(iphdr))
daddr = ip_ntop(__ip_skb_daddr(iphdr))
protocol = __ip_skb_proto(iphdr)
tcphdr = __get_skb_tcphdr($skb)
dport = __tcp_skb_dport(tcphdr)
sport = __tcp_skb_sport(tcphdr)
urg = __tcp_skb_urg(tcphdr)
ack = __tcp_skb_ack(tcphdr)
psh = __tcp_skb_psh(tcphdr)
rst = __tcp_skb_rst(tcphdr)
syn = __tcp_skb_syn(tcphdr)
fin = __tcp_skb_fin(tcphdr)
}