关于VirtualBox中CentOS7的IP地址的困惑

Windows 10 平台,安装了甲骨文的虚拟机软件VirtualBox 5, 而后在里面安装了CentOS 7.linux

内网shell

  • Windows 10系统的IP固定为192.168.1.2,
  • Virtual Host-Only Network的IPV4地址为192.168.1.3 (在网络链接界面下设置)
  • VirtualBox实例界面设置->网络->端口转发里面设置的主机地址为192.168.1.3

不知道 1###

打开CentOS,在终端中输入如下命令, 查到的IP地址inet不一样于以上我设置的192.168.1.3, 而是一个会变化的地址, 如10.0.2.15/24.服务器

#ip add

或者用ifconfig也能够查到inet和ip add获得的是同样的10.0.2.15,但就是不一样于192.168.1.3.网络

#ifconfig

不知道 2###

用xshell链接CentOS时,设置的主机IP地址也是192.168.1.3, 但确实能连上.数据结构

那么为何用ip add查看的地址不对呢?架构

看来之后得好好读读关于网络这块的东西.负载均衡


下面是摘录别人的解释:函数

若是你很是理解网络协议的原理以及网络的分层架构那么我想你就不会有这个问题,实际上,每个网卡设备都有一个mac地址,可是却可 以有多个网络层地址,好比IP地址,然而这个事实没法很好地像用户提供操做接口,因此就引出了ip别名(IP aliases)和辅助ip(secondary IP addresses)的概念。其实很容易理解这个事实,按照分层的思想,下层老是为上层服务,也就是为上层提供舞台,上层利用下层的服务,而没必要让下层知 道本身的状况,若是一个拥有合理mac地址的网卡没有配置网络层地址(好比IP地址)这件事合理的话,那么为这个设备配置多个IP地址也是合理的,正好像 一个ip能够对应多个应用层端口同样,也就是说,下层对上层老是一对多的关系,在分层架构中这种关系是合理的。下面咱们就看一下linux的网卡的ip地 址结构。刚才说了在linux中,一个网卡能够有多个IP,那么这多个ip有什么关系呢?其实这些ip组成了一个吊链结构,所谓吊链结构就是一些节点连接 成一条链,而后每一个节点带有本身的一条链.布局

每一个节点表明的ip地址标识一个网段,这个节点的ip就是这个网段的 Primary地址,它下面所带的ip就是这个网段的Secondary地址,也就是说一个网卡能够带有各个节点所带链表长度之和个ip地址,并且这些 ip不是线形的,而是上述的吊链结构。咱们看一下这么作有什么好处。玩过Cisco路由器的朋友可能都知道有个Secondary IP的概念,这个特性能够建立逻辑子网,也就是说在一个物理网口上链接两个子网,这咋看起来好像难以想象,其实很简单,好比这个网口接到一台交换机上,如 果这个网口没有配置Secondary IP的话,那么这台交换机只能链接一个网段的主机,好比192.168.1.1/24,可是,若是它配置了Secondary IP,那么就能够链接两个网段的主机,好比192.168.1.1/24和10.0.0.1/24,道理就是这么简单,可是却颇有用,该机制能够被路由汇 总策略所使用。注意上面这个例子中的Secondary IP不是这里说的linux的Secondary address,在linux中偏偏相反,只要一个网卡上配置的ip不是一个网段的,那么都是Primary IP,就是吊链结构中上面的那条主链中的IP,linux中的Secondary address是主链结点的子链结点中的IP,这一点必定注意,概念是不能混淆的。前面说的只是吊链中主链的做用,那么子链呢?其实想象一下也很简单,比 如一台机器上运行着一个代理服务器或者负载均衡服务,代理服务器或者负载均衡服务和主服务器要监听相同的端口,那么就能够用secondary address来解决了,只要须要在同一网段监听同一个端口的应用都是吊链中子链存在的缘由,所以能够说,主链对外部或者说对下面链路层虚拟了多块网卡, 而子链向上层虚拟了多台机器,配置了吊链结构的linux主机若是说只有一块网卡,那么外部会认为它有多块网卡,对于内部,应用层会认为彼此在不一样的主机 上,这就是效果。atom

