linux 获取公网流量 tcpdump + python + C++

news2024/12/27 0:16:02

前言

需求为,统计linux上得上下行公网流量,常规得命令如iftop 、sar、ifstat、nload等只能获取流量得大小,不能区分公私网,所以需要通过抓取网络包并排除私网段才能拿到公网流量。下面提供了一些有效得解决思路,提供了部分得代码片段,但不提供整个代码内容。

方案

方案一:在Linux服务器上安装服务,抓取到数据包后发送到中央计算服务器

优点:①由于计算是在中央,并不会占用linux业务服务器得资源性能

缺点:①通过scp或其他传输手段,会占用一定得网络资源,特别当数据量大时

②中央计算服务器得资源易达到瓶颈,需要开多个计算节点

方案二:在Linux服务器上安装服务,抓取到数据包后本地处理,直接将结果发至中央

优点:①中央节点压力减小,数据实时性提升

缺点:①每个linux服务器都需要抓包并且处理,需要占用一定的计算资源,且单位包越大消耗越高

方案三:在同层交换机上放置旁路监控服务器,抓取广播域内所有包,逐个分析每个linux服务器上的流量情况

优点:

①独立部署,不会占用linux服务器资源

②维护相对容易,linux业务服务器与监控节点解耦,只需要维护监控节点而不需要维护linux服务器

缺点:

①需要跟linux服务器处在一个广播域,并且要求性能足够,当linux业务服务器数据过多时,易有瓶颈

②可能存在漏抓包的情况

③需要监听所有得vlan,并分别部署监控节点

方案四:加入流控设备,旁路到核心网关

优点:

①无需自研,有成熟的抓取功能,且有更多丰富的功能

缺点:

①成本较高

②缺乏控制能力,比如流量限速,阻断等

下面主要介绍第一和第二种方法得实现

方案一技术实现

先来看技术拓扑图,流程为:

  1. linux服务器开启tcpdump抓包
  2. linux服务器将pcap包发送到流量分析服务器
  3. 流量服务器进行公私网流量拆分,公网白名单过滤
  4. 获取步骤三得结果并传输到中央

下面讲解每一步得实现细节

1.linux服务器开启tcpdump抓包

核心命令为"tcpdump", "-i", interface , f"host {target_ip}", '-s 96',"-w", output_file ,'-p'

