What is Network Device Naming In Linux-Free Linux Device Drivers Course

Network Device Naming In Linux

A free Linux kernel development course lecture on how network interfaces get identified without major and minor numbers

Free Linux Kernel Development Course
Free Embedded Systems Course
Hands-on Demo Included

Every driver class you have studied so far in this free Linux device drivers course — character and block — is reached through a device node under /dev, keyed by a major/minor pair. Network devices break that pattern entirely. There is no /dev/eth0 on a modern Linux system, and no minor number to look up. This lecture, part of our free embedded Linux course, explains how the kernel names network interfaces instead, how you interact with them from user space, and how to read hardware details directly through a raw socket ioctl.

What You Will Learn

Lecture Roadmap
1. Why network devices skip the major/minor device-node model 2. How a driver requests a name and instance number for its interface 3. How udev/systemd may rename interfaces after registration 4. The normal path: configuring with ip, then using sockets 5. The direct path: querying a driver with ioctl from user space 6. A minimal ep_netdemo driver showing registration on a modern kernel

Prerequisites

This lecture assumes you have completed the character and block device lectures earlier in this free Linux kernel development course, and that you are comfortable building and loading a kernel module.

Why No Device Node?

Character and block drivers exist to move bytes or blocks in and out of a file-like object, so a filesystem node is a natural interface: open it, read it, write it, close it. A network interface does not work that way. Data does not flow through a single file descriptor tied to the interface itself; instead, the networking stack routes individual packets to whichever interface a socket’s routing decision selects. Because there is no one file to open, the kernel never allocates a major/minor pair or a /dev entry for a network device at all. Instead, it allocates the interface a name.

How a Driver Registers an Interface Name

A network driver asks the core networking code for a net_device structure using an allocation call, supplying a name template rather than a fixed name. On a current mainline kernel this is done with alloc_netdev() (or one of its wrappers such as alloc_etherdev() for Ethernet-style hardware), followed by register_netdev() to make the interface live.

Name Template Resolution
Driver requests template: “eth%d” First driver instance registered -> eth0 Second driver instance registered -> eth1 Third driver instance registered -> eth2 Driver requests template: “wlan%d” -> wlan0, wlan1, … Driver requests template: “net%d” -> net0, net1, … (generic fallback)

The %d in the template is filled in by the kernel with the next free instance number for that prefix, starting from zero. This is only the starting name, though. A user-space device manager such as udev, driven by rules keyed on bus location, MAC address, or firmware description, is free to rename the interface to something more predictable — which is exactly why modern distributions show names like enp3s0 or wlp2s0 rather than plain eth0, even though the driver itself only ever asked for eth%d.

The Normal Path: ip and Sockets

Once an interface exists, its name is normally used for exactly one thing from a human’s perspective: configuring it with a tool like ip or the older ifconfig, to assign an address and bring up routes.

$ ip link show
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...

$ sudo ip addr add 192.168.1.50/24 dev eth0
$ sudo ip link set eth0 up

After that point, applications never mention the interface name again. They open a socket, and the network layer inside the kernel decides — based on the routing table, not on any file path — which interface actually carries each packet.

The Direct Path: Talking to a Driver with ioctl

It is also possible to reach a network driver directly from user space, bypassing the routing layer, by creating a socket purely as a handle and issuing one of the ioctl commands the networking core defines for driver queries and configuration. A common example is asking for a device’s hardware (MAC) address with SIOCGIFHWADDR.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/sockios.h>
#include <net/if.h>

int main(int argc, char *argv[])
{
    struct ifreq ifr;
    int sock, i;

    if (argc != 2) {
        fprintf(stderr, "usage: %s \n", argv[0]);
        return 1;
    }

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        return 1;
    }

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, argv[1], IFNAMSIZ - 1);

    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
        perror("ioctl(SIOCGIFHWADDR)");
        close(sock);
        return 1;
    }

    printf("%s hwaddr: ", argv[1]);
    for (i = 0; i < 6; i++)
        printf("%02x%s", (unsigned char)ifr.ifr_hwaddr.sa_data[i],
               i < 5 ? ":" : "\n");

    close(sock);
    return 0;
}

Build and run it against a real interface:

$ gcc -o ep_getmac ep_getmac.c
$ ./ep_getmac eth0
eth0 hwaddr: 02:42:ac:11:00:02

The socket here is only ever used as a handle for the ioctl call — no packet is actually sent. This is a standard ioctl the generic network layer handles on the driver’s behalf, but drivers are free to define their own private ioctl numbers for hardware-specific configuration that has no generic equivalent.

Registering a Minimal Network Interface

You can see the naming mechanism in isolation with a tiny driver that registers an interface but implements no real transmit/receive path.

#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>

