跳转到内容

Linux 网络协议栈面试题

12 道题
分类
Linux
题目数
12 道
已阅读 0 / 12 题
1 Linux 网络协议栈的分层结构是什么?一次 TCP 三次握手在协议栈中如何流转?

答案:

Linux 网络栈遵循 OSI 分层,核心实现(自底向上):

  • 驱动层:NIC 驱动(e1000eixgbemlx5 等),DMA 收发。
  • 链路层:网卡驱动 + netif_receive_skb
  • 网络层ip_rcv / ip_forward / ip_output
  • 传输层tcp_v4_rcv / udp_rcv / tcp_sendmsg
  • 应用层:Socket 层(BSD socket API)。

TCP 三次握手路径:

  1. SYN 接收tcp_v4_rcvtcp_conn_request → 分配 request_sock → 发送 SYN+ACK
  2. SYN+ACK 发送inet_csk_route_child_sock + tcp_v4_send_synack
  3. ACK 接收完成连接tcp_v4_rcvtcp_check_req → 创建完整 sock → 加入 accept 队列。
  4. 应用层accept() 系统调用从完成队列取出已建立连接。

关键队列:

  • SYN 队列(半连接)/proc/sys/net/ipv4/tcp_max_syn_backlog,存放收到 SYN 等待 ACK 的请求。
  • accept 队列(全连接)/proc/sys/net/ipv4/somaxconn,存放已完成三次握手等待应用 accept 的连接。
  • SYN Cookie:当 SYN 队列满时启用(tcp_syncookies=1),通过加密 cookie 状态避免半连接耗尽。
2 Netfilter 与 iptables 的关系是什么?五链五表是如何组织的?

答案:

Netfilter 是 Linux 内核网络包处理框架,在内核协议栈关键路径(NF_INET_PRE_ROUTING、NF_INET_LOCAL_IN、NF_INET_FORWARD、NF_INET_LOCAL_OUT、NF_INET_POST_ROUTING)注册 hook 点;iptables 是用户空间工具策略管理接口,通过 xtables 内核模块(iptable_filter / iptable_nat / iptable_mangle 等)注册规则到对应 hook。nftables 是独立的 nf_tables 后端体系,与 iptables-nft 兼容层共存。

五表(table)优先级与功能:

表名优先级主要功能内核模块
raw最高连接追踪豁免iptable_raw
mangle中高修改包头(TTL、TOS、MARK)iptable_mangle
natSNAT、DNAT、Masqueradeiptable_nat
filter中低过滤(ACCEPT/DROP/REJECT)iptable_filter
security最低SELinux/AppArmor 标记iptable_security

五条内置链:

  • PREROUTING:raw、mangle、nat(DNAT)
  • INPUT:mangle、filter、security
  • FORWARD:mangle、filter、security
  • OUTPUT:raw、mangle、nat、filter、security
  • POSTROUTING:mangle、nat(SNAT/MASQUERADE)

执行顺序: 表内按优先级,链内按规则顺序,首条匹配即停止(除 LOG)。数据包遍历路径由 conntrack 决定,本机包走 PREROUTING → INPUT,转发包走 PREROUTING → FORWARD → POSTROUTING。

nftables:iptables 替代品,语法统一、规则编译为字节码、性能更优(5.10+ 内核默认)。

# 替代 nftables
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
3 epoll 与 select/poll 的本质区别是什么?epoll 的 LT 与 ET 模式各有什么特点?

答案:

select / poll 每次调用需全量复制 fd 集合到内核并线性扫描所有 fd 检测就绪状态,时间复杂度 O(n),且 fd 数受 FD_SETSIZE(1024)限制。

epoll 优势:

  • 红黑树 + 就绪链表:内核维护 epitem 事件表(红黑树)和就绪链表,回调驱动(ep_poll_callback)将就绪 fd 加入就绪链表。
  • epoll_wait 仅返回就绪 fd:复杂度 O(1),不随 fd 数量增长。
  • mmap 共享:通过 mmap 避免用户态-内核态复制。
  • 无 fd 数上限:仅受 /proc/sys/fs/epoll/max_user_watches(默认 1/25 内存)。

LT(Level-Triggered,电平触发,默认)vs ET(Edge-Triggered,边沿触发):

  • LT 模式:只要 fd 处于就绪状态(可读/可写),epoll_wait 就持续返回;编程简单,但可能有"唤醒风暴"。
  • ET 模式:仅在状态变化时通知一次;必须一次性读完缓冲区(循环 readEAGAIN),避免事件丢失;性能更优(EPOLLET),是 Nginx/Libuv 默认。
// ET 模式典型读取循环
while (1) {
    ssize_t n = read(fd, buf, sizeof(buf));
    if (n > 0) { /* process */ }
    else if (n == 0) break;        // EOF
    else if (errno == EAGAIN) break; // 读完
    else { /* error */ break; }
}
4 tcpdump 与 wireshark 抓包实战:常见网络故障如何定位?

答案:

tcpdump 是 Linux 事实标准抓包工具,wireshark 是图形化分析工具。两者结合是网络故障定位的"瑞士军刀"。

tcpdump 核心用法:

# 基本语法
tcpdump [options] [filter expression]

# 常用选项
# -i eth0              指定接口
# -n                   不解析域名(更快)
# -nn                  不解析域名和端口名
# -A                   ASCII 输出包内容
# -X                   16 进制 + ASCII
# -w file.pcap         保存到文件(wireshark 打开)
# -r file.pcap         读 pcap 文件
# -c 100               抓 100 个包就退出
# -v/-vv/-vvv          详细信息等级