-i 接网卡名,host接希望抓取得ip(可不填),-s 96 表示只抓取包头部(可能有得包头超过这个长度,但96依然是个比较适中得值,-w 保存为pcap包,-p 关闭混杂模式(避免监听到广播得其他包使流量过多)

#执行tcpdump抓包
def run_tcpdump(target_ip):
    INTERVAL = 60
    interface = getInterface() #获取网卡名
    timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") 
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") #获取开始时间
    output_file = f"{SAVEPATH}/xxx_{timestamp}.pcap"
    tcpdump_command = ["tcpdump", "-i", interface , f"host {target_ip}", '-s 96',"-w", output_file ,'-p']

    try:
        process = subprocess.Popen(tcpdump_command) #使用subprocess执行shell命令
        time.sleep(INTERVAL) #定义休眠时间
        process.terminate()  #结束shell命令
        process.wait()       #等待子进程退出,确保子进程清理完成

    except Exception as e:
        logger.error(f"xxx")

2.linux服务器将pcap包发送到流量分析服务器

这一个步骤没有太多可讲得,使用socket 或requests 或 subprocess(scp)都可以,目的就是将抓取到得包发送到流量分析服务器。

3.流量服务器进行公私网流量拆分,公网白名单过滤

这一步是关键步骤,公私网流量,公网白名单过滤拆分需要一定得计算性能,所以核心代码推荐用C++语言编写,博主测试过同代码情况下,python C++ java得表现能力,C++(或C)性能要远超其他语言,如同体积包,假设占用30%得单核心性能,C++可以做到1%以内。

代码要实现两点:

1.公私网彻底拆分

2.过滤公网白名单内的(不希望统计得)数据

公私网流量拆分参考以下代码片段,首先剔除三类子网地址(0xFF000000 是掩码(255.0.0.0 的二进制形式),只保留 IP 地址的最高 8 位。0x0A000000 是 10.0.0.0 的二进制形式。)

注意:现在得大型网络多采用overlay得方式,有些ip虽然在私网段,但由于走了vxlan隧道占用了公网带宽,其实也是数据公网得。后续得处理需读者自行处理。

bool is_private_ip(const std::string &ip) {
    struct in_addr addr;
    inet_pton(AF_INET, ip.c_str(), &addr);

    uint32_t ip_addr = ntohl(addr.s_addr);

    // 10.0.0.0/8
    if ((ip_addr & 0xFF000000) == 0x0A000000) return true;
    // 172.16.0.0/12
    if ((ip_addr & 0xFFF00000) == 0xAC100000) return true;
    // 192.168.0.0/16
    if ((ip_addr & 0xFFFF0000) == 0xC0A80000) return true;

    return false;
}

定义4个变量分别存公网下行,公网上行,私网下行,私网上行

    uint64_t public_in= 0;
    uint64_t public_out = 0;
    uint64_t private_in = 0;
    uint64_t private_out = 0;  
    
    pcap_t *handle = pcap_open_offline(pcap_file.c_str(), errbuf); #获取pcap包
    pcap_next_ex(handle, &header, &data)  #调用libpcap 捕获库 
    const struct ip *ip_header = (struct ip *)(data + sizeof(struct ether_header)); #获取包得head
    std::string src_ip = inet_ntoa(ip_header->ip_src); #获取src ip
    std::string dst_ip = inet_ntoa(ip_header->ip_dst); #获取dst ip
    
    #判断是私网还是公网
    if (dst_ip == target_ip) {
            if (is_private_ip(src_ip)) {
                private_in += pkt_len;
            } else {
                public_in += pkt_len;
            }
        } else if (src_ip == target_ip) {
            if (is_private_ip(dst_ip)) {
                private_out += pkt_len;
            } else {
                public_out += pkt_len;
            }
        }

接下来是过滤不希望统计的ip名单,在上面代码的基础上再做一层判断即可,

    #判断是私网还是公网
    if (dst_ip == target_ip) {
            if (is_private_ip(src_ip)) {
                private_in += pkt_len;
            } else {
                if (filter_write(src_ip)){  #return true则公网ip不在白名单内,需要加和
                     public_in += pkt_len;
                }
            }
        } else if (src_ip == target_ip) {
            if (is_private_ip(dst_ip)) {
                private_out += pkt_len;
            } else {
                if (filter_write(src_ip)){
                     public_out += pkt_len;
                }
            }
        }

4.获取步骤三得结果上报并持久化

拿到公网流量后,传输给用于持久化的程序进行入库操作(对于此类数据,建议用clickhouse进行建库,对体量小的用mysql也可以接受).关于传输方式,可以选择使用生产消费者方式,使用消息中间件缓冲数据(如Kafka)。数据量小通过http/https协议传输后插入也可以。

kafka模板

def push_kafka(data, retries=5, backoff_factor=1):
    logger.info(f"上传 {data}")

    # 配置 Kafka 生产者
    kafka_config = {
        'bootstrap.servers': KAFKA_URL,  # 替换为你的 Kafka 地址
        'client.id': 'my-producer',
    }

    producer = Producer(kafka_config)

    topic = KAFKA_TOPIC  # 替换为你的 Kafka 主题名

    # 重试逻辑
    for attempt in range(retries):
        try:
            # 将数据转换为 JSON 格式并发送到 Kafka
            producer.produce(
                topic=topic, 
                value=json.dumps(data), 
                callback=delivery_report
            )
            producer.flush()  # 强制刷新缓冲区
            logger.info("数据上传成功")
            return  # 成功后返回

        except KafkaException as e:
            logger.error(f"Kafka error occurred: {e}")
        except Exception as e:
            logger.error(f"Unexpected error: {e}")

        # 等待后重试
        wait_time = backoff_factor * (2 ** attempt)
        logger.info(f"等待 {wait_time} 秒后重试...")
        time.sleep(wait_time)

    logger.error("所有重试均失败")

http/https直传

def post_with_retries_center(data, retries=5, backoff_factor=1):
    URL_CENTER = "192.168.10.10" #填自己的地址
    logger.info(f"上传{data}")
    session = requests.Session()

    headers = {
        'Content-Type': 'application/json',
    }

    #  定义重试策略
    retry_strategy = Retry(
        total=retries,
        status_forcelist=[404, 500, 502, 503, 504],
        allowed_methods=["POST"],
        backoff_factor=backoff_factor
    )

    #  创建适配器并将其安装到会话对象中
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("http://", adapter)
    session.mount("https://", adapter)

    try:
        response = requests.post(URL_CENTER, json=data, headers=headers)
        response.raise_for_status()  # 如果响应状态码不是200,则抛出异常
    except requests.exceptions.ConnectionError as conn_err:
        logger.error(f"Connection error occurred: {conn_err}")
    except requests.exceptions.Timeout as timeout_err:
        logger.error(f"Timeout error occurred: {timeout_err}")
    except requests.exceptions.RequestException as req_err:
        logger.error(f"An error occurred: {req_err}")
    except Exception as e:
        logger.error(f"Unexpected error: {e}")

    return None

方案二技术实现

在Linux服务器上安装服务,抓取到数据包后本地处理,直接将结果发至中央

流程为

  1. linux服务器开启tcpdump抓包
  2. linux服务器分析包,获取私网、公网流量结果
  3. linux服务器推送消息中间件
  4. 中央消费者对消息消费并入库

方案二与方案一代码几乎相同,只是职权不同,原linux不需要处理包,现在需要处理,所以处理程序应该在linux服务器上,处理后推送到消息中间件,由中央消费者进行消费即可。代码片段这里就不贴了

结尾

本文章内容适用于基于linux系统的流量包分析,方案本身是完整闭环的。其中对包的处理快慢以及对公、私网的ip段判断、消息推送的方式这些是可以持续优化的点。由于方案本身具备一定商用价值,故没有贴上源码,若读者需要可联系博主提供。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2253187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Node.js:开发和生产之间的区别

Node.js 中的开发和生产没有区别,即,你无需应用任何特定设置即可使 Node.js 在生产配置中工作。但是,npm 注册表中的一些库会识别使用 NODE_ENV 变量并将其默认为 development 设置。始终在设置了 NODE_ENVproduction 的情况下运行 Node.js。…

KAN-Transfomer——基于新型神经网络KAN的时间序列预测

1.数据集介绍 ETT(电变压器温度):由两个小时级数据集(ETTh)和两个 15 分钟级数据集(ETTm)组成。它们中的每一个都包含 2016 年 7 月至 2018 年 7 月的七种石油和电力变压器的负载特征。 traffic(交通) :描…

中安证件OCR识别技术助力鸿蒙生态:智能化证件识别新体验

在数字化和智能化的浪潮中,伴随国产化战略的深入推进,国产操作系统和软件生态的建设逐渐走向成熟。鸿蒙操作系统(HarmonyOS Next)作为华为推出的重要操作系统,凭借其开放、灵活和高效的特点,正在加速在多个…

Java设计模式之状态模式架构高扩展的订单状态管理

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…

【排序用法】.NET开源 ORM 框架 SqlSugar 系列

💥 .NET开源 ORM 框架 SqlSugar 系列 🎉🎉🎉 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列…

家政小程序开发,打造便捷家政生活小程序

目前,随着社会人就老龄化和生活压力的加重,家政服务市场的需求正在不断上升,家政市场的规模也正在逐渐扩大,发展前景可观。 在市场快速发展的影响下,越来越多的企业开始进入到市场中,同时家政市场布局也发…

自然语言处理:基于BERT预训练模型的中文命名实体识别(使用PyTorch)

命名实体识别(NER) 命名实体识别(Named Entity Recognition, NER)是自然语言处理(NLP)中的一个关键任务,其目标是从文本中识别出具有特定意义的实体,并将其分类到预定义的类别中。这…

掌握 Spring Boot 中的缓存:技术和最佳实践

缓存是一种用于将经常访问的数据临时存储在更快的存储层(通常在内存中)中的技术,以便可以更快地满足未来对该数据的请求,从而提高应用程序的性能和效率。在 Spring Boot 中,缓存是一种简单而强大的方法,可以…

Unity 模拟百度地图,使用鼠标控制图片在固定区域内放大、缩小、鼠标左键拖拽移动图片

效果展示: 步骤流程: 1.使用的是UGUI,将下面的脚本拖拽到图片上即可。 using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems;public class CheckImage : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragH…

基础入门-Web应用OSS存储负载均衡CDN加速反向代理WAF防护部署影响

知识点: 1、基础入门-Web应用-防护产品-WAF保护 2、基础入门-Web应用-加速服务-CDN节点 3、基础入门-Web应用-文件托管-OSS存储 4、基础入门-Web应用-通讯服务-反向代理 5、基础入门-Web应用-运维安全-负载均衡 一、演示案例-Web-拓展架构-WAF保护-拦截攻击 原理&a…

指针(上)

目录 内存和地址 指针变量和地址 取地址(&) 解引用(*) 大小 类型 意义 const修饰 修饰变量 修饰指针 指针运算 指针- 整数 指针-指针 指针的关系运算 野指针 概念 成因 避免 assert断言 指针的使用 strl…

警惕开源信息成为泄密源头

文章目录 前言一、信息公开需谨慎1、警惕采购招标泄密。2、警惕信息公开泄密。3、警惕社交媒体泄密。 二、泄密风险需严防1、健全制度,明确责任。2、加强管控,严格审查。3、提高意识,谨言慎行。 前言 大数据时代,信息在网络空间发…

LearnOpenGL学习(光照 -- 颜色,基础光照,材质,光照贴图)

光照 glm::vec3 lightColor(0.0f, 1.0f, 0.0f); glm::vec3 toyColor(1.0f, 0.5f, 0.31f); glm::vec3 result lightColor * toyColor; // (0.0f, 0.5f, 0.0f); 说明:当我们把光源的颜色与物体的颜色值相乘,所得到的就是这个物体所反射的颜色。 创建…

面向对象(二)——类和对象(上)

1 类的定义 做了关于对象的很多介绍,终于进入代码编写阶段。 本节中重点介绍类和对象的基本定义,属性和方法的基本使用方式。 【示例】类的定义方式 // 每一个源文件必须有且只有一个public class,并且类名和文件名保持一致! …

3GPP R18 LTM(L1/L2 Triggered Mobility)是什么鬼?(三) RACH-less LTM cell switch

这篇看下RACH-less LTM cell switch。 相比于RACH-based LTM,RACH-less LTM在进行LTM cell switch之前就要先知道target cell的TA信息,进而才能进行RACH-less过程,这里一般可以通过UE自行测量或者通过RA过程获取,而这里的RA一般是通过PDCCH order过程触发。根据38.300中的描…

实验13 使用预训练resnet18实现CIFAR-10分类

1.数据预处理 首先利用函数transforms.Compose定义了一个预处理函数transform,里面定义了两种操作,一个是将图像转换为Tensor,一个是对图像进行标准化。然后利用函数torchvision.datasets.CIFAR10下载数据集,这个函数有四个常见的…

P1226 快速幂

【STUACM-算法入门-快速幂】https://www.bilibili.com/video/BV1Hi4y1L7qB?p2&vd_sourcee583d26dc0028b3e6ea220aadf5bc7fe 想先把a的b次方算出来再对p取模是不可能的,因为肯定超出long long 范围。 需要知道:(x*y)mod p (x mod p)*(y mod p) mo…

【力扣热题100】—— Day3.反转链表

你不会永远顺遂,更不会一直年轻,你太安静了,是时候出发了 —— 24.12.2 206. 反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出&…

containerd安装

containerd安装 参考资料前置准备Installing containerdInstalling runcInstalling CNI plugins containerd is available as a daemon for Linux and Windows. It manages the complete container lifecycle of its host system, from image transfer and storage to containe…

底部导航栏新增功能按键

场景需求: 在底部导航栏添加power案件,单击息屏,长按 关机 如下实现图 借此需求,需要掌握技能: 底部导航栏如何实现新增、修改、删除底部导航栏流程对底部导航栏部分样式如何修改。 比如放不下、顺序排列、坑点如…