目录:
一 Networking Namespaces
(1)有那些namespeace?
(2)命名空间设备指的是那些?
(3)namespace与socket, 与网络设备的关系:
二 代码分析
2.1 数据结构 struct net {
2.2 “网络命名空间”与“ 网络设备”的组织关系图:
2.3 网络命名空间的操作
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
一 Networking Namespaces
(1)有那些namespeace?
(2)命名空间设备指的是那些?
(3)namespace与socket, 与网络设备的关系:
1.0 整体框架:namespace与socket,协议栈,网络设备的关系:
图片引用自:http://www.cnblogs.com/zmkeil/archive/2013/05/01/3053545.html>
(1)有那些namespeace?linux系统包括默认的命名空间:“init_net”和用户自定义的net;
这里namespace 一般是默认的命名空间:“init_net”,也就是所有的"网络通信协议"+"网络设备"都是属于默认的命名空间.
网络命名空间定义了2个链表,pernet_list和net_namespace_list
如下
static LIST_HEAD(pernet_list);
static struct list_head *first_device = &pernet_list;
LIST_HEAD(net_namespace_list);
init_net会被链接到net_namespace_list这个双向链表上。
pernet_operations结构将被链接到pernet_list这个双向链表上。
以后,如果没自定义网络命名空间的话,所有想用网络命名空间时都将利用默认的“init_net”。
引用自:源文档
(2)命名空间设备指的是那些?就是网络设备。通过register_pernet_device注册:就是“注册一个网络设备”到“所有的网络命名空间net”,网络设备包括两类:虚拟的网络设备和物理网络设备:
a)虚拟的网络设备:参考"核心层:命名空间:register_pernet_device作用“
虚拟网络设备的协议根据自身设计特点对skb数据进行处理,并通过全局变量xx_net_id和各个协议私有的特殊数据结构xx_net,寻找到该数据包对应的应用层socket插口,并将其放在该socket插口的接收队列中;最后应用层在某个时刻会通过read系统调用读取该数据
ipv6隧道设备 |
static struct pernet_operations ip6_tnl_net_ops = {用于ipv6与iPv4之间互通 |
pppoe设备 |
pppoe是一个完整的协议,是从应用层到设备之间的协议模块,从这个意义上来讲,它和INET域中的协议是等价的,定义协议族:PF_PPPOX |
ipv4gre设备(GRE路由协议) |
|
vti协议设备 |
|
---各个设备私有的特殊数据结构xx_net和全局变量xx_net_id有那些?例如:
1)static int pppoe_net_id__read_mostly;
2)
staticint ip6_tnl_net_id __read_mostly;
struct ip6_tnl_net {
/*the IPv6 tunnel fallback device */
structnet_device *fb_tnl_dev;
/*lists for storing tunnels in use */
structip6_tnl __rcu *tnls_r_l[HASH_SIZE];
structip6_tnl __rcu *tnls_wc[1];
structip6_tnl __rcu **tnls[2];
};
3)ipv4的:static int ipgre_net_id__read_mostly;
4)static int vti_net_id__read_mostly;
struct vti_net {
structip_tunnel __rcu *tunnels_r_l[HASH_SIZE];
structip_tunnel __rcu *tunnels_r[HASH_SIZE];
structip_tunnel __rcu *tunnels_l[HASH_SIZE];
structip_tunnel __rcu *tunnels_wc[1];
structip_tunnel __rcu **tunnels[4];
structnet_device *fb_tunnel_dev;
};
b) 物理网络设备,
比如网卡驱动、无线网卡驱动。
======================================================================================
(3)namespace与socket, 与网络设备的关系:
上述的socket索引方法有个绕弯的地方:就是每个协议私有的xx_net结构可以直接由协议模块本身分配,索引起来也方便,不要用到全局的net_generic。而目前内核所用的方法,其实是为了另外的目的,那就是命名空间namespace。也就是虚拟多用户的一套机制,具体的也没细看,好像目前内核整个namespace还没有全部完成。
network的命名空间问题主要在于,每个协议模块的xx_net私有结构不仅是一个,而是由内核全局决定的,即每注册一个新的用户(有点像虚拟机机制),就分配一个新的xx_net结构,这样多用户间可以用参数相同的socket连接,但却指向不同的socket。
图片引用自
http://www.cnblogs.com/zmkeil/archive/2013/05/01/3053545.html>
代码如下:
intsock_create(int family, int type, int protocol, struct socket **res)
{
return__sock_create(current->nsproxy->net_ns,family, type, protocol, res, 0);
}
int__sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
参数:
Net |
current->nsproxy->net_ns获取当前线程的网络命名空间,“当前线程”被“创建”的时候,就是同时“copy了一个网络命名空间”,参考: ”核心层:命名空间-创建命名空间-内核层代码分析“ 作用就是:表明该线程所在的命名空间,进而找到该空间内的网络设备,路由表,ARP表,ip地址表,iptables,ebtables,等等。总之,与网络有关的组件, 比如:ipv4操作路由表,通过socket参数成员的:命名空间net: int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){ struct net *net = sock_net(sk); ….. case SIOCADDRT: case SIOCDELRT: case SIOCRTMSG: err = ip_rt_ioctl(net, cmd, (void __user *)arg);
int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg) { ….. tb = fib_new_table(net, cfg.fc_table); …..
static inline struct fib_table *fib_new_table(struct net *net, u32 id) { return fib_get_table(net, id); } static inline struct fib_table *fib_get_table(struct net *net, u32 id) { struct hlist_head *ptr;
ptr = id == RT_TABLE_LOCAL ? &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX] : &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]; return hlist_entry(ptr->first, struct fib_table, tb_hlist); } |
Family |
指明协议族(af_inet.c,af_inet6.c,ieee802154等) |
protocol |
该协议族下的那一种协议(tcp.udp,icmp,等) |
二 代码分析
2.1 数据结构 struct net {
2.2 “网络命名空间”与“ 网络设备”的组织关系图:
2.3 网络命名空间的操作
=======================================================================================================
从2.6开始,kernel采用namespaces管理networking subsystem.
namesapce,如下:
// include/net/net_namespace.h
2.1 数据结构 struct net {
atomic_t count; /* Todecided when the network
* namespaceshould be freed.
*/
...
struct list_head list; /* list ofnetwork namespaces */
...
struct proc_dir_entry *proc_net;
struct proc_dir_entry *proc_net_stat;
struct proc_dir_entry *proc_net_root;
struct net_device *loopback_dev; /* Theloopback */
struct list_head dev_base_head;
struct hlist_head *dev_name_head;
struct hlist_head *dev_index_head;
};
Devices |
表示提供物理传输能力的硬件设备. |
Interfaces |
可以是虚拟的点.可能基于实际设备上实现.比如一个网卡可以提供两个interfaces. |
2.2 “网络命名空间”与“ 网络设备”的组织关系图:
在Linux协议栈中引入网络命名空间,是为了支持网络协议栈的多个实例,而这些协议栈的隔离就是由命名空间来实现的(有点像进程的线性地址空间,协议栈不能访问其他协议栈的私有数据)。需要纳入命名空间的元素包括进程,套接字,网络设备。进程创建的套接字必须属于某个命名空间,套接字的操作也必须在命名空间内进行,网络设备也必须属于某个命名空间,但可能会改变,因为网络设备属于公共资源<~/include/net.h>。
在内核中引入命名空间工作量非常大,现在还没有全部完成。为了保持与向后兼容,网络系统在初始化的时候只初始化了一个命名空间,即init_net命名空间。所有的命名空间通过list项组织起来。每个网络设备都对应有一个命名空间。命名空间下的所有网络设备通过dev_base_head组织在一起。
引用 源文档
2.3 网络命名空间的创建
每个networknamespace包换许多元件,所以当一个新的network namespace被创建,这些元件必须被初始化.同样,当它被删除时,需要做必要的清理工作.
Kernel 引入了如下结构 pernet_operations:来维护所有需要做的 initialization/cleanup 工作:
// include/net/net_namespace.h
structpernet_operations {
struct list_headlist;
int (*init)(struct net *net);
void (*exit)(struct net *net);
};
当一个新的network namespace被创建,kernel遍历 pernet_operations 的list,并调用其init函数.
在linux内核中默认情况下,会有一个“默认的网络命名空间”,其名为init_net,并也将其导出,作为全局变量。
struct netinit_net;
EXPORT_SYMBOL_GPL(init_net);
kernel2.4、2.6:向内核中添加一个网络命名空间:
struct net *net_create(void);//这个函数用于向内核中添加一个网络
//命名空间;
这个函数主要做了三件事:
1.通过struct net*net_alloc(void)函数分配了一个structnet结构体
2.通过setup_net(struct net*ns)函数对分配的struct net结构体进行了相应的设置;
3.最后,将分配的struct net结构体加入到 net_namespace_list的双链表尾部。
Kernel 3.10是copy_net_ns()添加一个网络命名空间
释放一个网络命名空间:
voidnet_free(struct net *ns);