Keep learning, keep living...

0%

内核模块识别network namespace

业务场景中,需要创建指定的network namespace, 并且内核模块中的netfilter逻辑只应生效在该network namespace中。这就需要我们创建network namespace之后,将指定的namespace传递给内核模块。

用户态创建network namespace可以使用ip命令指定名称,如:

1
ip netns add ns1

但实际上在内核中,network namespace并不具备名称信息,名称信息只存在于用户态。可以参考man ip-netns:

1
2
3
4
By convention a named network namespace is an object at /var/run/netns/NAME that can be opened. The file descriptor resulting
from opening /var/run/netns/NAME refers to the specified network namespace. Holding that file descriptor open keeps the network
namespace alive. The file descriptor can be used with the setns(2) system call to change the network namespace associated with a
task.

network namespace/proc文件系统中存在,因而可以使用文件的inode来标识。inode信息则可以通过network namespace中的进程从/proc获取到namespace的文件来识别。

下边来实验。
先在上边创建的ns1中执行一个sleep 3600

1
ip netns exec ns1 sleep 3600

通过pid获取network namespaceinode:

1
2
3
4
5
[root@localhost ~]# ps aux |grep sleep
root 22371 0.0 0.0 108052 356 pts/2 S+ 17:59 0:00 sleep 3600
root 22620 0.0 0.0 9092 680 pts/3 S+ 18:01 0:00 grep --color=auto sleep
[root@localhost ~]# ls -l /proc/22371/ns/net
lrwxrwxrwx 1 root root 0 Jan 2 17:59 /proc/22371/ns/net -> net:[4026532631]

CentOS7的内核源码中,inode信息保存在struct net结构的proc_inum字段中。后来迁移到了struct ns_common结构中:

不同版本的内核需要兼容处理一下。这里以CentOS7为实验环境,编写内核模块源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#define pr_fmt(fmt) "[%s]: " fmt, KBUILD_MODNAME

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/net.h>
#include <net/net_namespace.h>
#include <linux/proc_ns.h>
#include <linux/nsproxy.h>
#include <linux/dcache.h>
#include <linux/sched.h>


static unsigned int inode = 0;
module_param(inode, uint, 0400);

int __init netns_inode_init(void)
{
struct net *net;

pr_info("netns_inode module init\n");

for_each_net(net) {
pr_info("namespace: p: %px, inode: %u%s", net, net->proc_inum, (inode == net->proc_inum) ? " ***\n" : "\n");
}

return -1;
}

void __exit netns_inode_exit(void)
{
pr_info("netns_inode module exit\n");
return;
}

module_init(netns_inode_init);
module_exit(netns_inode_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("netns inode");

编译后执行内核模块,可以识别到传入的inode标识:

1
2
3
4
5
6
7
8
[root@localhost netns_inode]# insmod netns_inode.ko inode=4026532631
insmod: ERROR: could not insert module netns_inode.ko: Operation not permitted
[root@localhost netns_inode]# dmesg
[1216768.454246] [netns_inode]: netns_inode module init
[1216768.454250] [netns_inode]: namespace: p: ffffffffa3111640, inode: 4026531956
[1216768.454252] [netns_inode]: namespace: p: ffffa0c93f6b9480, inode: 4026532511
[1216768.454253] [netns_inode]: namespace: p: ffffa0c91c738000, inode: 4026532574
[1216768.454254] [netns_inode]: namespace: p: ffffa0c613f70000, inode: 4026532631 ***