# ethtool: network interface diagnostics and tuning

LLMS index: [llms.txt](/en/llms.txt)

---

Network issues hide well — interface is up, IP is assigned, iptables is quiet, yet packet loss or micro-freezes only surface under load. ethtool gives direct access to hardware state, driver behavior, and offload mechanisms that neither `ip` nor `netstat` expose.

## Basic Output: Link State

Installation is straightforward:

```bash
# RHEL/Alma/Rocky
sudo dnf install ethtool -y

# Debian/Ubuntu
sudo apt install ethtool
```

Running ethtool without flags prints a summary:

```bash
$ ethtool eth0
Settings for eth0:
    Supported ports: [ TP ]
    Supported link modes:   1000baseT/Full
    Supported pause frame use: Symmetric
    Supported auto-negotiation: Yes
    Advertised link modes:  1000baseT/Full
    Advertised pause frame use: Symmetric
    Advertised auto-negotiation: Yes
    Speed: 1000Mb/s
    Duplex: Full
    Port: Twisted Pair
    PHYAD: 0
    Transceiver: internal
    Auto-negotiation: on
    MDI-X: Unknown
    Link detected: yes
```

First thing I check when network complaints come in — `Link detected`. If `no`, cable or transceiver is dead. `Speed` and `Duplex` tell you whether the link dropped to 100Mb/s or half-duplex.

## Driver and Hardware: -i and -a

`-i` shows driver information:

```bash
$ ethtool -i eth0
driver: ixgbe
version: 5.19.0
firmware-version: 0x8000095d
expansion-rom-version: [trimmed]
bus-info: 0000:01:00.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: yes
supports-register-dump: yes
supports-priv-flags: yes
```

Firmware version matters for Intel and Broadcom — older versions have known bugs. If you do not see error counters you expect, update firmware first, not the driver.

`-a` displays auto-negotiation and pause settings:

```bash
$ ethtool -a eth0
Pause parameters for eth0:
Autonegotiate:  on
RX:             on
TX:             on
```

> [!WARNING]
> Disabling flow control on one end of a link without agreement on the other causes packet loss during bursty traffic. If the switch has PAUSE disabled — disable it on the host too.

## Speed and Duplex: -s and autoneg

`-s` changes interface parameters. To set fixed speed and duplex, disable auto-negotiation first, then specify the values:

```bash
# Fix 1G full-duplex, disable auto-negotiation
sudo ethtool -s eth0 speed 1000 duplex full autoneg off

# Re-enable auto-negotiation
sudo ethtool -s eth0 autoneg on
```

After changing parameters, verify the link again — not all cards re-negotiate cleanly without an interface bounce.

| ethtool flag | Purpose |
|---|---|
| `speed N` | Speed in Mb/s (100, 1000, 10000, ...) |
| `duplex full\|half` | Duplex mode |
| `autoneg on\|off` | Auto-negotiation control |
| `port tp\|fiber\|aui\|bnc\|mii` | Port type (not available on all cards) |
| `advertise N` | Bitmask of modes for auto-negotiation |

All flags combine in a single call. Persisting across reboots requires writing to `/etc/sysconfig/network-scripts/ifcfg-eth0` on RHEL or using a systemd override:

```bash
# RHEL-style: /etc/sysconfig/network-scripts/ifcfg-eth0
ETHTOOL_OPTS="speed 1000 duplex full autoneg off"
```

## Offload Flags: -k, -K and Common Pitfalls

`-k` shows current offload flags, `-K` modifies them:

```bash
$ ethtool -k eth0 | head -20
Features for eth0:
tcp-segment-offload: on
tcp-segment-offload: on [fixed]
generic-segment-offload: on
generic-receive-offload: on
generic-segment-offload: on [fixed]
large-receive-offload: on
rx-vlan-offload: on
tx-vlan-offload: on [fixed]
ntuple-filters: off [fixed]
receive-hashing: off
```

The `[fixed]` label means the flag is hardware-enforced and cannot be changed.

