Python Network Interface Monitoring — Core Concepts
What network interfaces are
A network interface is a point of connection between your computer and a network. Physical interfaces include Ethernet ports (eth0, enp0s3) and Wi-Fi adapters (wlan0, wlp2s0). Virtual interfaces include the loopback (lo), VPN tunnels (tun0), Docker bridges (docker0), and VLAN interfaces.
Each interface has its own IP address, traffic counters, error statistics, and link state (up or down).
Discovering interfaces
Using psutil
import psutil
# Get all interface addresses
for iface, addrs in psutil.net_if_addrs().items():
print(f"\n{iface}:")
for addr in addrs:
if addr.family.name == 'AF_INET':
print(f" IPv4: {addr.address}/{addr.netmask}")
elif addr.family.name == 'AF_INET6':
print(f" IPv6: {addr.address}")
elif addr.family.name == 'AF_PACKET':
print(f" MAC: {addr.address}")
Getting interface status
stats = psutil.net_if_stats()
for iface, stat in stats.items():
status = "UP" if stat.isup else "DOWN"
speed = f"{stat.speed} Mbps" if stat.speed > 0 else "unknown speed"
print(f"{iface}: {status}, {speed}, MTU {stat.mtu}")
Measuring bandwidth
Network counters are cumulative since boot. To get bandwidth (bytes per second), you need two measurements:
import psutil
import time
def measure_bandwidth(interval: float = 1.0) -> dict[str, dict]:
"""Measure per-interface bandwidth over an interval."""
counters_before = psutil.net_io_counters(pernic=True)
time.sleep(interval)
counters_after = psutil.net_io_counters(pernic=True)
results = {}
for iface in counters_after:
if iface not in counters_before:
continue
before = counters_before[iface]
after = counters_after[iface]
results[iface] = {
'recv_bytes_per_sec': (after.bytes_recv - before.bytes_recv) / interval,
'sent_bytes_per_sec': (after.bytes_sent - before.bytes_sent) / interval,
'recv_packets_per_sec': (after.packets_recv - before.packets_recv) / interval,
'sent_packets_per_sec': (after.packets_sent - before.packets_sent) / interval,
}
return results
# Pretty-print bandwidth
for iface, bw in measure_bandwidth().items():
recv_mbps = bw['recv_bytes_per_sec'] * 8 / 1_000_000
sent_mbps = bw['sent_bytes_per_sec'] * 8 / 1_000_000
print(f"{iface}: ↓ {recv_mbps:.2f} Mbps ↑ {sent_mbps:.2f} Mbps")
Error and drop monitoring
Packet errors and drops indicate hardware problems, driver bugs, or network congestion:
def check_interface_health() -> list[dict]:
"""Identify interfaces with errors or drops."""
issues = []
counters = psutil.net_io_counters(pernic=True)
for iface, c in counters.items():
total_packets = c.packets_recv + c.packets_sent
if total_packets == 0:
continue
error_rate = (c.errin + c.errout) / total_packets * 100
drop_rate = (c.dropin + c.dropout) / total_packets * 100
if error_rate > 0.01 or drop_rate > 0.1:
issues.append({
'interface': iface,
'error_rate_pct': error_rate,
'drop_rate_pct': drop_rate,
'errors_in': c.errin,
'errors_out': c.errout,
'drops_in': c.dropin,
'drops_out': c.dropout,
})
return issues
Error rates above 0.01% or drop rates above 0.1% typically warrant investigation. Common causes include failing network cables, misconfigured MTU settings, and network buffer overflows.
Detecting link state changes
Monitoring whether interfaces go up or down:
import psutil
import time
class LinkStateMonitor:
def __init__(self):
self.previous_states = {
iface: stat.isup
for iface, stat in psutil.net_if_stats().items()
}
def check(self) -> list[dict]:
changes = []
current_states = psutil.net_if_stats()
for iface, stat in current_states.items():
prev = self.previous_states.get(iface)
if prev is not None and prev != stat.isup:
changes.append({
'interface': iface,
'previous': 'up' if prev else 'down',
'current': 'up' if stat.isup else 'down',
'timestamp': time.time(),
})
self.previous_states[iface] = stat.isup
# Check for disappeared interfaces
for iface in set(self.previous_states) - set(current_states):
changes.append({
'interface': iface,
'previous': 'present',
'current': 'removed',
'timestamp': time.time(),
})
del self.previous_states[iface]
return changes
Common misconception
People often monitor only the total network I/O (psutil.net_io_counters() without pernic=True). This hides which interface is carrying the traffic. A server with both a public internet connection and a private backend network needs per-interface monitoring — a spike on the public interface might be a DDoS attack, while the same spike on the private interface could be a normal database backup.
Practical use cases
- Server monitoring that tracks bandwidth per interface and alerts on unusual traffic patterns
- Network troubleshooting that identifies interfaces with high error or drop rates
- Link failover detection that alerts when a redundant connection goes down
- Bandwidth accounting that logs per-interface data transfer for billing or capacity planning
- Container networking that monitors Docker bridge and overlay network interfaces
One thing to remember: Per-interface monitoring reveals problems that aggregate network stats hide — a failing Ethernet cable, a VPN tunnel going down, or one interface saturated while others sit idle. Always monitor interfaces individually, not just system-wide totals.
See Also
- Python Crontab Management How Python can set up automatic timers on your computer — like programming an alarm clock that runs tasks instead of waking you up.
- Python Disk Usage Monitoring How Python helps you keep an eye on your computer's storage — like a fuel gauge that warns you before you run out of space.
- Python Log Rotation Management Why your program's diary needs page limits — and how Python keeps log files from eating all your disk space.
- Python Process Management How Python lets you see and control all the programs running on your computer — like being the manager of a busy office.
- Python Psutil System Monitoring How Python's psutil library lets your program check on your computer's health — like a doctor with a stethoscope for your machine.