从逻辑上说,网络命名空间是网络栈的副本,有自己的网络设备、路由选择表、邻接表、Netfilter表、网络套接字、网络procfs条目、网络sysfs条目和其他网络资源。
网络命名空间可以营造出多个内核网络栈实例的假象。网络命名空间的结构为net,定义在文件include/net/net_namespace.h
struct net {
refcount_t passive; /* To decided when the network
* namespace should be freed.
*/
atomic_t count; /* To decided when the network
* namespace should be shut down.
*/
spinlock_t rules_mod_lock;
atomic64_t cookie_gen;
struct list_head list; /* list of network namespaces */
struct list_head cleanup_list; /* namespaces on death row */
struct list_head exit_list; /* Use only net_mutex */
struct user_namespace *user_ns; /* Owning user namespace */
struct ucounts *ucounts;
spinlock_t nsid_lock;
struct idr netns_ids;
struct ns_common ns;
struct proc_dir_entry *proc_net;
struct proc_dir_entry *proc_net_stat;
#ifdef CONFIG_SYSCTL
struct ctl_table_set sysctls;
#endif
struct sock *rtnl; /* rtnetlink socket */
struct sock *genl_sock;
struct list_head dev_base_head;//指向一个包含所有网络设备的链表
struct hlist_head *dev_name_head;//指向一个包含所有网络设备的散列表,键为网络设备名
struct hlist_head *dev_index_head;//指向一个包含所有网络设备的散列表,键为网络设备索引
unsigned int dev_base_seq; /* protected by rtnl_mutex */
int ifindex;//网络命名空间中分配的最有一个设备索引
unsigned int dev_unreg_count;
/* core fib_rules */
struct list_head rules_ops;
struct list_head fib_notifier_ops; /* protected by net_mutex */
struct net_device *loopback_dev; /* The loopback */
struct netns_core core;
struct netns_mib mib;
struct netns_packet packet;
struct netns_unix unx;
struct netns_ipv4 ipv4;//是一个netns_ipv4结构实例,用于ipv4子系统
#if IS_ENABLED(CONFIG_IPV6)
struct netns_ipv6 ipv6;
#endif
#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
struct netns_ieee802154_lowpan ieee802154_lowpan;
#endif
#if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE)
struct netns_sctp sctp;
#endif
#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
struct netns_dccp dccp;
#endif
#ifdef CONFIG_NETFILTER
struct netns_nf nf;
struct netns_xt xt;
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct netns_ct ct;
#endif
#if defined(CONFIG_NF_TABLES) || defined(CONFIG_NF_TABLES_MODULE)
struct netns_nftables nft;
#endif
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
struct netns_nf_frag nf_frag;
#endif
struct sock *nfnl;
struct sock *nfnl_stash;
#if IS_ENABLED(CONFIG_NETFILTER_NETLINK_ACCT)
struct list_head nfnl_acct_list;
#endif
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
struct list_head nfct_timeout_list;
#endif
#endif
#ifdef CONFIG_WEXT_CORE
struct sk_buff_head wext_nlevents;
#endif
struct net_generic __rcu *gen;
/* Note : following structs are cache line aligned */
#ifdef CONFIG_XFRM
struct netns_xfrm xfrm;//是一个netns_xfrm结构实例,用于IPsec子系统。
#endif
#if IS_ENABLED(CONFIG_IP_VS)
struct netns_ipvs *ipvs;
#endif
#if IS_ENABLED(CONFIG_MPLS)
struct netns_mpls mpls;
#endif
#if IS_ENABLED(CONFIG_CAN)
struct netns_can can;
#endif
struct sock *diag_nlsk;
atomic_t fnhe_genid;
} __randomize_layout;
其中user_ns表示创建网络命名空间的用户命名空间。
proc_inum是网络命名空间独一无二的proc inode号.
proc_net表示网络命名空间procfs条目(/proc/net)。每个网络命名空间都维护着自己的procfs条目。
proc_net_stat表示网络命名空间procfs统计信息条目(/proc/net/stat)。每个网络命名空间都维护着自己的profcfs统计条目
结构netns_ipv4包含很多ipv4专用的表和变量,如路由选择表、Netfilter表、组播路由选择表等。
在网络设备对象(net_device)中添加了成员nd_net,是一个指向网络命名空间的指针。这个是通过函数dev_net_set()来设置的
static inline
void dev_net_set(struct net_device *dev, struct net *net)
{
write_pnet(&dev->nd_net, net);
}
而要获取网络设备相关联的网络命名空间通过dev_net()来完成。
在一个时刻,一个网络设备只能属于一个网络命名空间。
在表示套接字的结构sock中,添加了成员sk_net,是一个指向网络命名空间的指针。
有些网络设备和网络子系统必须由独特的网络命名空间数据,为了提供这种支持,添加了结构pernet_operations,其包含回调函数init和exit, 用于执行设备初始化和清理工作。在文件include/net/net_namespace.h如下:
struct pernet_operations {
struct list_head list;
int (*init)(struct net *net);
void (*exit)(struct net *net);
void (*exit_batch)(struct list_head *net_exit_list);
unsigned int *id;
size_t size;
};
同时,在模块初始化和被删除时分别调用方法register_pernet_device和unregister_pernet_device,入参为pernet_operations.
在网络命名空间模块定义了一个net_ns_ops对象如下:
static struct pernet_operations __net_initdata net_ns_ops = {
.init = net_ns_net_init,
.exit = net_ns_net_exit,
};
位于文件net/core/net_namespace.c 中,在启动阶段在net_ns_init函数中注册。
创建新的网络命名空间时,都将调用init回调函数net_ns_net_init。
static __net_init int net_ns_net_init(struct net *net)
{
#ifdef CONFIG_NET_NS
net->ns.ops = &netns_operations;
#endif
return ns_alloc_inum(&net->ns);
}
网络命名空间刚创建时,只包含网络环回设备。
1. 网络命名空间管理
可以使用ip netns来执行创建、删除、显示网络命名空间。
l  创建网络命名空间ns1
#ip netns add ns1
会创建文件/var/run/netns/ns1。然后关联到网络命名空间/proc/self/ns/net。注意的是网络命名空间可以嵌套创建的。
l  #ip netns del ns1
可以将网络命名空间进行删除。如果有一个或多个进程与这个网络命名空间相关联,就不会删除该命名空间。网络命名空间被删除时,所有网络设备都移到默认的初始网络命名空间init_net中。
l  #ip netns list
可以显示所有通过ip netns add添加的网络命名空间。并不会添加其他例如unshare创建的网络命名空间。
l  监视网络命名空间的创建和删除,可以使用#ip netns monitor
l  指定网络命名空间中启动shell
可以使用命令ip netns exec ns1 bash
或者执行# ip netns exec ns1 ifconfig -a
可以显示ns1网络命名空间中所有网络接口,这个命令执行是docker是很像的。
l  显示指定pid相关联的网络命名空间
可以使用命令#ip netns identify #pid
例如:
#ip netns identify 1
l  显示与网络命名空间ns1相关联的进程PID
可以使用名了#ip netns pids ns1
l  网络接口移到另一个网络命名空间
#ip link set eth0 netns ns1
该命令会在net_device对象中添加新特征NETIF_F_NETNS_LOCAL。该特征表示网络设备是网络命名空间的本地设备,会调用函数dev_change_net_namespace。然后这个eth0就要在ns1空间中才能看见。
通过命令可以查看设备是否是网络命名空间的本地设备。
# ethtool -k enp0s3 | grep -i netns-local
设置无线网络接口移到其他网络命名空间,使用iw命令。
查看当前网络设备的命名命名空间,
2. 命名空间之间通信
网络命名空间之间通信可以使用Unix套接字,也可以使用虚拟以太网(VETH)网络驱动程序来创建一对虚拟网络设备,将其中给一个移到另一个网络命名空间中。例如:
#ip netns add ns1
#ip netns add ns2
在ns1中启动shell
# ip netns exec ns1 bash
创建虚拟以太网设备,类型为veth.
# ip link add name if_one type veth peer name if_one_peer
将if_one_peer移动到ns2中
# ip link set dev if_one_peer netns ns2
这样就在ns1和ns2中都用了一个网络接口,可以通过设置IP来进行通信了。
支持网络命名空间,需要配置内核参数CONFIG_NET_NS.