Disabling offload flags is a frequent cause of VPN, monitoring, and virtualization issues:

```bash
# Disable TSO to force the kernel to send raw segments
sudo ethtool -K eth0 tso off

# Disable VLAN offload if the 802.1Q driver filter is buggy
sudo ethtool -K eth0 rxvlan off txvlan off

# Verify changes
ethtool -k eth0 | grep -E 'tcp-segment|vlan'
```

> [!NOTE]
> GRO (generic-receive-offload) and TSO (tcp-segment-offload) work as a pair. Disabling one without the other causes fragmentation at the kernel level — CPU usage spikes for no good reason.

## Statistics: -S and Packet Loss Investigation

`-S` outputs driver statistics. Format and available counters vary by driver:

```bash
$ ethtool -S eth0 | grep -E 'error|drop|miss'
     rx_errors: 0
     tx_errors: 0
     rx_dropped: 0
     tx_dropped: 0
     multicast: 42
     rx_no_buffer_count: 0
     rx_missed_errors: 0
```

For Intel drivers (ixgbe, i40e), relevant counters include:

```bash
$ ethtool -S eth0 | grep -iE 'flow-director|rss|mbus|over'
     rx_fifo_errors: 0
     rx_pause_pfc_ignored: 0
     tx_fifo_errors: 0
```

> [!TIP]
> `rx_fifo_errors` and `tx_fifo_errors` indicate congestion. If they grow despite normal CPU utilization, the problem lies with memory or the bus.

For scripted problem detection:

```bash
#!/bin/bash
IFACE=${1:-eth0}
ethtool -S $IFACE | awk '/error|drop|miss|overflow|fifo|discard/ {if ($2 > 0) print}'
```

Run periodically via cron — peak loss spikes become impossible to explain retroactively.

## Comparison with ip link: What ethtool Covers

| Task | ip link | ethtool |
|---|---|---|
| Bring interface up/down | `ip link set eth0 up/down` | no |
| MAC address | `ip link show eth0` | no |
| MTU | `ip link set eth0 mtu 9000` | no |
| Speed/duplex | no | `ethtool -s eth0 speed 1000 duplex full` |
| Auto-negotiation | no | `ethtool -s eth0 autoneg off` |
| Offload flags | partially via `ethtool -k` | `ethtool -K eth0 tso off` |
| Error statistics | `ip -s link show eth0` | `ethtool -S eth0` (more detailed) |
| Driver information | no | `ethtool -i eth0` |
| Wake-on-LAN | no | `ethtool eth0` (shown in output) |

ethtool does not replace `ip`, it complements it. Network configuration stack: `ip link` → `ip addr` → `ethtool` → `tc`.

## Troubleshooting: Link/Duplex Mismatch

Classic scenario: server and switch failed to agree on parameters. Symptoms — link is up, pings work, but under load there are sudden drops.

Diagnostic sequence:

```bash
# 1. Check what the host sees
ethtool eth0 | grep -E 'Speed|Duplex|Auto-negotiation|Link'

# 2. Inspect error counters
ethtool -S eth0 | grep -iE 'error|drop|miss|fifo'

# 3. Compare with the peer
# On the switch (Cisco): show interfaces GigabitEthernet0/1

# 4. Lock parameters on both sides
# On the host:
sudo ethtool -s eth0 speed 1000 duplex full autoneg off

# On the switch (Cisco):
interface Gi0/1
  speed 1000
  duplex full
  no negotiate
```

> [!WARNING]
> Always negotiate both ends. If the switch runs fixed mode without autoneg and the host has autoneg enabled — the 802.3 standard mandates fallback behavior, but vendors implement it poorly.

If the interface still drops packets after parameter alignment, investigate:

- Driver and firmware: update the network card firmware
- Cable or transceiver: a 10G SFP module plugged into a 1G port without auto-negotiation guarantees loss
- RSS and IRQ issues: `cat /proc/interrupts | grep eth0`, check IRQ balancing