除了上面大致的介绍以外,还有不少细节,吊链在主链上是没有主次的,子链除了第一个节点其它节点也不分主次,都是平行的关系,可是子链中的第一个节点老是 连接在主链中,它们携带的地址就是primary地址,它们下面隶属的子链携带的地址就是这个primary地址的secondary地址,如此看来,一 旦主链上一个节点被删除了,那么它的子链也将不复存在,所谓皮之不存毛将焉附。可是这种策略老是显得不是那么优美,由于父亲犯错,儿子也要受连累,这在现 代社会早就不时行了,那么就须要改变机制了,所以linux中特地有了一个选项,就是当一个primary地址被删除时,若是它有secondary地址 的话,那么它的第一个secondary地址(长子)继承被删除的primary地址的位置成为primary地址,这样就显得很合理了,要否则在删除 primary地址的时候,若是有程序用secondary地址,那么要么延迟删除,要么程序崩溃,采用自动提高策略的话就不会出现问题。

至于说IP aliases,那是之前版本有的了,就是一个实现问题,解决的问题和如今的secondary IP机制同样,它主要就是在物理网卡名字后面加上后缀从而成为虚拟网络接口,本质上和secondary IP机制没有区别,区别就是IP aliases显得不是那么直观,而secondary IP倒是真正让应用看到了一个网卡的多个地址,好比你要是用IP aliases的话,有的时候你老是会问eth0:0是什么?我就曾经在内核里面拼命找eth0:0这个网络设备的注册代码,都要疯掉了也没有找到,其实 我并非很傻,可是我却由于那个该死的名字做出了傻事。

下面就能够看看linux内核的实现代码了,首先弄明白一些数据结构,最重要的就是net_device,其次就是in_device,而后就是in_ifaddr,明白了这三个数据结构,一切就明白了,这是真的。

struct net_device 
{ 
... 
     void                    *ip_ptr;       //指向一个in_device结构,这字段从net_device中分离代表一个网卡能够支持多种网络层协议的 
... 
} 
struct in_device 
{ 
         struct net_device       *dev;           //指向它隶属的net_device,也就是网卡 
         atomic_t                refcnt;         //引用计数 
         int                     dead; 
         struct in_ifaddr        *ifa_list;      //全部的ip地址链表 
... 
}; 
struct in_ifaddr   //表明一个ip地址 
{ 
         struct in_ifaddr        *ifa_next;       //上面的in_device中的ifa_list字段就是靠这个字段连成链的 
         struct in_device        *ifa_dev;        //回指in_device结构 
         struct rcu_head         rcu_head; 
         u32                     ifa_local;       //ip地址 
         u32                     ifa_address; 
         u32                     ifa_mask;        //掩码 
         u32                     ifa_broadcast;   //广播地址 
         u32                     ifa_anycast; 
         unsigned char           ifa_scope;  
         unsigned char           ifa_flags;           //只有IFA_F_SECONDARY标志,由于除了这个就是primary地址了 
         unsigned char           ifa_prefixlen; 
         char                    ifa_label[IFNAMSIZ]; //名字,在ip aliases时代,它就多是ethx:y的形式,在secondary ip时代,它统一就是ethx 
};

注 意,上面的结构并无将linux网卡的ip地址结构表示为吊链结构,所谓的吊链结构只是逻辑上的,在数据结构上,一个网卡全部的ip地址所有都在 ifa_list中被连接成一个线性的链表,至因而primary地址仍是secondary地址就看in_ifaddr的ifa_flags字段了。每 当有新的地址被设置的时候,inet_insert_ifa老是被调用,linux为什么没有在代码上将ip地址表示为吊链结构呢?我也不知道,我的感受一 个net_device带有一个primary ip链表,而后每一个primary ip节点带有一个secondary ip链表,这样会更好一些的,我以为inet_insert_ifa实现的十分拙劣。添加地址能够经过两个用户空间程序搞定,一个是ifconfig,另 一个是ip addr add,ifconfig是基于ioctl进行地址添加的,而ip程序是基于netlink进行地址添加的,无论哪种方式均可以达到目的,如今就能够看 看另外一个问题了:为什么用ip addr add添加的ip地址用ifconfig看不到,而ifconfig设置的地址ip addr show倒是能够看到。这个问题经过看代码一眼就能够明白,在ifconfig得到ip地址的时候,代码:

for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; ifap = &ifa->ifa_next) 
{ 
    if (!strcmp(ifr.ifr_name, ifa->ifa_label) && sin_orig.sin_addr.s_addr == ifa->ifa_address) 
    { 
        break; 
    } 
}

