zoukankan      html  css  js  c++  java
  • Tcpprobe 在网络中的使用 --- tcp_probe has been removed from the kernel. --- Using ip-tcp_metrics --- Using ss (socket statistics)

    https://elixir.bootlin.com/linux/v4.0/source/net/ipv4/tcp_probe.c

    https://askubuntu.com/questions/1164562/tcp-probe-could-not-insert-tcp-probe/1164634#1164634

    tcp_probe functionality has been removed from the kernel. I believe, but am not certain, that its removal and the associated removal of some of the required infrastructure got out of sync. I am saying that even though the module is still present for /lib/modules/4.15.0-55-generic/kernel/net/ipv4/tcp_probe.ko, it doesn't work:

    doug@s17:~$ sudo modprobe tcp_probe port=5001
    modprobe: ERROR: could not insert 'tcp_probe': Function not implemented
    doug@s17:~$ uname -a
    Linux s17 4.15.0-55-generic #60-Ubuntu SMP Tue Jul 2 18:22:20 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
    

    Please also see here, which also tells you how to achieve the same thing using other methods:

    That function is now replaced by tcp/tcp_probe trace-event. You can use it via ftrace or perftools.

    By the way, the related kernel configuration parameter is CONFIG_NET_TCPPROBE:

    doug@s17:~$ grep CONFIG_NET_TCPPROBE /boot/config-4.15.0-55-generic
    CONFIG_NET_TCPPROBE=m
    

    1

    net: tcp: Add trace events for TCP congestion window tracing This adds an event to trace TCP stat variables with slightly intrusive trace-event. This uses ftrace/perf event log buffer to trace those state, no needs to prepare own ring-buffer, nor custom user apps.

    User can use ftrace to trace this event as below;

    # cd /sys/kernel/debug/tracing
    # echo 1 > events/tcp/tcp_probe/enable
    (run workloads
    # cat trace
    

    source: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c3fde1bd28f7c720d7bc587e85e54706df4f8163



    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    https://blog.csdn.net/lishanmin11/article/details/77098136

    这篇文章介绍最近用到的一个监听TCP信息的内核模块:tcpprobe.
    主要说说这个内核模块:

    1. tcpprobe是什么?
    2. tcpprobe的基本用法?
    3. tcpprobe是怎么实现的?
    4. 实践中如何用好tcpprobe?

    1. tcp probe 是什么?

    下面摘录LINUX FOUNDATION对于tcpprobe上对于tcpprobe的一段解释:

    tcpprobe is a module that records the state of a TCP connection in response to incoming packets.  
    It works by inserting a hook into the tcp_recv processing path using kprobe, so that the 
    congestion window and sequence number can be captured.

    首先tcpprobe是一个内核模块,可以按需加载与卸载。在下文的实践中如何用好tcpprobe可以看出模块化的好处。
    同时tcpprobe是用于监听某些特定TCP的,而不是整个TCP/IP协议栈的内容。在使用tcpprobe模块时需要指定特定的端口号用于监听。
    这个限制主要是出于效率上的考虑,但是基本上也能满足大部分的需求了,后面我们也将介绍如何根据需要实现我们自己的限制条件。
    最后tcpprobe是基于kprobe机制实现的,它能捕捉cwnd和seq等信息。这些内容将在后续的内容中具体介绍。


    2. tcpprobe的基本用法

    Linux Foundation上有一个简单地例子,这里我结合我的实际使用给出一个更加具体的例子。

    2.1 编译tcpprobe模块

    // 方法1:tcpprobe是一个内核模块,在Linux 3.10中它的实现代码是net/ipv4/tcp_probe.c 
    // 可以设置在编译内核时,将tcpprobe也编译了。方法就是在.config中添加
    CONFIG_NET_TCPPROBE=m  
    
    // 方法2:可以像一个普通内核模块一样,只编译tcpprobe模块,然后加载 
    # vim Makefile    // 为tcpprobe模块建立一个新Makefile文件,最好把整个tcp_probe.c和Makefile放到单独的一个文件夹中
    obj-m += tcp_probe.o
    all:
        make -C /lib/modules/`uname -r`/build M=${PWD} modules  
    clean:
        make -C /lib/modules/`uname -r`/build M=${PWD} clean

    2.2 tcpprobe模块的加载与卸载

    // 如果是通过修改.config来编译tcpprobe的,可以使用modprobe加载 
    # modprobe tcp_probe port=5001 // 监听所有本地端口是5001的TCP流,仅在cwnd变化时捕捉信息 
    # modprobe tcp_probe full=1 port=5001 // full选项表示每次收到数据包都捕捉信息
    
    // 如果是普通内核模块形式编译的,则使用insmod加载 
    # insmod tcp_probe.ko full=1 port=5001 
    
    // 卸掉tcpprobe模块的命令也很简单,只要当前没有任务在使用tcpprobe模块即可卸载 
    # rmmod tcp_probe 

    2.3 tcpprobe模块的使用
    加载tcpprobe模块后,会新增一个/proc/net/tcpprobe的接口,可以通过这个接口获取tcpprobe捕捉的信息。

    # cat /proc/net/tcpprobe > data.out & // tcpprobe捕捉的信息是持续性的,因此读这个接口可以放到后台读 
    # pid=$! // 保存上一个读命令的pid,用于结束读tcpprobe接口 
    # iperf -c otherhost // 使用iperf建立一个TCP流 
    # kill $pid 

    记录在data.out中的数据大致是这个样子:

    1
    2
    3
    11 .172120204 193 .168 .0 .2 :8089 193 .168 .0 .25 :54320 32 0 x842ccad 0 x84299c5 10 168 14608 143
    11 .172141113 193 .168 .0 .2 :8089 193 .168 .0 .25 :54320 32 0 x842ccad 0 x842b60d 11 168 29088 137
    11 .172283353 193 .168 .0 .2 :8089 193 .168 .0 .25 :54320 32 0 x842f9ed 0 x842bbb5 11 168 31984 133

    每行的各列分别为:

    timestamp  
    saddr:port  // 源IP及端口,我的数据是在发送端捕捉的,所以port是固定的8089 
    daddr:port  // 目的IP及端口 
    skb->len    // 收到的数据包skb大小,收到的都是ACK包,所以len都比较小。 
    snd_nxt     // 下一个待发送数据的序列号 
    snd_una     // 待确认数据的序列号 
    snd_cwnd    // 拥塞窗口大小 
    ssthresh    // 慢启动阈值 
    snd_wnd     // 接收窗口大小 
    srtt        // smoothed RTT 

    可以使用简单地文本处理及gnuplot将得到的数据进行可视化,不过这里就不展开了。


    3. tcpprobe是怎么实现的?

    tcpprobe是一个机遇kprobe机制实现的模块,代码量其实不大,这节算是我的一个学习笔记吧。

    首先tcpprobe定义了两个由于记录捕捉信息的数据结构:struct tcp_log和static struct tcp_probe。
    tcp_log的元素基本就是上面介绍的一行中各列的数据,而tcp_probe结构体算是一个管理结构体,比如spinlock和用于判断cwnd是否改变的lastcwnd。

    作为一个内核模块,必不可少的就是初始化和退出函数。下面先总体上说下这两个函数完成的任务。

    static __init int tcpprobe_init(void)  
        => spin_lock_init(&tcp_probe.lock)  // 初始化tcp_probe结构体中的内容  
        => tcp_probe.log = kcalloc(bufsize, sizeof(struct tcp_log), GFP_KERNEL) // 为捕捉数据分配内存空间  
        => proc_create(procname, S_URUSR, init_net.proc_net, &tcpprobe_fops)    // 为tcpprobe模块创建一个proc文件系统接口  
        => register_jprobe(&tcp_jprobe) // 将tcp_jprobe注册到内核中  
    
    static __exit void tcpprobe_exit(void)
        => remove_proc_entry(procname, init_net.proc_net);  // 删除tcpprobe模块的proc接口  
        => unregister_jprobe(&tcp_jprobe);  // 注销tcp_jprobe
        => kfree(tcp_probe.log)      // 释放内存

    tcp_probe中锁的初始化和内存分配就不说了,先介绍一下proc_create()函数创建的proc接口,再说说register_jprobe()函数。
    proc_create()函数的原型如下,在include/linux/proc_fs.h中实现。

    static inline struct proc_dir_entry *proc_create(
            const char *name, umode_t mode, struct proc_dir_entry *parent,
            const struct file_operations *proc_fops)

    tcpprobe模块中的各个参数的含义如下:

    1. name = “tcpprobe”
    2. mode = S_IRUSR, 允许读文件
    3. parent = init_net.proc_net,新建的proc接口将出现在/proc/net/目录中
    4. proc_fops = &tcpprobe_fops

    tcpprobe_fops结构体的声明如下

    static const struct file_operations tcpprobe_fops = {
        .owner = THIS_MODULE,
        .open = tcpprobe_open,  
        .read = tcpprobe_read,  
        .llseek = noop_llseek,
    };

    proc接口也是被当做文件来处理的,且由于是只读的,所以主要需要实现open和read这两个函数。
    tcpprobe_open()主要就是将tcp_probe结构体的各项内容进行reset,
    tcpprobe_read()则是读取已经记录的数据,当没有数据可读时则睡眠,正常情况就将tcp_probe.log数组的最后一项读取出来。

    至此还只是建好了一个只读的proc接口,但是数据的采集函数并没有注册到内核中,接下来就简单说说tcp_jprobe这个数据结构体。
    register_jprobe()函数的原型如下,在kernel/kprobes.c中实现。

    int __kprobes register_jprobe(struct jprobe *jp)

    tcp_jprobe结构体的定义如下:

    static struct jprobe tcp_jprobe = {
        .kp = {
            .symbol_name = "tcp_rcv_established",
        },
        .entry = jtcp_rcv_established,
    };

    可以看出来,tcpprobe其实是用的jprobe方式实现的,监听tcp_rcv_established函数(这也是为什么tcpprobe不会抓取三次握手时候的包信息的原因)。
    对kprobe和jprobe感兴趣的话,可以看看最后给出的几个参考资料。

    最后说说jtcp_rcv_established()函数,由于它是用于监听tcp_rcv_established()函数的,因此函数的参数需要完全一致,从而jtcp_rcv_established()就拿到了一个TCP流最关键的sk结构体。
    具体实现就是如果这个包的Port是监听的那一个,则往tcp_probe.log中新写入一项,然后唤醒等待数据的读进程。
    还有一点就是tcp_probe.log是以一个循环数组形式实现的,再具体点就是那种保留一项不用的实现。代码就补解释了。

    4. 实践中如何用好tcpprobe?

    在实践中往往会发现tcpprobe的功能还不够强大,但只要适当修改,利用tcpprobe的实现原理还是能做很多的事情的。
    tcpprobe算是目前为止我了解到的用于分析Linux内核TCP流的最赞的工具之一了。

    首先,由于tcpprobe能够拿到TCP流的sk结构体,其实它能用于打印输出的信息远远不止是cwnd。
    sk结构体是内核中记录一个TCP流最关键的数据结构,里面包含一条TCP流几乎任何的信息。
    比如内存相关的可以看发送缓冲区的大小和占用情况,接收缓存区的大小。
    其实只要在内核中拿到了一条TCP流的sk结构体,几乎可以对这条TCP流做任何事情。
    所以建议在使用tcpprobe的时候,根据自己的需求修改tcp_probe.log结构体的内容。

    其次,有时候无法或不便知道端口号,tcpprobe的现有实现就无法满足要求了。但是其实只要根据实际情况适当的修改tcpprobe模块,或许就能达到目的。比如如果我们知道src IP和dst IP,那么只要修改jtcp_rcv_established()函数记录数据的判断条件即可。

    最后如果对TCP的内核实现熟悉的话,甚至可以改掉tcpprobe的监听入口函数。比如我们想监听skb发送时的一些信息,可以把监听入口函数改为tcp_transmit_skb()。

  • 相关阅读:
    希尔排序
    插入排序
    Unity创建一个简易的弹簧(弹动)效果
    看到个美到爆的菜单,忍不住扒下来~
    用avalon实现一个完整的todomvc(带router)
    页面动态加入<script>标签并执行代码
    一个简单粗暴的前后端分离方案
    常用的HTML5、CSS3新特性能力检测写法
    犀利的background-clip:text,实现K歌字幕效果
    用canvas开发H5游戏小记
  • 原文地址:https://www.cnblogs.com/ztguang/p/14401120.html
Copyright © 2011-2022 走看看