Systemtap examples, Network - 4 Monitoring TCP Packets
背景
例子来自tcpdumplike.stp脚本, 当tcp.receive事件触发后, 取出类似tcpdump输出的源ip, 目的ip, 源端口, 目的端口, 以及6个tcp包的控制比特位信息.
tcp.receive alias实际上包含2个内核函数, 分别代表ipv4和ipv6.
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