Systemtap examples, Network - 4 Monitoring TCP Packets

4 minute read

背景

例子来自tcpdumplike.stp脚本, tcp.receive事件触发后, 取出类似tcpdump输出的源ip, 目的ip, 源端口, 目的端口, 以及6tcp包的控制比特位信息.  
tcp.receive alias实际上包含2个内核函数, 分别代表ipv4ipv6.   
kernel.function("tcp_v4_rcv")  
kernel.function("tcp_v6_rcv")!, module("ipv6").function("tcp_v6_rcv")  
// !表示有限匹配kernel.function("tcp_v6_rcv"), 匹配后下面的module就不触发了.  
  
脚本内容以及注解  
[root@db-172-16-3-150 network]# cd /usr/share/systemtap/testsuite/systemtap.examples/network  
[root@db-172-16-3-150 network]# cat tcpdumplike.stp  
#!/usr/bin/stap  
  
// A TCP dump like example  
  
probe begin, timer.s(1) {  
  printf("-----------------------------------------------------------------\n")  
  printf("       Source IP         Dest IP  SPort  DPort  U  A  P  R  S  F \n")  
  printf("-----------------------------------------------------------------\n")  
}  
// stap脚本开始, 并且以后每秒输出一次头信息. 方便阅读.  
  
probe tcp.receive {  
  printf(" %15s %15s  %5d  %5d  %d  %d  %d  %d  %d  %d\n",  
         saddr, daddr, sport, dport, urg, ack, psh, rst, syn, fin)  
}  
// 跟踪tcp.receive事件, 事件出发时, 输出  
// saddr 源IP  
// daddr 目的IP  
// sport 源端口  
// dport 目的端口  
// urg, ack, psh, rst syn, fin 6个tcp包的控制比特位信息  
  
执行输出举例  
[root@db-172-16-3-150 network]# stap ./tcpdumplike.stp   
-----------------------------------------------------------------  
       Source IP         Dest IP  SPort  DPort  U  A  P  R  S  F   
-----------------------------------------------------------------  
     172.16.8.31    172.16.3.150  51167     22  0  1  0  0  0  0  
     172.16.8.31    172.16.3.150  54223     22  0  1  1  0  0  0  
     172.16.8.31    172.16.3.150  54223     22  0  1  1  0  0  0  
     172.16.8.31    172.16.3.150  54223     22  0  1  0  0  0  0  
     172.16.8.31    172.16.3.150  51167     22  0  1  1  0  0  0  
     172.16.3.40    172.16.3.150  51927   9000  0  0  0  0  1  0  
最后一行的A=0, S=1, 表示这个包是从172.16.3.40发过来的建立三次握手的第一个包.  
U=1的话, 表示重要的包, 接收到后不要放到缓冲区, 直接处理.  
  
本文用到的tcp.receive probe alias原型.  
/usr/share/systemtap/tapset/tcp.stp  
/**  
 * probe tcp.receive - Called when a TCP packet is received  
 * @name: Name of the probe point  
 * @iphdr: IP header address  
 * @protocol: Packet protocol from driver  
 * @family: IP address family  
 * @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 = tcp.ipv4.receive, tcp.ipv6.receive  
{  
}  
// tcp.receive包含ipv4和ipv6的alias.  
  
probe tcp.ipv4.receive = kernel.function("tcp_v4_rcv")  
{  
        name = "tcp.ipv4.receive"  
        iphdr = __get_skb_iphdr($skb)  
        # If we're here, by definition we're doing AF_INET, not AF_INET6.  
        family = %{ /* pure */ AF_INET %}  
        saddr = format_ipaddr(__ip_skb_saddr(iphdr), %{ /* pure */ AF_INET %})  
        daddr = format_ipaddr(__ip_skb_daddr(iphdr), %{ /* pure */ AF_INET %})  
        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)  
}  
  
probe tcp.ipv6.receive = kernel.function("tcp_v6_rcv")!,  
        module("ipv6").function("tcp_v6_rcv")  
{  
        name = "tcp.ipv6.receive"  
        iphdr = __get_skb_iphdr(@defined($skb) ? $skb : kernel_pointer($pskb))  
        # If we're here, by definition we're doing AF_INET6, not AF_INET.  
        family = %{ /* pure */ AF_INET6 %}  
        saddr = format_ipaddr(&@cast(iphdr, "ipv6hdr")->saddr,  
                              %{ /* pure */ AF_INET6 %})  
        daddr = format_ipaddr(&@cast(iphdr, "ipv6hdr")->daddr,  
                              %{ /* pure */ AF_INET6 %})  
        # If we're here, by definition we're doing IPPROTO_TCP.  There  
        # isn't a protocol field in 'struct ipv6hdr'.  There is one in  
        # 'struct sk_buff', but that protocol field is an Ethernet  
        # Procol ID (ETH_P_*), not an IP protocol ID (IPPROTO_*).  
        protocol = %{ /* pure */ IPPROTO_TCP %}  
  
        tcphdr = __get_skb_tcphdr(@defined($skb) ? $skb : kernel_pointer($pskb))  
        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)  
}  
// 一些tcp常用的函数  
//  
//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) STAP_ARG_sock;  
        STAP_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<net/ip.h>")->inet->dport))  
}  
// 内嵌了C代码, 为了取出sock的值.  
  
TCP 包头信息  
TCP Header Format  
  
                                      
    0                   1                   2                   3     
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1   
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
   |          Source Port          |       Destination Port        |  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
   |                        Sequence Number                        |  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
   |                    Acknowledgment Number                      |  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
   |  Data |           |U|A|P|R|S|F|                               |  
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |  
   |       |           |G|K|H|T|N|N|                               |  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
   |           Checksum            |         Urgent Pointer        |  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
   |                    Options                    |    Padding    |  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
   |                             data                              |  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
  
                            TCP Header Format  
  
          Note that one tick mark represents one bit position.  
  
                               Figure 3.  
控制比特信息 :   
Control Bits: 6 bits (from left to right):  
  
    URG:  Urgent Pointer field significant  
    ACK:  Acknowledgment field significant  
    PSH:  Push Function  
    RST:  Reset the connection  
    SYN:  Synchronize sequence numbers  
    FIN:  No more data from sender  

参考

1. /usr/share/systemtap/testsuite/systemtap.examples

2. https://sourceware.org/systemtap/SystemTap_Beginners_Guide/useful-systemtap-scripts.html

3. systemtap-testsuite

4. https://sourceware.org/systemtap/examples/

5. /usr/share/systemtap/testsuite/systemtap.examples/index.txt

6. /usr/share/systemtap/testsuite/systemtap.examples/keyword-index.txt

7. /usr/share/systemtap/tapset

8. http://blog.163.com/digoal@126/blog/static/1638770402013811957335/

9. http://www.freesoft.org/CIE/Course/Section4/8.htm

Flag Counter

digoal’s 大量PostgreSQL文章入口