# 抓取 HTTP 请求
tcpdump -i eth0 -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

# 抓取特定主机
tcpdump -i eth0 host 192.168.1.100

# 抓取特定网段
tcpdump -i eth0 net 192.168.1.0/24

# 抓取 SYN 包(TCP 握手)
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0 and not src host 192.168.1.1'

# 抓取 DNS 查询
tcpdump -i eth0 -n udp port 53

# 抓取 HTTP GET 请求
tcpdump -i eth0 -A -s 0 'tcp port 80 and (tcp[(tcp[12]>>2):4] = 0x47455420)'
# 0x47455420 = "GET "

# 排除 SSH 流量(避免被自己的 SSH 包淹没)
tcpdump -i eth0 not port 22

# 滚动日志(每 100MB 或 60s 切一个文件)
tcpdump -i eth0 -w /var/log/pcap/capture-%Y%m%d-%H%M%S.pcap -G 60 -W 24

BPF 过滤表达式(Berkeley Packet Filter):

type    host/net/port/portrange
dir     src/dst
proto   tcp/udp/icmp/ip/ether
组合   and/or/not

示例:
tcp dst port 80                # TCP 目标端口 80
host 10.0.0.1 and not arp      # 排除 ARP
tcp[tcpflags] & (tcp-syn|tcp-fin) != 0   # SYN 或 FIN 包
tcp[0:2] > 50000               # 端口大于 50000(高端口连接)
greater 1500                   # 包大于 1500 字节(大包)

经典故障定位场景:

场景 1:TCP 连接慢 / 握手失败

# 抓三次握手
tcpdump -i eth0 -nn 'tcp port 443 and host api.example.com'
# 看到 SYN → SYN+ACK(重传多次)→ ACK
# → 重传说明丢包或 RTT 高
# → 看到 SYN 但没 SYN+ACK → 目标端口未开
# → 看到 RST → 防火墙拒绝

场景 2:SYN Flood 攻击

# 抓 SYN 包统计来源 IP
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0' -c 10000 | \
    awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head
# 看到大量 SYN 来自不同 IP → 真实攻击
# 看到 SYN 集中来自单一 IP → botnet 或扫描

场景 3:DNS 解析慢

# 抓 DNS 请求和响应
tcpdump -i eth0 -nn port 53
# 看到客户端向多个 DNS 服务器查询 → 配置问题
# 看到请求无响应 → DNS 服务器问题或 UDP 53 端口被防火墙挡

场景 4:丢包诊断

# 持续抓包 + 显示每 10s 统计
tcpdump -i eth0 -nn -c 100000 'not port 22' 2>&1 | \
    awk '/packets captured/ {print}'
# 对比 ping 丢包率,定位是否网络层丢包

wireshark 与 tcpdump 配合:

# tcpdump 抓包保存
tcpdump -i eth0 -w capture.pcap -c 5000

# scp 到本地
scp user@server:capture.pcap .

# wireshark 打开分析(GUI)
# 关键功能:
#   Statistics → Conversations   看所有连接
#   Statistics → Endpoints       看所有端点
#   Statistics → I/O Graphs      画 IO 时序图
#   Statistics → TCP Stream Graph  画 TCP 时序图
#   Follow → TCP Stream          看单连接完整数据

生产实践:

  • 抓包前用 tcpdump -D 列出接口,避免 -i any 误抓 veth
  • 长抓用 -w 保存到盘,定期轮转(用 -G -Wlogrotate
  • 生产抓包前必须申请——含敏感数据
  • K8s 节点抓包:kubectl debug 启动带 tcpdump 镜像的 sidecar 容器
5 nftables 与 iptables 对比:nftables 的优势是什么?如何迁移?

答案:

nftables(nft)是 iptables/nftables 的继任者,由 netfilter 团队从 2.6 内核起开发,2014 年合并,RHEL 8+/Ubuntu 20.04+/Debian 11+ 默认

核心差异:

维度iptablesnftables
工具集iptables/ip6tables/arptables/ebtables 多套统一 nft 工具
语法链式命令(无事务)配置文件 + 原子提交
规则编译每次重启 iptables-service 重新加载nft -c 语法检查 + nft -f 原子加载
字节码直接注册 hook编译为字节码,性能更优
性能(5.10+ 内核)略低15-30% 提升(Cloudflare 合成测试,合并的 netfilter hook;生产差异因规则复杂度而异)
集(set)支持弱(ipset 独立)原生支持(map、interval、concat)
表(table)固定 5 表自定义任意表(任意族)
IPv4/IPv6分开管理统一 ip/ip6/inet(双栈)
兼容性提供 iptables-nft 兼容层

nftables 核心概念:

# 语法:nft [add|delete|flush|list] [table] [chain] [rule]
# 表 → 链 → 规则
# table: 容器(族:ip/ip6/inet/arp/bridge/netdev)
# chain: 钩子点(hook)+ 优先级 + 策略
# rule: 表达式 → 判决(accept/drop/queue/continue/return)

配置示例:

# /etc/nftables.conf

# 清理已有规则
flush ruleset

# 1. 定义表(inet 族同时管 IPv4/IPv6)
table inet filter {

    # 2. 定义链
    chain input {
        type filter hook input priority 0; policy drop;

        # 3. 规则
        # 允许本地回环
        iif lo accept comment "loopback"

        # 允许已建立连接
        ct state established,related accept comment "established"

        # 允许 SSH
        tcp dport 22 accept comment "SSH"

        # 允许 ICMP
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # 允许 HTTP/HTTPS
        tcp dport { 80, 443 } accept comment "web"

        # 限制 SSH 防爆破(每秒最多 4 个新连接)
        tcp dport 22 meter ratelimit { ip saddr limit rate 4/second } accept

        # 记录并丢弃其他
        log prefix "nft-drop: " drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

关键特性:

1. 集合(Sets)—— 高效匹配:

# 简单 set
set blackhole {
    type ipv4_addr
    flags interval
    elements = { 192.168.1.0/24, 10.10.10.0/24 }
}

# 在规则中引用
ip saddr @blackhole drop

# 动态添加
nft add element inet filter blackhole { 1.2.3.4 }

2. 映射(Maps)—— 字典:

map port-services {
    type inet_service : ipv4_addr
    elements = { 80 : 192.168.1.10, 443 : 192.168.1.11 }
}

# 规则中查表
ip saddr . tcp dport vmap @port-services accept

3. 计量(Metering)—— 限速:

# 限制每 IP 每秒 100 个新连接
tcp dport 22 meter ratelimit { ip saddr limit rate 100/second } accept

4. 透明 NAT:

table ip nat {
    chain prerouting {
        type nat hook prerouting priority -100;
        tcp dport 80 dnat to 192.168.1.10:8080
    }
    chain postrouting {
        type nat hook postrouting priority 100;
        oifname "eth0" masquerade
    }
}

从 iptables 迁移:

# 1. 转换已有 iptables 规则为 nft 语法
iptables-save > /tmp/iptables-rules.txt
iptables-restore-translate -f /tmp/iptables-rules.txt > /tmp/nftables-rules.nft
# 或用 nft 自带工具
nft translate rule < iptables-rule.txt

# 2. 加载转换后规则
nft -f /tmp/nftables-rules.nft

# 3. 验证
nft list ruleset

# 4. 切换前测试
# /etc/nftables.conf 与 iptables-services 互斥,需禁用后者
systemctl disable --now iptables
systemctl enable --now nftables

生产建议:

  • 新部署直接用 nftables,干净语法
  • 旧系统迁移前用 iptables-restore-translate 工具
  • K8s 用 nftables 后端(如 Cilium)
  • 调试:nft monitor 实时看规则匹配事件
6 ss 与 netstat 区别是什么?ss 性能优势在哪?

答案:

ss(Socket Statistics)是 netstat 的现代替代品,从 iproute2 包提供,直接读取内核 sockfs 信息,速度比 netstat 快 10-100 倍。

核心对比:

维度netstatss
数据源遍历 /proc/net/tcp直接读内核 netlink socket 信息
速度(10K 连接)10-30s0.1-0.5s
过滤能力弱(grep 拼接)原生 BPF-like 过滤
输出格式传统(兼容 BSD)现代(更详细)
状态码标准(ESTABLISHED/LISTEN)同 + 内部状态
多协议多个命令(netstat/arp/route)ss/ip/bridge 一体化

ss 核心用法:

# 1. 列出所有 TCP 连接
ss -tan
# -t TCP, -u UDP, -x UNIX, -w RAW, -a 所有状态, -n 不解析

# 2. 列出所有监听端口
ss -tlnp
# -l 监听, -p 显示进程(需 root), -n 不解析

# 3. 统计各状态的连接数
ss -s
# TCP:   200 (estab 180, closed 10, orphaned 0, timewait 5)
# UDP:   5
# UNIX:  20

# 4. 看具体进程(关键运维命令)
ss -tlnp
# State   Recv-Q Send-Q  Local Address:Port  Peer Address:Port  Process
# LISTEN  0      128     0.0.0.0:22          0.0.0.0:*          users:(("sshd",pid=1234,fd=3))
# LISTEN  0      128     0.0.0.0:80          0.0.0.0:*          users:(("nginx",pid=5678,fd=6))

# 5. 看 ESTABLISHED 连接
ss -tnp state established

# 6. 看连接到特定 IP 的连接
ss -tn dst 192.168.1.100
ss -tn src 192.168.1.100

# 7. 看特定端口的连接
ss -tn 'sport = :443'
ss -tn 'dport = :80'

# 8. 高级过滤(BPF 风格)
ss -tn '( dport = :80 or dport = :443 ) and state established'

# 9. 看 socket 内存使用
ss -tm
# skmem:(r0,rb87380,t0,tb16384,f0,w0,o0,bl0)

# 10. 看 TCP 内部信息(拥塞控制、窗口等)
ss -tni
# 详细:cwnd、rtt、retrans、rto 等

# 11. 持续监控
watch -n 1 'ss -s'
watch -n 1 'ss -tnp state established | head -20'

过滤表达式语法:

条件语法:
  state  <state>          连接状态(established、listen、time-wait、syn-sent 等)
  sport  = :port          源端口
  dport  = :port          目标端口
  src    host:port        源
  dst    host:port        目标

支持 and / or / not

常用 state 值:
  established    已建立
  syn-sent       主动握手
  syn-recv       被动握手
  fin-wait-1     主动关闭方
  fin-wait-2     关闭等待
  time-wait      2MSL 等待
  close          关闭
  close-wait     被动关闭方
  last-ack       最后确认
  listening      监听
  closing        双向同时关闭

生产实战命令:

# 1. 找占用连接数最多的 IP
ss -tan state established | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head

# 2. TIME_WAIT 过多
ss -tan state time-wait | wc -l
# 解决:调小 net.ipv4.tcp_fin_timeout(默认 60s)

# 3. SYN_RECV 异常(可能 SYN 攻击)
ss -tan state syn-recv | wc -l
# 解决:开启 tcp_syncookies=1,缩短 net.ipv4.tcp_synack_retries

# 4. 看进程及其网络连接
ss -tnp '( dport = :80 )'

# 5. 看 socket buffer 配置
ss -tm
# Recv-Q / Send-Q 非 0 → 队列积压

生产建议:

  • 弃用 netstat,全用 ss
  • 大规模环境(10K+ 连接)必须用 ss(netstat 慢到无法用)
  • 配合 ip 命令替代 ifconfigroutearp
  • 监控告警:time-wait 比例 > 30% 时检查 FIN 超时配置
7 TCP 性能调优:缓冲区、窗口、拥塞控制的关键 sysctl 参数是什么?

答案:

TCP 性能调优是 SRE 高级技能,核心在于理解 TCP 状态机内核可调参数

关键 sysctl 参数:

1. 缓冲区(Buffer)—— 控制单个 socket 内存:

# 自动调优窗口(min default max 三档)
net.ipv4.tcp_rmem = 4096 87380 6291456   # 读缓冲 4K-64K-6M
net.ipv4.tcp_wmem = 4096 65536 4194304   # 写缓冲 4K-64K-4M
net.core.rmem_max = 16777216             # 全局读缓冲上限
net.core.wmem_max = 16777216             # 全局写缓冲上限

# UDP 缓冲
net.core.rmem_default = 212992
net.core.wmem_default = 212992

2. 拥塞控制(Congestion Control)—— 选算法:

# 可用算法(内核 5.10+)
sysctl net.ipv4.tcp_available_congestion_control
# reno cubic bbr

# 设置 BBR(Bottleneck Bandwidth and Round-trip propagation time)
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq   # fq 排队规则配套

# 验证 BBR 启用
sysctl net.ipv4.tcp_congestion_control
# 启动后效果:跨大带宽高延迟链路(卫星、跨海)吞吐提升 2-10x
# 注:BBR v2 (2023) 与 v3 (2024 实验) 改善了丢包场景公平性,内核 6.2+ 可启用 tcp_congestion_control=bbr2

3. 连接队列:

# 半连接队列(SYN 队列)
net.ipv4.tcp_max_syn_backlog = 65535

# accept 队列(全连接)
net.core.somaxconn = 32768   # 听端口 backlog 上限

# 应用层 listen() 第二个参数
# Python: socket.listen(backlog)

4. TIME_WAIT 优化:

# 允许 TIME_WAIT socket 复用(NAT/代理后端关键)
net.ipv4.tcp_tw_reuse = 1
# 注意:tcp_tw_recycle 已废弃(4.12+ 内核移除),不要用

# TIME_WAIT 超时(默认 60s = 2*MSL)
net.ipv4.tcp_fin_timeout = 15

5. 快速回收与重传:

# SYN 重试次数
net.ipv4.tcp_synack_retries = 2    # 默认 5,调小加速拒绝
net.ipv4.tcp_syn_retries = 2

# 失败重传次数
net.ipv4.tcp_retries2 = 8           # 默认 15

# 保活(Keepalive)
net.ipv4.tcp_keepalive_time = 600   # 空闲 600s 后探测
net.ipv4.tcp_keepalive_intvl = 30   # 探测间隔
net.ipv4.tcp_keepalive_probes = 3   # 探测次数

6. 内存压力:

# TCP 占用内存上限(防止 OOM)
net.ipv4.tcp_mem = 102400 873800 16777216
# 三个值:low pressure / pressure / max(页数)

# socket 占用内存上限
net.ipv4.tcp_wmem / tcp_rmem 见上

7. 高级功能:

# TCP Fast Open(减少握手)
net.ipv4.tcp_fastopen = 3   # 1=客户端 2=服务端 3=都启用

# MTU 探测
net.ipv4.tcp_mtu_probing = 1   # 自动探测路径 MTU

# 启用 ECN(显式拥塞通知)
net.ipv4.tcp_ecn = 1   # 0=禁用 1=允许 2=主动(激进)

典型场景配置:

1. 高并发代理(Nginx HAProxy):

net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_fastopen = 3

2. 数据库(MySQL/PostgreSQL):

# 大单连接、高吞吐、低并发
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_slow_start_after_idle = 0   # 禁用空闲后慢启动

3. 长肥网络(Long Fat Network,跨国/卫星):

# 启用 BBR
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
# 启用窗口缩放(默认已开)
net.ipv4.tcp_window_scaling = 1

生产实践:

  • 调优后用 iperf3 测试吞吐提升
  • 监控 ss -itn 看 cwnd、rtt、retrans
  • 高并发场景必备:BBR + tcp_tw_reuse + 大 somaxconn
  • 修改 /etc/sysctl.d/99-network.conf 持久化
8 iptables 实战:如何配置生产级防火墙规则?

答案:

iptables 规则配置是 SRE 基础,错误配置可直接锁死自己(特别是云上跳板机)。

安全黄金原则:

  1. 配置前先放行 SSH(防止锁死)
  2. 配置后立即测试新连接(不要关闭当前 SSH)
  3. 改 cron 定时回滚(5 分钟无操作自动恢复)
  4. 从 allowlist 出发,不是 blocklist
  5. 使用 iptables-restore 而非 iptables 命令逐条加(保证原子性)

生产级规则集:

#!/bin/bash
# /etc/iptables.rules

*filter
:INPUT DROP [0:0]    # 默认拒绝入站
:FORWARD DROP [0:0]  # 默认拒绝转发
:OUTPUT ACCEPT [0:0] # 允许所有出站

# 1. 允许本地回环
-A INPUT -i lo -j ACCEPT

# 2. 允许已建立的连接
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# 3. 防止无效包
-A INPUT -m state --state INVALID -j DROP

# 4. 防护常见扫描
-A INPUT -p tcp --tcp-flags ALL NONE -j DROP    # NULL 扫描
-A INPUT -p tcp --tcp-flags ALL ALL -j DROP     # XMAS 扫描
-A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

# 5. 限制 SSH(新连接速率)
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update \
    --seconds 60 --hitcount 4 -j DROP
-A INPUT -p tcp --dport 22 -j ACCEPT

# 6. 允许 Web
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

# 7. K8s 节点间通信
-A INPUT -s 10.0.0.0/8 -p tcp --dport 6443 -j ACCEPT    # kube-apiserver
-A INPUT -s 10.0.0.0/8 -p tcp --dport 10250 -j ACCEPT   # kubelet
-A INPUT -s 10.0.0.0/8 -p udp --dport 8472 -j ACCEPT    # flannel vxlan

# 8. 内部监控(限定源 IP)
-A INPUT -s 192.168.1.0/24 -p tcp --dport 9090 -j ACCEPT    # prometheus
-A INPUT -s 192.168.1.0/24 -p tcp --dport 9100 -j ACCEPT    # node_exporter

# 9. ICMP 限速
-A INPUT -p icmp -m limit --limit 1/s --limit-burst 4 -j ACCEPT
-A INPUT -p icmp -j DROP

# 10. 记录并丢弃其他
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-drop: " --log-level 4
COMMIT

应用规则:

# 1. 语法检查
iptables-restore -t < /etc/iptables.rules

# 2. 应用
iptables-restore < /etc/iptables.rules

# 3. 持久化(不同发行版)
# Debian/Ubuntu
apt install iptables-persistent
netfilter-persistent save

# RHEL/CentOS
service iptables save
# 或
iptables-save > /etc/sysconfig/iptables

生产安全回滚机制:

# 在 crontab 加自动回滚(关键!防止锁死)
# crontab -e
*/5 * * * * /usr/local/bin/iptables-auto-rollback.sh

# 脚本内容
#!/bin/bash
# /usr/local/bin/iptables-auto-rollback.sh
# 触摸 /tmp/iptables-ok 确认无问题,5 分钟内未触摸则回滚
if [ ! -f /tmp/iptables-ok ]; then
    # 第一次检测到缺失则创建文件
    touch /tmp/iptables-ok
    echo "5 分钟后无操作将自动回滚 iptables" | wall
else
    # 第二次检测(10 分钟)才回滚
    if [ $(find /tmp/iptables-ok -mmin +5 2>/dev/null) ]; then
        iptables-restore < /etc/iptables.backup
        rm -f /tmp/iptables-ok
        echo "iptables 已自动回滚" | wall
    fi
fi

运维常用命令:

# 查看当前规则(带行号)
iptables -L -n -v --line-numbers

# 插入规则到指定行
iptables -I INPUT 5 -p tcp --dport 8080 -j ACCEPT

# 删除规则
iptables -D INPUT 5   # 按行号
iptables -D INPUT -p tcp --dport 8080 -j ACCEPT   # 按内容

# 清空规则
iptables -F

# 看连接跟踪表
conntrack -L | head
conntrack -L -p tcp --dport 443

# 限制并发连接数(防 CC 攻击)
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j DROP

生产建议:

  • 跳板机、堡垒机使用硬件防火墙 / WAF,iptables 仅做兜底
  • K8s 用 NetworkPolicy + Cilium/Calico,iptables 仅做节点级保护
  • 关键节点不直连公网,通过跳板机/NAT 网关
  • 任何规则变更先在测试环境验证,生产用 git 管理规则文件
9 TCP TIME_WAIT 状态为什么大量产生?有什么危害?如何优化?

答案:

TIME_WAIT 是 TCP 主动关闭方在发送最后一个 ACK 后进入的状态,持续 2 倍 MSL(Maximum Segment Lifetime,默认 60s),确保最后一个 ACK 到达对端、且旧报文段从网络中消散。

状态机回顾:

主动关闭方                      被动关闭方
   CLOSE_WAIT ←FIN←               (未启动)

   (先) 主动关闭方:A 发送 FIN
         FIN_WAIT_1
         ←ACK
         FIN_WAIT_2
         ←FIN
         CLOSING
         →ACK
         TIME_WAIT     ← 等待 2MSL = 60s × 2 = 60s
         (关闭)         确保对端收到最后 ACK,旧报文段不干扰新连接

为什么大量产生?

  • 短连接高频服务:HTTP API、Redis 客户端、API 网关每秒关闭上万连接,每个连接 TIME_WAIT 60s → 峰值可达 6 万 × 60s 累积。
  • HTTP/1.0 默认短连接:每次请求都是独立 TCP 握手 + 关闭(HTTP/1.1 keep-alive 改善但 POST 后仍关)。
  • 客户端主动关闭:写完数据后 close(),客户端(不是服务端)成为 TIME_WAIT 持有方。
  • SYN 重传 / 异常关闭:FIN_WAIT 残留被回收为 TIME_WAIT。

TIME_WAIT 过多的危害:

  • 占用端口 + socket 资源:每个 TIME_WAIT 占用 1 个四元组(src_ip:src_port, dst_ip:dst_port),net.ipv4.ip_local_port_range 默认 32768-60999(约 2.8 万),高频短连接下可能耗尽。
  • 占用内核内存tcp_tw_count 高 → slabtoptcp_tw 对象增加。
  • 不直接影响服务端:服务端一般是 CLOSE_WAIT 或 LAST_ACK,问题多在客户端侧。

优化手段(按推荐顺序):

# 1. 启用 TCP Fast Open(握手同时传数据)
sysctl -w net.ipv4.tcp_fastopen=3

# 2. 调整 TIME_WAIT 回收策略
# 谨慎启用 tcp_tw_reuse(仅对出向连接有效,安全性高)
sysctl -w net.ipv4.tcp_tw_reuse=1
# 禁用 tcp_tw_recycle(已废弃,在 NAT 环境下会导致连接失败)
# sysctl -w net.ipv4.tcp_tw_recycle=0  # 不要开启

# 3. 扩大可用端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# 4. 缩短 TIME_WAIT 时间(生产慎用)
sysctl -w net.ipv4.tcp_fin_timeout=15    # 默认 60,缩短到 15-30

# 5. 减少短连接(应用层)
# HTTP/1.1 keep-alive
# 连接池(HTTP/Redis/Memcached)
# HTTP/2 / HTTP/3 多路复用

状态观测:

# 看全网状态计数
ss -s
# TCP:   1234 (estab 800, closed 200, orphaned 0, timewait 234)
#                            ^^^^^
#                            TIME_WAIT 数量

# 看本机主动连接出去的 TIME_WAIT(高频短连接客户端典型)
ss -tan state time-wait | head
ss -tan state time-wait | wc -l

# 看哪个客户端端口范围 TIME_WAIT 多
ss -tan state time-wait | awk '{print $4}' | cut -d: -f2 | sort | uniq -c | sort -rn | head

# 历史趋势(Prometheus 指标)
# node_sockstat_TCP_tw       # 当前 TIME_WAIT 数量
# node_sockstat_TCP_alloc    # 分配过的 socket 总数

典型场景:

  • 客户端(如爬虫、API 调用方)TIME_WAIT 高:开 tcp_tw_reuse=1 + 改用连接池
  • 服务端(如 Nginx)出现 TIME_WAIT:通常正常(被动方也可能在主动关),检查 keep-alive 是否开启
  • 出现 Cannot assign requested address 错误:本地端口耗尽,开 tcp_tw_reuse + 扩端口范围
  • K8s 中大量 tcp_tw 占用 cgroup 内存:调整 net.ipv4.tcp_max_tw_buckets(默认 8192)太小,调到 200000
10 服务器连接数过高(`too many open files` / `connection refused`)该如何排查?

答案:

“连接数过高"通常表现为:客户端报 Connection refused、服务进程日志报 Too many open files、监控 ss -sestablished 持续增长。需先分清是 fd 耗尽、端口耗尽、还是内核连接跟踪表满

Step 1: 区分问题类型

# 1.1 fd 耗尽(最常见)
cat /proc/sys/fs/file-nr
# 8192  0  50000      # 已分配 全部空闲 最大值(系统级)
# 实际进程级限制
cat /proc/<pid>/limits | grep "open files"
# Max open files            65536               65536               files

# 当前进程用了多少 fd
ls /proc/<pid>/fd | wc -l
# 1.2 端口耗尽(TIME_WAIT 累积)
cat /proc/sys/net/ipv4/ip_local_port_range
ss -tan state time-wait | wc -l

# 1.3 连接跟踪表满(iptables/nftables 环境)
sysctl net.netfilter.nf_conntrack_max
sysctl net.netfilter.nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_buckets

Step 2: 定位进程与文件描述符类型

# 看系统 fd 用量前 10 进程
ls /proc/*/fd 2>/dev/null | awk -F/ '{print $3}' | sort | uniq -c | sort -rn | head -10

# 看进程 fd 详情(按类型分组)
lsof -p <pid> | awk '{print $5}' | sort | uniq -c | sort -rn | head
# 234567  socket
# 12345   pipe
#  1234   /var/log/app.log
#   100   /dev/null
#    50   REG

# 看具体哪些 socket 是 ESTABLISHED
lsof -p <pid> -i | head -20

# 找进程内的 fd 泄漏源
ls -la /proc/<pid>/fd | head -20
# 看是否有大量 sock、pipe、deleted file

Step 3: 调整限制

# 进程级:临时调大
prlimit --pid <pid> --nofile=131072

# 进程级:永久生效(systemd)
# /etc/systemd/system/myapp.service
[Service]
LimitNOFILE=131072
# 重新加载
systemctl daemon-reload
systemctl restart myapp

# 全局系统级
echo "* soft nofile 131072" >> /etc/security/limits.conf
echo "* hard nofile 131072" >> /etc/security/limits.conf

# 端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# 连接跟踪表
sysctl -w net.netfilter.nf_conntrack_max=1048576
# 调 bucket(与 max 比例约 1:8)
echo 131072 > /proc/sys/net/netfilter/nf_conntrack_buckets

Step 4: 监控与告警

# Prometheus 关键告警
# 单进程 fd 使用率 > 80%
process_open_fds / process_max_fds > 0.8

# 全局 fd 耗尽风险
node_filefd_allocated / node_filefd_maximum > 0.8

# TIME_WAIT 占比
node_sockstat_TCP_tw / (node_sockstat_TCP_tw + node_sockstat_TCP_established) > 0.3

Step 5: 应用层优化

  • 连接池:HTTP/DB/Redis 客户端复用连接,避免每次新建
  • 超时配置:连接 connect_timeout、读 read_timeout、空闲 idle_timeout,避免长时间挂起
  • IO 多路复用:用 epoll/io_uring 单线程管理万级连接(如 Nginx、Netty)
  • 关闭 keep-alive 滥用:长连接长时间空闲也占资源,配置合理 idle 回收

典型场景:

  • Java 应用报 Too many open files → 线程池过大 / 数据库连接未释放 / Socket 未 close
  • 服务端 Connection refused + tcp_tw 10万+ → 端口耗尽,开 tcp_tw_reuse
  • K8s 节点报 nf_conntrack: table full → 调 nf_conntrack_max + 排查滥用 conntrack 的 iptables 规则
  • 进程 lsof 看到大量 pipe → 父子进程 IPC 泄漏或 os.pipe 未关闭
11 Linux 网络故障排查的标准流程是什么?

答案:

Linux 网络故障排查遵循自底向上(物理层 → 应用层)或端到端(源 → 目的)的方法,常用工具有 pingtraceroutesstcpdumpmtripethtool。SRE 面试必问。

标准化排查流程(OSI 分层):

1. 物理层 / 链路层(L1/L2)
   - 网卡是否 up?网线是否插好?光功率?
   - ethtool <nic>  # 看 link detected, speed, duplex
   - ip link show   # 看网卡状态

2. 网络层(L3)
   - 本机 IP 配置:ip addr / ip route
   - 网关是否可达:ip route get <dst>
   - DNS 解析是否正常:dig / nslookup / getent hosts

3. 传输层(L4)
   - 端口是否在监听:ss -lntp
   - 防火墙是否阻断:iptables -L -n / nft list ruleset
   - 连接跟踪表:conntrack -L

4. 应用层(L7)
   - curl / wget / 业务客户端测试
   - 服务端日志
   - 应用层超时配置

5. 抓包分析
   - tcpdump -i any -nn port <port>
   - 看三次握手 / 四次挥手 / 重传 / RST

核心工具清单:

# 网卡层
ip link show                       # 网卡状态、MAC
ip -s link show <nic>              # 收发包统计、丢包
ethtool <nic>                      # 速率、双工、光功率
ethtool -S <nic> | grep -i drop    # 网卡丢包统计(rx_dropped、tx_dropped)

# IP 层
ip addr show                       # IP 地址
ip route show                      # 路由表
ip neigh show                      # ARP/ND 表
ip rule show                       # 策略路由
ip route get <dst>                 # 看具体路由走哪个网卡

# DNS
dig +short example.com
dig +trace example.com             # 完整解析过程
cat /etc/resolv.conf
systemd-resolve --status           # systemd-resolved 状态

# 端口与连接
ss -tunlp                          # 监听端口
ss -tan                            # 所有 TCP 连接 + 状态
ss -s                              # 汇总
netstat -s                         # 全协议统计

# 路径与连通性
ping -c 4 <dst>                    # 基础连通性
mtr -n <dst>                       # 持续路径探测(traceroute + ping)
traceroute -n -T <dst>             # TCP 模式(避免被 ICMP 屏蔽)

# 抓包
tcpdump -i eth0 -nn -c 100 port 80         # 抓 80 端口 100 个包
tcpdump -i eth0 -nn -w /tmp/cap.pcap port 443  # 存盘 Wireshark 分析
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0'  # 只看 SYN

典型故障对照表:

现象根因定位命令常见根因
间歇性连接失败mtr <dst>中间链路丢包、ISP 问题
特定服务访问慢tcpdump 抓包TCP 重传、Nagle 与延迟 ACK 冲突
DNS 解析慢dig +statsDNS 服务器响应慢、缓存未命中
大量 RSTtcpdump 'tcp[tcpflags] & tcp-rst != 0'防火墙拒绝、应用层主动 reset
大量 SYN_SENTss -tan state syn-sent目标端口未监听、防火墙丢 SYN
网卡丢包ip -s link showring buffer 满(netdev_max_backlog)、驱动 bug
路由走错ip route get <dst>策略路由配置错误、metric 异常

抓包分析套路:

# 1. 三次握手是否完整
tcpdump -i eth0 -nn -S 'host 1.2.3.4 and port 80' | head
# 应看到 SYN → SYN+ACK → ACK 三次交换

# 2. 看是否重传
tcpdump -i eth0 -nn -S 'tcp[tcpflags] & tcp-syn != 0 and tcp[13:1] & 0x10 != 0'
# 同一个 SYN 多次发送 = 重传,丢包严重

# 3. 看 RST 来源
tcpdump -i eth0 -nn -S 'tcp[tcpflags] & tcp-rst != 0'
# RST 通常来自:防火墙(REJECT)、服务应用(异常关闭)、操作系统(半开连接清理)

# 4. 存盘分析
tcpdump -i eth0 -nn -w /tmp/cap.pcap
# Wireshark: Statistics → Conversations → 看哪个流最大
# Wireshark: Statistics → I/O Graphs → 重传统计
# Wireshark: Expert Information → 自动诊断

典型场景:

  • “服务访问慢” → curl -w "%{time_connect}\n%{time_starttransfer}\n%{time_total}\n" -o /dev/null -s <url> 拆解连接/TLS/TTFB
  • “K8s pod 之间不通” → 节点间 ping/traceroute + kubectl exec 进 pod ss/tcpdump
  • “服务突然不可用” → journalctl -u myapp + ss -s + dmesg | tail 同步看
  • “丢包率高” → sar -n EDEV 1 看网卡错误 + ip -s link 看 drops + iperf3 测带宽
12 DNS 解析异常该如何排查?

答案:

DNS 异常表现为:访问域名慢、getaddrinfo 失败、curl: (6) Could not resolve hostping: unknown host。需分清是本机 DNS 客户端问题、DNS 服务器问题,还是链路问题

Step 1: 区分本地解析器类型

# 查看本地 DNS 配置
cat /etc/resolv.conf
# nameserver 8.8.8.8
# nameserver 1.1.1.1
# search example.com
# options ndots:5

# 关键:看是否被 systemd-resolved 管理
ls -la /etc/resolv.conf
# /etc/resolv.conf -> /run/systemd/resolve/stub-resolv.conf  ← systemd-resolved
# /etc/resolv.conf -> ../run/resolvconf/resolv.conf         ← resolvconf
# /etc/resolv.conf -> /etc/resolvconf/run/resolv.conf      ← 老式

Step 2: 验证 DNS 解析

# 系统解析
getent hosts example.com
# 或
host example.com
nslookup example.com
dig example.com

# 指定 DNS 服务器测试(绕开本地配置)
dig @8.8.8.8 example.com
dig @1.1.1.1 example.com

# 看完整解析过程
dig +trace example.com
# .  → com. → example.com.  逐级查

# 看解析耗时
dig +stats example.com
# ;; Query time: 234 msec

Step 3: 看 DNS 客户端行为

# 抓 DNS 流量
tcpdump -i eth0 -nn -c 100 'port 53'
# 看本机发往哪个 DNS 服务器、响应时间、是否有重传

# 看 systemd-resolved 缓存
resolvectl statistics
# Cache hits:缓存命中次数(高 = 命中率高)
# Cache misses:未命中次数

# 清缓存
resolvectl flush-caches        # systemd-resolved
systemctl restart nscd         # nscd
kill -TERM $(pidof unbound)    # unbound 本地缓存

Step 4: 常见根因与修复

现象根因修复
间歇性解析超时DNS 服务器不可达改用可靠 DNS(内网 DNS、阿里 DNS、Cloudflare 1.1.1.1)
解析返回错误 IPDNS 劫持 / 缓存污染用 DoH / DoT 加密(systemd-resolved 支持 DNSOverTLS=yes
内网域名解析不到/etc/resolv.conf search 域配置错误检查 search 行、配置 nsswitch.conf
解析慢(> 1s)DNS 服务器慢、跨地域启用本地缓存(systemd-resolved、nscd、unbound)
getaddrinfo 失败 + 错误码配置文件语法错检查 resolv.conf 格式、systemd-resolved 日志
K8s pod 内解析失败CoreDNS 异常kubectl logs -n kube-system -l k8s-app=kube-dns
resolv.conf 被覆盖K8s/DHCP/NetworkManager 重写resolvconf 锁定、加 dns=none 给 NetworkManager

K8s DNS 排查:

# 看 Pod 内 resolv.conf
kubectl exec -it <pod> -- cat /etc/resolv.conf
# nameserver 10.96.0.10   ← CoreDNS Service IP

# 测 CoreDNS 连通
kubectl exec -it <pod> -- nslookup kubernetes.default

# CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100

# CoreDNS 状态
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl top pods -n kube-system -l k8s-app=kube-dns

# 跳过 CoreDNS 直接用外部 DNS
kubectl exec -it <pod> -- nslookup example.com 8.8.8.8

# 调整 Pod dnsPolicy
# Default / ClusterFirst / ClusterFirstWithHostNet / None

Step 5: 长期优化

  • DoH/DoT 加密:避免运营商劫持。systemd-resolved 配置示例:

    # /etc/systemd/resolved.conf
    [Resolve]
    DNS=1.1.1.1 8.8.8.8
    DNSOverTLS=yes
    
  • 本地缓存:高频短解析场景(如 K8s 大量 service 解析),用 unbound/dnsmasq 做本地缓存,命中率提升 10x

  • monitoring:监控解析耗时、错误率

    # CoreDNS 指标
    coredns_dns_request_duration_seconds_count
    rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m])
    

典型场景:

  • “间歇性 Could not resolve host” → DNS 服务器偶发不可达,配多个 nameserver + 健康检查
  • “K8s pod 内能解析外网,内网域名不行” → CoreDNS 配置 stub_domains 转发内网
  • “解析某些域名被劫持到广告页” → 切 DoH 或换内网 DNS
  • “解析很慢 1s+” → 启用 systemd-resolved 缓存,看 Cache hits 是否增加