前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[network][udp]你不要偷偷发包,我跟你讲

[network][udp]你不要偷偷发包,我跟你讲

作者头像
皮振伟
发布2018-04-09 10:57:59
1.8K0
发布2018-04-09 10:57:59
举报
文章被收录于专栏:皮振伟的专栏皮振伟的专栏

前言: 互联网后台的服务器上,通常需要运行多达数百个进程,甚至更多。 有一天,运维兄弟突然找上门,说:xx服务器上为什么要访问yy服务(例如yy服务使用UDP的12345端口)? 开发一脸懵逼:没有呀!并不是我部署的服务访问的。。。 运维兄弟:我不管,这台机器分配给你了,你要负责,要不你抓包看看? 开发兄弟娴熟的一手tcpdump -iany -Xnnls0 udp port 12345:哎呦我去,还真有进程在访问,but,是哪个进程呢?UDP无连接,netstat是没有办法了,tcpdump只能证明有包发送。好吧,这个问题怎么搞@#¥%& 作者曾经就是那个开发,被运维diao的一愣一愣的。于是,就有了这个问题的一个解决方案。 准备知识: netfilter:源代码见linux-4.0.4/net/netfilter目录,并需要参考linux-4.0.4/net/ipv4目录。常用的命令iptables就是基于netfilter实现的。关于netfilter,作者也只是大致了解了一下基本逻辑,大致知道xtable而已。而ipv4协议栈中,会有代码和netfilter相关。在ipv4目录下,grep -i nf_hook *.c,可以看到ipv4实现的几个hook点的位置和大致逻辑。其中,这里要使用的就是NF_INET_LOCAL_OUT这个hook点。 关于NF_INET_LOCAL_OUT:ipv4的hook实现是在linux-4.0.4/net/ipv4/ip_output.c文件的__ip_local_out函数中。因为用户进程使用sendto陷入到kernel mode,再从syscall到__ip_local_out,一直都是同步调用,所以,如果运行到这个hook点,那么就是正在调用sendto的进程。而这,正是解决问题的基础。 代码: 路径:https://github.com/pacepi/port_connection

代码语言:javascript
复制

#include <linux/netfilter.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/inet.h>
#include <linux/sysctl.h>

static unsigned int min_port = 0;
static unsigned int max_port = 65535;
static unsigned int sysctl_udp_port_connection = 0;
static struct ctl_table_header *ctl_header = NULL;

static struct ctl_table port_conn_table[] = {
    {
        .procname    = "udp_port_connection",//注册sysctl的控制入口
        .data        = &sysctl_udp_port_connection,
        .maxlen        = sizeof(sysctl_udp_port_connection),
        .mode        = 0644,
        .proc_handler    = proc_dointvec_minmax,
        .extra1        = &min_port,
        .extra2        = &max_port,
    },
    {
    }
};

unsigned int __port_connection_hookfn(unsigned int hooknum,
        struct sk_buff *skb,
        const struct net_device *in,
        const struct net_device *out,
        int (*okfn)(struct sk_buff *))
{
    struct iphdr *iph;
    struct udphdr *udph;
    iph = ip_hdr(skb);

    //printk(KERN_INFO"%d, protocol = %d,  src IP %pI4\n", __LINE__, iph->protocol, &iph->saddr);
    if (iph->protocol == IPPROTO_UDP)//这里也可以选择TCP,TCP短连接的情况下,同样也需要类似的办法抓取
    {
        udph = udp_hdr(skb);
        if (udph->dest == ntohs(sysctl_udp_port_connection))
        {
            printk(KERN_INFO"UDP : pid = %d, comm = %s\n\n", current->pid, current->comm);//把发送特定UDP端口数据的进程打印出来,dmesg可以看到
        }
    }

    return NF_ACCEPT;//accept不会影响原来的逻辑,如果不想继续发送了,就drop
}

static struct nf_hook_ops port_conn_hook = {
    .hook = __port_connection_hookfn,
    .pf = PF_INET,
    .hooknum = NF_INET_LOCAL_OUT,
    .priority = NF_IP_PRI_FIRST,
    //.owner = THIS_MODULE,
};

static int __init nf_init(void)
{//ko的八股文,module init的时候,会调用这里
    int err = 0;

    err = nf_register_hook(&port_conn_hook);//注册netfilter的hook
    if (err) {
        printk(KERN_ERR"nf_register_hook() failed\n");
        goto out;
    }

    ctl_header = register_net_sysctl(&init_net, "net/ipv4", port_conn_table);//注册/proc/sys/net/ipv4/udp_port_connection
    if (ctl_header == NULL)
    {
        err = -ENOMEM;
        goto unregister_hook;
    }
    goto out;

unregister_hook :
    nf_unregister_hook(&port_conn_hook);

out :
    return err;
}

static void __exit nf_exit(void)
{
    nf_unregister_hook(&port_conn_hook);
    unregister_net_sysctl_table(ctl_header);
    ctl_header = NULL;
}

module_init(nf_init);
module_exit(nf_exit);
MODULE_AUTHOR("PiZhenwei p_ace@126.com");
MODULE_LICENSE("GPL");

后记:

其实这并不是当时解决问题的原版代码。原版中,作者是把代码写在linux-4.0.4/net/ipv4/udp.c中,需要重新编译kernel才行,并不方便易用。直到后来,大致翻翻netfilter的代码,才想到这个方法来解决。

当然,这里也可以使用kprobe或者systemtap来解决,也同样可以kprobe在__ip_local_out这个函数,核心逻辑也差不多:同样判断udp协议下,满足端口判断就记录一下。不过需要明确的是,二者的原理差别非常非常大。更重要的是,在选择解决方案时,看看当前的kernel是否支持kprobe。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AlwaysGeek 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档