Learn › The Network Stack › Your Network Interface
cat /sys/class/net/eth0/device/uevent - a hands-on Linux lab on a real virtual machine.
Every NIC runs a kernel driver. The sysfs device/uevent file tells you which one, and modinfo tells you what it does.
Boot any Linux machine and the network just works. Nobody downloaded a driver. Nobody clicked Install. Nobody was even asked.
But your NIC is hardware, and hardware does nothing on its own. Somewhere inside the kernel, a small program is moving every packet this machine sends. It loaded itself before you logged in. It knows exactly which device it is driving. It keeps a diary. And it has an author whose name you can read straight off the file.
This lesson is about finding that program. By the end you will have named it, listed what it depends on, read its papers off the disk, and read one of its settings live out of the running kernel.
The black boxes below are a practice terminal: a safe sandbox that checks the one command each step teaches. The real VM comes at the end. In the lab itself your progress is tracked automatically, so type commands naturally, and run check-progress any time to see your status.
A driver is the piece of software that translates between the operating system and one specific piece of hardware. The kernel is the core of the operating system, the program that owns the hardware. Without a driver, your NIC is a chunk of silicon the kernel cannot talk to. No driver, no packets.
On Linux, most drivers are kernel modules: pieces of kernel code that can be loaded and unloaded while the system runs. Think of the kernel as an engine and modules as parts you can bolt on without switching the engine off.
Modules live on disk as .ko files (kernel object) under /lib/modules/, one directory per kernel version. The alternative would be compiling a driver for every device ever made into one giant kernel that wastes memory on hardware you do not own. Modules keep the kernel lean: at boot, the hardware is detected and only the matching modules load.
How that matching happens, with nobody clicking anything, is the first thing you are going to see.
When virtualization took off, every hypervisor invented its own fake network card, and every guest operating system needed a separate driver for each one. VMware had one. Xen had another. The zoo grew with every vendor.
In 2007, Linux kernel developer Rusty Russell proposed virtio: one standard family of virtual devices that any hypervisor can offer and one small set of drivers that every Linux guest carries. The network flavor is virtio_net, and in a few minutes you will read his name straight off the module's own metadata.
Virtio devices are paravirtual: they do not pretend to be a real chip. They admit they are virtual and use a fast shared-memory protocol with the hypervisor instead of imitating hardware registers. That honesty is why virtio is fast.
This lab runs in a Firecracker microVM. Firecracker is a lightweight virtual machine monitor built by AWS: it boots tiny VMs in about a second, and each one runs its own Linux kernel. Your eth0 is a virtio NIC. No chip, no copper, no firmware. Every packet becomes a memory operation on the host.
On a physical server, the classic first move is ethtool -i eth0. Under the hood, ethtool sends an ioctl, a system call for device-specific commands, named SIOCETHTOOL. The kernel hands it to the driver, whose get_drvinfo handler fills in the name, firmware version, and bus address by asking the hardware. On this microVM that path does not return the full report, because there is no hardware to ask.
So this track uses the way that works everywhere: sysfs, the virtual filesystem at /sys where the kernel presents its device records as readable files. Bare metal, cloud VM, container host, microVM: the device records are always there, because the kernel itself wrote them.
The name you find will depend on where you are standing:
| Driver | Hardware | Where you see it |
|---|---|---|
virtio_net | Virtual NIC (KVM, QEMU) | Cloud VMs, Firecracker microVMs, local QEMU |
ena | Elastic Network Adapter | AWS EC2 instances, Amazon custom hardware |
gve | Google Virtual Ethernet | Google Cloud VMs |
e1000e | Intel PRO/1000 | Older physical servers, some VMware NICs |
igb | Intel Gigabit Ethernet | Servers with Intel I350/I210 NICs |
ixgbe | Intel 10 Gigabit | Datacenter servers with Intel X520/X540 |
bnxt_en | Broadcom NetXtreme | HP and Dell servers with Broadcom NICs |
mlx5_core | Mellanox ConnectX | High-performance 25/50/100 GbE datacenter NICs |
Here you will meet virtio_net. In a datacenter you will meet the rest of the table.
Every interface has a directory in /sys/class/net/. Inside it, device/ leads to the underlying device, and the device carries an identity card: a file named uevent. Before you read it, commit to a prediction using the table above: which driver name will this VM report?
cat /sys/class/net/eth0/device/uevent
These paths are long, so let the shell type them. Enter cat /sys/cl and press Tab, and the shell completes the word. Press Tab twice to list the choices. Engineers never type sysfs paths out in full.
prompt: student@driver-lab:~$ answer: cat /sys/class/net/eth0/device/uevent output: DRIVER=virtio_net MODALIAS=virtio:d00000001v... hint: cat the uevent file under /sys/class/net/eth0/device/
Two lines, and each one earns its keep.
DRIVER=virtio_net is the answer to the lesson's question: the kernel module managing your NIC is virtio_net. This line only appears once a driver has actually bound to the device. If you ever see a MODALIAS with no DRIVER line, you have found a device the kernel detected but nothing claimed: the module is missing or its probe failed. That one missing line is a diagnostic in itself.
MODALIAS is the device describing itself in a machine-readable string. Decode it: virtio: names the bus, and d00000001 is the virtio device type in hex. Type 1 is a network card in the virtio specification, type 2 is a block device, type 4 is an entropy source, type 5 is a memory balloon. The digits after v are the vendor id and differ by hypervisor, which is why they are elided here.
This string is how drivers load with nobody clicking anything. When the kernel discovers a device, it broadcasts the MODALIAS to userspace as an event. The udev daemon catches it and calls modprobe with the string. modprobe matches it, glob-style, against /lib/modules/$(uname -r)/modules.alias, a catalog built by depmod from every module's declared aliases. A match names the module to load. Devices that appear before udev is even running are not missed: at boot, udev walks sysfs and replays every uevent file, a pass known as coldplug.
The uevent file states the driver's name. The binding itself is a symlink, a file that points at another path. Inside device/ there is a link named driver, and readlink prints where a link points without following it.
readlink /sys/class/net/eth0/device/driver
prompt: student@driver-lab:~$ answer: readlink /sys/class/net/eth0/device/driver output: ../../../../bus/virtio/drivers/virtio_net hint: readlink on the driver link under /sys/class/net/eth0/device/
The last component is the driver name again, but the path is the real lesson. It climbs out of the device's directory and lands under /sys/bus/virtio/drivers/. Devices hang off buses, drivers register with buses, and when a driver's probe function accepts a device, the kernel ties the two objects together.
That tie is drawn in both directions. The device gets this driver link, and the driver's own directory, /sys/bus/virtio/drivers/virtio_net/, gains a link back to every device it has claimed. So sysfs can answer both working questions: which driver runs this device, and which devices does this driver run. On a server with four ports on one controller, that second question is the useful one.
You have the name. Now find the module actually sitting in kernel memory. lsmod lists every loaded module. It invents nothing: it is a formatter for /proc/modules, the kernel's own list, padded into three columns: the name, kernel memory used in bytes, and a reference count followed by who is using it.
Before you run it, commit to a number. On a kernel that keeps its drivers as modules, virtio_net sits in this list, driving the machine's only NIC this very second. What does its Used by count say?
lsmod
The screen below is a general-purpose kernel answering, the kind a laptop or a cloud VM runs, and it is trimmed to the three rows this lesson is about. A real list runs to hundreds of rows, and the sizes differ from build to build.
prompt: student@driver-lab:~$ answer: lsmod output: Module Size Used by virtio_net 57344 0 net_failover 24576 1 virtio_net failover 16384 1 net_failover hint: The module lister needs no arguments: lsmod
Zero. The module driving all of your network traffic reports Used by 0, and that is the most misread number in this output.
The count is a reference count, and driving hardware does not raise it. It protects module-to-module dependencies and explicitly taken holds, not activity. Read the chain: failover shows 1 because net_failover needs it, net_failover shows 1 because virtio_net needs it, and nothing needs virtio_net. The number can also exceed the names shown: a mounted filesystem holds its module with a bare count and no name in the list.
The consequence is sharp: Used by 0 does not mean safe to remove. Nothing stops root from unloading a NIC driver that is busy. The interface simply vanishes, mid-flight, along with your SSH session. Linux assumes root means it.
As for why a simple virtual NIC drags in a failover engine at all: that answer is printed on the module's papers, next.
Your own VM may answer this one differently, and the difference is worth knowing before it confuses you. A kernel can carry a driver two ways: as a loadable module on disk, or compiled straight into the kernel image at build time. Distribution kernels prefer modules, because one kernel has to run on every machine anybody owns. Stripped kernels, the kind a microVM boots, often compile the handful of virtio drivers straight in and ship no modules at all. On a machine like that, lsmod has nothing to list and prints little more than its header, while virtio_net runs the whole time. The last step of this lesson is where you prove exactly that, so read an empty list as a finding, not a fault.
modinfo prints everything a module declares about itself. Note what it does not do: it never asks the running kernel. It opens the .ko file on disk and reads a section of metadata compiled into it, which is why it works on modules that are not even loaded.
Which disk location? /lib/modules/ followed by the running kernel's version, the string uname -r prints. That detail decides whether the command can answer at all. A kernel that ships no modules has no such directory, and then modinfo replies that the module was not found however healthy the driver is. Same finding as an empty lsmod, different messenger, and both have an entry in the errors list below.
One trap first. modinfo lives in /usr/sbin, and on Debian a normal user's PATH does not include the sbin directories. Run it with sudo, which searches its own trusted path. Somewhere in this output is the name from the 2007 story. Find it.
sudo modinfo virtio_net
The screen is trimmed: on a machine that keeps its modules on disk, yours adds a srcversion and several signature lines.
prompt: student@driver-lab:~$ answer: sudo modinfo virtio_net ||| modinfo virtio_net output: filename: /lib/modules/6.1.0-26-amd64/kernel/drivers/net/virtio_net.ko license: GPL description: Virtio network driver author: Rusty Russell <[email protected]> alias: virtio:d00000001v* depends: net_failover intree: Y vermagic: 6.1.0-26-amd64 SMP preempt mod_unload modversions parm: napi_weight:int parm: csum:bool parm: gso:bool parm: napi_tx:bool hint: Ask for the papers with sudo modinfo virtio_net
There he is: author: Rusty Russell. The person who proposed virtio wrote the driver you are running. Now read the lines that do real work:
| Field | What it tells you |
|---|---|
filename | The compiled object on disk, under this kernel's version directory |
alias | The device patterns this module volunteers for |
depends | Modules that must load first |
intree | Y means shipped with the official kernel source, not built externally |
vermagic | The exact kernel this module was compiled for |
parm | Settings the module accepts |
Look at alias: virtio:d00000001v* next to the MODALIAS you read earlier. The uevent was the question. This line is the answer. modprobe is the matchmaker between the two, and the * swallows the vendor digits, so any hypervisor's network device matches this one driver. That pairing is the entire auto-loading mechanism, demystified.
depends: net_failover explains your lsmod screen. modprobe reads modules.dep and loads the chain bottom-up: failover, then net_failover, then virtio_net. Why does a plain NIC driver carry a failover engine? In clouds, a VM can be handed a fast passthrough NIC alongside its virtio one. net_failover welds the pair into a single interface, so the fast card can be yanked during live migration without dropping the network. The plumbing is loaded so the feature exists the moment a hypervisor offers it.
vermagic is a gate, not trivia. At load time the kernel compares this string against itself, and a mismatch is refused with an Invalid module format error. It is why modules are filed under one directory per kernel version.
Those parm lines are module parameters: knobs you can set when the module loads, either on the command line, like modprobe virtio_net napi_weight=128, or permanently in a file under /etc/modprobe.d/. sudo modinfo -p virtio_net prints just the knob list.
But modinfo reads the disk, so it can only tell you what knobs exist and their compiled-in defaults. The running values live somewhere else: /sys/module/, where the kernel gives every loaded module a directory, with one file per parameter.
ls /sys/module/virtio_net/parameters
prompt: student@driver-lab:~$ answer: ls /sys/module/virtio_net/parameters output: csum gso napi_tx napi_weight hint: ls the parameters directory under /sys/module/virtio_net/
Four files, matching the four parm lines exactly. Each file holds the value the running kernel is using right now. Their permissions mirror what the driver declared: read-only ones are fixed until the module reloads, and writable ones, napi_tx here, can be changed live by root.
One more thing about /sys/module, and it is a fact worth carrying: it lists every module, including code compiled directly into the kernel. lsmod cannot see built-ins, because /proc/modules only tracks loadable ones. So a driver can be absent from lsmod and still be right there, running, with its parameters exposed under /sys/module.
That is the resolution of both puzzles above. If your lsmod came back with nothing and modinfo could not find a file to read, this directory still exists, because the kernel registers built-in code here exactly as it registers a loaded module. The interface works, the driver is real, and /sys/module is the window that proves it. Recent versions of modinfo can describe built-ins too, printing filename: (builtin), but only when the kernel's /lib/modules directory is installed for it to read.
Stop and look at the chain you just walked. Device to driver name, name to module in memory, module to file on disk, file to author, dependencies, and knobs, knobs to their live values inside the running kernel.
That walk is identical on your laptop, on an EC2 instance running ena, and on a 100 GbE Mellanox rig running mlx5_core. Only the name in the middle changes. You did not learn one VM's trivia. You learned the route.
When a driver initializes, finds hardware, or hits trouble, it writes a line into the kernel ring buffer. That is a fixed-size log in memory: as new lines arrive, the oldest fall off the end. dmesg prints it.
Debian locks that buffer away from normal users. Run it bare and you get the literal complaint:
dmesg: read kernel buffer failed: Operation not permitted
That is the kernel.dmesg_restrict setting doing its job, so use sudo. On the real VM, filter the diary down to your driver:
sudo dmesg | grep -i virtio
You will see timestamped lines from virtio_net and its bus, written seconds after power-on. The bracketed number on each line is seconds since the kernel started, on the kernel's own monotonic clock. sudo dmesg -T converts them to wall-clock time, but it assumes the machine never slept, so on laptops those timestamps drift. Handy variants:
| Command | What it does |
|---|---|
sudo dmesg -w | Follow the buffer live, like tail -f for the kernel |
sudo dmesg -T | Human-readable timestamps, with the suspend caveat |
sudo dmesg --level=err,warn | Only errors and warnings |
journalctl -k | The same diary through the journal, kept across reboots when persistent |
When an interface misbehaves, this diary is where the driver complains: link flaps, resets, watchdog timeouts. It is one of the five things you will do on the real VM.
Scaffolding off. No command is shown from here on.
This VM has a second interface: lo, the loopback, the one programs use to talk to the machine itself. Read its identity card exactly the way you read eth0's, and read the reply carefully.
prompt: student@driver-lab:~$ answer: cat /sys/class/net/lo/device/uevent output: cat: /sys/class/net/lo/device/uevent: No such file or directory hint: Same path shape as eth0: cat the uevent file under the lo interface's device directory.
The error is the answer. lo has no device/ directory at all, because no device backs it. It is software all the way down: no bus, no binding, no driver to name. Bridges, veth pairs, bonds, and tunnels are the same.
That makes this failed command a working diagnostic. The presence of a device/ link is how you split every interface on a box into hardware-backed and purely virtual, before you trust anything else it tells you.
The papers said virtio_net has a knob named napi_weight. Read its value out of the running kernel. Not from modinfo. From the kernel.
prompt: student@driver-lab:~$ answer: cat /sys/module/virtio_net/parameters/napi_weight output: 64 hint: One file per knob under /sys/module/virtio_net/parameters/. cat the one you want.
64. Here is what you just read. Under load, the kernel stops taking one interrupt per packet and switches the driver to polling, a scheme called NAPI. The weight is the budget: how many packets one poll m
Practice The Driver in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.