SystemTap----通过pid和fd来获取对应的socket实例地址

本文介绍了一个SystemTap脚本,用于通过进程PID和文件描述符(FD)查找对应的Socket实例地址。脚本可能存在风险,不适用于生产环境,并且在使用LXC或cgroup时可能结果不准确。脚本首先找到task_struct结构,然后查找文件描述符表,最终获取Socket实例。文章还提供了脚本执行示例,展示了如何查找nginx的监听套接字地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  说明:1.这个脚本可能会导致内核panic,所以一定不要在生产环境中使用,切记!
           2.这个脚本不具备通用性,如果你使用了lxc或者cgroup,获得的结果可能有误或者找不到
           3.这个脚本只是提供了找到socket实例的方法,如果你想获取这个套接字的其他信息需要自己编写脚本,过获得的socket实例来获取。插入的位置在脚本中已标注,可以直接插入代码或函数。如果你对SystemTap比较熟悉的话可以自己灵活地修改。
  思路很简单,根据pid查找对应的task_struct结构实例,然后找到打开文件描述符表,查找fd对应的file结构实例。如果fd是套接字,socket结构实例保存在file结构的private_data成员中。在从private_data成员中获取数据前,会检查文件的操作是否指向socket_file_ops,但是该成员没有导出,所以脚本中没有这个校验,由使用者自己来保证指定的fd是套接字。
  脚本如下所示:
%{
#include <linux/fdtable.h>
#include <linux/file.h>
#include <net/inet_connection_sock.h>
%}

%{
int err = 0, fput_needed = 0;
%}

function stap_fget_light(fd_param:long, pid_param:long)%{
    int fd = STAP_ARG_fd_param;
    struct file *file;
    struct files_struct *files;
    struct task_struct *p;

    p = pid_task(find_pid_ns(STAP_ARG_pid_param, 
                current->nsproxy->pid_ns), PIDTYPE_PID);
    if (!p) {
        _stp_printf("Find task %d failed\n", STAP_ARG_pid_param);
        STAP_RETVALUE = (long)NULL;
        return;
    }

    files = p->files;
    fput_needed = 0;

    if (likely((atomic_read(&files->count)== 1))) {
        file = fcheck_files(files, fd);
    } else {
        rcu_read_lock();
        file = fcheck_files(files, fd);
        if (file) {
            if (atomic_long_inc_not_zero(&file->f_count))
                fput_needed = 1;
            else
                /* Didn't get the reference, someone's freed */
                file = NULL;
        }
        rcu_read_unlock();
    }

    if (!file) {
        _stp_printf("Task %d did not have fd %d\n" ,STAP_ARG_pid_param, STAP_ARG_fd_param);
    }

    STAP_RETVALUE = (long)file;
    return;

%}

function stap_sockfd_lookup_light(file_param:long)%{
    struct file *file = (typeof(file))STAP_ARG_file_param;

    STAP_RETVALUE = (long)file->private_data;
    return;
%}

global file_ptr

function get_socket_by_pid_and_fd()
{
    sock_ptr = 0;

    file_ptr = stap_fget_light($1, $2);
    if (file_ptr) {
        sock_ptr = stap_sockfd_lookup_light(file_ptr);
    }

    return sock_ptr;
}

function stap_fput_light(file_param:long)%{
    struct file *file = (typeof(file))STAP_ARG_file_param;

    if (file) {
        fput_light(file, fput_needed);
    }
%}

function verify_socket(socket_param:long)%{
    struct socket *sock = (typeof(sock))STAP_ARG_socket_param;
    struct sock *sk = sock->sk;
    struct inet_sock *isk = inet_sk(sk);

    _stp_printf("\n\nverify_socket: local IP: " NIPQUAD_FMT", port: %u\n", NIPQUAD(isk->saddr), isk->num);

%}

probe begin
{
    printf("Start to get socket. fd: %d, process: %d\n", $1, $2);

    sock_address = get_socket_by_pid_and_fd();
    if (sock_address) {

        /* You can remove printf and verify_socket, just for test */
        printf("sock_address = %p\n", sock_address);
        verify_socket(sock_address);
        

        /* ******** Insert your code or function here ******** */

        stap_fput_light(file_ptr);
    }

    exit();
}
  在上面的脚本中,verify_socket()函数是为了验证获得的结果,实际使用时可以去掉。将上面的脚本保存为get_socket_by_pid_and_fd.stp文件,使用方法如下所示(fd是文件描述符,pid是fd所属的进程的PID):
stap -g get_socket_by_pid_and_fd.stp fd pid
  下面我们通过一个简单的实例,来了解一下具体的使用。这里以nginx为例,查找其监听套接字的地址,监听端口是80。首先获取nginx的pid和监听套接字的fd,过程如下所示:
[root@CentOS_102 ~]# lsof -iTCP -sTCP:LISTEN -P | grep ":80"
nginx     6770    root    6u  IPv4  34649      0t0  TCP192.168.56.102:80 (LISTEN)
nginx     6771  nobody    6u  IPv4  34649      0t0  TCP192.168.56.102:80 (LISTEN)
[root@CentOS_102 ~]# ps aux | grep -v grep | grep nginx
root      6770  0.0  0.2  23932  616 ?        Ss  13:15  0:00 nginx: master process/usr/local/nginx-1.5.8/sbin/nginx
nobody    6771  0.0  0.3  24336  1180?        S    13:15  0:00 nginx: worker process            
[root@CentOS_102 ~]
  我们这里使用nginx的master进程中的fd,通过上面的两个命令,我们可以看到fd为6,pid是6770。下面执行我们的脚本,输出结果如下所示:
[root@CentOS_102 ~]# stap -g get_socket_by_pid_and_fd.stp 6 6770
Start to get socket. fd: 6, process: 6770
sock_address = 0xffff88001783fa00


verify_socket: local IP:192.168.56.102, port:80
  测试机器的IP地址就是192.168.56.102,根据输出结果验证了这个脚本是可以工作的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值