取 的是这个被找到的ifa的ip地址,而咱们知道,全部的ifa连接成一个线性链表,那么找到了第一个就不会再日后走了,所以只能获得一个结果,就是链表最 前面的那个,而ip add show就不一样了,具体在函数inet_dump_ifaddr中实现,该函数遍历全部的ifa,而且传到用户空间缓冲区。这里能够作一个实验:首先用 ip addr add添加几个不在同一个网段的primary ip地址,而后再ifconfig一个和前面的ip都不在一个网段的ip,而后能够用ifconfig查看一下,发现不是刚刚用ifconfig设置进去 的那个ip,而是用ip addr add添加进去的,这就说明ifconfig永远都是取的ifa链表最前面的那一个,还有一点要注意,就是若是你用ip addr add添加了不少的secondary ip地址,那么刚好你用ifconfig设置的ip地址和那些secondary ip在一个网段,那么全部的secondary ip都将被删除,这些都是sencondary ip的规范决定的,并且在代码中也有体现。另外还要注意,路由表的表项都是基于primary ip的,由于全部的操做都是以primary ip为主的,好比在添加路由的时候:

void fib_add_ifaddr(struct in_ifaddr *ifa) 
{ 
         struct in_device *in_dev = ifa->ifa_dev; 
         struct net_device *dev = in_dev->dev; 
         struct in_ifaddr *prim = ifa; 
... 
         if (ifa->ifa_flags&IFA_F_SECONDARY) {   //若是ifa是个sencondary地址,那么就找到它隶属的primary地址后而后以这个primary为主进行设置 
                 prim = inet_ifa_byprefix(in_dev, prefix, mask); 
                 if (prim == NULL) { 
                         printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL/n"); 
                         return; 
                 } 
         } 
         fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);    //添加进路由表 
... 
}

到 此为止咱们知道了很多东西,最重要的就是linux中网卡ip地址的吊链结构以及这么设计的好处,另外就是设置ip地址的方式有ioctl和 netlink。其实网卡拥有多个ip并不会带来什么冲突,本质上ip和网卡没有什么关系,它们惟一的关系就是靠网络分层模型联系在一块儿的,细节上就是靠 路由联系在一块儿的,好比我添加路由的时候指定了一个目的地址和下一跳ip地址以及一个网卡出口,那么内核会根据提供的目的地址将路由插在合式的位置,而后 将nh的网络设备设置为你提供的网卡出口,等到传输数据的时候就会查找路由从而找到出口,就是这么简单,你本身手动设置的路由能够随意设置,即便彻底错误 内核也会将之加入路由表的,还有一种路由是内核自动生成的,就是在网卡刚刚up的时候,这时经过网卡的net_device找到其in_device而后 找到其ip地址,这样的路由称为链路路由。

经过secondary IP机制,你能够认为你的机器有不少网卡,对于应用,监听同一端口的应用会认为它们在局域网中不一样的机器上,你能够随意使用这些ip地址而不会发生混乱,路由和底层的arp会处理好这一切,固然前提是你将路由设置对。
附: 用户空间有ifup/ifdown,/sbin/ip,ifconfig,还有netplugd守护进程,这些有何关系吗?这中间ip程序是最基本的,没 有任何策略,策略就是参数指定,要么就是别的程序调用它,而netplugd就是一个监控守护进程,经过netlink监控网卡状态,而后根据不一样的监控 结果调用/etc/netplug.d/netplug脚本,进而可能调用ifup/ifdown脚本,然后者就是脚本,其中会调用ifup-eth脚 本,最终整理好参数后调用ip程序(典型的就是:ip link set eth0 up/down),固然ip程序彻底能够本身调用,好比ip addr add以及ip route add等等,而ifconfig没有那么绕圈子,就是经过ioctl进行设置,能够经过strace来观察。这其中奥妙大了去了,说白了就是策略和机制分 离,另外还体现出linux中的不少功能都是很小的程序组合而成的。

Linux的ip地址的吊链结构以及ip地址的寻址特性(详见《关于IP网段间互访的问题—路由是根本》)充分说明了linux的协议栈实现多么的完美,彻底符合分层和封装模型,使得下层的逻辑和上层的逻辑彻底解除耦合,也就是说ip层彻底不依赖链路层以及物理层的物理布局,最后记住,ip层事情好比寻址路由只由ip层实现,之全部有链路层发现的路由,彻底是为了方便。

相关文章
相关标签/搜索