网络命名空间(内核源码实现)

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

  1. 239 SYSCALL_DEFINE2(setns, int, fd, int, nstype)  
  2. 240 {  
  3. 241     const struct proc_ns_operations *ops;  
  4. 242     struct task_struct *tsk = current; 获得当前进程描述结构体  
  5. 243     struct nsproxy *new_nsproxy;  
  6. // 创建一个新的命名空间的调用  
  7. 258     new_nsproxy = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs);  
  8. //proc目录下信息注册  
  9. 264     err = ops->install(new_nsproxy, ei->ns);  
  10. //将新创建的命名空间new_nsproxy赋值给当前进程,即替换掉以前的命名空间。  
  11. 269     switch_task_namespaces(tsk, new_nsproxy);  
  12. 273 }  

258调用的函数依然在nsproxy.c文件,该函数定义见59行。

  1. 59 static struct nsproxy *create_new_namespaces(unsigned long flags,  
  2.  60     struct task_struct *tsk, struct user_namespace *user_ns,  
  3.  61     struct fs_struct *new_fs)  
  4.  62 {  
  5.  63     struct nsproxy *new_nsp;  
  6.  64     int err;  
  7. //从nsproxy_cachep中申请一个nsproxy缓存,并将引用计数置1,  
  8.  66     new_nsp = create_nsproxy();  
  9. /mnt命名空间  
  10. 70     new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs);  
  11. //uts命名空间  
  12.  76     new_nsp->uts_ns = copy_utsname(flags, user_ns, tsk->nsproxy->uts_ns);  
  13. //ipc命名空间  
  14.  82     new_nsp->ipc_ns = copy_ipcs(flags, user_ns, tsk->nsproxy->ipc_ns);  
  15. //ns命名空间  
  16.  88     new_nsp->pid_ns = copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns);  
  17. //网络命名空间  
  18. 94     new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns);  
  19. 117 }  

类似图13.1.1中进程3那样需要部分复制命名空间时(clone系统调用),copy_namespaces()将被调用。

  1. 123 int copy_namespaces(unsigned long flags, struct task_struct *tsk)  
  2. 124 {  
  3. 125     struct nsproxy *old_ns = tsk->nsproxy; //当前进程命名空间  
  4. 126     struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);  
  5. 127     struct nsproxy *new_ns;  
  6. 128     int err = 0;  
  7. 129   
  8. 130     if (!old_ns)  
  9. 131         return 0;  
  10. //引用计数原子加1  
  11. 133     get_nsproxy(old_ns);  
  12. //复制标志  
  13. 135     if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |  
  14. 136                 CLONE_NEWPID | CLONE_NEWNET)))  
  15. 137         return 0;  
  16. //权能  
  17. 139     if (!ns_capable(user_ns, CAP_SYS_ADMIN)) {  
  18. 140         err = -EPERM;  
  19. 141         goto out;  
  20. 142     }  
  21. 143   
  22. //在复制ipc空间时,存在不同命名空间信号量的问题,切换到新的命名空间意味着原来的信号量将不可达,undolist(未处理  
  23. //信号的链表必须和新的ipc空间分离),如果标志设置有CLONE_SYSVSEM,意味着要求信号量共享,那就意味着不能创  
  24. //建新的ipc空间了。  
  25. 151     if ((flags & CLONE_NEWIPC) && (flags & CLONE_SYSVSEM)) {  
  26. 152         err = -EINVAL;  
  27. 153         goto out;  
  28. 154     }  
  29. //创建命名空间  
  30. 156     new_ns = create_new_namespaces(flags, tsk, user_ns, tsk->fs);  
  31. 157     if (IS_ERR(new_ns)) {  
  32. 158         err = PTR_ERR(new_ns);  
  33. 159         goto out;  
  34. 160     }  
  35. //将进程的命名空间空间指针指向新的命名空间  
  36. 162     tsk->nsproxy = new_ns;  
  37. 163   
  38. 164 out:  
  39. 165     put_nsproxy(old_ns);  
  40. 166     return err;  
  41. 167 }  

13.2 网络命名空间管理

网络命名空间操作主要在net/core/net_namespace.c,接13.1节的copy_net_ns()函数。该函数返回值是一个网络空间。创建网络命名空间时以创建网络命名空间进程的nsproxy-> net_ns为网络命名空间的原型,这是因为不同的网络命名空间很多的基础设施是一样的,比如对loopback回环接口的支持、TCP的支持、UDP支持、IP支持等等。

  1. 238 struct net *copy_net_ns(unsigned long flags,  
  2. 239             struct user_namespace *user_ns, struct net *old_net)  
  3. 240 {  
  4. 241     struct net *net;  
  5. 242     int rv;  
  6. //如果flag中CLONE_NEWNET没有设置,说明不需要创建新的网络命名空间,直接返回原来的命名空间。否则创建新的网  
  7. //络命名空间  
  8. 244     if (!(flags & CLONE_NEWNET))  
  9. 245         return get_net(old_net);  
  10. //从net_cachep为新的网络命名空间申请内存。  
  11. 247     net = net_alloc();  
  12. //增加user命名空间的引用计数   
  13. 251     get_user_ns(user_ns);  
  14. 252   
  15. 253     mutex_lock(&net_mutex);  
  16. //调用pernet_list上注册的服务函数,对这个新的网络命名空间执行init。  
  17. 254     rv = setup_net(net, user_ns);  
  18. 255     if (rv == 0) {  
  19. 256         rtnl_lock();  
  20. 257         list_add_tail_rcu(&net->list, &net_namespace_list); // net_namespace_list为所有命名空间串接的链表。  
  21. 258         rtnl_unlock();  
  22. 259     }  
  23. 260     mutex_unlock(&net_mutex);  
  24. 261     if (rv < 0) { //出错处理  
  25. //user命名空间计数值减一  
  26. 262         put_user_ns(user_ns);  
  27. //释放net命名空间。  
  28. 263         net_drop_ns(net);  
  29. 264         return ERR_PTR(rv);  
  30. 265     }  
  31. 266     return net;  
  32. 267 }  

254行的setup_net()如下, 其主要工作就是遍历pernet_list获得对应的structpernet_operations结构体,然后将struct pernet_operations结构对应的实例的init成员函数作用于新创建的命名空间。

  1. 150 static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)  
  2. 151 {  
  3. 166     list_for_each_entry(ops, &pernet_list, list) {  
  4. 167         error = ops_init(ops, net);  
  5. 168         if (error < 0)  
  6. 169             goto out_undo;  
  7. 170     }  
  8. 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注销

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值