# HYPER-V虚拟机网卡因流量过高当机分析与解决方法

昨天2台有30万访问量的虚拟机网卡当机，执行ping命令发现有这个报错：

```  
ping: sendmsg: Operation not permitted  
```

结合“虚拟机网卡在流量高峰时才出现，重启后恢复”，问题大概率和 \*\*虚拟化平台的网卡/驱动/安全策略\*\* 有关，而不是简单的网络不通。常见原因有：

\---

\### 🔎 可能原因

1\. \*\*虚拟化平台的安全策略限制\*\*

 \* 比如 KVM、VMware、Hyper-V 的防火墙、vSwitch 安全规则触发，丢弃 ICMP 或阻止报文发送。  
 \* 当流量高峰时，虚拟交换机可能触发 \*\*端口安全（port security）\*\* 或 \*\*风暴控制（storm control）\*\*，直接禁止虚拟机继续发包。

2\. \*\*iptables/ebtables/SELinux/AppArmor 限制\*\*

 \* 在流量异常时，iptables 可能被防护脚本（比如 Fail2Ban、DDOS 防御）自动插入 DROP 规则，导致 ping 报错为 \*Operation not permitted\*。  
 \* `sendmsg: Operation not permitted` 一般是内核禁止应用层发包，而不是网络栈完全不可达。

3\. \*\*网卡驱动/队列过载\*\*

 \* virtio、e1000 等虚拟网卡驱动在高并发下容易队列塞满。  
 \* 如果 ring buffer 被填满，可能触发驱动错误，报文无法下发。

4\. \*\*ARP/邻居表溢出\*\*

 \* Linux 的 `neigh table` 在高并发时可能爆满，导致内核直接禁止发包。  
 \* 可以通过 `ip -s neigh` 或 `dmesg` 检查是否有 “neighbour table overflow”。

5\. \*\*内核资源耗尽 (socket buffer/netfilter conntrack)\*\*

 \* 高流量时，`net.ipv4.ip\_local\_port\_range`、`conntrack` 表、`net.core.somaxconn`、`netdev\_max\_backlog` 被用光，系统无法再发 ICMP 报文。  
 \* `sendmsg: Operation not permitted` 就是这种内核层拒绝。

\---

\### 🔧 建议排查步骤

1\. \*\*检查 dmesg / 内核日志\*\*

 ```bash  
 dmesg | grep -i denied  
 dmesg | grep -i neigh  
 ```

2\. \*\*确认防火墙\*\*

 ```bash  
 iptables -nvL  
 iptables -t raw -nvL  
 iptables -t mangle -nvL  
 ```

3\. \*\*查看 conntrack 是否溢出\*\*

 ```bash  
 cat /proc/sys/net/netfilter/nf\_conntrack\_count  
 cat /proc/sys/net/netfilter/nf\_conntrack\_max  
 ```

4\. \*\*检查邻居表是否爆满\*\*

 ```bash  
 ip -s neigh  
 ```

5\. \*\*检查虚拟机网卡驱动\*\*

 ```bash  
 ethtool -i eth0  
 ```

6\. \*\*提高内核资源参数（预防措施）\*\*  
 在 `/etc/sysctl.conf` 增加：

 ```conf  
 net.core.netdev\_max\_backlog = 8192  
 net.ipv4.neigh.default.gc\_thresh1 = 2048  
 net.ipv4.neigh.default.gc\_thresh2 = 4096  
 net.ipv4.neigh.default.gc\_thresh3 = 8192  
 net.netfilter.nf\_conntrack\_max = 262144  
 ```

 然后：

 ```bash  
 sysctl -p  
 ```

\---

⚠️ 你的情况 \*\*重启后恢复\*\*，说明资源是动态被耗尽/阻断的，不是永久配置问题。  
最可能的两个方向：

\* \*\*conntrack 表溢出\*\*（高峰时连接数过多，导致内核拒绝新连接/报文）。  
\* \*\*虚拟交换机风暴控制/限流\*\*（高峰时触发 vSwitch 安全机制）。

\---

要不要我帮你写一个 \*\*实时监控脚本\*\*（收集 conntrack、neigh table、带宽利用率、dmesg 关键字），这样下次再复现时，你能立刻知道是哪一块资源耗尽？

👌 收到，你这里的信息点挺关键：

