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

前言: 互联网后台的服务器上,通常需要运行多达数百个进程,甚至更多。 有一天,运维兄弟突然找上门,说: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

#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。

原文发布于微信公众号 - AlwaysGeek(gh_d0972b1eeb60)

原文发表时间:2017-01-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏腾讯Bugly的专栏

《Android外部存储》

| 导语 外部存储作为开发中经常接触的一个重要系统组成,在Android历代版本中,有过许许多多重要的变更。我也曾疑惑过,为什么一个简简单单外部存储,会存在存在...

66050
来自专栏搜云库

Spring Boot 中使用 LogBack 配置

LogBack是一个日志框架,它与Log4j可以说是同出一源,都出自Ceki Gülcü之手。(log4j的原型是早前由Ceki Gülcü贡献给Apache基...

3K60
来自专栏张善友的专栏

TCP/IP 选项TcpTimedWaitDelay设置

当TCP连接被关闭时,{ Protocol, Local IP, Local Port, Remote IP, Remote Port}五元组就进入TIME_W...

25990
来自专栏恰童鞋骚年

.NET Core微服务之基于Ocelot实现API网关服务(续)

  为了验证负载均衡,这里我们配置了两个Consul Client节点,其中ClientService分别部署于这两个节点内(192.168.80.70与192...

38330
来自专栏杨建荣的学习笔记

crontab导致CPU异常的问题分析及处理(r3笔记第100天)

今天查看数据库负载没有发现问题,但是当我使用top命令的时候,发现有一个进程占用了大量的cpu资源而且已经执行很长时间了。这一下子引起了我的注意。 PID ...

40070
来自专栏开发与安全

linux网络编程之socket(三):最简单的回射客户/服务器程序、time_wait 状态

下面通过最简单的客户端/服务器程序的实例来学习socket API。 ? echoser.c 程序的功能是从客户端读取字符然后直接回射回去。 /********...

28100
来自专栏张戈的专栏

分享一次Linux任务计划crontab不执行的问题排查过程

朋友弄了一个小项目,要我帮忙做下 Linux 系统运维,上线一段时间后,发现项目偶尔会挂掉导致服务不可用。开发朋友一时之间也没空去研究项目奔溃的根因,只好由我这...

43430
来自专栏LanceToBigData

SpringBoot(二)Web整合开发

Spring Boot (二):Web 综合开发 本篇文章接着上篇内容继续为大家介绍spring boot的其它特性(有些未必是spring boot体系桟的功...

35170
来自专栏Java职业技术分享

Java框架 Spring 核心机制

5.Spring并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

14800
来自专栏大闲人柴毛毛

手把手0基础项目实战(三)——教你开发一套电商平台的安全框架

写在最前 本文是《手把手项目实战系列》的第三篇文章,预告一下,整个系列会介绍如下内容: 《手把手0基础项目实战(一)——教你搭建一套可自动化构建的微服务框架(S...

49660

扫码关注云+社区

领取腾讯云代金券