1、安装kernel debug包,(kernel-debuginfo、 kernel-debuginfo-common、kernel-devel http://debuginfo.centos.org/7/x86_64/)
2、安装systemtap yum install systemtap
3、检查systemtap是否安装成功
stap -ve 'probe kernel.function("do_fork") { print("hello world\n") exit() }'
4、执行脚本
stap -vg xx.stp
常用:
1、print_backtrace 打印调用栈
2、$var 打印变量var
3、$$params 打印所有入参
4、打印返回值 $$return
内部变量名 功能 其他
$$locals 探测点上所有的本地变量(含参数) n/a
$$params 探测点上函数的参数 n/a
$$returns 探测点上所有的返回值 只对return probe生效
systemtap自定义stap:
/usr/share/systemtap/tapset/
例子一:
@__private30 function __mac_addr_to_string:string(addr:long) {
return sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
kernel_char(addr)&255, kernel_char(addr+1)&255,
kernel_char(addr+2)&255, kernel_char(addr+3)&255,
kernel_char(addr+4)&255, kernel_char(addr+5)&255)
}
probe module("i40e").function("i40e_cleanup_headers") {
dst_mac = __mac_addr_to_string(@cast($skb->data, "ethhdr", "kernel<include/linux/if_ether.h>")->h_dest)
src_mac = __mac_addr_to_string(@cast($skb->data, "ethhdr", "kernel<include/linux/if_ether.h>")->h_source)
if (dst_mac == "fa:16:3e:6a:1b:10")
printf("i40e_cleanup_headers dst_mac: %s, src_mac: %s\n", dst_mac, src_mac)
}
probe kernel.function("validate_xmit_skb") {
family = @const("AF_INET")
try {
iphdr = __get_skb_iphdr($skb)
saddr = format_ipaddr(__ip_skb_saddr(iphdr), @const("AF_INET"))
daddr = format_ipaddr(__ip_skb_daddr(iphdr), @const("AF_INET"))
protocol = __ip_skb_proto(iphdr)
} catch { }
try {
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)
check = __tcp_skb_check(tcphdr)
} catch { }
if (saddr == "172.20.30.35") {
printf("--- validate_xmit_skb saddr:%s, daddr:%s, gso_size: %d, ip_summed: %d, check: %ld \n", saddr, daddr, @cast($skb->head + $skb->end, "skb_shared_info", "kernel<include/linux/skbuff.h>")->gso_size, $skb->ip_summed, check)
}
probe kernel.function("skb_csum_hwoffload_help") {
family = @const("AF_INET")
try {
iphdr = __get_skb_iphdr($skb)
saddr = format_ipaddr(__ip_skb_saddr(iphdr), @const("AF_INET"))
daddr = format_ipaddr(__ip_skb_daddr(iphdr), @const("AF_INET"))
protocol = __ip_skb_proto(iphdr)
} catch { }
if (saddr == "172.20.30.35")
printf("---- skb_csum_hwoffload_help saddr:%s, daddr:%s ,csum_not_inet %d \n", saddr, daddr, $skb->csum_not_inet)
}
例子二:
利用systemtap简单实现数据包的snat;
安装以下脚本,然后通过ping -I 172.20.21.3 175.43.23.53验证连通性。
#!/usr/bin/stap -g
%{
#include <linux/tcp.h>
#include <linux/ip.h>
%}
function replace_out_src_ip(skb:long)
%{
struct sk_buff *tmp_skb = (struct sk_buff *)STAP_ARG_skb;
struct iphdr *iph = ip_hdr(tmp_skb);
__be32 old_saddr = iph->saddr;
//源ip是内网ip:0x031514ac(172.20.21.3), 则将源ip替换成公网ip:0x34172baf(175.43.23.52)
if (iph->saddr == 0x031514ac) {
iph->saddr = 0x34172baf;
inet_proto_csum_replace4(&iph->check, tmp_skb, old_saddr, iph->saddr, true);
}
%}
function replace_rcv_src_ip(skb:long)
%{
struct sk_buff *tmp_skb = (struct sk_buff *)STAP_ARG_skb;
struct iphdr *iph = ip_hdr(tmp_skb);
__be32 old_daddr = iph->daddr;
//源ip是0x35172baf(175.43.23.53),则将目的ip重新修改成内网地址0x031514ac(172.20.21.3)
if (iph->saddr == 0x35172baf) {
iph->daddr = 0x031514ac;
inet_proto_csum_replace4(&iph->check, tmp_skb, old_daddr, iph->daddr, false);
printk("ip rcv ip_src: %d, ip_dst: %d, type: %d, ip_summed: %d\n", iph->saddr, iph->daddr, tmp_skb->pkt_type, tmp_skb->ip_summed);
}
%}
function probe_ip_rcv_finish(skb:long)
%{
struct sk_buff *tmp_skb = (struct sk_buff *)STAP_ARG_skb;
struct iphdr *iph = ip_hdr(tmp_skb);
if (iph->saddr == 0x35172baf) {
printk("ip_rcv_finish ip_src: %d, ip_dst: %d \n", iph->saddr, iph->daddr);
}
%}
probe kernel.function("ip_output") {
replace_out_src_ip($skb)
}
probe kernel.function("ip_rcv") {
replace_rcv_src_ip($skb)
}
probe kernel.function("ip_rcv_finish") {
probe_ip_rcv_finish($skb)
}