clinuxnetworkinglinux-kernelnetfilter

Obtain interface netmask in Linux kernel module


I write Linux kernel module with netfilter hook. I want to block any packet that is not from my subnet.

Is there any simple method to get netmask of the interface in kernel-mode? I found only the way to get it using ioctl() in user-mode.


Solution

  • There is a pretty easy way to get it.
    Network device is described by struct net_device.
    <linux/netdevice.h>:

    struct net_device {
        ...
        struct in_device __rcu  *ip_ptr;
        ...
    

    net_device has a pointer to "inet" device (in_device).

    <linux/inetdevice.h>:

    struct in_device {
        ...
        struct in_ifaddr    *ifa_list;  /* IP ifaddr chain      */
        ...
    

    which finnaly points to chain of in_ifaddr that contains all the interface info:

    struct in_ifaddr {
        struct hlist_node   hash;
        struct in_ifaddr    *ifa_next;
        struct in_device    *ifa_dev;
        struct rcu_head     rcu_head;
        __be32              ifa_local;
        __be32              ifa_address;
        __be32              ifa_mask;
        __u32               ifa_rt_priority;
        __be32              ifa_broadcast;
        unsigned char       ifa_scope;
        unsigned char       ifa_prefixlen;
        __u32               ifa_flags;
        char                ifa_label[IFNAMSIZ];
    
        /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
        __u32               ifa_valid_lft;
        __u32               ifa_preferred_lft;
        unsigned long       ifa_cstamp; /* created timestamp */
        unsigned long       ifa_tstamp; /* updated timestamp */
    };
    

    To make my answer more versatile, here is an abstract example (without binding to and devices logic):

    struct in_ifaddr *ifa;
    struct net_device *dev = dev_get_by_name(&init_net, "wlp7s0");
    if(!dev) {
        printk(KERN_ERR "Can't obtain device\n");
        return;
    }
    
    // roughly
    rcu_read_lock();
    for(ifa = rcu_dereference(dev->ip_ptr->ifa_list);
              ifa;
              ifa = rcu_dereference(ifa->ifa_next))
        printk("address: %pI4, mask: %pI4\n", &ifa->ifa_address, &ifa->ifa_mask);
    rcu_read_unlock();
    

    From example you can see that you can handle the whole chain(that @larsks mentioned in comment) depending on some specific logic.

    P.S. don't forget to include <linux/netdevice.h> and <linux/inetdevice.h>.