static struct net_device *ep_netdev;

static netdev_tx_t ep_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    /* Demo only: drop every packet, just prove registration/naming works */
    dev_kfree_skb(skb);
    return NETDEV_TX_OK;
}

static const struct net_device_ops ep_netdev_ops = {
    .ndo_start_xmit = ep_start_xmit,
};

static int __init ep_netdemo_init(void)
{
    int ret;

    ep_netdev = alloc_netdev(0, "ep_net%d", NET_NAME_UNKNOWN, ether_setup);
    if (!ep_netdev)
        return -ENOMEM;

    ep_netdev->netdev_ops = &ep_netdev_ops;
    eth_hw_addr_random(ep_netdev);

    ret = register_netdev(ep_netdev);
    if (ret) {
        free_netdev(ep_netdev);
        return ret;
    }

    pr_info("ep_netdemo: registered as %s\n", ep_netdev->name);
    return 0;
}

static void __exit ep_netdemo_exit(void)
{
    unregister_netdev(ep_netdev);
    free_netdev(ep_netdev);
}

module_init(ep_netdemo_init);
module_exit(ep_netdemo_exit);
MODULE_LICENSE("GPL");
$ sudo insmod ep_netdemo.ko
$ dmesg | tail -1
[  987.654321] ep_netdemo: registered as ep_net0

$ ip link show ep_net0
5: ep_net0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN
    link/ether 4a:2f:91:0c:88:7e brd ff:ff:ff:ff:ff:ff

$ sudo rmmod ep_netdemo

Notice there is no corresponding entry under /dev at any point, and no major/minor pair shows up anywhere — the interface is only ever reachable by the name ep_net0 that the kernel handed back from the %d template.

Comparison With Character and Block Devices

AspectCharacter/Block DevicesNetwork Devices
IdentifierMajor + minor numberInterface name (eth0, wlan0, …)
Access point/dev nodeNone — accessed via sockets
Registration callregister_chrdev_region / register_blkdevalloc_netdev + register_netdev
RenamingNot applicable (node path can be symlinked)udev/systemd may rename after registration
Direct user-space controlopen/read/write/ioctl on the nodeioctl on a throwaway socket

Common Mistakes

  • Looking for a /dev entry for a network interface — it does not exist and never will.
  • Hardcoding an interface name like eth0 in scripts, when udev naming rules can and do change it on real hardware.
  • Forgetting that the driver’s requested name (eth%d, wlan%d) is only a starting point, not a guarantee.
  • Using a full AF_PACKET raw socket when a simple AF_INET/SOCK_DGRAM socket is enough just to issue a driver ioctl.

Best Practices

  • Prefer generic ioctl numbers and the standard netlink/ip tooling over private driver ioctls whenever the functionality already exists generically.
  • Use stable identifiers (MAC address matching, systemd link files) rather than the raw kernel-assigned name if your application must survive interface renaming.
  • Always free the net_device and unregister cleanly in your module’s exit path to avoid leaving a stale interface behind.

Summary

Network devices are the odd one out among the driver classes in this free Linux kernel development course: no major/minor pair, no /dev node, and no open/read/write model. Instead, the kernel hands out a name built from a driver-supplied template and an auto-incrementing instance number, and user space normally talks to the interface only indirectly, through routing and sockets — though a direct ioctl path remains available for querying or configuring the driver when needed.

Frequently Asked Questions

Why don’t network devices have major and minor numbers?

Because there is no single file-like object to open for a network interface — packets are routed by the networking stack rather than read or written through one file descriptor, so the kernel assigns a name instead of a device node.

What determines whether an interface is called eth0 or something else?

The driver supplies a name template such as “eth%d” when it calls alloc_netdev(), and the kernel fills in the next free instance number; a device manager like udev may rename it afterward based on its own rules.

Can user space talk to a network driver without going through the routing layer?

Yes, by creating a socket purely as a handle and issuing an ioctl such as SIOCGIFHWADDR, which the network layer forwards to the driver on the caller’s behalf.

Why did my eth0 interface get renamed to something like enp3s0?

Modern distributions use udev/systemd predictable network interface naming rules based on bus location or firmware information, which override the driver’s original eth%d name.

Do custom ioctl numbers work the same way for network drivers as for character drivers?

Yes, a network driver can define private ioctl numbers for functionality with no generic equivalent, handled the same way a character driver would handle its own custom ioctl commands.

Is this lecture part of a free Linux kernel development course I can follow from the start?

Yes, this is one lecture in an ongoing free Linux kernel development course and free Linux device drivers course covering driver types in order, starting from character devices.

Continue Your Free Embedded Linux Course

Next up: inspecting drivers already loaded on a running system through /proc and /sys.

Next Lecture Back to Course Index