<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>易博云天 &#187; 抓包</title>
	<atom:link href="http://www.eavea.com/blog/index.php/tag/zhuabao/feed" rel="self" type="application/rss+xml" />
	<link>http://www.eavea.com/blog</link>
	<description>专注技术：学习、交流、分享、免费，每天进步一点点！</description>
	<lastBuildDate>Mon, 13 Mar 2023 07:04:16 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.6.1</generator>
		<item>
		<title>【Python实战】实时获取tcpdump输出</title>
		<link>http://www.eavea.com/blog/index.php/pythonshizhanshishihuoqutcpdumpshuchu.html</link>
		<comments>http://www.eavea.com/blog/index.php/pythonshizhanshishihuoqutcpdumpshuchu.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:37:49 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[后端技术]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[抓包]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=56</guid>
		<description><![CDATA[【Python实战】实时获取tcpdump输出 Python实时获取tcpdump输出。 一、背景 今天有个小 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>【Python实战】实时获取tcpdump输出</div>
<blockquote><p>Python实时获取tcpdump输出。</p></blockquote>
<h2 id="toc-heading-1">一、背景</h2>
<p>今天有个小需求，要确认客户端有没有往服务端发送udp包，但为了减轻工作量，不想每次到机器上手动执行tcpdump抓包命令。<br />
于是就写了个脚本来释放人力。</p>
<h2 id="toc-heading-2">二、代码实现</h2>
<p>整个脚本我还加了一些其他功能：时间戳、发送端IP提取，数据包分析，数据持久化等。这里都先去掉，仅记录下简单的实时获取tcpdump输出功能。<br />
代码如下：</p>
<div>python<i></i><i></i></div>
<pre><code># -*- 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)
</code></pre>
<p>输出如下（实时）：</p>
<div>bash<i></i><i></i></div>
<pre><code>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 &gt; 192.168.10.251.514: UDP, length 139
1499774953.125664 IP 192.168.10.210.54995 &gt; 192.168.10.251.51414: UDP, length 139
1499774956.128498 IP 192.168.10.210.56748 &gt; 192.168.10.251.514: UDP, length 139
1499774958.129918 IP 192.168.10.210.53883 &gt; 192.168.10.251.51414: UDP, length 139
1499774961.132921 IP 192.168.10.210.58803 &gt; 192.168.10.251.514: UDP, length 139
5 packets captured
6 packets received by filter
0 packets dropped by kernel
tcpdump finished...
</code></pre>
<p>以上代码相当于手动执行了 <code>sudo tcpdump -tt -l -nn -c 5 -i enp4s0 udp port 514 or 51414</code> 这条命令。<br />
注意参数-l很重要（行显）。</p>
<h2 id="toc-heading-3">三、代码实现（更新）</h2>
<p>上面的代码能实现tcpdump的功能，但是有一个问题：没有做超时保护。即当程序执行时间过长时kill该进程（这里使用ctrl+c的方式）。<br />
要实现这个功能有很多种方案，例如定时器+多线程等，这里仅演示一种方案，代码如下：</p>
<div>python<i></i><i></i></div>
<pre><code># -*- 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 &gt;= 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(.+?)&gt;', 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'&gt;(.+?):', 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()
</code></pre>
<h2 id="toc-heading-4">四、总结</h2>
<p>比较简单，仅仅是记录下。如果想基于Python的tcpdump做一些业务上的逻辑，可以参考下面的“参考链接”。<br />
参考<br />
<a href="https://blog.csdn.net/wskzgz/article/details/83822780" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/wskzgz/article/details/83822780</a><br />
<a href="https://blog.csdn.net/wangqiuyun/article/details/46966839" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/wangqiuyun/article/details/46966839</a><br />
<a href="http://www.cnblogs.com/idvcn/p/8716066.html" target="_0" rel="external nofollow noopener noreferrer">http://www.cnblogs.com/idvcn/p/8716066.html</a><br />
<a href="https://blog.csdn.net/kobeyan/article/details/4344192" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/kobeyan/article/details/4344192</a><br />
<a href="https://blog.csdn.net/xhw88398569/article/details/48022967" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/xhw88398569/article/details/48022967</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/pythonshizhanshishihuoqutcpdumpshuchu.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
