Translated message

A translation of this page exists in English.

使用 tcpdump 捕获网络数据包

Solution In Progress - Updated -

Environment

  • Red Hat Enterprise Linux 3 及更新版本

Issue

在对网络问题进行故障排除时,可能需要在数据包一级进行行为分析。 成功分析数据包的关键是在第一个位置获取正确的数据包数据。 本文档介绍了与此相关的信息,同时回答了以下问题:

  • 如何使用 tcpdump 捕获网络数据包?
  • 如何在我的系统中监控网络流量?
  • 请为网络捕获提供一个推荐的工具

Resolution

安装

tcpdump 工具程序在 RPM 软件包 tcpdump-..rpm 中提供。

tcpdump 示例

# tcpdump -s 0 -i {INTERFACE} -w {FILEPATH} [filter expression] 

完成后,使用 ctrl+c 终止 tcpdump

参数

  • -s 是一个 snaplen 参数。它告知 tcpdump 每个数据包应捕获的信息量(字节)。对于 RHEL 6 及更新版本,tcpdump 默认为每个数据包捕获 65535 字节数据。在以前的版本中 (RHEL 5 和更早的版本),tcpdump 默认捕获 68 个字节。 68 个字节通常不足以执行任何类型的故障排除或诊断。 如果不确定您的系统的默认设置,请参阅 tcpdump(8) 手册页,搜索 "snaplen"。 如果要从网络数据包中捕获所有数据,请使用 "-s 0" 选项运行 tcpdump 命令,如上所示。 如果网络流量较大,或者需要长时间捕获网络流量,这可能不会获得最佳结果。

在捕获数据包时,性能也很重要;如果缺少资源,tcpdump 可能不能捕获所有数据包。要调优 tcpdump,建议将 snaplen 限制为捕获用于后续分析所需的所有重要协议信息的最小值。

需要的字节数取决于具体的问题。 例如,如果您对 NFS v3 进行故障排除,snaplen 为 256 通常就足够了,除非您对 READDIR/READDIRPLUS 问题进行故障排除,在这种情况下,您可能需要使用 snaplen 为 0。 对于 NFS v4,通常使用 snaplen 为 0。

  • -i 将 {INTERFACE} 替换为调查流量通过的网络接口名称。您可以捕获不同类型接口(物理接口、vlan、绑定、team 等)的流量,但在大多数情况下,您应避免使用关键字 any。 使用 any 将从所有接口捕获数据包,并在使用绑定、team 时可能多次捕获相同的流量。这会使故障排除变得更加复杂,因为不同的工具会将额外的数据包标记为重新传输的数据。 如果您知道要与之通信的远程系统的地址,您可以通过 ip route get 轻松确定源接口。

    # ip route get 10.16.29.55
    10.16.29.55 via 10.16.47.254 dev eth0  src 10.16.46.34 
        cache 
    

在上例中,源接口是 eth0。这是传递给 tcpdump 中的 -i 选项的接口名称。
如果需要同时在多个接口上运行 tcpdump,请对每个接口运行单独的 tcpdump (写入不同文件)。

  • 此处指定的 -w FILEPATH 应指向一个本地文件系统。这将防止 tcpdump 捕获不需要的流量。另外,请确保有足够的可用空间。如果挂载到本地存储,建议使用 /tmp。为确保捕获以二进制格式保存(强烈首选),文件名应以二进制扩展名结尾 (.cap、.pcap 等)。

过滤表达式

您可以使用 expression 在捕获数据包时过滤数据包。有关 expression 的信息,请参阅 pcap-filter 的 man page。另外,如果您计划在捕获 vlan 接口时进行过滤,请参阅以下 KCS 数据库:"在指定 VLAN 上的一个端口和接口时,为什么 tcpdump 不会捕获任何流量?"建议仅在绝对必要时才使用过滤。使用过滤可以会造成更多压力,并会减少执行tcpdump 的服务器的性能。它还通常会导致分析失败,因为没有捕获足够的信息。如果您必须使用过滤,在上传捕获文件用于分析时,请说明使用了哪些过滤以及使用的原因。

有关 tcpdump 的更多信息,请参阅 man-page。

捕获提示

tcpdump 命令的语法相对简单,但为了捕获真正相关的网络流量用于稍后进行分析,请考虑以下非常有用的规则:

  • 在连接两端(客户端和服务器端)同时运行数据包捕获
  • 确保在问题发生时捕获数据包
  • 确保捕获的文件被保存到本地文件系统中
  • 请记录下相关系统的时区,这对稍后的分析有益并与系统日志关联
  • 对于难以重现的问题,或防止捕获的文件太大,可以使用滚动捕获
  • 使用 tcpdump -s 限制将每个数据包保存的信息量
  • 不用使用文本输出,而是使用二进制 (文件扩展为 .cap, .pcap, 等)
  • 避免使用 tcpdump -i any,并确保您在正确的接口上进行捕获
  • 如果可能,尽量避免使用捕获过滤,这可能会过滤掉有所帮助的信息。但是,如果接口上存在高带宽使用量,这个原则可能并不总是可行。因为不使用过滤可能会生成一个巨大的文件。如果需要过滤,请首先咨询红帽支持团队。当对指定主机或端口进行过滤时,需要注意不要丢弃网络对话的一个方向。
  • 红帽客户门户网站提供了一个应用程序,可以帮助生成命令语法(如果需要)。请参阅 :https://access.redhat.com/labs/nptcpdump/
  • 压缩捕获文件(例如使用 gzip)

如何上传捕获数据

  1. 在运行 tcpdump 命令时使用 '-r' 选项来验证流量是否已正确捕获。如果正确捕获,此命令的输出应该会显示从客户端和服务器发送的数据包,类似于以下的 NFS 'getattr' 请求和响应示例:

    # tcpdump -r /tmp/client.cap
     ...
    09:39:06.272688 IP 192.168.155.74.3337237041 > 192.168.155.2.nfs: 216 getattr fh 0,0/22
    09:39:06.273342 IP 192.168.155.2.nfs > 192.168.155.74.3337237041: reply ok 88 getattr NON 2 ids 0/9 sz 0
    09:39:06.273365 IP 192.168.155.3354014257 > 192.168.155.2.nfs: 220 getattr fh 0,0/22
    09:39:06.273840 IP 192.168.155.2.nfs > 192.168.155.74.3354014257: reply ok 108 getattr NON 2 ids 0/9 sz 0
    
  2. 如果 trace 文件较大(几百 MB 甚至几 GB),使用 gzip 压缩它们。也可以使用其他压缩方式(如 bzip2),但有些工具(如 tshark)可能无法读取它们,因此建议使用 gzip

    # gzip /tmp/client.cap
    # gzip /tmp/server.cap
    
  3. 将客户端和服务器系统的压缩的 trace ( *.cap.gz 文件) 附加到支持问题单中,或上传到 Red Hat Global Support Services sFTP 服务器

安装

  1. 确保客户端和服务器系统上都安装了 tcpdump RPM。

    • 在 Red Hat Enterprise Linux 3 或 4 中,使用 'up2date' 命令:

      # up2date -i tcpdump
      
    • 在 Red Hat Enterprise Linux 5 或更高版本中,使用 'yum' 命令:

      # yum install tcpdump
      

滚动捕获

有时,捕获需要在一个较长的时间段内运行。在这种情况下,使捕获文件无限期地增长并不是一个好主意。 在进行滚动捕获时,随时检查是否有足够的磁盘空间。参数 -w path -W n -C m 指示 tcpdump 创建每个大约 "m" MB 的 "n" 个文件。生成的文件名称为 path0 到 path (n-1)。当 pathX 的大小超过 m MB 时,会写入或重写 path(mod (X+1, n)。因为 tcpdump 默认使用 tcpdump 用户 ID 运行,因此需要确保此用户 ID 具有创建 pathX 文件所需的权限。"-Z userID" 可用于更改运行 tcpdump 命令的 ID。

重要的一点是,设置的文件大小和文件数量组合需要满足捕获数据的要求。需要在相关文件被覆盖之前,有足够的时间来捕获相关事件的数据。以下脚本可以帮助实现这个目的。可以使用它来启动 tcpdump,然后当在日志文件中看到触发消息时停止它。

示例:./pcapLogwatch.sh eth0 5 250 /tmp dumpfilename.pcap "" /var/log/messages "Time has been changed"

SCRIPT NAME DEVICE NUMBER-OF-FILES SIZE-OF-FILES DUMP-DIRECTORY DUMP-FILE-NAME FILTER LOG-FILE-NAME TRIGGER-STRING
./pcapLogwatch.sh eth0 5 250 /tmp dumpfilename.pcap "" /var/log/messages "Time has been changed"

其中:

  • DEVICE =(eth0) 您从中捕获的接口 (#ip a)
  • NUMBER-OF-FILES =(5) 文件数
  • SIZE-OF_FILES = (250),大小为 250,单位是 MB
  • DUMP-DIRECTORY = (/tmp)要转储到的目录
  • DUMP-FILE-NAME =(dumpfilename.pcap) 捕获文件的名称
  • FILTER =("") tcpdump 过滤,可以是协议或 IP
  • LOG-FILE-NAME = (/var/log/messages) 为触发器监视的文件
  • TRIGGER-STRING = (Time has been changed) 在监视的文件中停止滚动捕获的错误消息

pcapLogwatch.sh 脚本:

#!/bin/sh
# pcaplogwatch.sh
# - capture packets until a given string is seen in a given logfile
#
# Usage:
#   pcapLogwatch.sh DEVICE NUMBER-OF-FILES SIZE-OF-FILES DUMP-DIRECTORY DUMP-FILE-NAME FILTER LOG-FILE-NAME TRIGGER-STRING
#
#
if [ $# -ne 8 ]
     then echo "Usage:"
          echo pcapLogwatch.sh DEVICE NUMBER-OF-FILES SIZE-OF-FILES \
               DUMP-DIRECTORY DUMP-FILE-NAME FILTER LOG-FILE-NAME \
               TRIGGER-STRING
          echo Where:
          echo "  DEVICE           name of the device used for capturing packets"
          echo "  NUMBER-OF-FILES number of files in the capture ring"
          echo "  SIZE-OF-FILES   size of each file (approximately)"
          echo "  DUMP-DIRECTORY  directory to put the files"
          echo "  DUMP-FILE-NAME  base name of the capture files"
          echo "  FILTER          tcpdump filter string, may be \"\""
          echo "  LOG-FILE-NAME   name of log file containing the trigger"
          echo " TRIGGER-STRING   when this string is seen tcpdump will be stopped"  
          exit
fi

# After setting the parameters below, this script will run
# a rolling packet capture until the string in $LOG_STRING
# is found in $LOG_FILE. Set the optional $DUMP_FILTER with
# whatever libpcap filter as needed.

# Interface to capture on:
DUMP_INTERFACE="$1"

# Number of capture files:
DUMP_NUM="$2"

# Size of capture files:
DUMP_SIZE="$3"

# Directory to save file to:
DUMP_DIR="$4"

# Filename to capture to:
DUMP_NAME="$5"

# libpcap Dump filter:
DUMP_FILTER="$6"

# Log file to monitor:
LOG_FILE="$7"

# String to match in LOG_FILE:
LOG_STRING="$8"

# Time to wait after LOG_STRING found
# until the dump is stopped (in seconds):
TIME_AFTER=1

mkdir -p "$DUMP_DIR"
chmod g+w "$DUMP_DIR"
chgrp tcpdump "$DUMP_DIR"

/usr/sbin/tcpdump -s 0 -w "$DUMP_DIR/$DUMP_NAME" -W "$DUMP_NUM" -C "$DUMP_SIZE" -i "$DUMP_INTERFACE" "$DUMP_FILTER" &DUMP_PID=$!

tail --follow=name --pid=$DUMP_PID -n 0 "$LOG_FILE" | awk "/$LOG_STRING/{system(\"sleep $TIME_AFTER; kill $DUMP_PID\")}" & 
disown -a -h
exit 0
# END pcapLogwatch.sh

在某些情况下,事件不会由事件触发,而是在一定时间之后触发。 在这种情况下,您可以运行以下命令。务必将接口 (eth0) 改为您的服务器上的相关接口名称:

# /usr/sbin/tcpdump -s 0 -w /tmp/reproduced_issue.pcap -W 4 -C 300 -i eth0

Root Cause

Diagnostic Steps

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.

Comments