python如何实时获取tcpdump输出

作者:文渊 时间:2021-05-05 14:41:36 

一、背景

今天有个小需求,要确认客户端有没有往服务端发送udp包,但为了减轻工作量,不想每次到机器上手动执行tcpdump抓包命令。
于是就写了个脚本来释放人力。

二、代码实现

整个脚本我还加了一些其他功能:时间戳、发送端IP提取,数据包分析,数据持久化等。这里都先去掉,仅记录下简单的实时获取tcpdump输出功能。
代码如下:


# -*- coding: utf-8 -*-
# !/usr/bin/env python

# sudo tcpdump -tt -l -nn -c 5 -i enp4s0 udp port 514 or 51414

import subprocess

cmd = ['sudo', 'tcpdump', '-tt', '-l', '-nn', '-c', '5', '-i', 'enp4s0', 'udp', 'port', '514', 'or', '51414']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)

while True:
 line = proc.stdout.readline()
 line = line.strip()
 if not line:
   print('tcpdump finished...')
   break
 print(line)

输出如下(实时):


wenyuanblog@localhost:/home/test/script# python tcpdump_udp.py
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp4s0, link-type EN10MB (Ethernet), capture size 262144 bytes
1499774951.124178 IP 192.168.10.210.41974 > 192.168.10.251.514: UDP, length 139
1499774953.125664 IP 192.168.10.210.54995 > 192.168.10.251.51414: UDP, length 139
1499774956.128498 IP 192.168.10.210.56748 > 192.168.10.251.514: UDP, length 139
1499774958.129918 IP 192.168.10.210.53883 > 192.168.10.251.51414: UDP, length 139
1499774961.132921 IP 192.168.10.210.58803 > 192.168.10.251.514: UDP, length 139
5 packets captured
6 packets received by filter
0 packets dropped by kernel
tcpdump finished...

以上代码相当于手动执行了 sudo tcpdump -tt -l -nn -c 5 -i enp4s0 udp port 514 or 51414 这条命令。
注意参数-l很重要(行显)。

三、代码实现(更新)

上面的代码能实现tcpdump的功能,但是有一个问题:没有做超时保护。即当程序执行时间过长时kill该进程(这里使用ctrl+c的方式)。
要实现这个功能有很多种方案,例如定时器+多线程等,这里仅演示一种方案,代码如下:


# -*- coding: utf-8 -*-
# !/usr/bin/env python

# sudo tcpdump -tt -l -nn -c 50 -i enp4s0 udp port 514 or 51414

import subprocess
import signal
import time
import os
import re
import json

class CmdServer:

def __init__(self, cmd, timeout=120):
   '''
   :param cmd: 执行命令(列表形式)
   :param timeout: 任务超时时间(seconds,进程运行超过该时间,kill该进程)
   :param taskname: 任务名称(根据该任务名称记录命令输出信息)
   '''
   self.cmd = cmd
   self.timeout = timeout
   self.base_path = reduce(lambda x, y: os.path.dirname(x), range(1), os.path.abspath(__file__))
   self.output_path = os.path.join(self.base_path, 'data.json')
   self.udp_flow_list = []
   self.begin_time = int(time.time())

# 执行tcpdump任务
 def run(self):
   if os.path.exists(self.output_path):
     with open(self.output_path, 'r') as f:
       self.udp_flow_list = json.load(f)

proc = subprocess.Popen(self.cmd, stdout=subprocess.PIPE)
   stdout = ''

while proc.poll() == None:
     current_time = int(time.time())
     if current_time - self.begin_time >= self.timeout:
       print('tcpdump timeout...')
       proc.send_signal(signal.SIGINT)
       stdout = proc.stdout.read()

if proc.poll() is not None and not stdout:
     print('tcpdump finished...')
     stdout = proc.stdout.read()

stdout_list = stdout.split('\n')
   if stdout_list:
     self._merge_data(stdout_list)
     self._save_data()

# 数据合并(新增/更新)
 def _merge_data(self, stdout_list):
   for line in stdout_list:
     line = line.strip()
     if not line:
       continue
     timestamp = int(float(line.split('IP')[0].strip())) * 1000
     # 源
     src_ip_port_list = re.findall(r'IP(.+?)>', line)
     if not src_ip_port_list:
       continue
     src_ip_port_str = src_ip_port_list[0].strip()
     src_ip = '.'.join(src_ip_port_str.split('.')[0:4])
     # 目的
     dst_ip_port_list = re.findall(r'>(.+?):', line)
     if not dst_ip_port_list:
       continue
     dst_ip_port_str = dst_ip_port_list[0].strip()
     dst_port = dst_ip_port_str.split('.')[-1]

# 新增/更新latest_timestamp
     src_item = filter(lambda x: src_ip == x['src_ip'], self.udp_flow_list)
     if src_item:
       src_item[0]['dst_port'] = dst_port
       src_item[0]['latest_timestamp'] = timestamp
     else:
       self.udp_flow_list.append(dict(
         src_ip=src_ip,
         dst_port=dst_port,
         latest_timestamp=timestamp
       ))

# 保存数据
 def _save_data(self):
   # 写入文件
   with open(self.output_path, 'w') as f:
     json.dump(self.udp_flow_list, f, encoding="utf-8", ensure_ascii=False)

if __name__ == '__main__':
 cmd = ['sudo', 'tcpdump', '-tt', '-l', '-nn', '-c', '5', '-i', 'enp4s0', 'udp', 'port', '514', 'or', '51414']
 cmd_server = CmdServer(cmd, 10)
 cmd_server.run()

四、总结

比较简单,仅仅是记录下。

来源:https://www.wenyuanblog.com/blogs/python-realtime-tcpdump.html

标签:python,tcpdump
0
投稿

猜你喜欢

  • python 实现读取一个excel多个sheet表并合并的方法

    2023-06-25 20:11:51
  • pytorch打印网络结构的实例

    2023-11-04 15:15:51
  • mysql增量备份及断点恢复脚本实例

    2024-01-27 04:54:34
  • vue实现input输入模糊查询的三种方式

    2024-05-08 10:42:12
  • python+pyqt实现右下角弹出框

    2023-09-07 16:04:22
  • python实现在pandas.DataFrame添加一行

    2022-04-14 01:53:23
  • FrontPage2002简明教程三:网页布局

    2008-09-17 11:19:00
  • Python 功能和特点(新手必学)

    2022-02-23 04:11:25
  • Python中的tkinter库简单案例详解

    2021-01-20 14:25:16
  • Python 常用 PEP8 编码规范详解

    2022-09-03 06:21:12
  • python异常中else的实例用法

    2021-02-05 06:18:56
  • 使用Django+Pytest搭建在线自动化测试平台

    2021-10-18 05:00:13
  • 解决python 两个时间戳相减出现结果错误的问题

    2023-01-18 17:41:44
  • python计算二维矩形IOU实例

    2022-03-07 23:40:18
  • 实现asp长文章自动分页插件

    2011-02-26 13:51:00
  • pygame实现俄罗斯方块游戏

    2023-04-06 20:17:14
  • 前端模板引擎

    2010-07-27 12:33:00
  • python中的try except与R语言中的tryCatch异常解决

    2021-10-22 02:24:48
  • mysql和oracle默认排序的方法 - 不指定order by

    2024-01-27 17:44:28
  • SpringBoot集成Flyway进行数据库版本迁移管理的步骤

    2024-01-13 19:18:53
  • asp之家 网络编程 m.aspxhome.com