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.

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.

pythonnetworkingmonitoringsystem-administration

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.