Sindbad~EG File Manager
#!/usr/bin/stap
/*
* Copyright (C) 2009 IBM Corp.
* 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.
*
* Version 1.0 wilder@us.ibm.com 2009-07-06
*
* Name:
* tcpipstat.stp - Per-socket tcpip statistics collection facility.
* (netstat -s on steroids)
*
* Description:
* The purpose of tcpipstat is to collect and display network statistics
* related to individual TCP sockets or groups of sockets. The statistics
* that are collected are simmer to that of the command netstat -s, only
* sorted and grouped by individual sockets. The statistics collected are
* based on the SNMP TCP and IP MIBS. As the name implies this command
* collects data relating to TCP sockets, not UDP sockets. I plan to
* support a UDP tool in a later version. The tcpipstat tool uses
* systemtap to collect data from the linux kernel; see the stap command
* manual page for instructions and options available in systemtap. For
* more information on the systemtap project see:
* http://sourceware.org/systemtap/documentation.html
*
*
* Synopsis:
* tcpstat.stp [index=laddr|raddr|lport|rport|tuple]\
* [timeout=<N sec>]\
* [nozeros=1|0]\
* [filters...]
*
* index (optional) tcpipstat collects, sorts and displays network
* statistics into buckets. The buckets are indexed based on
* one of several index types. By default the index is
* constructed from all four elements of the socket tuple,
* providing a separate bucket for each socket. However, any
* individual element of the tuple can be used as the index.
* Valid values for index are laddr (local address), raddr
* (remote address) , lport, rport or tuple. For example if
* index=lport is specified network statistics will be collected
* and reported based on the value of the local port thus
* providing statistics related to each network service provided
* by the system.
*
* timeout (optional) When a timeout value (in seconds) is specified
* tcpstat will automatically terminate it's run at the end of
* the specified time and produce a report. When timeout is
* omitted the script will run until the user terminates it by
* typing a ^c.
*
* onclose (optional) In onclose mode (onclose=1) statistics are reported
* only when a socket closes. The generated report reflect
* statistics for the entire life on the socket, assuming
* tcpipstat was running at the time the socket was opened.
* The option index=tuple must be set for onclode to work
* (index=tuple is default).
*
* nozeros (optional) When nonzero=1 is specified statistics with
* a value of zero will not be printed.
*
* filters (optional) By default statistics are counted for every socket
* on the system. Address filters can be used to limit what
* sockets data is collected for. All filters are
* pass-through filters. Multiple filters may be given separated
* by a space. When multiple filters are given an event only
* needs to be accepted by one filter to be counted. A wild-card
* (*) value can be used for any component of the filter.
*
* The format of a filter is:
* <local-address>:<local-port>-<remote address>:<remote-port>
*
* Addresses are specified as ipv4 dot notation address. Ports
* are specified as decimal numbers. A "*" character can be used
* in any field to indicate a wild-card value. If no filters are
* given the following wild-card filter is automatically used:
* *.*.*.*:*-*.*.*.*:*
*
* Known Bugs:
* Not all MIB values are currently supported.
* Ipv6 in not yet supported.
*
* Examples:
* Here are some ways that tcpipstat could be used.
*
* List per-socket statistics for every active socket for the
* next 60 seconds.
* $ tcpipstat timeout=60
*
* Who is talking to my webserver?
* $ tcpipstat.stp index=raddr *.*.*.*:80-*.*.*.*:*
*
* What services (on this system) is host 192.168.1.103 using
* $ tcpipstat.stp index=rport *.*.*.*:*-192.168.1.103:*
*
* What hosts in the 9.0.0.0 network is this system communicating
* with?
* $ tcpipstat.stp index=raddr *.*.*.*:*-9.*.*.*:*
*
* What ports are people connecting to? or which services are busy?
* $ tcpipstat.stp index=lport
*
* Show network statistics related to a single run of netperf.
* $ stap -c netperf -H <server-ip>\
* tcpipstat.stp *.*.*.*:*-<server-ip>:*
*
*/
global filter;
global number_of_filters;
global key_list;
global lastkey;
// Command arguments and default values
global index= "tuple";
global timeout = -1;
global nozeros = 1;
global onclose = 0;
probe begin {
number_of_filters=process_cmdline()
if ( index != "laddr" &&
index != "raddr" &&
index != "lport" &&
index != "rport" &&
index != "tuple" ) usage("Invalid index value!");
if ( onclose && (index!="tuple") )
usage("onclose must be used with index=tuple!");
/* The user did not supply a filter, build a wild-card filter */
if ( number_of_filters == 0 ){
number_of_filters++;
j=6;
filter[j] = ipv4_pton("*.*.*.*",0)
filter[j+1] = ipv4_pton("*.*.*.*",1)
filter[j+2] = ipv4_portton("*")
filter[j+3] = ipv4_pton("*.*.*.*",0)
filter[j+4] = ipv4_pton("*.*.*.*",1)
filter[j+5] = ipv4_portton("*")
}
if ( number_of_filters < 0 ){
usage("Bad filter format!");
}
printf("Indexing collected stats using %s values\n",index);
for (i=1; i <= number_of_filters; i++){
print_filter(i);
}
}
/* All command line arguments other than the filters are processed
* first and must be placed on the command line prior to any filters.
*/
function process_cmdline:long ()
{
filter_number=0;
for (i=1; i <= argc; i++){
argument= tokenize(argv[i], "=")
if ( argument == "index" ){
argv[i]=""
index=tokenize(argv[i], "=")
continue;
}
if ( argument == "timeout" ){
argv[i]=""
timeout=strtol(tokenize(argv[i], "="),10)
continue;
}
if ( argument == "nozeros" ){
argv[i]=""
nozeros=strtol(tokenize(argv[i], "="),10)
continue;
}
if ( argument == "onclose" ){
argv[i]=""
onclose=strtol(tokenize(argv[i], "="),10)
continue;
}
/* Example: adding more option flags
if ( argument == "stuff" ){
argv[i]=""
stuff=tokenize(argv[i], "=")
continue;
}
*/
/* Anything on the command line after this point must
* be a filter.
*/
local = tokenize(argv[i], "-")
argv[i] = ""
remote = tokenize(argv[i], "-")
local_addr = tokenize(local, ":")
local=""
local_port = tokenize(local, ":")
remote_addr = tokenize(remote, ":")
remote=""
remote_port = tokenize(remote, ":")
/* stap bug */
if ( remote_port == "fobar") i=i;
++filter_number;
j=filter_number*6;
filter[j] = ipv4_pton(local_addr,0) // Local address
filter[j+1] = ipv4_pton(local_addr,1) // Local address mask
filter[j+2] = ipv4_portton(local_port) // Local port
filter[j+3] = ipv4_pton(remote_addr,0) // Remote address
filter[j+4] = ipv4_pton(remote_addr,1) // Remote address mask
filter[j+5] = ipv4_portton(remote_port) // Remote port
if (filter[j]< -1 ||
filter[j+1] < -1 ||
filter[j+2] < -1 ||
filter[j+3] < -1 ||
filter[j+4] < -1 ||
filter[j+5] < -1 ) return -1;
}
return filter_number;
}
/*
* Convert an ascii integer values between 0 and 65534 to a u16 port number.
* "*" are treated as wildcards and will be converted to 0xffff (65535).
*/
function ipv4_portton:long (port:string)
{
if ( port == "*" ) port="65535";
pport=strtol(port,10);
if ( pport > 0xffff ){
printf("Bad port number %s\n",port)
return -22;
}
return pport
}
/*
* Convert an ipv4 dot notation address into longs.
* Supports "*" in any field treating it as a wildcard by making the byte=0.
* If make_mask is set, it creates a mask based on the "*" fields. All non='*'
* bytes are set to 0xff all * fields are set to 0x0;.
*/
function ipv4_pton:long (addr:string, make_mask:long)
{
i=32;
ip=0;
ips=addr;
while(strlen(byte = tokenize(ips, ".")) != 0) {
i-=8;
ips="";
if ( byte == "*" ){
byte = "0"
} else {
if ( make_mask ) byte = "255";
}
j=strtol(byte,10);
if ( j > 255 ){
printf("bad address %s\n",addr)
return -22;
}
ip=ip+(j<<i) // left shift the byte into the address
}
if ( i != 0 ){
printf("bad address %s\n",addr)
return -22;
}
return ip;
}
function usage (msg:string)
{
printf("\nUsage:\n");
printf("\ttcpipstat.stp\t[index=laddr|raddr|lport|rport|tuple]\n");
printf("\t\t\t[timeout=<sec>] [nozeros=0|1] [onclose=0|1]\n");
printf("\t\t\t[filter ......]\n\n");
printf("\tfilter format:\n");
printf("\t<local ip-address>:<local-port>-<remote ip-address>:<remote-port>\n\n");
error(msg);
}
/* Print filter number n. This is helpful for debugging */
function print_filter (n:long)
{
j=n*6;
printf("Processed filter #%d = %s[0x%x]:%d --> %s[0x%x]:%d \n",
n,ip_ntop(htonl(filter[j])), filter[j+1], filter[j+2],
ip_ntop(htonl(filter[j+3])), filter[j+4], filter[j+5]);
}
/*
* Returns a unique value (stored in the global key_list) based on the socket
* address tuple and the global collection index value. A new value is created
* if one does not already exist.
*/
function build_key:long (laddr:long, raddr:long, lport:long, rport:long)
{
if ( index == "laddr" ) raddr = lport = rport = 0
if ( index == "raddr" ) laddr = lport = rport = 0
if ( index == "lport" ) laddr = raddr = rport = 0
if ( index == "rport" ) laddr = raddr = lport = 0
if ( key_list[laddr, raddr, lport, rport] )
return key_list[laddr, raddr, lport, rport]
else
return key_list[laddr, raddr, lport, rport] = ++lastkey
}
/*
* This is where the real work of the probe filtering is done.
* Important: this function is run for every probe hit so
* keep it small and fast!
*
* If the probe passes through the filters a "key" value is
* returned otherwise it returns zero.
*/
function filter_key:long (laddr:long, raddr:long, lport:long, rport:long)
{
for (i=1; i <= number_of_filters; i++){
j=i*6;
/*
printf("\n %s\n",pp())
print_filter(i);
printf("local=0x%x:0x%x remote=0x%x:0x%x ",
laddr, lport, raddr, rport);
*/
// Local filter
local_filter=remote_filter=0;
if ( (laddr&filter[j+1]) == filter[j] ) {
if ( (filter[j+2] == 0xffff) || (lport == filter[j+2]))
local_filter = 1;
}
// Remote filter
if ( (raddr&filter[j+4]) == filter[j+3] ) {
if ( (filter[j+5] == 0xffff) || (rport == filter[j+5]))
remote_filter = 1;
}
// printf("local=%d remote=%d ",local_filter,remote_filter);
if(local_filter && remote_filter){
// key = build_key(laddr, raddr, lport, rport);
// printf("|Collected: key=%d\n",key);
// return key;
return build_key(laddr, raddr, lport, rport);
}
}
// printf("\n");
return 0;
}
/* This function is called by every ipmib probe handler.
* Returns a "key" value to be used as an index into the collection arrays.
*/
function ipmib_filter_key:long (skb:long, op:long, SourceIsLocal:long)
{
if ( !skb ) return 0;
// We only care about events with protocol IPPROTO_TCP(=6)
if( !(ipmib_get_proto(skb) == 6) ) return 0;
raddr = ipmib_remote_addr(skb, SourceIsLocal);
laddr = ipmib_local_addr(skb, SourceIsLocal);
rport = ipmib_tcp_remote_port(skb, SourceIsLocal);
lport = ipmib_tcp_local_port(skb, SourceIsLocal);
return filter_key(laddr, raddr, lport, rport);
}
/* This function is called by every tcpmib probe handler.
* Returns a "key" value to be used as an index into the collection arrays.
*/
function tcpmib_filter_key:long (sk:long, op:long)
{
if ( !sk ) return 0;
laddr = tcpmib_local_addr(sk);
raddr = tcpmib_remote_addr(sk);
lport = tcpmib_local_port(sk);
rport = tcpmib_remote_port(sk);
return filter_key(laddr, raddr, lport, rport);
}
/* This function is called by every linuxmib probe handler.
* Returns a "key" value to be used as an index into the collection arrays.
* For now this is just the same as the tcpmib_filter_key.
*/
function linuxmib_filter_key:long (sk:long, op:long)
{
return tcpmib_filter_key(sk,op);
}
function print_sockmib (key:long)
{
printf("Socket:\n");
if (SockSendbytes[key]||!nozeros)
printf("\tBytes Sent = %d\n", SockSendbytes[key]);
if (SockSendmsg[key]||!nozeros)
printf("\tMessages sent = %d\n", SockSendmsg[key]);
if (SockRecvbytes[key]||!nozeros)
printf("\tBytes Received = %d\n", SockRecvbytes[key]);
if (SockRecvmsg[key]||!nozeros)
printf("\tMessages Received = %d\n", SockRecvmsg[key]);
}
/*
* Prints the collected values for the IP mib.
*/
function print_ipmib (key:long)
{
printf("Ip:\n");
if (InReceives[key]||!nozeros)
printf("\tInReceives = %d\n", InReceives[key]);
if (OutRequests[key]||!nozeros)
printf("\tOutRequests = %d\n", OutRequests[key]);
if (ReasmTimeout[key]||!nozeros)
printf("\tReasmTimeout = %d\n", ReasmTimeout[key]);
if (ReasmReqds[key]||!nozeros)
printf("\tReasmReqds = %d\n", ReasmReqds[key]);
if (FragOKs[key]||!nozeros)
printf("\tFragOKs = %d\n", FragOKs[key]);
if (FragFails[key]||!nozeros)
printf("\tFragFails = %d\n", FragFails[key]);
}
/*
* Prints the collected values for the TCP mib.
*/
function print_tcpmib (key:long)
{
printf("Tcp:\n");
if (ActiveOpens[key]||!nozeros)
printf("\tActiveOpens = %d\n", ActiveOpens[key]);
%( kernel_v > "2.6.21" %?
if (AttemptFails[key]||!nozeros)
printf("\tAttemptFails = %d\n", AttemptFails[key]);
%)
%( kernel_v > "2.6.24" %?
if (CurrEstab[key]||!nozeros)
printf("\tCurrEstab = %d\n", CurrEstab[key]);
%)
%( kernel_v > "2.6.24" %?
if (EstabResets[key]||!nozeros)
printf("\tEstabResets = %d\n", EstabResets[key]);
%)
// if (InErrs[key]||!nozeros)
// printf("\tInErrs = %d\n", InErrs[key]);
if (InSegs[key]||!nozeros)
printf("\tInSegs = %d\n", InSegs[key]);
if (OutRsts[key]||!nozeros)
printf("\tOutRsts = %d\n", OutRsts[key]);
if (OutSegs[key]||!nozeros)
printf("\tOutSegs = %d\n", OutSegs[key]);
if (PassiveOpens[key]||!nozeros)
printf("\tPassiveOpens = %d\n", PassiveOpens[key]);
if (RetransSegs[key]||!nozeros)
printf("\tRetransSegs = %d\n", RetransSegs[key]);
}
/*
* Prints the collected values for the linux mib.
*/
function print_linuxmib (key:long)
{
printf("TcpExt:\n");
if (DelayedACKs[key]||!nozeros)
printf("\tdelayed acks sent = %d\n", DelayedACKs[key]);
if (ListenOverflows[key]||!nozeros)
printf("\ttimes the listen queue of a socket overflowed = %d\n", ListenOverflows[key]);
if (ListenDrops[key]||!nozeros)
printf("\tSYNs to LISTEN sockets ignored = %d\n", ListenDrops[key]);
if (TCPMemoryPressures[key]||!nozeros)
printf("\ttcp memory pressure = %d\n", TCPMemoryPressures[key]);
}
function report ()
{
number_of_keys=0
foreach ([laddr, raddr, lport, rport] in key_list) {
++number_of_keys;
_report(laddr, raddr, lport, rport);
}
if ( !number_of_keys )
printf("\nNo packets were accepted by the filters.\n");
}
function _report(laddr:long, raddr:long, lport:long, rport:long)
{
printf("\n-----------------------------------------\n");
if ( laddr && raddr && lport && rport ) // index=tuple
printf("%s:%d <-> %s:%d\n",
ip_ntop(htonl(laddr)), lport,
ip_ntop(htonl(raddr)), rport)
else {
if ( laddr )
printf("Local address: %s\n",ip_ntop(htonl(laddr)));
if ( raddr )
printf("Remote address: %s\n",ip_ntop(htonl(raddr)));
if ( lport )
printf("Local port: %d\n",lport);
if ( rport )
printf("Remote port: %d\n",rport);
}
key = key_list[laddr, raddr, lport, rport]
%( kernel_v > "2.6.24" %?
if ( onclose && (CurrEstab[key] < 0) )
printf("Stats were not collected for the entire socket life.\n")
%)
printf("-----------------------------------------\n");
print_sockmib(key)
print_ipmib(key)
print_tcpmib(key)
print_linuxmib(key)
printf("\n\n")
}
/* Terminates the run in timeout seconds, using global timeout value */
probe timer.s(1) {
if ( timeout == -1 ) next
if ( !timeout-- ) exit()
}
/* We are done, print a report and exit. */
probe end {
if ( !onclose )
report()
}
/* Enable the probes for the statictics we want to count.
*
* The impact of running this script on your system can
* be reduced by enabling only the probes corresponding to the
* statictics you are interested in. For example, if all you care about
* is counting the number of incoming connections that are established use:
* "probe tcpmib.PassiveOpens {}"
*/
/* Collect all tcpmib stats */
probe tcpmib.* {}
/* Collect all ip stats */
probe ipmib.* {}
/* Collect the extended linux stats */
probe linuxmib.* {}
/* This probe supports the onclose option. When a connection closes
* CurrEstab will return an op of -1, we use that condition to trigger
* the dump off all stats for this socket. This feature only makes sense
* when index=tuple.
*/
%( kernel_v > "2.6.24" %?
probe tcpmib.CurrEstab
{
if (!onclose) next
if (!key) next
if ( op != -1 ) next;
if ( !sk ) next;
laddr = tcpmib_local_addr(sk);
raddr = tcpmib_remote_addr(sk);
lport = tcpmib_local_port(sk);
rport = tcpmib_remote_port(sk);
_report(laddr, raddr, lport, rport)
delete key_list[laddr, raddr, lport, rport]
delete CurrEstab[key]
}
%)
/* SNMP has no counter for the number of bytes sent or received by TCP.
* The next two probes give us these raw byte counts.
*/
global SockSendbytes;
global SockSendmsg;
probe tcp.sendmsg.return {
sk = @defined(@entry($sk)) ? @entry($sk) : @entry($sock->sk);
op = size; // $return
if ( op <= 0 ) next;
if ( !sk ) next;
key = tcpmib_filter_key(sk, op);
if ( key ) {
SockSendbytes[key] += op;
++SockSendmsg[key];
}
}
global SockRecvbytes;
global SockRecvmsg;
probe tcp.recvmsg.return{
sk = @entry($sk)
op = size // $return
if ( op <= 0 ) next;
if ( !sk ) next;
key = tcpmib_filter_key(sk, op);
if ( key ) {
SockRecvbytes[key] += op;
++SockRecvmsg[key];
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists