一、邮件系统是什么?

电子邮件是互联网上最早也是最重要的应用之一。每个用户有一个唯一的邮件地址,格式为:

username@domainname.com

比如:zhangsan@gmail.com
邮件系统由三个核心协议组成,它们各司其职:

         发件人                                    收件人
    [张三的邮件客户端]                         [李四的邮件客户端]
          |                                          |
          | SMTP(发送)                    POP3/IMAP(接收)
          |                                          |
    [张三的邮件服务器] ---SMTP(服务器间传输)---> [李四的邮件服务器]

二、三大协议概览

2.1 协议对比


协议 全名 作用 默认端口 加密端口(TLS)
POP3 Post Office Protocol 3 客户端从服务器邮件 TCP 110 TCP 995
IMAP Internet Mail Access Protocol 客户端从服务器邮件 TCP 143 TCP 993
SMTP Simple Mail Transfer Protocol 客户端邮件,服务器间邮件 TCP 25 TCP 465

2.2 POP3 和 IMAP 的核心区别

打个比方:

  • POP3 像去邮局取信,取走之后邮局里就没有了(邮件下载到本地,服务器删除)
  • IMAP 像在邮局里看信,信始终放在邮局(邮件留在服务器,多设备同步)

特性 POP3 IMAP
邮件存储位置 下载到本地,服务器可选删除 始终在服务器
多设备访问 不方便 完全支持
离线访问 支持(已下载) 需要同步
适用场景 单设备、低带宽 多设备、随时访问

三、POP3 协议详解

3.1 POP3 工作流程

POP3服务器(110端口) 邮件客户端 POP3服务器(110端口) 邮件客户端 TCP 连接建立 +OK 欢迎消息(服务器就绪) USER zhangsan@gmail.com +OK 请输入密码 PASS mypassword123 +OK 登录成功 STAT(查询邮件状态) +OK 3 1200(3封邮件,共1200字节) LIST(列出所有邮件) +OK 邮件列表 RETR 1(取第1封邮件) +OK 邮件内容... DELE 1(删除第1封) +OK 已标记删除 QUIT(退出) +OK 再见,删除已执行

3.2 POP3 常用命令


命令 作用 示例
USER 发送用户名 USER zhangsan
PASS 发送密码 PASS mypass
STAT 查询邮箱状态 返回邮件数和总大小
LIST 列出所有邮件 返回每封邮件编号和大小
RETR 取回指定邮件 RETR 1 取第1封
DELE 删除指定邮件 DELE 1 删第1封
NOOP 保持连接(心跳) 无操作,防超时断开
QUIT 退出,提交所有删除 断开连接

3.3 POP3 响应格式

POP3 只有两种响应:

+OK 具体信息      ← 成功
-ERR 错误原因     ← 失败

失败示例(认证失败):

客户端: USER zhangsan
服务器: +OK
客户端: PASS wrongpassword
服务器: -ERR Login failed: invalid username or password
(随后服务器主动关闭TCP连接)

3.4 Wireshark 过滤 POP3

# 过滤所有POP3报文
pop
# 过滤POP3请求命令(客户端发出的)
pop.request
# 过滤用户名命令
pop.request.command == "USER"
# 过滤密码命令
pop.request.command == "PASS"
# 过滤POP3响应(服务器发出的)
pop.response
# 过滤成功响应
pop.response.indicator == "+OK"
# 过滤错误响应
pop.response.indicator == "-ERR"
# 加密POP3(TLS)
tcp.port == 995

四、IMAP 协议详解

4.1 IMAP 工作流程

IMAP服务器(143端口) DNS服务器 邮件客户端 IMAP服务器(143端口) DNS服务器 邮件客户端 查询 imap.gmail.com 的IP 返回IP地址 TCP 连接到143端口 * OK IMAP服务器就绪 A001 CAPABILITY(查询服务器能力) * CAPABILITY IMAP4rev1 AUTH=PLAIN... A002 LOGIN zhangsan mypassword A002 OK 登录成功 A003 SELECT INBOX(选择收件箱) * 10 EXISTS(10封邮件) A004 FETCH 1 BODY[](取第1封邮件) * 1 FETCH 邮件内容... A005 LOGOUT * BYE 再见

4.2 IMAP 响应状态码

IMAP 响应分为标记响应(带命令编号)和无标记响应(以 * 开头):

状态 含义 示例场景
OK 命令成功 登录成功、邮件获取成功
NO 命令失败(可重试) 用户名密码错误、邮件不存在
BAD 命令语法错误 格式不对的命令
BYE 服务器断开连接 超时或LOGOUT后
PREAUTH 已预认证 某些安全连接方式

4.3 Wireshark 过滤 IMAP

# 过滤所有IMAP请求(客户端→服务器)
imap.request
# 过滤特定IMAP命令
imap.request.command == "LOGIN"
imap.request.command == "FETCH"
imap.request.command == "DELETE"
# 过滤所有IMAP响应(服务器→客户端)
imap.response
# 过滤特定响应状态
imap.response.status == "OK"
imap.response.status == "NO"
imap.response.status == "BAD"
# 加密IMAP(TLS)
tcp.port == 993

五、SMTP 协议详解

5.1 SMTP 工作流程(发送邮件)

SMTP服务器(25端口) DNS服务器 邮件客户端 SMTP服务器(25端口) DNS服务器 邮件客户端 查询 smtp.gmail.com 的IP 返回IP地址 TCP 连接到25端口 220 smtp.gmail.com 服务就绪 EHLO client.example.com(问候) 250 OK,列出服务器能力 AUTH LOGIN(请求认证) 334 请输入用户名(Base64编码) (Base64编码的用户名) 334 请输入密码(Base64编码) (Base64编码的密码) 235 认证成功 MAIL FROM:<zhangsan@gmail.com> 250 OK 发件人已接受 RCPT TO:<lisi@example.com> 250 OK 收件人已接受 DATA(开始发送邮件正文) 354 请输入邮件,以.结束 Subject:你好 正文内容... .(单独一行点号结束) 250 OK 邮件已加入队列 QUIT 221 再见

5.2 SMTP 状态码结构

SMTP 状态码的格式是:
状态码=类别. 主题. 细节\text{状态码} = \text{类别}.\ \text{主题}.\ \text{细节}状态码=类别. 主题. 细节
例如状态码 450

  • 类别 4 → 临时性问题(可重试)
  • 主题 5 → 邮件投递状态
  • 细节 0 → 未定义的错误
    类别说明:

类别 代码 含义 说明
成功 2.x.x 操作成功 邮件已正常处理
临时失败 4.x.x 暂时性问题 稍后重试可能成功,如服务器繁忙
永久失败 5.x.x 永久性问题 无法投递,如地址不存在

主题说明:

主题代码 含义
x.0.x 其他或未定义状态
x.1.x 地址相关问题
x.2.x 邮箱状态问题
x.3.x 邮件系统问题
x.4.x 网络和路由问题
x.5.x 邮件投递协议状态
x.6.x 邮件内容或媒体问题
x.7.x 安全或策略问题

5.3 常见 SMTP 状态码速查


状态码 含义 可能原因
220 服务就绪 服务器正常运行
221 服务关闭传输通道 正常关闭,无问题
235 认证成功 用户名密码正确
250 请求成功 邮件发送/操作成功
251 非本地用户,将转发 邮件将被中继转发
354 开始输入邮件内容 服务器等待邮件正文
421 服务不可用 服务未运行或超负荷
422 邮件大小超限 收件方邮箱配额已满
431 内存或磁盘已满 服务器存储问题
442 连接问题 到目标服务器的连接有问题
450 请求操作未执行 远程邮件服务问题
451 无效命令 服务器繁忙导致操作中止
452 存储不足 接收服务器存储空间不足
500 语法错误 命令不被识别
512 DNS错误 目标服务器找不到
530 认证问题 需要认证或服务器被拉黑
542 收件人地址被拒绝 被反垃圾邮件系统拦截

5.4 Wireshark 过滤 SMTP

# 过滤所有SMTP响应报文
smtp.response
# 过滤特定状态码
smtp.response.code == 250
smtp.response.code == 535
# 过滤状态码范围(大于400的都是错误)
smtp.response.code > 400
# 过滤响应参数(如AUTH LOGIN)
smtp.rsp.parameter == "AUTH LOGIN"
# 加密SMTP(TLS)
tcp.port == 465

六、邮件协议完整工作架构

SMTP 发送
TCP 25/465

SMTP 服务器间传输
TCP 25

存储邮件

POP3 取信
TCP 110/995

IMAP 读信
TCP 143/993

张三的电脑
邮件客户端

张三的邮件服务器
smtp.example.com

李四的邮件服务器
smtp.company.com

李四的邮箱

李四的电脑
邮件客户端

李四的手机
邮件客户端

七、故障排查方法

7.1 故障排查思路

用户报告邮件问题
        |
        +---> 发不出去? --> 检查 SMTP (端口25/465)
        |
        +---> 收不到?   --> 检查 POP3(端口110/995)
                                 或 IMAP(端口143/993)

第一步:确认TCP连接是否建立

# 如果TCP连接失败(SYN包无响应),是网络/防火墙问题
# 如果TCP连接成功,但登录失败,是用户名密码问题
# 如果登录成功,但邮件操作失败,是协议/服务器问题

第二步:检查认证
常见认证失败错误:

协议 错误响应 含义
POP3 -ERR Login failed 用户名或密码错误
IMAP NO LOGIN failed 登录失败
SMTP 535 Authentication failed 认证失败

第三步:检查TCP层问题
如果能登录但速度很慢,检查:

重传(Retransmission)       → 链路丢包
重复ACK(Duplicate ACK)     → 链路质量差
零窗口(Zero Window)        → 服务器/客户端处理太慢
窗口满(Window Full)        → 接收缓冲区满

7.2 服务器间通信慢的排查

是,同一机房

否,跨WAN链路

服务器间SMTP通信慢

两台服务器
在同一地点吗?

检查服务器性能
(CPU/内存/磁盘)
或应用程序问题

检查WAN链路带宽利用率

带宽是否拥塞?

大附件邮件
占满了带宽
限制邮件大小或升级带宽

检查TCP重传
是否为邮件专有问题

八、垃圾邮件和恶意邮件分析

8.1 分析步骤

第一步:过滤出带数据的邮件报文

# POP3协议中含有数据的报文
pop || data-text-lines

第二步:通过数据量初步判断

数据量很小(几KB)  → 纯文本邮件,通常无附件
数据量很大(几百KB以上)→ 可能含有附件,需重点分析

第三步:追踪TCP流
在 Wireshark 中右键报文 → Follow TCP Stream,可以看到完整的邮件内容:

From: spam@malicious.com
To: victim@company.com
Subject: 您中奖了!
Content-Type: application/pdf; name="prize.pdf"
...
(PDF文件的Base64编码内容)

第四步:识别垃圾邮件特征

检查发件人地址:是否来自可疑域名
检查附件类型:.exe .pdf .doc 等可执行或宏文件
检查主题行:含有"中奖"、"紧急"等诱导词
检查相似邮件:多封邮件来自同一IP或域名

第五步:设置过滤规则
在邮件服务器上,根据分析结果设置规则:

发件人域名黑名单
发件人IP黑名单
附件类型过滤
主题关键词过滤

九、SSL/TLS 加密与解密

9.1 为什么需要加密?

不加密的邮件传输,用 Wireshark 就能看到明文的用户名和密码,非常危险:

不加密:
  客户端 → USER zhangsan          ← Wireshark 可见
  客户端 → PASS mypassword123     ← Wireshark 可见!
加密后(TLS):
  客户端 → [加密数据,无法读取]

9.2 各协议加密端口


协议 明文端口 加密端口(TLS)
POP3 TCP 110 TCP 995
IMAP TCP 143 TCP 993
SMTP TCP 25 TCP 465

9.3 Wireshark 解密 TLS(需要私钥)

获取 SSL 私钥的方式:

  • macOS:钥匙串访问(Keychain Access)→ 找到对应证书并导出
  • Windows:MMC(微软管理控制台)→ 证书 → 找到对应证书并导出
    在 Wireshark 中配置:
菜单:编辑(Edit) → 首选项(Preferences)
→ Protocols → TLS
→ RSA keys list → 添加私钥文件

十、完整 C++ 代码:SMTP 状态码解析器

下面的代码演示如何解析 SMTP 状态码的三段结构,并给出详细的含义说明。

#include <iostream>
#include <string>
#include <map>
#include <sstream>
// SMTP 状态码解析器
// 状态码格式:三位数字,例如 250、421、535
// 结构:类别(第1位) + 主题(第2位) + 细节(第3位)
// 获取状态码类别含义(第一位数字)
std::string getClassMeaning(int cls) {
    switch (cls) {
        case 2: return "成功(Success)- 操作已成功完成";
        case 4: return "临时失败(Transient Failure)- 暂时性问题,稍后可重试";
        case 5: return "永久失败(Permanent Failure)- 永久性错误,重试也无用";
        default: return "未知类别";
    }
}
// 获取状态码主题含义(第二位数字)
std::string getSubjectMeaning(int subj) {
    switch (subj) {
        case 0: return "其他/未定义状态";
        case 1: return "地址相关状态(Addressing)";
        case 2: return "邮箱状态(Mailbox Status)";
        case 3: return "邮件系统状态(Mail System)";
        case 4: return "网络和路由状态(Network/Routing)";
        case 5: return "邮件投递协议状态(Delivery Protocol)";
        case 6: return "邮件内容或媒体状态(Content/Media)";
        case 7: return "安全或策略状态(Security/Policy)";
        default: return "未知主题";
    }
}
// 常见 SMTP 状态码数据库
// key: 状态码, value: 说明
std::map<int, std::string> buildSmtpCodeMap() {
    std::map<int, std::string> codes;
    // 2xx 成功类
    codes[220] = "服务就绪(Service Ready)- 服务器等待命令";
    codes[221] = "服务关闭传输通道(Closing Channel)- 正常断开";
    codes[235] = "认证成功(Authentication Successful)";
    codes[250] = "请求操作完成(Requested Mail Action OK)- 邮件成功";
    codes[251] = "非本地用户,将转发(User Not Local, Will Forward)";
    codes[252] = "无法验证用户,仍将尝试投递";
    codes[354] = "开始输入邮件内容,以单独一行的.结束";
    // 4xx 临时失败类
    codes[421] = "服务不可用(Service Not Available)- 服务器过载或未运行";
    codes[422] = "邮件大小超过配额(Mailbox Full)";
    codes[431] = "磁盘已满或内存不足(Out of Disk/Memory)";
    codes[432] = "邮件队列已停止(Queue Stopped)";
    codes[441] = "接收服务器无响应(Receiving Server Not Responding)";
    codes[442] = "连接中断(Bad Connection)";
    codes[444] = "无法路由(Unable to Route)";
    codes[445] = "邮件系统拥塞(Mail System Congestion)";
    codes[447] = "投递超时(Delivery Timeout)";
    codes[450] = "请求操作未执行(Action Not Taken)- 远程邮件服务问题";
    codes[451] = "本地错误,操作中止(Local Error in Processing)";
    codes[452] = "存储空间不足(Insufficient Storage)";
    // 5xx 永久失败类
    codes[500] = "语法错误,命令无法识别(Syntax Error)";
    codes[501] = "参数或参数中的语法错误";
    codes[503] = "命令顺序错误(Bad Sequence of Commands)";
    codes[512] = "DNS错误(Host Not Found)- 找不到目标服务器";
    codes[530] = "需要认证(Authentication Required)或服务器被黑名单";
    codes[535] = "认证失败(Authentication Failed)- 用户名/密码错误";
    codes[542] = "收件人地址被拒绝(Recipient Address Rejected)- 被反垃圾系统拦截";
    codes[550] = "邮件被拒绝,邮箱不可用";
    codes[552] = "超出存储分配(Exceeded Storage Allocation)";
    codes[554] = "事务失败(Transaction Failed)";
    return codes;
}
// 解析三位 SMTP 状态码
void parseSmtpCode(int code) {
    // 提取三段数字
    int cls    = code / 100;         // 第一位:类别
    int subj   = (code / 10) % 10;  // 第二位:主题
    int detail = code % 10;          // 第三位:细节
    std::cout << "==============================" << std::endl;
    std::cout << " SMTP 状态码解析:" << code << std::endl;
    std::cout << "==============================" << std::endl;
    // 检查状态码范围
    if (cls < 2 || cls > 5) {
        std::cout << " 无效的 SMTP 状态码(应为 2xx/4xx/5xx)" << std::endl;
        return;
    }
    std::cout << " 类别(第1位 = " << cls << "): "
              << getClassMeaning(cls) << std::endl;
    std::cout << " 主题(第2位 = " << subj << "): "
              << getSubjectMeaning(subj) << std::endl;
    std::cout << " 细节(第3位 = " << detail << "): "
              << detail << std::endl;
    // 查表获取详细说明
    auto codeMap = buildSmtpCodeMap();
    auto it = codeMap.find(code);
    if (it != codeMap.end()) {
        std::cout << " 详细说明: " << it->second << std::endl;
    } else {
        std::cout << " 详细说明: (未在常用列表中,请查阅 RFC 3463)" << std::endl;
    }
    // 给出操作建议
    std::cout << " 处理建议: ";
    if (cls == 2) {
        std::cout << "正常,无需处理。" << std::endl;
    } else if (cls == 4) {
        std::cout << "临时性问题,建议等待后重试,或检查服务器状态。" << std::endl;
    } else if (cls == 5) {
        std::cout << "永久性错误,重试无效,需检查配置或联系管理员。" << std::endl;
    }
    std::cout << std::endl;
}
int main() {
    std::cout << "====================================" << std::endl;
    std::cout << "  SMTP 状态码解析演示程序" << std::endl;
    std::cout << "====================================" << std::endl << std::endl;
    // 演示常见状态码解析
    int testCodes[] = {220, 250, 354, 421, 451, 500, 535, 512};
    for (int code : testCodes) {
        parseSmtpCode(code);
    }
    // 交互模式:让用户输入状态码
    std::cout << "------------------------------" << std::endl;
    std::cout << "请输入要查询的 SMTP 状态码(输入0退出):" << std::endl;
    int userCode = 0;
    while (true) {
        std::cout << "> ";
        std::cin >> userCode;
        if (userCode == 0) {
            std::cout << "退出程序。" << std::endl;
            break;
        }
        if (userCode < 100 || userCode > 599) {
            std::cout << "无效输入,请输入100-599之间的整数。" << std::endl;
            continue;
        }
        parseSmtpCode(userCode);
    }
    return 0;
}

编译和运行:

g++ -std=c++17 -o smtp_parser smtp_parser.cpp
./smtp_parser

部分输出示例:

==============================
 SMTP 状态码解析:250
==============================
 类别(第1位 = 2): 成功(Success)- 操作已成功完成
 主题(第2位 = 5): 邮件投递协议状态(Delivery Protocol)
 细节(第3位 = 0): 0
 详细说明: 请求操作完成(Requested Mail Action OK)- 邮件成功
 处理建议: 正常,无需处理。
==============================
 SMTP 状态码解析:535
==============================
 类别(第1位 = 5): 永久失败(Permanent Failure)- 永久性错误,重试也无用
 主题(第2位 = 3): 邮件系统状态(Mail System)
 细节(第3位 = 5): 5
 详细说明: 认证失败(Authentication Failed)- 用户名/密码错误
 处理建议: 永久性错误,重试无效,需检查配置或联系管理员。

十一、知识树总结

邮件协议体系
|
+-- POP3(接收邮件)
|   |-- 端口: TCP 110 / TLS 995
|   |-- 特点: 邮件下载到本地,服务器可删除
|   |-- 响应: +OK / -ERR
|   +-- 常用命令: USER PASS STAT LIST RETR DELE NOOP QUIT
|
+-- IMAP(读取邮件)
|   |-- 端口: TCP 143 / TLS 993
|   |-- 特点: 邮件留在服务器,多设备同步
|   |-- 响应: OK / NO / BAD / BYE
|   +-- 常用命令: LOGIN SELECT FETCH DELETE LOGOUT
|
+-- SMTP(发送邮件)
|   |-- 端口: TCP 25 / TLS 465
|   |-- 特点: 客户端→服务器、服务器→服务器
|   |-- 状态码结构: 类别.主题.细节
|   |   |-- 2xx 成功
|   |   |-- 4xx 临时失败(可重试)
|   |   +-- 5xx 永久失败(无需重试)
|   +-- 常用命令: EHLO AUTH MAIL FROM RCPT TO DATA QUIT
|
+-- 安全加密 TLS/SSL
|   |-- 全部协议均支持 TLS
|   +-- Wireshark 需要私钥才能解密
|
+-- 故障排查思路
    |-- 发不出去 → 检查 SMTP
    |-- 收不到   → 检查 POP3/IMAP
    |-- TCP连接失败 → 网络/防火墙
    |-- 认证失败    → 用户名/密码
    +-- 速度慢      → 检查TCP重传/带宽

NetBIOS 与 SMB 协议分析详解(从零开始)

一、为什么需要 NetBIOS 和 SMB?

在企业内网中,员工经常需要:

  • 访问共享文件夹(“网络驱动器”)
  • 使用共享打印机
  • 远程执行文件
    这些功能在 Windows 网络里,都依赖 NetBIOSSMB 这两套协议来实现。

打个比方:NetBIOS 是"楼道"(提供基础通道),SMB 是"快递员"(在楼道里搬运具体的文件和数据)。

二、NetBIOS 协议详解

2.1 什么是 NetBIOS?

NetBIOS(Network Basic Input/Output System,网络基本输入输出系统)是 1980 年代初为局域网通信开发的一套协议,工作在 OSI 模型的第五层(会话层)
后来微软将其迁移到 TCP/IP 网络上运行,称为 NBT(NetBIOS over TCP/IP),规范见 RFC 1001 和 RFC 1002。

2.2 NetBIOS 的三大服务

NetBIOS 协议族
|
+-- NetBIOS-NS  名称服务      UDP 137   (负责"查电话本":名字→IP)
|
+-- NetBIOS-DGM 数据报服务   UDP 138   (负责"广播通告":我提供哪些服务)
|
+-- NetBIOS-SSN 会话服务      TCP 139   (负责"打电话":建立连接访问文件)

用 mermaid 展示三大服务的职责:

NetBIOS 协议族

名称服务 NetBIOS-NS
UDP 端口 137
名字注册和名字→IP解析

数据报服务 NetBIOS-DGM
UDP 端口 138
广播通告自身服务能力

会话服务 NetBIOS-SSN
TCP 端口 139
建立会话、访问文件目录

SMB 协议运行于其上
文件共享、打印机访问等

2.3 三大服务详细说明

名称服务(NBNS,UDP 137)

类似 DNS,但用于局域网内的 NetBIOS 名称解析:

NBNS服务器(域控) 客户端 NBNS服务器(域控) 客户端 注册请求(我叫 FILE-SRV,IP是xxx) 正面响应(OK,名字已注册)或 负面响应(名字已被占用) 查询请求(FILE-SRV 的IP是什么?) 应答(IP = 10.1.70.95)或 错误码3(名字不存在)
数据报服务(NBDS,UDP 138)

设备定期广播自己提供哪些服务,所有人都能收到:

172.16.100.10  (FILE-SRV)  广播:我是 工作站 + 文件服务器 + SQL服务器
172.16.100.204 (GOLF)      广播:我是 工作站 + 文件服务器 + 打印队列服务器
会话服务(NBSS,TCP 139)

用于实际的文件操作,SMB 协议就跑在这一层上。

三、SMB 协议详解

3.1 什么是 SMB?

SMB(Server Message Block,服务器消息块)是在 NetBIOS 会话层之上运行的协议,主要用于:

  • 浏览和访问共享目录
  • 复制/读写文件
  • 访问网络打印机
  • 进程间通信(IPC)

CIFS(Common Internet File System)是 SMB 的一个变体/别名,本质相同。
SMB 可以跑在两个地方:

  • TCP 139(通过 NetBIOS 会话层)
  • TCP 445(直接运行,不需要 NetBIOS,更现代)

3.2 SMB 版本演进

SMB 1.0  (早期版本,命令繁多,效率低)
    |
    v
SMB 2.0  (2006年,Windows Vista,简化命令,微软后开放标准)
    |
    v
SMB 2.1  (Windows 7 / Server 2008 R2,性能提升)
    |
    v
SMB 3.0  (Windows 8 / Server 2012,大幅性能提升,支持虚拟化环境)

3.3 SMB 工作模型

SMB 是客户端-服务器模型:

文件服务器(NAS01) 客户端(Windows PC) 文件服务器(NAS01) 客户端(Windows PC) TCP 连接(端口 445 或 139) 连接确认 SMB 协商(版本协商) 协商响应 Session Setup(认证请求) Session Setup Response(认证结果) Tree Connect(连接到共享目录 \\NAS01\HOMEDIR) Tree Connect Response(成功/拒绝) 文件操作请求(Create/Read/Write/Close) 文件操作响应(数据/错误码)

四、NetBIOS/SMB 故障分析

4.1 通用排查步骤

第一步:先看全局
用 Wireshark 接入网络,检查以下几点:

1. NBNS 广播是否正常(nbns.flags.response == 0 过滤请求包)
2. 广播频率是否过高(正常:每设备 5~10 次/分钟)
3. 是否有 169.254.x.x 地址(APIPA地址,说明该PC没拿到DHCP地址)
4. 是否有异常的扫描行为(某些蠕虫病毒会大量发 NBNS 查询)

第二步:过滤 SMB 错误

# 过滤所有 SMB 错误(状态码不为0即为错误)
smb.nt_status != 0x0
# 过滤 SMB2 错误
smb2.nt_status != 0x0

常见 SMB 错误码:

错误码 名称 含义 常见原因
0x0 STATUS_OK 一切正常 正常操作
0xC0000022 STATUS_ACCESS_DENIED 访问被拒绝 权限不足,账户问题
0xC0000034 STATUS_OBJECT_NAME_NOT_FOUND 文件不存在 路径错误
0xC000006D STATUS_LOGON_FAILURE 登录失败 密码错误
0x00000016 STATUS_MORE_PROCESSING_REQUIRED 需要更多处理 可能是凭据问题

4.2 Wireshark 过滤命令汇总

# NetBIOS 通用
netbios
# 名称服务(端口137)
nbns
nbns.flags.response == 0    过滤所有NBNS查询请求
nbns.flags.response == 1    过滤所有NBNS响应
# 数据报服务(端口138)
nbds
# 会话服务(端口139)
nbss
# SMB 1.x
smb
smb.nt_status != 0x0        所有SMB错误
# SMB 2.x/3.x
smb2
smb2.nt_status != 0x0       所有SMB2错误

五、真实案例分析

案例一:应用程序卡死(Freeze)

现象: 用户打开某个网络应用,程序卡住不动。
Wireshark 分析流程:

文件服务器(10.1.70.95) 客户端(203.x.x.x) 文件服务器(10.1.70.95) 客户端(203.x.x.x) 应用程序卡在这里等待... 等待超时后... 用户看到:程序无响应/闪退 尝试连接 \\NAS01\SAMIM STATUS_ACCESS_DENIED(访问拒绝!) TCP Keep-Alive(保持连接不断开) TCP Keep-Alive ACK Disconnect(断开请求) 确认断开 TCP RST(强制重置连接)

根本原因: 账户没有访问该共享目录的权限,但程序没有给用户友好的提示,只是一直等待,表现为"卡死"。
解决方法: 检查并修复 Windows 文件共享权限(文件夹权限 + 共享权限都要检查)。

案例二:广播风暴导致网络中断

现象: 某分公司突然完全断网,连本地网关都 ping 不通。
网络结构:

分公司子网: 172.30.121.0/24
  网关: 172.30.121.254
  |
  | MPLS L3 VPN
  |
总部子网: 172.30.0.0/24

排查经过:

步骤1: ping 总部服务器 → 无响应
步骤2: 联系运营商    → 运营商监控显示线路正常(没有流量负载)
步骤3: ping 本地网关 172.30.121.254 → 无响应!
       → 说明问题在本地局域网,不是WAN
步骤4: Wireshark 接到网关端口 → 发现异常!

Wireshark 发现的问题:

源IP: 172.30.121.1
目标: 广播地址(255.255.255.255)
协议: SMB Mailslot(SMB邮槽协议)
操作: Write Mail Slot(写邮槽)
速率: 5000 包/秒 = 10 Mbps ← 直接打满了路由器端口!

流量计算:
设每个广播包约 200 字节,速率为 5000 包/秒:
带宽消耗=5000×200×8≈8,000,000 bps=8 Mbps\text{带宽消耗} = 5000 \times 200 \times 8 \approx 8,000,000 \text{ bps} = 8 \text{ Mbps}带宽消耗=5000×200×88,000,000 bps=8 Mbps
一个 10 Mbps 的路由器端口就此被打满,其他所有流量(包括 ping、DNS、正常上网)全部被挤出去。
根因: 一个叫 LS3Bcast.exe 的服务程序出现 bug,疯狂发送 SMB 广播包。
解决方法: 找到并停止 LS3Bcast.exe 服务,防止其自动重启。
广播风暴 ASCII 示意:

正常状态(广播很少):
时间轴 ──────────────────────────────────>
广播包  .    .    .    .    .    .    .
        (稀少,每设备约5~10次/分钟)
广播风暴状态:
时间轴 ──────────────────────────────────>
广播包  ||||||||||||||||||||||||||||||||||||
        (5000包/秒,路由器/交换机被打爆)
        所有正常流量 → 全部丢弃!

六、数据库流量与网络延迟分析

6.1 问题背景

当用户抱怨"数据库查询很慢"时,可能有三种原因:

数据库查询慢

问题在哪里?

网络延迟高
(WAN链路、丢包)

服务器性能差
(CPU/内存/磁盘)

应用程序效率低
(查询包次数太多)

6.2 延迟对数据库性能的影响

假设客户端和数据库服务器之间的往返延迟(RTT)为 35ms,完成一次查询需要 371 个来回包:
总耗时≈371×35ms=12,985ms≈13秒\text{总耗时} \approx 371 \times 35\text{ms} = 12,985\text{ms} \approx 13\text{秒}总耗时371×35ms=12,985ms13
加上可能的重传和低效操作,用户实际等待时间可能达到 10~15 秒甚至更长
这说明:即使网络本身没问题,高延迟链路上"来回次数太多"就会导致严重的性能问题。

6.3 排查问题的提问清单

在开始抓包之前,先问清楚以下问题:

问题 目的
问题是全局还是局部?(所有人还是某些人) 判断是WAN带宽还是特定用户/应用问题
所有应用都慢,还是只有这个数据库应用? 判断是网络问题还是特定应用问题
问题总是有,还是特定时间出现? 判断是否和高峰时段、特定操作相关
客户端和服务器在同一地点吗? 判断是LAN还是WAN链路问题

6.4 用 Wireshark 定位数据库延迟

步骤一: 用 TCP 流追踪(Follow TCP Stream)看整个会话
步骤二: 观察每个请求-响应之间的时间间隔

包编号    时间          说明
------    -----         ------
100       0.000         客户端发出DB查询
101       0.035         服务器响应(35ms后)
102       0.035         客户端发出下一个请求
103       0.070         服务器响应
...
(如此重复371次)

步骤三: 用 TCP 对话统计找出高重传率的连接
正常内网重传率:0.1%0.1\%0.1%0.5%0.5\%0.5%
超过此范围说明有网络问题,需要先解决网络层问题再分析应用层。

6.5 数据库依赖链问题

         用户
          |
          | (看起来正常)
          v
     数据库服务器
          |
          | (这里才是瓶颈!)
          v
     文件服务器(共享存储)

重要提醒:客户端连数据库服务器可能很快,但数据库服务器去读共享文件服务器很慢,这时瓶颈在"数据库服务器到文件服务器"这段,必须把 Wireshark 接到这段链路才能发现。

6.6 解决方案


问题类型 解决方案
WAN 延迟高(35ms以上) 减少数据库来回包次数(DBA优化查询),或改用终端服务/Web访问
WAN 带宽不足 升级带宽,或压缩传输数据
重传率过高 检查链路质量,修复网络层问题
应用本身效率低 DBA 优化 SQL 查询,减少 N+1 查询问题

七、导出 SMB 对象(Export SMB Objects)

7.1 功能说明

Wireshark 可以从抓包文件中提取通过 SMB 传输的文件(图片、文档、可执行文件等),这对于:

  • 网络取证(查看传输了什么文件)
  • 备份验证
  • 安全审计
    非常有用。

7.2 操作步骤

Wireshark 菜单:
File(文件)
  └─ Export Objects(导出对象)
       └─ SMB/SMB2
            └─ 弹出对象列表窗口

对象列表中的字段含义:

字段 含义
Packet 该对象对应的报文编号
Hostname 被访问的服务器名称
Content Type FILE(文件)或 PIPE(命名管道/IPC通信)
Size 文件大小
Filename 文件路径和名称

Content Type 说明:

FILE → 实际的共享文件(文档、图片、可执行文件等)
PIPE → 命名管道,用于IPC(进程间通信)
       例如:微软 RPC over SMB 就使用命名管道

7.3 前提条件

在使用此功能前,需要在 TCP 首选项里开启流重组:

Wireshark 菜单:
Edit(编辑)→ Preferences(首选项)
  └─ Protocols(协议)
       └─ TCP
            └─ 勾选 "Allow subdissector to reassemble TCP streams"
                    (允许子解析器重组 TCP 流)

八、完整 C++ 代码:SMB 错误码查询工具

#include <iostream>
#include <string>
#include <map>
#include <iomanip>
#include <sstream>
// SMB NT 状态码查询工具
// NT 状态码是 32 位整数,高2位表示严重程度
// 格式:SSFF xxxx xxxx xxxx xxxx xxxx xxxx xxxx
//       SS: 严重程度(00=成功, 01=信息, 10=警告, 11=错误)
//       FF: Facility(设施码)
//       后28位: 具体错误编号
// 获取严重程度(取最高2位)
std::string getSeverity(uint32_t code) {
    // 右移30位取最高2位
    uint32_t sev = code >> 30;
    switch (sev) {
        case 0: return "成功(SUCCESS)";
        case 1: return "信息(INFORMATIONAL)";
        case 2: return "警告(WARNING)";
        case 3: return "错误(ERROR)";
        default: return "未知";
    }
}
// 建立常见 SMB/NT 状态码数据库
std::map<uint32_t, std::string> buildNtStatusMap() {
    std::map<uint32_t, std::string> m;
    // 成功码(0x0开头)
    m[0x00000000] = "STATUS_SUCCESS / STATUS_OK - 操作成功";
    m[0x00000016] = "STATUS_MORE_PROCESSING_REQUIRED - 需要更多处理(常见于NTLM认证过程,可能是凭据问题)";
    m[0x0000007B] = "STATUS_OBJECT_NAME_INVALID - 对象名称无效";
    // 警告码(0x4/0x8开头)
    m[0x40000006] = "STATUS_WORKING_SET_QUOTA - 工作集配额不足";
    m[0x80000005] = "STATUS_BUFFER_OVERFLOW - 缓冲区溢出(数据被截断,不一定是错误)";
    m[0x80000006] = "STATUS_NO_MORE_FILES - 没有更多文件(目录枚举结束,正常)";
    // 错误码(0xC开头)
    m[0xC0000001] = "STATUS_UNSUCCESSFUL - 操作未成功";
    m[0xC0000002] = "STATUS_NOT_IMPLEMENTED - 功能未实现";
    m[0xC0000005] = "STATUS_ACCESS_VIOLATION - 访问违规";
    m[0xC0000008] = "STATUS_INVALID_HANDLE - 无效句柄";
    m[0xC0000017] = "STATUS_NO_MEMORY - 内存不足";
    m[0xC000001C] = "STATUS_INVALID_DEVICE_REQUEST - 无效设备请求";
    m[0xC0000022] = "STATUS_ACCESS_DENIED - 访问被拒绝(权限不足,检查共享和NTFS权限)";
    m[0xC0000023] = "STATUS_BUFFER_TOO_SMALL - 缓冲区太小";
    m[0xC0000034] = "STATUS_OBJECT_NAME_NOT_FOUND - 对象/文件不存在(检查路径是否正确)";
    m[0xC0000035] = "STATUS_OBJECT_NAME_COLLISION - 对象名称冲突(文件已存在)";
    m[0xC000003A] = "STATUS_OBJECT_PATH_NOT_FOUND - 路径不存在";
    m[0xC000006D] = "STATUS_LOGON_FAILURE - 登录失败(用户名或密码错误)";
    m[0xC000006E] = "STATUS_ACCOUNT_RESTRICTION - 账户受限制";
    m[0xC0000070] = "STATUS_INVALID_WORKSTATION - 工作站无效";
    m[0xC0000071] = "STATUS_INVALID_LOGON_HOURS - 登录时间段无效";
    m[0xC0000072] = "STATUS_ACCOUNT_DISABLED - 账户已禁用(联系管理员启用账户)";
    m[0xC000007F] = "STATUS_DISK_FULL - 磁盘已满(服务器存储空间不足)";
    m[0xC0000101] = "STATUS_DIRECTORY_NOT_EMPTY - 目录非空(无法删除非空目录)";
    m[0xC0000103] = "STATUS_NOT_A_DIRECTORY - 不是目录";
    m[0xC000013B] = "STATUS_UNEXPECTED_NETWORK_ERROR - 意外的网络错误";
    m[0xC00000BB] = "STATUS_NOT_SUPPORTED - 操作不受支持";
    m[0xC00000CC] = "STATUS_BAD_NETWORK_NAME - 网络名称错误(共享路径不存在)";
    m[0xC00000D0] = "STATUS_REQUEST_NOT_ACCEPTED - 请求未被接受(服务器繁忙)";
    return m;
}
// 将 uint32_t 格式化为十六进制字符串
std::string toHex(uint32_t val) {
    std::stringstream ss;
    ss << "0x" << std::uppercase << std::hex << std::setw(8)
       << std::setfill('0') << val;
    return ss.str();
}
// 解析并显示 NT 状态码
void parseNtStatus(uint32_t code) {
    auto statusMap = buildNtStatusMap();
    std::cout << "==============================" << std::endl;
    std::cout << " NT 状态码解析:" << toHex(code) << std::endl;
    std::cout << "==============================" << std::endl;
    // 严重程度(最高2位)
    std::cout << " 严重程度: " << getSeverity(code) << std::endl;
    // 设施码(第16~28位)
    uint32_t facility = (code >> 16) & 0x0FFF;
    std::cout << " 设施码 (Facility): " << facility << std::endl;
    // 错误编号(低16位)
    uint32_t errCode = code & 0xFFFF;
    std::cout << " 错误编号: " << errCode << " (0x"
              << std::hex << errCode << std::dec << ")" << std::endl;
    // 查表
    auto it = statusMap.find(code);
    if (it != statusMap.end()) {
        std::cout << " 含义说明: " << it->second << std::endl;
    } else {
        std::cout << " 含义说明: (未在常用列表中)" << std::endl;
        std::cout << "   参考: https://msdn.microsoft.com/en-us/library/ee441884.aspx"
                  << std::endl;
    }
    // 操作建议
    std::cout << " 处理建议: ";
    uint32_t sev = code >> 30;
    if (sev == 0) {
        std::cout << "操作成功,无需处理。" << std::endl;
    } else if (sev == 1) {
        std::cout << "信息性状态,通常无需处理。" << std::endl;
    } else if (sev == 2) {
        std::cout << "警告,可能需要关注但不影响操作。" << std::endl;
    } else {
        std::cout << "错误!需要排查。检查权限、路径、账户状态等。" << std::endl;
    }
    std::cout << std::endl;
}
int main() {
    std::cout << "========================================" << std::endl;
    std::cout << "  SMB NT 状态码解析工具" << std::endl;
    std::cout << "========================================" << std::endl << std::endl;
    // 演示常见错误码
    uint32_t demoCodes[] = {
        0x00000000,   // STATUS_SUCCESS
        0x00000016,   // STATUS_MORE_PROCESSING_REQUIRED
        0xC0000022,   // STATUS_ACCESS_DENIED
        0xC000006D,   // STATUS_LOGON_FAILURE
        0xC00000CC,   // STATUS_BAD_NETWORK_NAME
        0xC0000034    // STATUS_OBJECT_NAME_NOT_FOUND
    };
    for (uint32_t code : demoCodes) {
        parseNtStatus(code);
    }
    // 交互模式
    std::cout << "------------------------------" << std::endl;
    std::cout << "输入十六进制状态码查询(例如 C0000022),输入 0 退出:" << std::endl;
    std::string input;
    while (true) {
        std::cout << "> ";
        std::cin >> input;
        if (input == "0") {
            std::cout << "退出程序。" << std::endl;
            break;
        }
        // 解析十六进制输入(允许带或不带0x前缀)
        try {
            // 去掉可能的 0x 前缀
            if (input.size() > 2 &&
                (input.substr(0,2) == "0x" || input.substr(0,2) == "0X")) {
                input = input.substr(2);
            }
            uint32_t code = static_cast<uint32_t>(std::stoul(input, nullptr, 16));
            parseNtStatus(code);
        } catch (...) {
            std::cout << "输入无效,请输入十六进制数(如 C0000022)。" << std::endl;
        }
    }
    return 0;
}

编译和运行:

g++ -std=c++17 -o smb_status smb_status.cpp
./smb_status

输出示例:

==============================
 NT 状态码解析:0xC0000022
==============================
 严重程度: 错误(ERROR)
 设施码 (Facility): 0
 错误编号: 34 (0x22)
 含义说明: STATUS_ACCESS_DENIED - 访问被拒绝(权限不足,检查共享和NTFS权限)
 处理建议: 错误!需要排查。检查权限、路径、账户状态等。

九、综合知识树

NetBIOS 与 SMB 协议体系
|
+-- NetBIOS 三大服务
|   |
|   +-- NBNS(UDP 137)名称服务
|   |     |-- 注册:客户端告诉域控"我叫XXX"
|   |     +-- 查询:谁叫XXX,它的IP是什么?
|   |
|   +-- NBDS(UDP 138)数据报服务
|   |     +-- 广播通告自己提供的服务(文件、打印等)
|   |
|   +-- NBSS(TCP 139)会话服务
|         +-- SMB 在此之上运行(文件读写等)
|
+-- SMB 协议
|   |
|   +-- 版本:1.0 → 2.0 → 2.1 → 3.0
|   |
|   +-- 运行端口:TCP 139(经由NetBIOS)/ TCP 445(直连)
|   |
|   +-- 主要功能
|   |   |-- 共享目录访问
|   |   |-- 文件读写复制
|   |   |-- 打印机访问
|   |   +-- IPC(命名管道通信)
|   |
|   +-- 错误码(NT Status)
|       |-- 0x00000000 成功
|       |-- 0xC0000022 访问拒绝
|       |-- 0xC000006D 登录失败
|       +-- 0xC00000CC 网络名称错误
|
+-- 常见故障
|   |
|   +-- 应用程序卡死
|   |     原因:权限拒绝后程序无限等待
|   |
|   +-- 广播风暴
|   |     原因:程序bug疯狂发SMB广播
|   |     影响:打满带宽,整网瘫痪
|   |
|   +-- 数据库查询慢
|         原因:WAN延迟 × 大量来回包 = 极长等待时间
|         公式:总耗时 ≈ 包次数 × 单次RTT
|
+-- 排查工具
    |-- Wireshark 过滤器
    |-- SMB 对象导出(File→Export Objects→SMB)
    +-- TCP 流追踪(Follow TCP Stream)

企业应用行为分析详解(从零开始)

一、为什么要分析企业应用?

在企业网络中,应用变慢的原因通常有四种:

应用变慢
|
+-- LAN(局域网)问题       → 有线LAN中极少见
|
+-- WAN(广域网)问题       → 最常见,带宽不足或延迟高
|
+-- 服务器/客户端性能差      → CPU、内存、磁盘不足
|
+-- 应用本身的问题          → 程序设计低效、Bug

Wireshark 是定位这些问题的核心工具。本章涉及四类企业应用:

  • 网络协议识别
  • 终端服务(RDP / Citrix)
  • 数据库流量
  • SNMP 网络管理协议

二、识别网络上运行的应用

2.1 为什么要先识别应用?

接手一个新网络时,首先要搞清楚"线上跑的是什么"。不同应用会相互影响,带宽竞争会导致彼此都变慢。
几个重要概念:

概念 含义 位置
VLAN 虚拟局域网,OSI 1-2 层隔离 交换机上配置
VRF 虚拟路由转发,多个独立路由表实例 路由器上配置
Blade Server 刀片服务器,多块服务器板卡 + 后置交换机的机箱 数据中心

VRF 类比:一栋楼里有多家公司,各自有独立的门禁系统(路由表),互相看不见对方,但共用同一栋楼(同一台路由器)。这在运营商 MPLS 网络中很常见。

2.2 Wireshark 工具:协议层级(Protocol Hierarchy)

菜单路径:Statistics → Protocol Hierarchy
这个功能会展示当前捕获中所有协议的流量占比,像一棵树一样展开:

以太网 Ethernet (100%)

IPv4 (85%)

LLC逻辑链路控制 (10%)

Loopback测试 (5%)

TCP (60%)

UDP (20%)

PIM组播 (3%)

IGMP (2%)

HTTP (30%)

SMB (15%)

数据库协议 (10%)

XML / MIME / JS

2.3 Wireshark 工具:对话统计(Conversations)

菜单路径:Statistics → Conversations
列出所有"谁在和谁说话",按协议分类(Ethernet / IP / TCP / UDP),可以看到:

  • 源 IP、目标 IP
  • 数据量
  • 使用的端口(即什么应用)

2.4 抓包位置选择


要监控的对象 Wireshark 接入位置
某台服务器 端口镜像(Port Mirror)到该服务器的交换机端口
远程分公司 端口镜像到连接 WAN 的路由器端口
互联网出口 端口镜像到互联网出口链路

三、终端服务分析(RDP / Citrix)

3.1 什么是终端服务?

终端服务让用户的电脑(或瘦客户端)连接到远程服务器,在服务器上运行应用,本地屏幕只显示"画面变化"。

RDP / ICA协议
传输屏幕变化

正常协议
数据库请求等

文件访问

用户PC / 瘦客户端

终端服务器
Terminal Server

数据库服务器

文件服务器

两种主要实现:

产品 协议 端口
Microsoft Terminal Server RDP(远程桌面协议) TCP 3389
Citrix Metaframe ICA(独立计算架构) TCP 1494 或 2598

3.2 终端服务的流量特征

终端服务传输的是屏幕变化,所以流量高度不对称:

下行(服务器→客户端):几十 Kbps ~ 几 Mbps(屏幕内容)
上行(客户端→服务器):最多几 Kbps(鼠标点击、键盘输入)

不同场景的带宽消耗:

使用场景 下行带宽需求
纯文字 ERP 界面 几十~几百 Kbps
普通 Word 文档 几十~几百 Kbps
PowerPoint 编辑 几百 Kbps ~ 几 Mbps
PowerPoint 全屏播放(F5) 可达 8~10 Mbps!
网页浏览 几百 Kbps ~ 几 Mbps
视频播放 极高,不建议在终端服务上用

3.3 故障排查思路

用户反映"慢"时,先问问题,区分根因:

切换窗口很慢
画面卡顿、滚动慢

生成报表很慢
但窗口切换正常

带宽打满

服务器卡

用户说:慢!

切换窗口慢还是
数据处理慢?

是终端服务问题
检查网络带宽和延迟

不是终端服务问题
是数据库或服务器问题

是网络问题
还是服务器问题?

升级链路带宽

检查服务器内存RAM
终端服务最耗内存

高延迟链路的特殊问题:
当用户通过高延迟线路(如卫星链路、国际专线)使用终端服务时,快速打字会出现字符延迟——因为每次按键都要等服务器返回屏幕更新,延迟越高越明显。

3.4 用 Wireshark 监控终端服务带宽

在 I/O Graphs 中配置:

  • Y 轴:Bits/Tick(比特/时间片)
  • 用过滤器分别监控上行和下行流量
    正常模式下,下行远大于上行。当下行触顶时,用户开始感受到屏幕卡顿。

四、数据库流量分析

4.1 网络工程师和数据库的关系

用户说:数据库好慢!→ 找网络工程师
                          |
              +-----------+-----------+
              |                       |
       真的是网络问题            其实是应用/DB问题
    (丢包/延迟/带宽不足)      (SQL低效/服务器性能差)
              |                       |
         我们来解决              转给DBA处理

我们的任务:证明网络没问题,或者找到网络层面的证据帮 DBA 定位

4.2 排查前先问清楚


问题 判断依据
所有人慢还是个别人慢? 个别人 → 特定客户端/应用问题
所有应用慢还是只有这个数据库? 只有这个 → 数据库或其服务器问题
远程办公室也慢还是总部也慢? 都慢 → 不是 WAN 带宽问题
客户端和服务器之间的链路满了吗? 是 → 带宽不足

4.3 网络延迟对数据库的影响

这是最关键的部分。假设:

  • 客户端和数据库服务器的往返延迟(RTT)= 35ms
  • 完成一次完整查询需要 371 个数据包来回
    则总等待时间约为:
    总等待时间≈371×35ms=12,985ms≈13秒\text{总等待时间} \approx 371 \times 35\text{ms} = 12{,}985\text{ms} \approx 13\text{秒}总等待时间371×35ms=12,985ms13
    加上偶发的重传和低效操作,用户实际感受可能是 10~15 秒甚至更久
    这说明:网络延迟本身没问题(35ms也不算差),但数据库应用"来回包次数"太多,把延迟放大了几百倍。

4.4 TCP 重传率参考


网络类型 正常重传率
内部企业网络 0.1%0.1\%0.1%0.5%0.5\%0.5%
互联网连接 高得多,属正常

超出内网正常范围则需要先解决网络层问题。

4.5 特殊情况:软件问题被误判为网络问题

有时候 Wireshark 里看到大量重传,但仔细分析会发现:

  • 重传只发生在某个特定软件窗口/操作
  • 随后客户端主动重新建立了 TCP 连接
    这通常说明是软件 Bug:某个处理流程卡死,导致 TCP 长时间没有响应,触发了超时重传,最终客户端断开重连。

4.6 数据库架构依赖链(重要!)

       用户客户端
           |
           | 看起来这段很正常
           v
      数据库服务器
           |
           | 这段才是瓶颈!(但容易被忽视)
           v
    共享文件服务器(NAS)
    (数据库的数据文件存在这里)

必须确认所有依赖关系再开始测试,否则会测错位置。

4.7 解决方案


根因 解决方案
WAN 延迟高 × 包次数多 1. DBA 优化查询减少来回包 2. 改用终端服务/Web访问
带宽不足 升级 WAN 链路
数据库服务器到文件服务器慢 优化存储网络,或将数据库迁移到本地存储
软件 Bug 导致 TCP 超时 联系软件开发商修复

五、SNMP 协议分析

5.1 什么是 SNMP?

SNMP(Simple Network Management Protocol,简单网络管理协议)是用来监控和管理网络设备的协议。

打个比方:SNMP 就像是工厂里的"传感器+报警系统"——每台设备(交换机、路由器、服务器)上都有一个小探针,定期把自己的运行状态报告给中央监控室。

5.2 SNMP 三大组件

收集并存储数据

UDP 161
定期上报数据 + 事件告警

查询/配置

被管理设备
Managed Device
(路由器、交换机、服务器等)

SNMP 代理
Agent
运行在设备上的软件

MIB 数据库
Managed Information Base
设备本地的统计数据仓库

SNMP 服务器
NMS(网络管理服务器)
中央监控平台

5.3 SNMP 三个版本对比


版本 特点 安全性
SNMPv1 最早版本,功能简单 无加密,仅社区字符串(明文)
SNMPv2c 性能改进,增加批量查询 无加密,仍用社区字符串
SNMPv3 当前推荐版本 支持认证 + 加密,安全性高

5.4 MIB 和 OID 概念

MIB(管理信息库):定义了设备可以被查询的所有信息的"目录"。
OID(对象标识符):MIB 中每个数据项的唯一编号,用数字串表示,像文件路径一样层级展开。
举例:

.1.3.6.1.2.1.2.2.1.16
 |___________|___|__|__|
   固定前缀   接口表 出字节数
.1.3.6.1.2.1.2.2.1.16.1   → 第1个接口的出字节数(ifOutOctets接口1)
.1.3.6.1.2.1.2.2.1.16.2   → 第2个接口的出字节数(ifOutOctets接口2)

5.5 SNMP 工作流程

被管设备Agent (10.81.64.22) NMS服务器 (172.18.254.139) 被管设备Agent (10.81.64.22) NMS服务器 (172.18.254.139) 正常轮询(SNMP Walk) 设备主动告警 错误场景:查询不存在的OID GET-NEXT-REQUEST(获取下一个OID的值) GET-RESPONSE(返回数据) GET-NEXT-REQUEST(继续查下一个) GET-RESPONSE(返回数据) TRAP(异常事件,如端口Down) GET-REQUEST(查询第3个接口,实际只有2个) noSuchInstance(该实例不存在!)

5.6 SNMP 故障排查思路

第一步:先问清楚问题性质

问题一:这是新设备还是以前正常工作过的设备?
|
+-- 新设备 → 检查配置
|   |-- 社区字符串(Community String)是否填写正确?
|   |-- NMS 的 IP 地址是否配置对?
|   |-- SNMPv3 的加密参数是否匹配?
|   +-- 网络是否互通?(先 ping 一下)
|
+-- 以前正常,现在不行 → 检查变化
    |-- 设备自身有没有故障(控制平面/管理平面问题)?
    |-- 网络中间是否发生了中断(如 STP 收敛导致短暂断流)?
    +-- 是单个设备不报还是多个设备都不报?

注意:SNMP 是低优先级协议
大多数网络设备把 SNMP 处理放在低优先级队列。当设备繁忙时(高 CPU、大量路由计算等),SNMP 响应会被延迟甚至丢弃。这会导致 NMS 误判设备"失联"。

5.7 常见 SNMP 故障类型


故障类型 现象 原因 解决方法
版本不匹配 NMS 无响应 NMS 用 v2c 查,设备只支持 v1 统一 SNMP 版本
社区字符串错误 NMS 无响应 密码填错 核对社区字符串
OID 不存在 返回 noSuchInstance 查询了不存在的接口/对象 确认设备支持的 OID
网络不通 完全无响应 防火墙拦截 UDP 161 放通 UDP 161 端口
设备繁忙 偶发丢失 SNMP 被高优先级任务挤占 降低轮询频率
STP 收敛 短暂丢失 链路重新收敛期间流量中断 正常现象,等待恢复

六、完整 C++ 代码:SNMP OID 解析与 MIB 查询工具

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
// SNMP OID 解析工具
// OID(Object Identifier)是由数字和点组成的层次路径
// 例如:.1.3.6.1.2.1.2.2.1.16.1
//        |___________________|_|
//          标准MIB前缀         实例索引(第几个接口)
// 将 OID 字符串拆分为数字数组
// 例如 "1.3.6.1.2.1" → {1, 3, 6, 1, 2, 1}
std::vector<int> parseOID(const std::string& oid) {
    std::vector<int> parts;
    std::stringstream ss(oid);
    std::string token;
    while (std::getline(ss, token, '.')) {
        if (!token.empty()) {
            try {
                parts.push_back(std::stoi(token));
            } catch (...) {
                // 忽略非数字部分
            }
        }
    }
    return parts;
}
// 将数字数组还原为 OID 字符串
std::string formatOID(const std::vector<int>& parts) {
    std::string result;
    for (size_t i = 0; i < parts.size(); i++) {
        if (i > 0) result += ".";
        result += std::to_string(parts[i]);
    }
    return result;
}
// 建立常见 OID 含义数据库
// key: OID前缀字符串, value: 说明
std::map<std::string, std::string> buildOIDMap() {
    std::map<std::string, std::string> m;
    // 系统信息(sysDescr等)
    m["1.3.6.1.2.1.1.1"]     = "sysDescr - 系统描述(设备型号、OS版本等)";
    m["1.3.6.1.2.1.1.2"]     = "sysObjectID - 设备厂商OID标识";
    m["1.3.6.1.2.1.1.3"]     = "sysUpTime - 系统运行时间(单位:百分之一秒)";
    m["1.3.6.1.2.1.1.4"]     = "sysContact - 联系人信息";
    m["1.3.6.1.2.1.1.5"]     = "sysName - 设备名称(hostname)";
    m["1.3.6.1.2.1.1.6"]     = "sysLocation - 设备物理位置";
    // 接口信息表(ifTable)
    m["1.3.6.1.2.1.2.1"]     = "ifNumber - 接口数量";
    m["1.3.6.1.2.1.2.2.1.1"] = "ifIndex - 接口索引编号";
    m["1.3.6.1.2.1.2.2.1.2"] = "ifDescr - 接口描述(如 GigabitEthernet0/1)";
    m["1.3.6.1.2.1.2.2.1.3"] = "ifType - 接口类型(以太网=6,串口=22等)";
    m["1.3.6.1.2.1.2.2.1.5"] = "ifSpeed - 接口速率(bps)";
    m["1.3.6.1.2.1.2.2.1.7"] = "ifAdminStatus - 管理状态(1=up, 2=down)";
    m["1.3.6.1.2.1.2.2.1.8"] = "ifOperStatus - 实际运行状态(1=up, 2=down)";
    m["1.3.6.1.2.1.2.2.1.10"] = "ifInOctets - 接口接收字节总数(入流量)";
    m["1.3.6.1.2.1.2.2.1.16"] = "ifOutOctets - 接口发送字节总数(出流量)";
    m["1.3.6.1.2.1.2.2.1.13"] = "ifInDiscards - 接口入方向丢弃包数";
    m["1.3.6.1.2.1.2.2.1.14"] = "ifInErrors - 接口入方向错误包数";
    m["1.3.6.1.2.1.2.2.1.19"] = "ifOutDiscards - 接口出方向丢弃包数";
    m["1.3.6.1.2.1.2.2.1.20"] = "ifOutErrors - 接口出方向错误包数";
    // IP 信息
    m["1.3.6.1.2.1.4.1"]     = "ipForwarding - 是否开启IP转发(路由功能)";
    m["1.3.6.1.2.1.4.3"]     = "ipInReceives - 收到的IP包总数";
    m["1.3.6.1.2.1.4.5"]     = "ipInDiscards - 丢弃的IP入包数";
    // TCP 统计
    m["1.3.6.1.2.1.6.9"]     = "tcpCurrEstab - 当前已建立的TCP连接数";
    m["1.3.6.1.2.1.6.10"]    = "tcpInSegs - 接收的TCP段总数";
    m["1.3.6.1.2.1.6.11"]    = "tcpOutSegs - 发送的TCP段总数";
    m["1.3.6.1.2.1.6.12"]    = "tcpRetransSegs - 重传的TCP段总数(重要!过高说明网络有问题)";
    // UDP 统计
    m["1.3.6.1.2.1.7.1"]     = "udpInDatagrams - 接收的UDP数据报总数";
    m["1.3.6.1.2.1.7.4"]     = "udpOutDatagrams - 发送的UDP数据报总数";
    return m;
}
// 查询 OID 含义(支持带实例索引的OID)
// 例如 1.3.6.1.2.1.2.2.1.16.1 → 先精确匹配,再去掉最后一段做前缀匹配
std::string lookupOID(const std::string& oid,
                      const std::map<std::string, std::string>& oidMap) {
    // 精确匹配
    auto it = oidMap.find(oid);
    if (it != oidMap.end()) {
        return it->second;
    }
    // 去掉最后一个数字(实例索引)再查
    size_t lastDot = oid.rfind('.');
    if (lastDot != std::string::npos) {
        std::string prefix = oid.substr(0, lastDot);
        std::string instance = oid.substr(lastDot + 1);
        auto it2 = oidMap.find(prefix);
        if (it2 != oidMap.end()) {
            return it2->second + " [实例索引=" + instance + "]";
        }
    }
    return "(未在常用MIB列表中,可能是厂商私有OID)";
}
// 模拟 SNMP 轮询场景演示
void demonstratePolling() {
    std::cout << "\n===== 模拟 SNMP 轮询场景 =====" << std::endl;
    struct PollResult {
        std::string oid;
        std::string value;
        bool success;
    };
    // 模拟 NMS 对设备的轮询结果(假设设备有2个接口)
    std::vector<PollResult> results = {
        {"1.3.6.1.2.1.1.5.0",          "Router-HQ-01",         true},
        {"1.3.6.1.2.1.1.3.0",          "1234500",              true},
        {"1.3.6.1.2.1.2.2.1.16.1",     "98234567890",          true},
        {"1.3.6.1.2.1.2.2.1.16.2",     "12345678",             true},
        {"1.3.6.1.2.1.2.2.1.16.3",     "noSuchInstance",       false},  // 第3个接口不存在
        {"1.3.6.1.2.1.6.12.0",         "1523",                 true},
    };
    auto oidMap = buildOIDMap();
    std::cout << std::string(70, '-') << std::endl;
    std::cout << " OID                              | 结果          | 含义" << std::endl;
    std::cout << std::string(70, '-') << std::endl;
    for (const auto& r : results) {
        std::string meaning = lookupOID(r.oid, oidMap);
        // 截断过长的含义字符串用于显示
        std::string shortMeaning = meaning.substr(0, 30);
        std::cout << " " << r.oid;
        // 对齐
        for (int i = r.oid.size(); i < 35; i++) std::cout << " ";
        std::cout << "| ";
        if (!r.success) {
            std::cout << "noSuchInstance    ← 错误!" << std::endl;
            std::cout << "   > 原因:查询了不存在的OID(如设备只有2个接口,却查第3个)"
                      << std::endl;
        } else {
            std::cout << r.value << std::endl;
            std::cout << "   > " << meaning << std::endl;
        }
    }
    std::cout << std::string(70, '-') << std::endl;
}
int main() {
    std::cout << "========================================" << std::endl;
    std::cout << "  SNMP OID 查询与解析工具" << std::endl;
    std::cout << "========================================" << std::endl;
    auto oidMap = buildOIDMap();
    // 演示查询几个常用OID
    std::cout << "\n===== 常用 OID 含义速查 =====" << std::endl;
    std::vector<std::string> demoOIDs = {
        "1.3.6.1.2.1.1.1",
        "1.3.6.1.2.1.2.2.1.16",
        "1.3.6.1.2.1.2.2.1.16.1",  // 带实例索引
        "1.3.6.1.2.1.6.12",
        "1.3.6.1.2.1.2.2.1.8",
    };
    for (const auto& oid : demoOIDs) {
        std::string meaning = lookupOID(oid, oidMap);
        std::cout << "\n OID: " << oid << std::endl;
        std::cout << "  => " << meaning << std::endl;
    }
    // 模拟轮询演示
    demonstratePolling();
    // 交互查询
    std::cout << "\n===== 交互查询模式 =====" << std::endl;
    std::cout << "输入 OID 查询含义(输入 exit 退出):" << std::endl;
    std::string input;
    while (true) {
        std::cout << "> ";
        std::cin >> input;
        if (input == "exit") {
            std::cout << "退出程序。" << std::endl;
            break;
        }
        // 去掉开头的点
        if (!input.empty() && input[0] == '.') {
            input = input.substr(1);
        }
        std::string result = lookupOID(input, oidMap);
        std::cout << "  => " << result << std::endl;
    }
    return 0;
}

编译和运行:

g++ -std=c++17 -o snmp_tool snmp_tool.cpp
./snmp_tool

输出示例(部分):

===== 常用 OID 含义速查 =====
 OID: 1.3.6.1.2.1.1.1
  => sysDescr - 系统描述(设备型号、OS版本等)
 OID: 1.3.6.1.2.1.2.2.1.16.1
  => ifOutOctets - 接口发送字节总数(出流量) [实例索引=1]
 OID: 1.3.6.1.2.1.6.12
  => tcpRetransSegs - 重传的TCP段总数(重要!过高说明网络有问题)
===== 模拟 SNMP 轮询场景 =====
 1.3.6.1.2.1.2.2.1.16.3    | noSuchInstance    ← 错误!
   > 原因:查询了不存在的OID(如设备只有2个接口,却查第3个)

七、综合知识树

企业应用行为分析
|
+-- 识别网络流量
|   |-- Protocol Hierarchy(协议层级分布)
|   +-- Conversations(对话统计,看谁在和谁通信)
|
+-- 终端服务(RDP / Citrix ICA)
|   |-- 特点:传输屏幕变化,流量高度不对称
|   |   下行多(屏幕内容)上行少(鼠标键盘)
|   |-- 端口:RDP=TCP 3389  ICA=TCP 1494/2598
|   |-- 常见问题
|   |   |-- 带宽打满 → 屏幕卡顿
|   |   |-- 高延迟 → 打字延迟
|   |   +-- 服务器内存不足 → 整体慢
|   +-- 排查:先问用户"窗口切换慢还是数据处理慢"
|
+-- 数据库流量
|   |-- 核心公式:总耗时 ≈ 包次数 × 单次RTT
|   |-- 内网重传率正常值:0.1% ~ 0.5%
|   |-- 常见问题
|   |   |-- 延迟×包次数 → 等待时间放大
|   |   |-- 软件Bug → 假冒TCP问题
|   |   +-- 依赖链隐藏瓶颈(DB服务器→文件服务器)
|   +-- 解决:DBA减少来回包,或改用终端/Web访问
|
+-- SNMP
    |-- 三组件:被管设备 + Agent + NMS服务器
    |-- 三版本:v1(弱)→ v2c(中)→ v3(强,加密)
    |-- 端口:UDP 161(查询)UDP 162(Trap告警)
    |-- MIB/OID:设备数据的"目录"和"地址"
    +-- 常见故障
        |-- 版本不匹配 → 无响应
        |-- 社区字符串错误 → 无响应
        |-- OID不存在 → noSuchInstance
        +-- 网络中断 → 短暂失联(STP收敛等)

第17章:SIP、多媒体与IP电话故障排查 — 从零详细解析

本文从零开始,把"IP电话是怎么打通的"以及"出了问题怎么用Wireshark查"讲得清清楚楚。

一、整体架构:两件事、两套协议

打一个网络电话,需要同时做两件事:

事情一:媒体传输(你说的话/你的视频)
         → 用 RTP 协议传输
事情二:信令控制(拨号、接听、挂断)
         → 用 SIP 协议控制

历史上有两套方案竞争,现在 IETF 方案赢了:

IP电话协议体系

ITU-T 方案
H.323 / H.225 / H.245
已逐渐淘汰

IETF 方案
现在主流

SIP
RFC 3261
负责信令控制

SDP
RFC 4566
描述会话参数

RTP
RFC 3550
负责媒体传输

RTCP
RFC 3550
负责传输质量控制

二、IP电话的基本原理

2.1 是什么

IP电话把模拟语音信号转换成IP数据包,通过网络传输。

你说话
  |
  | 模拟声音
  ↓
话筒/麦克风(模数转换)
  |
  | 数字音频
  ↓
编解码器(Codec,如G.711、G.729)
  |
  | 压缩后的音频数据
  ↓
RTP 协议打包
  |
  | RTP包 → UDP包 → IP包
  ↓
网络传输
  |
  ↓
对方收到、解包、播放声音

Wireshark 只能抓 IP 部分的包,模拟信号部分抓不到。

2.2 RTP 包结构

RTP 包头的字段含义(从高位到低位):

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|    PT       |       Sequence Number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Synchronization Source (SSRC) identifier           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Contributing source (CSRC) identifiers            |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        音频/视频数据                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

字段 大小 作用
V(Version) 2位 RTP版本号,当前为2
P(Padding) 1位 包尾是否有填充字节
X(Extension) 1位 是否有扩展头
CC(CSRC Count) 4位 贡献源数量
M(Marker) 1位 标记应用事件(如视频帧边界)
PT(Payload Type) 7位 负载类型(标识使用哪种编解码器)
Sequence Number 16位 序列号,每包加1,用于检测丢包
Timestamp 32位 时间戳,反映采样时刻
SSRC 32位 同步源标识符,唯一标识一路RTP流
CSRC 32位×CC 贡献源列表

2.3 序列号和时间戳的关系

序列号每包加1,时间戳按采样间隔增加:

包编号:   1      2      3      4      5
序列号:   1      2      3      4      5   ← 每包 +1
时间戳:   1     12     23     34     45   ← 按音频采样率增加
例如:G.711编解码器,采样率8000Hz,每包160个采样点
则时间戳每包增加 160

接收端用序列号判断包的顺序和是否丢包,用时间戳控制播放节奏(消除抖动)。

2.4 RTP 使用的端口范围

RTP 使用 UDP 作为传输层,端口范围为:
16384∼32767 16384 \sim 32767 1638432767
RTCP(控制协议)用 RTP 端口号加1:
RTCP 端口=RTP 端口+1 \text{RTCP 端口} = \text{RTP 端口} + 1 RTCP 端口=RTP 端口+1
例如 RTP 用 24950,则 RTCP 用 24951。

三、RTCP — 质量监控协议

3.1 作用

RTCP 与 RTP 配套工作,用于监控端到端通话质量,并把统计信息反馈给发送端,让发送端可以调整传输速率。

RTP媒体流

RTCP反馈报告
丢包率/抖动/延迟

RTCP发送者报告
已发多少包/多少字节

发送端

接收端

3.2 RTCP 报文类型


类型编号 名称 用途
200 Sender Report(SR) 发送端汇报:发了多少包、多少字节、时间戳
201 Receiver Report(RR) 接收端汇报:收到多少、丢了多少、抖动多少
202 Source Description(SDES) 描述源的信息(如用户名、邮件等)
203 BYE 会话结束,通知对端此SSRC退出
204 Application Specific(APP) 应用自定义报文

3.3 RTCP Sender Report 关键字段

在 Wireshark 中看 RTCP 发送者报告时,需要重点关注:

字段 正常值 含义
Fraction Lost 0 或极小值 当前周期内丢包比例(0=无丢包)
Cumulative Packets Lost 0 或极小值 整个会话累计丢包数
Inter-arrival Jitter 越小越好 包到达时间的抖动量(毫秒)

这三个值任何一个异常,都说明通话质量有问题。

四、SIP — 信令控制协议

4.1 SIP 是什么

SIP(Session Initiation Protocol,会话发起协议)负责:

  • 建立通话(拨号)
  • 修改通话(切换编解码器)
  • 终止通话(挂断)
    SIP 定义了两类角色:

角色 说明
UA(User Agent,用户代理) 终端设备,如IP电话、软件客户端、摄像头
SIP 服务器 提供注册、定位、转发等服务的网络设备

4.2 SDP — 会话描述协议

SIP 消息里的**正文(Body)**用 SDP(Session Description Protocol)格式写,告诉对方:

  • 用哪个 UDP 端口接收 RTP 音频
  • 用哪个 UDP 端口接收 RTP 视频
  • 支持哪些编解码器(G.711、G.729、H.264……)
    SDP 的核心作用就是两端"协商":我能发什么、你能收什么,最终选出双方都支持的方案。

五、SIP 完整通话流程

5.1 基本通话建立流程(两端直连)

被叫 UA 172.18.110.203 主叫 UA 172.18.110.200 被叫 UA 172.18.110.203 主叫 UA 172.18.110.200 RTP 双向媒体流开始 INVITE(携带SDP,说明我的媒体能力) 100 Trying(收到了,处理中) 180 Ringing(正在振铃) 200 OK(接听,携带SDP Answer) ACK(确认,携带最终选定的编解码器和端口) BYE(挂断) 200 OK

5.2 经过中间节点的完整流程(实际网络场景)

本章示例有三个节点:

IP电话端                   SBC/CUBE                   对端
172.18.110.200    →    172.18.110.203    →    172.18.110.206
被叫 .206 SBC .203 主叫 .200 被叫 .206 SBC .203 主叫 .200 RTP媒体流开始 音频:端口25944 和 8260 双向 INVITE 100 Trying INVITE 100 Trying 183 Session Progress 183 Session Progress 180 Ringing 180 Ringing 200 OK(含SDP,告知RTP端口25944) 200 OK(转发SDP) ACK(含SDP Answer,选定编解码器和端口8260) ACK BYE BYE 200 OK 200 OK

5.3 SDP 协商过程(Offer/Answer 模型)

第一步:主叫发 SDP Offer(在 INVITE 里)
  "我支持 G.711 和 G.729,
   请把音频发到我的 UDP 端口 8260"
第二步:被叫发 SDP Answer(在 200 OK 里)
  "我也支持 G.711,就用它吧,
   请把音频发到我的 UDP 端口 25944"
第三步:ACK 确认
  双方开始用 G.711,互相往对方端口发 RTP 包

六、SIP 响应码大全

响应码结构和 HTTP 完全一样,分六大类:

1xx → 进行中(还没完成)
2xx → 成功
3xx → 重定向(去别的地方)
4xx → 客户端错误(请求本身有问题)
5xx → 服务器错误(服务器自己的问题)
6xx → 全局失败(任何服务器都无法处理)

6.1 1xx — 临时/信息性响应


代码 名称 含义(大白话)
100 Trying “收到了,正在处理”
180 Ringing “对方电话在响铃”
181 Call Forward “呼叫被转接到别处了”
182 Queued “对方暂时没空,排队等待”
183 Session Progress “会话正在建立中,有进展”

6.2 2xx — 成功


代码 名称 含义
200 OK “成功!请求已被接受并处理完毕”
202 Accepted “已接受,但还没处理完(异步处理)”

6.3 3xx — 重定向


代码 名称 含义
300 Multiple Choices “这个地址有多个目的地,你选一个”
301 Moved Permanently “用户已永久搬到新地址了”
302 Moved Temporarily “用户临时在另一个地址”
305 Use Proxy “必须通过指定的代理访问”
380 Alternative Service “本次呼叫失败,但有替代服务可用”

6.4 4xx — 客户端错误


代码 名称 含义
400 Bad Request 请求格式/语法有错误
401 Unauthorized 需要认证,客户端还没提供凭据
403 Forbidden 服务器拒绝,即使认证了也不行
404 Not Found 被叫用户不存在
405 Method Not Allowed 这个请求方法不被允许
407 Proxy Auth Required 需要先向代理服务器认证
408 Request Timeout 服务器等了太久,没收到响应
480 Temporarily Unavailable 对方暂时不在线/不可达
481 Call Does Not Exist 收到一个不认识的会话请求
482 Loop Detected 检测到请求在网络里绕圈
483 Too Many Hops Max-Forwards 已经减到0,包走太多跳了
486 Busy Here 对方忙,不接这个电话
487 Request Terminated 请求被 BYE 或 CANCEL 取消了

6.5 5xx — 服务器错误


代码 名称 含义
500 Server Internal Error 服务器内部错误(相当于服务器崩了)
501 Not Implemented 服务器不支持这个功能
502 Bad Gateway 网关从下游收到了无效响应
503 Service Unavailable 服务器过载或维护中,暂时无法处理
504 Server Timeout 上游服务器没有及时响应
505 Version Not Supported 不支持请求中使用的SIP协议版本
513 Message Too Large 消息体太长,服务器无法处理

6.6 6xx — 全局失败


代码 名称 含义
600 Busy Everywhere 用户在任何地方都忙,不接
603 Decline 用户明确拒绝接听
604 Does Not Exist Anywhere 该用户在任何地方都不存在
606 Not Acceptable 会话参数(SDP内容)完全无法接受

七、视频通话与 RTSP

7.1 SIP 视频通话

视频通话和语音通话的信令完全一样,区别在于 SDP 消息里会同时描述音频流视频流

SDP 消息示例(简化):
m=audio 23978 RTP/AVP 0          ← 音频:UDP端口23978,编解码器G.711
a=rtpmap:0 PCMU/8000
m=video 30290 RTP/AVP 96          ← 视频:UDP端口30290
a=rtpmap:96 H264/90000           ← 使用H.264编解码器

流程:

音频 RTP 流  →  UDP 端口 23978
视频 RTP 流  →  UDP 端口 30290

7.2 RTSP — 实时流媒体协议

RTSP 用于点播/流媒体场景(比如看视频),和 SIP 用于实时双向通话不同。
RTSP 端口:TCP 554(和 HTTP 非常相似)

RTSP 常用命令

命令 方向 作用
OPTIONS C → S 或 S → C 查询服务器/客户端支持哪些方法
DESCRIBE C → S 获取媒体流描述(返回SDP)
SETUP C → S 建立媒体会话,协商RTP端口
PLAY C → S 开始播放
PAUSE C → S 暂停播放
TEARDOWN S → C 立即关闭会话
RECORD C → S 开始录制
REDIRECT S → C 重定向到其他服务器

RTSP 与 SIP 的关键区别

对比点 SIP RTSP
场景 实时双向通话 点播/流媒体
端口协商方式 SDP 消息 SETUP 命令
典型端口 动态(5060为信令) TCP 554
媒体传输 RTP(UDP) RTP(UDP)
连接方式 UDP TCP

RTSP 完整会话流程
媒体服务器 184.72.239.149 客户端 10.83.218.91 媒体服务器 184.72.239.149 客户端 10.83.218.91 RTP音视频流开始传输 OPTIONS rtsp://184.72.239.149/BigBuckBunny.mov 200 OK(告知支持的方法列表) DESCRIBE rtsp://184.72.239.149/BigBuckBunny.mov 200 OK(返回SDP:音频用PT96,视频用PT97) SETUP audio(客户端RTP端口50960,RTCP端口50961) 200 OK(服务端分配的端口) SETUP video(类似) 200 OK PLAY 200 OK PAUSE(可选) 200 OK TEARDOWN(结束) 200 OK

RTSP 端口分配方式:

客户端在 SETUP 请求里告诉服务器我的端口:
  RTP 音频  → UDP 50960
  RTCP 音频 → UDP 50961  (= RTP端口 + 1)
视频流同理,用另一对端口

八、用 Wireshark 分析 RTP 流

8.1 查看所有 RTP 流

菜单路径:

Telephony → RTP → RTP Streams

列出的每条 RTP 流信息:

字段 含义
Source Address 发送方 IP
Source Port 发送方 UDP 端口
Destination Address 接收方 IP
Destination Port 接收方 UDP 端口(范围 16384~32767)
SSRC 同步源标识符(唯一标识这路流)
Payload 编解码器类型(如G.711)
数据包数 / 丢包数 / 抖动 质量统计

8.2 分析流质量

在 RTP Streams 窗口中:

1. 选中要分析的流
2. 点 "Find Reverse" → 找到反向流(双向通话各有一路)
3. 点 "Prepare Filter" → 过滤这对双向流的所有包
4. 点 "Analyze" → 弹出质量分析窗口
分析窗口显示:
  - 正向流/反向流的抖动(Jitter)
  - 延迟(Delay)
  - 丢包率(Packet Loss)
  - 以图形方式展示随时间变化的趋势

8.3 过滤 RTCP 包

如果 RTP 流的端口是 P,则过滤 RTCP:

udp.port == P+1

例如 RTP 用 23978,则:

udp.port == 23979

在 RTCP 包里验证 SSRC 是否与 RTP 流一致(0x252eb528)。

8.4 VoIP 通话回放

Wireshark 可以直接播放捕获到的语音通话:

步骤:
1. Telephony → RTP → RTP Streams
2. 选中音频流 + 反向流(两个方向都选)
3. 点 Analyze(合并双向流)
4. 点 Play Streams → 听到实际对话

注意事项:

  • 目前只支持 G.711 编解码器的回放,保存为 .au 格式
  • G.729 不支持直接回放,需要手动转换
  • 视频流不支持回放(只有音频)

九、常见故障排查思路

不能

能建立

通话有问题

能建立连接吗?

看SIP信令

收到了哪个错误码?

4xx: 检查拨号/认证/编解码器配置

5xx: 检查服务器状态

6xx: 对方拒绝或不存在

没有任何响应

UDP无连接,可能是网络不通
用 ping/traceroute 检查路径

通话质量差

看 RTCP Sender Report

Fraction Lost > 0?

有丢包 → 检查网络拥塞/QoS配置

Jitter 大?

抖动大 → 检查缓冲/网络稳定性

用Wireshark RTP分析图
看延迟趋势

故障排查速查表


现象 可能原因 排查方法
INVITE 发出无响应 网络不通,UDP丢包 ping/traceroute,检查防火墙
收到 404 被叫号码不存在 检查号码配置,DNS解析
收到 486 Busy 对方正在通话或拒接 正常现象
收到 488 Not Acceptable Here SDP协商失败,编解码器不兼容 检查双方支持的Codec列表
通话有杂音 RTP丢包/抖动 查RTCP丢包率和Jitter
单通(只能听不能说) 某方向RTP流缺失 Wireshark看双向流是否都存在
通话建立后立即中断 ACK未到达 查SIP ACK是否成功

十、协议栈层次总览

应用层    SIP(信令)  SDP(会话描述)  RTP(媒体)  RTCP(质量控制)  RTSP(流媒体信令)
            ↕               ↕               ↕              ↕                  ↕
传输层    TCP/UDP         (内嵌在SIP)    UDP            UDP               TCP(554)
            ↕                               ↕              ↕
网络层                                      IP             IP
            ↕
链路层                                   以太网/Wi-Fi/...

一次完整的视频通话,所有协议同时工作:

时间线:
t=0   SIP INVITE(SDP Offer)     → 信令,协商参数
t=1   SIP 200 OK(SDP Answer)    → 信令,确认参数
t=2   SIP ACK                     → 信令,三次握手完成
t=3   RTP 音频流开始(UDP)        → 媒体,持续传输
t=3   RTP 视频流开始(UDP)        → 媒体,持续传输
t=3   RTCP 报告开始(每5秒)       → 质量监控
...
t=n   SIP BYE                     → 信令,挂断
t=n+1 RTP/RTCP 停止               → 媒体停止

十一、关键知识点总结


知识点 核心要点
RTP 媒体传输协议,运行在UDP上,每包有序列号+时间戳
RTCP RTP配套,监控质量,端口=RTP端口+1
SSRC 每路RTP流的唯一ID,32位随机数
SIP 信令协议,负责建立/修改/终止会话
SDP 嵌在SIP消息里,描述RTP端口和编解码器
Offer/Answer SIP建话的协商模型,INVITE带Offer,200 OK带Answer
RTP端口范围 UDP 16384∼3276716384 \sim 327671638432767
RTSP 流媒体信令,TCP 554,用SETUP协商端口(不用SDP)
Wireshark分析 Telephony→RTP→RTP Streams,可分析抖动/丢包/延迟
VoIP回放 支持G.711,保存为.au,不支持视频和G.729

第18-19章:带宽延迟测量 + 网络安全取证 — 从零详细解析

两章合并讲解:第18章教你测量网络性能,第19章教你识别和排查网络攻击。

第18章:带宽与延迟问题排查

一、三个容易混淆的概念

网络性能有三个层面,必须分清楚:

概念 英文 含义 例子
速率 Speed 链路物理上能跑多快(上限) 千兆网卡 = 1 Gbps
带宽 Bandwidth 被允许使用的最大速率 运营商限速 10 Mbps
吞吐量 Throughput 实际应用层传输了多少数据 FTP 实际下载速度 8 Mbps

三者关系:
吞吐量≤带宽≤速率 \text{吞吐量} \leq \text{带宽} \leq \text{速率} 吞吐量带宽速率
现实中常见的情况:

WAN-Router1
    |
    | 物理接口:1 Gbps(速率)
    |
运营商设备
    |
    | CAR限速:10 Mbps(带宽)
    |
    → 超出10 Mbps的流量直接丢弃

CAR(Committed Access Rate,承诺接入速率) 是运营商在合同里承诺的带宽上限,超出部分会被丢弃或限速。

二、WAN 带宽测量的位置选择

在哪里抓包,测量结果就代表哪个位置的流量。位置不对,数据会不准。

企业内网

WAN-Router1

是否开启
流量整形
Traffic Shaping?

在 WAN-Router1
出方向抓包
数据准确

在运营商侧
或远端路由器
入方向抓包

流量整形(Traffic Shaping):企业主动控制发出流量不超过CAR,像一个水桶,超速的包先缓存、慢慢发,不丢包。
流量限速(Traffic Policing/CAR):运营商强制执行,超出的包直接丢弃。

三、用 Wireshark 测量带宽

3.1 查看平均带宽(Statistics → Summary)

输出内容:

  • 总包数
  • 平均速率(Avg. MBit/sec)
  • 平均包大小
    可以先用显示过滤器筛选某条流,再看 Summary,得到单条流的带宽

3.2 用 I/O Graph 看带宽随时间变化

菜单:Statistics → IO Graphs
两个轴的含义:

配置项 含义
X 轴 Tick Interval 采样间隔(1ms ~ 10min可调)
Y 轴 单位 每个采样间隔内的包数/字节数/比特数

常用配置:X轴 = 1秒,Y轴 = Bytes/Tick → 得到 字节/秒 的带宽曲线。
"View as time of day"选项:显示实际时刻(几点几分),方便找流量高峰时段。
可以叠加多条曲线,对比不同流/协议的带宽占用:

带宽图示意(多条曲线):
Mbps
 10 |  HTTP ████████          ████
  8 |       ████████          ████
  6 | FTP              ██████
  4 |              ██████████
  2 |
  0 +──────────────────────────► 时间
    0s   10s   20s   30s   40s

3.3 找出占用最多带宽的终端

菜单:Statistics → Endpoints
列出所有端点(IP地址)及其发送/接收的包数和字节数,按字节排序即可找到"流量大户"。

3.4 查看具体连接(会话)统计

菜单:Statistics → Conversations
显示每条流的:源IP、目的IP、包数、字节数、持续时间。

四、延迟与抖动的测量

4.1 概念区分

延迟(Delay / RTT):一个包从发出到收到回应的时间,即往返时间(Round Trip Time)。
RTT=T收到ACK−T发出包 \text{RTT} = T_{\text{收到ACK}} - T_{\text{发出包}} RTT=T收到ACKT发出包
抖动(Jitter):延迟的变化量(波动程度)。
抖动=∣最大延迟−最小延迟∣平均延迟×100% \text{抖动} = \frac{|\text{最大延迟} - \text{最小延迟}|}{\text{平均延迟}} \times 100\% 抖动=平均延迟最大延迟最小延迟×100%
举例:平均延迟100ms,最大120ms,最小80ms:
抖动=120−80100×100%=40% \text{抖动} = \frac{120 - 80}{100} \times 100\% = 40\% 抖动=10012080×100%=40%
帧间延迟(Interframe Delay):相邻两个数据包到达之间的时间差。

参数 对什么应用影响最大 原因
带宽/丢包 FTP、文件传输 大文件传输需要持续稳定的通道
延迟/抖动 VoIP、视频通话 声音/画面必须实时,延迟超过150ms人耳可感知

4.2 用 Wireshark 测量帧间延迟

步骤:

1. 点击某个 TCP/UDP 包
2. 右键 → Follow TCP/UDP Stream
3. 复制过滤字符串(如 tcp.stream eq 5)
4. Statistics → IO Graphs
5. Y轴选 Advanced...
6. 过滤器填入 tcp.stream eq 5
7. 统计方式选 AVG(*)
8. 字段填 frame.time_delta_displayed
9. 图中显示的就是帧间时间差(毫秒)

frame.time_delta_displayed 含义:当前包与上一个显示的包之间的时间差。

4.3 用 RTT 图测量往返延迟

菜单:Statistics → TCP Stream Graph → Round Trip Time Graph
图中显示每个 TCP 包的 ACK 往返时间,可直观看出延迟是否稳定。

4.4 Layer 4 延迟过滤

tcp.analysis.ack_rtt

此过滤器显示每个包被确认所花的时间,专门用于测量 TCP 层的延迟。

4.5 延迟数值参考


延迟范围 评价
< 10ms 优秀,局域网正常范围
10ms ~ 100ms 良好,WAN链路可接受
100ms ~ 300ms 较差,VoIP质量明显下降
> 300ms 很差,通话中断
秒级 严重异常,必须立即排查

五、网络瓶颈排查流程

是,关键流量

是,非关键流量

用户反映应用慢

第一步:检查 DNS 解析延迟
过滤 DNS 包,看 I/O Graph

DNS 解析时间
是否超阈值?

DNS服务器过载
考虑升级或增加DNS服务器

第二步:检查 WAN 带宽利用率
Statistics → IO Graphs

WAN 带宽
是否打满?

需要带宽升级

部署 QoS 限制低优先级流量

第三步:检查链路延迟/抖动
看 RTT 图和 frame.time_delta

延迟是否
异常高?

秒级延迟
检查物理层:光纤/电源/接口

第四步:检查服务器并发连接数
Statistics → Endpoints

并发连接数
超过服务器上限?

服务器升级或
检查是否遭受攻击

继续深入排查应用层

常见瓶颈场景速查


现象 可能原因 排查工具
所有用户都慢 WAN带宽打满 IO Graph
特定时段慢 带宽高峰 IO Graph + Time of Day
DNS解析慢 DNS服务器过载 DNS过滤 + IO Graph
链路延迟秒级 物理故障(光纤/端口) RTT Graph
某台服务器慢 并发连接过多 Endpoints统计
低优先级流量抢占 缺少QoS Conversations分析流量类型

QoS 的作用

默认队列机制是 FIFO(先进先出)——谁先到谁先发,不分优先级。

FIFO 问题(没有QoS):
队列:[视频流][垃圾广告][FTP][网页][P2P][P2P][P2P][P2P]...
                                              ↑
                              P2P占满带宽,视频卡顿
QoS 优先级队列(部署后):
高优先级:[视频流][VoIP] ← 最先发送
中优先级:[网页][FTP]
低优先级:[P2P][广告] ← 有剩余带宽才发

第19章:网络安全与取证

六、安全分析的基本思路

网络取证和刑事侦查一样——先建立"正常基线",再找"异常"。
在开始分析之前,必须了解网络的"正常面貌":

需要掌握的背景信息:
  - 所有服务器的 IP 地址和网段
  - 所有路由器、交换机的 IP 和拓扑
  - 防火墙、IDS/IPS、WAF 等安全设备的位置
  - 网络上运行的应用及其端口号

Wireshark 的部署位置

                    互联网
                      |
                 [1]  |  [2]
              ─────防火墙─────
                      |
              [3]  [4]路由器
                      |
                   核心交换
                  /        \
            [7]远程办公室    [8]可疑 PC/服务器
            [5/6]路由器

编号 位置 适合场景
1 防火墙外(互联网侧) 怀疑外部攻击穿过防火墙前
2 防火墙内(内网侧) 怀疑攻击已穿过防火墙
3/4 WAN路由器前后 怀疑远程办公室有问题
7 远程分支内部 隔离具体分支的攻击源
8 可疑PC/服务器端口 精准抓取特定设备流量

七、正常流量 vs 可疑流量对照

7.1 IP 地址


类型 特征
正常 来自已知的内部 IP 段(如 192.168.x.x)
可疑 来自不认识的地址,或目的是不该被访问的地址

7.2 端口号


类型 特征
正常 80(HTTP)、443(HTTPS)、53(DNS)、25(SMTP)、21(FTP)、3389(RDP)等标准端口
可疑 Web服务器上出现RDP流量;内网向外部随机高端口发包

7.3 TCP 标志组合


标志组合 含义 正常/可疑
SYN 建立连接请求 正常
SYN-ACK 连接确认 正常
ACK 数据确认 正常
FIN / FIN-ACK 正常断开连接 正常
RST 快速重置/拒绝 正常(少量)
大量 SYN 无回应 端口扫描 / DoS 可疑
RST+FIN 同时 非标准组合 可疑
URG 标志 极少正常使用 可疑
所有标志为0 Null Scan 攻击
FIN+PSH+URG Xmas Scan 攻击

7.4 流量模式


类型 特征
正常 流量有起伏,用户操作驱动(开文件大、关文件小)
可疑 固定带宽的持续流量(可能是远程控制、挖矿或数据泄露)

固定带宽模式示意:

Kbps
 150 |████████████████████████████  ← 持续固定,不正常
 100 |
  50 |
   0 +──────────────────────────► 时间
     对比正常流量:
 150 |   ██     ██
 100 |  ████   ████   ██
  50 | ██████ ███████████
   0 +──────────────────────────► 时间(有起伏,正常)

7.5 广播流量


类型 特征
正常 NetBIOS、少量ARP、少量DHCP,几秒一次
可疑 每秒数百上千个广播包,来自同一设备

7.6 DNS 流量


类型 特征
正常 每个查询都有对应的响应,每客户端每秒几十个以内
可疑 大量无对应查询的响应;或大量查询无响应(域名爆破)

八、常见网络攻击类型

网络攻击类型

病毒 Virus
破坏本地文件和系统

蠕虫 Worm
自我复制,消耗带宽和CPU

DoS / DDoS
拒绝服务攻击

中间人 MITM
截获并篡改流量

扫描 Scanning
探测存活主机和开放端口

应用层攻击
针对具体应用漏洞

暴力破解 Brute Force
枚举用户名/密码/资源

九、ARP 与 MAC 层攻击

9.1 ARP 工作原理(正常情况)

主机A 想和 192.168.1.10 通信:
主机A  →  广播:谁是 192.168.1.10?请告诉我你的MAC
主机B  →  单播:我是 192.168.1.10,我的MAC是 AA:BB:CC:DD:EE:FF
主机A  记录:IP 192.168.1.10 → MAC AA:BB:CC:DD:EE:FF

9.2 ARP 扫描特征

ARP 扫描是攻击者探测整个网段存活主机的手段:

ARP扫描模式(在Wireshark里看到的):
时间     源IP          目的IP           内容
0.001    192.168.1.5  192.168.1.1    Who has 192.168.1.1?
0.002    192.168.1.5  192.168.1.2    Who has 192.168.1.2?
0.003    192.168.1.5  192.168.1.3    Who has 192.168.1.3?
0.004    192.168.1.5  192.168.1.4    Who has 192.168.1.4?
...
                                      ← 顺序递增,速度极快 = 扫描

正常 ARP:随机目标,间隔较长。
扫描 ARP:顺序目标,间隔极短(毫秒级)。

9.3 ARP 欺骗 / 中间人攻击

正常情况:

主机A ──────────────────────────► 服务器
      192.168.1.1  →  MAC_Server

ARP 欺骗后:

主机A ──► 攻击者(伪装成服务器)──► 服务器
      192.168.1.1  →  MAC_Attacker

攻击者告诉主机A:“我就是服务器”,同时告诉服务器:“我就是主机A”,流量全部经过攻击者中转。
Wireshark 检测方法:看到同一个 MAC 地址对应两个不同 IP,或同一个 IP 地址对应两个不同 MAC,就要警惕。

十、扫描攻击详解

10.1 扫描的一般步骤

第一步:ARP 扫描
在本局域网内广播
找出存活的IP地址

第二步:ICMP 扫描
对存活IP发 ping
确认主机在线

第三步:TCP/UDP 端口扫描
对存活主机扫描开放的端口
找服务运行在哪里

第四步:应用层扫描
针对开放端口发送应用命令
探测版本/漏洞

发动攻击
利用找到的漏洞

10.2 ICMP 扫描(Ping 扫描)

正常 ping:少量,有目的性。
扫描 ping:大量,目标递增,时间间隔极短。

ICMP 扫描模式示意:
目标IP:
  192.168.110.1  ← ICMP Request
  192.168.110.2  ← ICMP Request
  192.168.110.3  ← ICMP Request
  192.168.110.4  ← ICMP Request(无回应,说明不在线)
  192.168.110.5  ← ICMP Request + Reply(在线!→ 进入下一步扫描)
  ...

蠕虫病毒 ICMP 扫描的传播机制:

感染主机1
  → 扫描整个网段
  → 找到主机2
  → 感染主机2
       → 主机2也开始扫描
       → 找到主机3
       → ...
  → 几分钟后,所有主机都在扫描
  → 网络带宽被 ICMP 包打满

10.3 TCP SYN 扫描

攻击者向多个端口发 SYN,根据响应判断端口状态:

端口状态判断:
攻击者 → SYN → 目标:80
目标   → SYN-ACK → 攻击者    ← 端口开放!(有服务在监听)
攻击者 → SYN → 目标:8080
目标   → RST → 攻击者        ← 端口关闭(有防火墙或无服务)
攻击者 → SYN → 目标:9999
(无响应)                    ← 被防火墙过滤/主机不在线

TCP SYN 扫描的危害:

半开连接(Half-Open Connection):
  攻击者发 SYN → 服务器回 SYN-ACK → 攻击者不回 ACK
  服务器维护着大量"等待ACK"的半开连接,消耗内存和CPU
  → 合法用户无法连接 → 变成 DoS 攻击

十一、DoS 与 DDoS 攻击

11.1 区别


类型 攻击源 特征
DoS(拒绝服务) 单一来源 一台机器发起大量请求
DDoS(分布式拒绝服务) 多个来源 数百上千台被控机器同时攻击

11.2 典型 DDoS 特征(Wireshark 中)

DoS 特征示意:
时间(μs)  源IP              目的IP         标志
0         192.168.1.1      94.23.71.12    SYN
11        192.168.1.2      94.23.71.12    SYN
22        192.168.1.3      94.23.71.12    SYN
33        192.168.1.4      94.23.71.12    SYN
...
← 源IP递增(伪造)  目的IP固定  时间间隔仅11微秒
关键线索:所有不同源IP竟然来自同一个MAC地址!
→ 说明是同一台机器在伪造源IP发包(IP spoofing)

IP 欺骗(IP Spoofing):攻击者伪造源IP地址,让服务器无法追踪真实攻击者,同时把回包发向被伪造的无辜IP。

11.3 难以识别的 DoS 伪装


伪装形式 说明
Ping Flood 大量ICMP包,可能伪装成网管软件探测
HTTP GET Flood 大量正常HTTP请求,每个单独看都合法
SNMP GET Flood 伪装成网络监控流量

识别关键:看数量来源,而不只是看类型。

十二、智能 TCP 攻击

12.1 ACK 扫描

大量 ACK 包发往多个端口,目的是打断已有的 TCP 连接:

攻击者 → ACK(无对应SYN) → 目标:80
目标   → RST(连接不存在)→ 攻击者  ← 连接被重置
大量ACK → 大量RST → 所有连接被终止

12.2 异常标志组合


攻击名称 标志组合 目的
Xmas Scan FIN + PSH + URG 让目标操作系统困惑,泄露端口状态
Null Scan 所有标志=0 绕过某些防火墙规则检测
FIN Scan 只有FIN 绕过只检查SYN的防火墙
FIN-ACK Flood FIN + ACK 大量发送,关闭连接或泛洪网络

Xmas Scan 的名字来源:像圣诞树一样"所有灯都亮了"(所有异常标志都置1)。

正常TCP标志:只有其中一两个为1
  SYN=1, ACK=0, FIN=0, PSH=0, URG=0, RST=0
Xmas Scan:
  SYN=0, ACK=0, FIN=1, PSH=1, URG=1, RST=0  ← 三个同时亮
Null Scan:
  SYN=0, ACK=0, FIN=0, PSH=0, URG=0, RST=0  ← 全灭

十三、暴力破解攻击

13.1 什么是暴力破解

暴力破解是"反复试"——枚举所有可能的用户名、密码、文件名,直到找到一个有效的。

13.2 DNS 暴力破解

攻击者枚举子域名,探测组织的服务器资产:

DNS 暴力破解模式(Wireshark中):
查询:dns.icomm.co           → 有回应(服务器存在)
查询:mail.icomm.co          → 无回应
查询:intranet.icomm.co      → 无回应
查询:vpn.icomm.co           → 无回应
查询:admin.icomm.co         → 无回应
查询:ftp.icomm.co           → 有回应(发现FTP服务器!)
...
特征:大量查询,大部分无回应,来源IP固定

Wireshark 过滤器:dns → 看哪些查询没有对应响应。

13.3 HTTP 暴力破解

攻击者枚举Web服务器上的目录和文件:

HTTP 暴力破解模式:
GET /admin/         → 404 Not Found
GET /backup/        → 404 Not Found
GET /config.php     → 404 Not Found
GET /wp-admin/      → 200 OK  ← 找到了!
GET /phpMyAdmin/    → 200 OK  ← 又找到了!
特征:大量 4xx 错误码,来自同一IP

Wireshark 过滤器:

http.response.code >= 400

也可以在 Statistics → HTTP → Packet Counter 看错误码统计。
找常见扫描工具特征:Edit → Find Packet → "nmap" 在 Packet Bytes 里搜索。

13.4 FTP 密码暴力破解

攻击者尝试登录 FTP 的流程:
第一步:尝试匿名登录
  攻击者 → USER anonymous
  服务器 → 230 Login successful(匿名允许登录)
第二步:尝试特权账户
  攻击者 → USER root
  服务器 → 530 Login incorrect(拒绝)
  攻击者 → USER admin
  服务器 → 530 Login incorrect(拒绝)
  攻击者 → USER administrator
  服务器 → 530 Login incorrect(拒绝)
  ...最终服务器发送 TCP Zero-Window 限制连接

TCP Zero-Window:服务器告诉攻击者"我的接收缓冲区已满,停止发送",是服务器的一种自保机制。

十四、安全防护体系

互联网

防火墙 Firewall
过滤未授权流量

IDS / IPS
检测并阻断入侵模式

WAF 应用防火墙
第7层深度检测

NAC 网络访问控制
只允许授权设备接入

内部服务器

内部 PC

邮件过滤器

Web 过滤器


安全组件 作用 工作层次
防火墙 Firewall 基于规则过滤流量,阻止未授权访问 L3/L4
NAC 网络访问控制 只允许授权设备连接网络(未授权的MAC被拒绝) L2
IDS(检测系统) 发现入侵模式,告警 L3-L7
IPS(防御系统) 发现入侵模式,自动阻断 L3-L7
WAF 应用防火墙 深度检测HTTP/HTTPS,防止SQL注入、XSS等 L7
邮件过滤 / Web过滤 过滤恶意邮件和网站内容 L7

IDS/IPS 的两种工作方式:

  • 基于NetFlow/Jflow:分析流量统计,发现大流量异常(DDoS等)
  • 基于内容分析:深度包检测,识别攻击特征字符串

十五、综合排查思路总图

性能问题

安全问题

发现网络异常

是性能问题
还是安全问题?

测量带宽
Statistics → Summary
IO Graphs

带宽是否
被打满?

找出流量大户
Endpoints + Conversations
判断是否需要QoS或扩容

测量延迟/抖动
RTT Graph
frame.time_delta_displayed

部署Wireshark
在可疑位置抓包

流量特征分析

大量ARP广播
→ ARP扫描 / ARP欺骗

大量ICMP请求
→ Ping扫描 / 蠕虫

大量SYN无回应
→ TCP SYN扫描/DoS

异常TCP标志
→ Xmas/Null/ACK扫描

大量DNS查询无回应
→ DNS暴力破解

大量HTTP 4xx错误
→ HTTP暴力破解

固定带宽持续流量
→ 远控木马/数据窃取

十六、关键知识点总结


知识点 核心要点
速率/带宽/吞吐量 三者关系:吞吐量 ≤ 带宽 ≤ 速率
CAR 运营商承诺速率,超出丢弃
流量整形 企业主动控速,超量缓存不丢包
IO Graph 带宽随时间变化,可多流对比
延迟 RTT 一去一回的总时间
抖动 Jitter 延迟的变化幅度,对VoIP影响大
frame.time_delta_displayed Wireshark测帧间延迟的字段
tcp.analysis.ack_rtt 测TCP层ACK往返时间
ARP扫描 顺序递增目标、毫秒级间隔
ICMP扫描 大量ping,目标递增,蠕虫传播方式
TCP SYN扫描 大量SYN,根据回应判断端口状态
IP欺骗 不同源IP来自同一MAC → 伪造
Xmas Scan FIN+PSH+URG同时置1
Null Scan 所有标志为0
暴力破解特征 大量请求大部分失败,来源IP固定
HTTP错误码过滤 http.response.code >= 400
QoS 优先级队列,保障关键业务带宽

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