Getting list of network devices inside the Linux kernel

Peter Murfitt picture Peter Murfitt · Dec 20, 2010 · Viewed 13.2k times · Source

I've been looking through net/core/dev.c and other files to try to find out how to get the list of network devices that are currently configured and it's proving to be a little difficult to find.

The end goal is to be able to get network device statistics using dev_get_stats in dev.c, but I need to know the current interfaces so I can grab the net_device struct to pass in. I'm having to do this inside the kernel as I'm writing a module which adds in a new /proc/ entry which relates to some statistics from the current network devices so from what I can gather this must be done inside the kernel.

If someone could point me to how to get the interfaces it would be much appreciated.

Answer

caf picture caf · Dec 21, 2010

Given a struct net *net identifying the net namespace that you are interested in, you should grab the dev_base_lock and use for_each_netdev():

read_lock(&dev_base_lock);
for_each_netdev(net, dev) {
   /* Inspect dev */
}
read_unlock(&dev_base_lock);

(In newer kernels, you can use RCU instead, but that is probably an overcomplication in this case).


To obtain the net namespace to use, you should be registering your proc file with register_pernet_subsys():

static const struct file_operations foostats_seq_fops = {
    .owner   = THIS_MODULE,
    .open    = foostats_seq_open,
    .read    = seq_read,
    .llseek  = seq_lseek,
    .release = foostats_seq_release,
};

static int foo_proc_init_net(struct net *net)
{
    if (!proc_net_fops_create(net, "foostats", S_IRUGO,
            &foostats_seq_fops))
        return -ENOMEM;
    return 0;
}

static void foo_proc_exit_net(struct net *net)
{
    proc_net_remove(net, "foostats");
}


static struct pernet_operations foo_proc_ops = {
    .init = foo_proc_init_net,
    .exit = foo_proc_exit_net,
};

register_pernet_subsys(&foo_proc_ops)

In your foostats_seq_open() function, you take a reference on the net namespace, and drop it in the release function:

static int foostats_seq_open(struct inode *inode, struct file *file)
{
    int err;
    struct net *net;

    err = -ENXIO;
    net = get_proc_net(inode);
    if (net == NULL)
        goto err_net;

    err = single_open(file, foostats_seq_show, net);
    if (err < 0)
        goto err_open;

    return 0;

err_open:
    put_net(net);
err_net:
    return err;
}

static int foostats_seq_release(struct inode *inode, struct file *file)
{
    struct net *net = ((struct seq_file *)file->private_data)->private;

    put_net(net);
    return single_release(inode, file);
}

The foostats_seq_show() function can then obtain the net, walk the devices, gather the statistics and produce the output:

static int sockstat6_seq_show(struct seq_file *seq, void *v)
{
    struct net *net = seq->private;
    struct net_device *dev;

    int foostat, barstat;

    read_lock(&dev_base_lock);
    for_each_netdev(net, dev) {
       /* Inspect dev */
    }
    read_unlock(&dev_base_lock);

    seq_printf(seq, "Foo: %d\n", foostat);
    seq_printf(seq, "Bar: %d\n", barstat);

    return 0;
}