下载地址《http://download.csdn.net/detail/shichaog/8620701》
有两篇翻译博文《Lxc之二—网络设置》和《linuxnamespace-之使用》;LXC文章中关于网络的设置是从用户空间配置的,从该文章可以知道网络命名空间的一些基本概念和其提供的功能。而《linuxnamespace-之使用》包括了网络命名空间管理、配置以及使用,这比LXC译文更接近网络命名空间的实现,但是都是基于用户空间的,这章是关于Linux网络命名空间内核源码。在Linux中,每一个网络空间都使用struct net表示。
13.1 命名空间创建
在当前Linux下,对进程而言一个命名空间包括五个具体的命名空间,mnt、uts、ipc、pid以及net,在os启动时,其会初始化一个称为init的初始命名空间,并将该命名空间给创建的进程,在后续创建进程时,将根据创建的flag确定是否创建新的命名空间。在图13.1.1中,进程1、2都指向了init命名空间,在创建进程3时,明确指定了复制网络命名空间,其它的命名空间依然会继承(copy)。
图13.1.1 命名空间和进程的组合
创建命名空间的系统调用如下,nstype是创建进程时指定的创建命名空间的标志。
kernel/nsproxy.c
239 SYSCALL_DEFINE2(setns, int, fd, int, nstype)
240 {
241 const struct proc_ns_operations *ops;
242 struct task_struct *tsk = current; 获得当前进程描述结构体
243 struct nsproxy *new_nsproxy;
// 创建一个新的命名空间的调用
258 new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs);
//proc目录下信息注册
264 err = ops->install(new_nsproxy, ei->ns);
//将新创建的命名空间new_nsproxy赋值给当前进程,即替换掉以前的命名空间。
269 switch_task_namespaces(tsk, new_nsproxy);
273 }
258调用的函数依然在nsproxy.c文件,该函数定义见59行。
59 static struct nsproxy *create_new_namespaces(unsigned long flags,
60 struct task_struct *tsk, struct user_namespace *user_ns,
61 struct fs_struct *new_fs)
62 {
63 struct nsproxy *new_nsp;
64 int err;
//从nsproxy_cachep中申请一个nsproxy缓存,并将引用计数置1,
66 new_nsp = create_nsproxy();
/mnt命名空间
70 new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs);
//uts命名空间
76 new_nsp->uts_ns = copy_utsname(flags, user_ns, tsk->nsproxy->uts_ns);
//ipc命名空间
82 new_nsp->ipc_ns = copy_ipcs(flags, user_ns, tsk->nsproxy->ipc_ns);
//ns命名空间
88 new_nsp->pid_ns = copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns);
//网络命名空间
94 new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns);
117 }
类似图13.1.1中进程3那样需要部分复制命名空间时(clone系统调用),copy_namespaces()将被调用。
123 int copy_namespaces(unsigned long flags, struct task_struct *tsk)
124 {
125 struct nsproxy *old_ns = tsk->nsproxy; //当前进程命名空间
126 struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);
127 struct nsproxy *new_ns;
128 int err = 0;
129
130 if (!old_ns)
131 return 0;
//引用计数原子加1
133 get_nsproxy(old_ns);
//复制标志
135 if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
136 CLONE_NEWPID | CLONE_NEWNET)))
137 return 0;
//权能
139 if (!ns_capable(user_ns, CAP_SYS_ADMIN)) {
140 err = -EPERM;
141 goto out;
142 }
143
//在复制ipc空间时,存在不同命名空间信号量的问题,切换到新的命名空间意味着原来的信号量将不可达,undolist(未处理
//信号的链表必须和新的ipc空间分离),如果标志设置有CLONE_SYSVSEM,意味着要求信号量共享,那就意味着不能创
//建新的ipc空间了。
151 if ((flags & CLONE_NEWIPC) && (flags & CLONE_SYSVSEM)) {
152 err = -EINVAL;
153 goto out;
154 }
//创建命名空间
156 new_ns = create_new_namespaces(flags, tsk, user_ns, tsk->fs);
157 if (IS_ERR(new_ns)) {
158 err = PTR_ERR(new_ns);
159 goto out;
160 }
//将进程的命名空间空间指针指向新的命名空间
162 tsk->nsproxy = new_ns;
163
164 out:
165 put_nsproxy(old_ns);
166 return err;
167 }
13.2 网络命名空间管理
网络命名空间操作主要在net/core/net_namespace.c,接13.1节的copy_net_ns()函数。该函数返回值是一个网络空间。创建网络命名空间时以创建网络命名空间进程的nsproxy-> net_ns为网络命名空间的原型,这是因为不同的网络命名空间很多的基础设施是一样的,比如对loopback回环接口的支持、TCP的支持、UDP支持、IP支持等等。
238 struct net *copy_net_ns(unsigned long flags,
239 struct user_namespace *user_ns, struct net *old_net)
240 {
241 struct net *net;
242 int rv;
//如果flag中CLONE_NEWNET没有设置,说明不需要创建新的网络命名空间,直接返回原来的命名空间。否则创建新的网
//络命名空间
244 if (!(flags & CLONE_NEWNET))
245 return get_net(old_net);
//从net_cachep为新的网络命名空间申请内存。
247 net = net_alloc();
//增加user命名空间的引用计数
251 get_user_ns(user_ns);
252
253 mutex_lock(&net_mutex);
//调用pernet_list上注册的服务函数,对这个新的网络命名空间执行init。
254 rv = setup_net(net, user_ns);
255 if (rv == 0) {
256 rtnl_lock();
257 list_add_tail_rcu(&net->list, &net_namespace_list); // net_namespace_list为所有命名空间串接的链表。
258 rtnl_unlock();
259 }
260 mutex_unlock(&net_mutex);
261 if (rv < 0) { //出错处理
//user命名空间计数值减一
262 put_user_ns(user_ns);
//释放net命名空间。
263 net_drop_ns(net);
264 return ERR_PTR(rv);
265 }
266 return net;
267 }
254行的setup_net()如下, 其主要工作就是遍历pernet_list获得对应的structpernet_operations结构体,然后将struct pernet_operations结构对应的实例的init成员函数作用于新创建的命名空间。
150 static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
151 {
166 list_for_each_entry(ops, &pernet_list, list) {
167 error = ops_init(ops, net);
168 if (error < 0)
169 goto out_undo;
170 }
189 }
structpernet_operations的实例还是很多的,以下截图展示了其中的一部分,对于arp.c文件,其arp_net_ops的arp_net_init()实例会在167行被调用,以初始化新网络命名空间arp协议在proc目录的接口。
图13.2.1 structpernet_operations实例
其它网络命名空间的函数如下:
get_net_ns_by_fd()根据文件描述获得网络命名空间
get_net_ns_by_pid()根据进程ID获得网络命名空间。
net_ns_init()网络命名空间初始化,pure_initcall声明,在系统启动时会被调用初始化网络命名空间。
register_pernet_subsys()注册图13.2.1中的各种操作集,调用register_pernet_operations(),完成,实质工作在__register_pernet_operations()函数中完成真正的注册。
unregister_pernet_subsys注销