1\. \*\*网卡驱动：hv\\\_netvsc\*\*

 \* 说明这台虚拟机是在 \*\*Hyper-V / Azure\*\* 上跑的。  
 \* `hv\_netvsc` 是 Hyper-V 的 paravirtualized 驱动，在高并发下偶尔会遇到队列溢出或转发 bug。  
 \* 一旦内部 ring buffer / channel 被占满，报文就会被丢弃，应用层收到的就是

 ```  
 ping: sendmsg: Operation not permitted  
 ```

 （这是 Linux 内核在无法下发数据时的报错，不是防火墙拒绝）。

2\. \*\*ARP/邻居表\*\*

 \* `ip -s neigh` 里没有看到 `FAILED`，大部分都是 `REACHABLE`，个别 `STALE` 正常。  
 \* 说明不是邻居表爆满。

3\. \*\*sysctl 配置\*\*

 \* 你已经把 `netdev\_max\_backlog`、`nf\_conntrack\_max`、`somaxconn` 等参数都调高了，说明连接跟 backlog 溢出的可能性已经大幅降低。  
 \* `nf\_conntrack\_max = 524288` 足够大，conntrack 不太会是瓶颈。

\---

\### 📌 综合分析

结合 \*\*虚拟机高峰时才触发\*\* + \*\*hv\\\_netvsc 驱动\*\*，大概率是：

\* \*\*Hyper-V vSwitch / hv\\\_netvsc 在高并发流量下丢包或阻止新报文\*\*。  
\* `sendmsg: Operation not permitted` 并不是权限问题，而是 \*\*驱动/内核层报文没法发出\*\*。  
\* 重启后恢复，是因为网卡队列 / vmbus 通道被清空，重新初始化了。

\---

\### 🔧 建议解决方案

1\. \*\*更新驱动 / 内核\*\*

 \* Ubuntu 20.04 的默认内核里，`hv\_netvsc` 有已知的稳定性 bug。  
 \* 建议升级到 \*\*5.15+ LTS 内核\*\*，或者在 Azure/Hyper-V 环境，直接用 \*\*最新的 cloud kernel\*\*。

2\. \*\*增加多队列\*\*

 \* 检查是否启用了多队列：

 ```bash  
 ethtool -l eth0  
 ```  
 \* 如果只有 1 队列，可以尝试增加：

 ```bash  
 ethtool -L eth0 combined 4  
 ```  
 \* 这样可以避免高并发时单队列被塞满。

3\. \*\*绕过 hv\\\_netvsc，用 SR-IOV / 合成网卡\*\*

 \* 如果平台支持，启用 \*\*SR-IOV\*\* 网卡直通，绕过 hv\\\_netvsc。  
 \* Azure 上是 \*\*Accelerated Networking\*\*，Hyper-V 上要在宿主机启用 SR-IOV。

4\. \*\*监控 vmbus 队列 / 网卡丢包情况\*\*

 \* 查看驱动统计：

 ```bash  
 ethtool -S eth0  
 ```

 看有没有 `dropped`, `errors`, `busy` 的计数。  
 \* 如果高峰时这些数暴增，就能确认是队列/驱动溢出。

\---

👉 总结：  
问题不是 Linux 配置不够，而是 \*\*Hyper-V 的 hv\\\_netvsc 驱动在高流量时掉坑\*\*。最有效的解决办法是 \*\*升级内核/驱动\*\* 或 \*\*改用 SR-IOV 网卡\*\*。

\---

要不要我帮你写一个小脚本，可以定时采集 `ethtool -S eth0` 和 `dmesg` 日志，把高峰期网卡丢包情况抓下来？这样你能确证是 \*\*hv\\\_netvsc driver bottleneck\*\*。

