1. 程式人生 > 程式設計 >python如何實時獲取tcpdump輸出

python如何實時獲取tcpdump輸出

一、背景

  今天有個小需求,要確認客戶端有沒有往服務端傳送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','51414']
  cmd_server = CmdServer(cmd,10)
  cmd_server.run()

四、總結

  比較簡單,僅僅是記錄下。

以上就是python如何實時獲取tcpdump輸出的詳細內容,更多關於python獲取tcpdump輸出的資料請關注我們其它相關文章!