linuxkernel-modulelinux-namespaces

How to write Linux kernel module that is a new instance in each network namespace?


is using struct pernet_operations and register_pernet_subsys(..) the correct way to have a state per network namespace in a Linux kernel module?

Or is there a way just mark a kernel module and it will have an independent state in each network namespace?


Solution

  • Linux Kernel modules have only one state. Network namespaces need to be explicitly handled in the kernel module.

    The key methods to use are register_pernet_subsys, unregister_pernet_subsys and net_generic.

    Here is an example kernel module:

    #include <net/sock.h>
    #include <net/netns/generic.h>
    #include <net/net_namespace.h>
    #include <linux/module.h>
    #include <linux/skbuff.h>
    #include <linux/pid_namespace.h>
    
    /*
     * Per network namespace data
     */
    struct ns_data {
      struct sock *sk;
    };
    
    /*
     * Index to store custom data for each network namespace.
     */
    static unsigned int net_id;
    
    /*
     * Called for every existing and added network namespaces
     */
    static int __net_init ns_test_init(struct net *net)
    {
      // create (if not present) and access data item in network namespace (net) using the id (net_id) 
      struct ns_data *data = net_generic(net, net_id);
      data->sk = -1; // initialize or example socket
    
      // ...
    
      return 0;
    }
    
    static void __net_exit ns_test_exit(struct net *net)
    {
      // called when the network namespace is removed
      struct ns_data *data = net_generic(net, net_id);
    
      // close socket
      netlink_kernel_release(data->sk);
    }
    
    // callback to make the module network namespace aware
    static struct pernet_operations net_ops __net_initdata = {
      .init = ns_test_init,
      .exit = ns_test_exit,
      .id = &net_id,
      .size = sizeof(struct ns_data),
    };
    
    static int __init netlink_test_init(void)
    {
      printk(KERN_INFO "netlink_test: Init module\n");
    
      register_pernet_subsys(&net_ops);
    
      return 0;
    }
    
    static void __exit netlink_test_exit(void)
    {
      printk(KERN_INFO "netlink_test: Exit module\n");
    
      unregister_pernet_subsys(&net_ops);
    }
    
    module_init(netlink_test_init);
    module_exit(netlink_test_exit);
    
    MODULE_LICENSE("GPL");