root@rulr-4:/var/log/sysstat# ethtool -S eth0  
NIC statistics:  
 tx\_scattered: 0  
 tx\_no\_memory: 0  
 tx\_no\_space: 0  
 tx\_too\_big: 0  
 tx\_busy: 0  
 tx\_send\_full: 1015  
 rx\_comp\_busy: 0  
 stop\_queue: 0  
 wake\_queue: 0  
 vf\_rx\_packets: 0  
 vf\_rx\_bytes: 0  
 vf\_tx\_packets: 0  
 vf\_tx\_bytes: 0  
 vf\_tx\_dropped: 0  
 tx\_queue\_0\_packets: 8786385  
 tx\_queue\_0\_bytes: 681809072  
 rx\_queue\_0\_packets: 10353578  
 rx\_queue\_0\_bytes: 783861988  
 tx\_queue\_1\_packets: 10558838  
 tx\_queue\_1\_bytes: 833217559  
 rx\_queue\_1\_packets: 10700441  
 rx\_queue\_1\_bytes: 807056969  
 tx\_queue\_2\_packets: 9822662  
 tx\_queue\_2\_bytes: 757879972  
 rx\_queue\_2\_packets: 11035513  
 rx\_queue\_2\_bytes: 835997833  
 tx\_queue\_3\_packets: 7852471  
 tx\_queue\_3\_bytes: 619014441

从你贴出的 \*\*`ethtool -S eth0`\*\* 输出看，有几个值得注意的指标：

```  
tx\_send\_full: 1015  
```

👉 这个计数器表示 \*\*发送队列已满（driver ring buffer full）\*\* 的次数，说明在流量高峰时，网卡的发送队列被打满，内核往驱动写包时被阻塞，导致出现你前面提到的 \*\*`ping: sendmsg: Operation not permitted`\*\* 问题。

其它计数器（如 `tx\_no\_memory`, `tx\_no\_space`, `rx\_comp\_busy`）都是 0，说明主要瓶颈出现在 \*\*发送队列拥塞\*\*。

\---

\### 可能原因

1\. \*\*虚拟化瓶颈\*\*

 \* 如果是虚拟机（KVM/VMware/Hyper-V 等），往往因为 vNIC 队列过小或虚拟交换机转发压力过大，容易在高流量时堆积。  
 \* 多队列（`tx\_queue\_x`）已经启用，但可能 \*\*分配的中断/CPU 核数不够\*\*，流量大时容易掉包。

2\. \*\*驱动/网卡队列太小\*\*

 \* 默认 ring buffer（`txqueuelen` 和 `ethtool -G` 设置的 ring size）过小，无法承受突发流量。

3\. \*\*宿主机/物理网卡瓶颈\*\*

 \* 如果虚拟机运行在物理机上，宿主机的物理网卡或 vSwitch 已经打满，也会反映到虚拟机侧。

\---

\### 建议排查 &amp; 优化

1\. \*\*查看当前队列深度\*\*

 ```bash  
 ethtool -g eth0  
 ```

 看 `TX` 和 `RX` ring buffer 默认值。很多虚拟网卡默认只有 256 或 512，很小。

2\. \*\*增大 ring buffer\*\*  
 比如调到 4096：

 ```bash  
 ethtool -G eth0 rx 4096 tx 4096  
 ```

 （注意部分 vNIC 可能不支持这么大，要看 `ethtool -g` 输出支持的 max 值）

3\. \*\*调整 txqueuelen\*\*

 ```bash  
 ifconfig eth0 txqueuelen 2000  
 ```

 或者用 ip 命令：

 ```bash  
 ip link set eth0 txqueuelen 2000  
 ```

4\. \*\*检查 CPU 中断绑定\*\*

 ```bash  
 cat /proc/interrupts | grep eth0  
 ```

 如果只落在一个 CPU 上，可以用 \*\*irqbalance\*\* 或手工 `smp\_affinity` 做中断多核分担。

5\. \*\*虚拟化层排查\*\*

 \* 如果是 KVM，确认 virtio-net 是否开了多队列（`mq=on`）。  
 \* VMware/Hyper-V 也要确认虚拟交换机性能参数，必要时开启 \*\*SR-IOV\*\* 或 \*\*DPDK\*\* 提升性能。

6\. \*\*监控宿主机带宽利用率\*\*  
 如果物理机本身已接近满速（比如 1Gbps 打满），虚拟机里当然会出现 send full。

\---

📌 总结：  
你的虚拟机 \*\*高峰时网络不通、`sendmsg: Operation not permitted`\*\*，结合 `tx\_send\_full: 1015`，几乎可以确认是 \*\*发送队列打满\*\*。  
首要优化手段：\*\*增大 ring buffer + txqueuelen\*\*，再确认虚拟化层是否支持多队列分流。

要不要我帮你写一份 \*\*Ubuntu20.04 下针对虚拟机网卡的优化脚本\*\*（自动调整 ring buffer、txqueuelen，并绑定多核中断）？