1、network namespace的建立網絡
在對iproute2的源碼進行分析後,咱們能夠知道,當咱們調用命令`ip netns add ns1`時,本質上就是調用`unshare(CLONE_NEWNET)`建立了一個新的network namespace。接着,咱們進一步對內核中對於unshare系統調用的實現進行分析,從而瞭解內核是如何建立一個network namespace的。數據結構
1、內核對unshare()的實現分爲兩步,第一步調用unshare_nsproxy_namespaces建立一個新的nsproxy,nsproxy的數據結構以下模塊化
struct nsproxy { atomic_t count; struct uts_namespace *uts_ns; struct ipc_namespace *ipc_ns; struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns; struct net *net_ns; };
一個nsproxy實例中包含了指向五種namespace結構的指針,一個process包含一個nsproxy,表明了這個process所在的各個namespace。當process調用unshare()函數時,內核就會其分配一個新的nsproxy結構,而且根據參數,新建部分namespace,並複製保留其他的namespace。例如,對於`unshare(CLONE_NEWNET)`語句,內核就會爲當前進程新建一個network namespace,其他namespace保持不變。函數
int unshare_nsproxy_namespaces(unsigned long unshare_flags, struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs)
一、若unshare_flags參數中沒有包含任何CLONE_NEW*參數,說明不用新建任何namespace,直接退出oop
2、檢驗所在的user namespace是否有CAP_SYS_ADMIN權限,沒有則報錯退出atom
3、調用*new_nsp = create_new_namespaces(unshare_flags, current, user_ns, new_fs ? new_fs : current->fs)建立新的namespacespa
static struct nsproxy *create_new_namespaces(unsigned long flags, struct task_struct *tsk, struct user_namespace *user_ns, struct fs_struct *new_fs)
一、調用new_nsp = create_nsproxy()建立一個新的nsproxy結構指針
2、再調用一系列例如new_nsp->mnt_ns = copy_mnt_ns(...)的命令,初始化新的nsproxy中的各個namespace指針。若是flags指示須要新建某個namespace,則copy_*函數就會新建一個對應的namespace,不然,就沿用以前的namespace。blog
3、最後調用new_nsp->net_ns = copy_net_ns(flags, user_ns, tsk->nsproxy->net_ns),同理,根據flags中的對應位,選擇新建或者沿用以前的network namespace進程
struct net *copy_net_ns(unsigned long flags, struct user_namespace *user_ns, struct net *old_net)
一、若是flags中不包含CLONE_NEWNET,則返回old_net,不然須要新建一個network namespace
二、調用net = net_alloc()分配一個新的struct net 結構
三、調用rv = setup_net(net, user_ns)對新分配的net結構進行初始化
四、調用list_add_tail_rcu(&net->list, &net_namespace_list),將新建的network namespace,添加到全局的network namespace列表net_namespace_list中
static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
一、對net中的某些字段進行初始化
2、遍歷pernet_list列表,對其中的每一個struct pernet_operations結構,調用err = ops_init(ops, net),完成各個模塊的初始化工做
咱們知道,每當建立一個新的network namespace,裏面總會默認存在一個loopback device,但這是怎麼完成的呢?其實在DEV模塊初始化的時候,會調用函數register_pernet_device(&loopback_net_ops),將loopback_net_ops掛載到pernet_list中,loopback_pernet_device結構以下所示:
struct pernet_operation __net_initdata loopback_net_ops = { .init = loop_net_init, }
而在ops_init(ops, net)就會調用loop_net_init(),建立該network namespace本身的loopback設備,對於路由表等其餘網絡資源的初始化,同理可得。
2、在建立了nsproxy以後,再調用switch_task_namespace(current, new_nsproxy)更換當前process的nsproxy
2、network devcie在network namespace之間的移動
經過命令`ip link set eth0 netns ns1`就能將eth0移動到network namespace ns1中。須要注意的是,當設備被標記爲NETIF_F_NETNS_LOCAL時,該設備不能在namespace間移動,物理設備只能存在於root namespace中。當namespace被刪除時,非NETIF_F_NETNS_LOCAL設備會被移回root namespace,而NETIF_F_NETNS_LOCAL設備會被刪除
int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat)
一、若dev->features中的NETIF_F_NETNS_LOCAL置位或者設備的狀態不是NETREG_REGISTERED則退出
二、進行一系列的檢測,將設備關閉而且從原先的device chain中取下,並進行設備被移除的通知工做
三、調用dev_net_set(dev, net)將設備放入新的network namespace中,其實就是將struct net_device中的nd_net字段設置爲net
四、調用__dev_get_by_index(net, dev->ifindex),若是在轉移過程當中,index有衝突,則另外分配一個
五、最後進行新設備添加的通知工做
3、總結
在對內核中network namespace相關的源碼進行分析以後,咱們能夠發現,其實network namespace特性的添加,對總體代碼的修改並非很大。事實上,它只是將一些本來全局的惟一的網絡資源變量,例如設備列表,路由表等等,包裹到了struct net這樣一個結構中。所以咱們建立多個net結構,就至關於擁有了多個本來的網絡空間。從本質上來講,咱們能夠把network namespace的出現,看作是一種網絡空間模塊化的從特殊到通常的推廣,本來全局惟一的網絡空間僅僅只是當前狀況的一種特例。