`

从ip addr add和ifconfig的区别看linux网卡ip地址的结构

 
阅读更多

转至:http://blog.csdn.net/dog250/article/details/5303542

今天一个老外在邮件列表上问了一个问题,就是ip addr add和ifconfig的区别,我给他进行了解答,可能因为英语不好吧,解答的很简单,因此我还是要在这里详细说明一下。其实它们之间没有什么区别,只 是表述方式不同罢了。如果你非常理解网络协议的原理以及网络的分层架构那么我想你就不会有这个问题,实际上,每一个网卡设备都有一个mac地址,但是却可 以有多个网络层地址,比如IP地址,然而这个事实无法很好地像用户提供操作接口,所以就引出了ip别名(IP aliases)和辅助ip(secondary IP addresses)的概念。其实很容易理解这个事实,按照分层的思想,下层总是为上层服务,也就是为上层提供舞台,上层利用下层的服务,而不必让下层知 道自己的情况,如果一个拥有合理mac地址的网卡没有配置网络层地址(比如IP地址)这件事合理的话,那么为这个设备配置多个IP地址也是合理的,正好像 一个ip可以对应多个应用层端口一样,也就是说,下层对上层总是一对多的关系,在分层架构中这种关系是合理的。下面我们就看一下linux的网卡的ip地 址结构。刚才说了在linux中,一个网卡可以有多个IP,那么这多个ip有什么关系呢?其实这些ip组成了一个吊链结构,所谓吊链结构就是一些节点链接 成一条链,然后每个节点带有自己的一条链,如下图所示:
clip_image001 clip_image002 clip_image003 clip_image004
每 个节点代表的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主机如果说只有一块网卡,那么外部会认为它有多块网卡,对于内部,应用层会认为彼此在不同的主机 上,这就是效果。
除了上面大体的介绍之外,还有很多细节,吊链在主链上是没有主次的,子链除了第一个节点其它节点也不分主次,都是平行 的关系,但是子链中的第一个节点总是 链接在主链中,它们携带的地址就是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层实现,之所有有链路层发现的路由,完全是为了方 便。

分享到:
评论

相关推荐

    linux网卡配置修改IP地址

    ### Linux网卡配置修改IP地址 在Linux系统中,网络配置是系统管理的重要组成部分之一,尤其是在需要更改IP地址的情况下。本文将详细介绍如何在Linux环境下通过命令行的方式对网卡进行IP地址的配置与修改。 #### 一...

    linux网卡配置修改IP地址宣贯.pdf

    本文将详细介绍 Linux 网卡配置修改 IP 地址的步骤和相关知识点。 一、网卡配置文件 Linux 系统中,网卡配置文件主要有四个:/etc/sysconfig/network-scripts/ifcfg-eth0、/etc/sysconfig/network、/etc/resolv....

    CentOSLinux 网卡设置 IP地址配置1

    在 CentOS/Linux 操作系统中,配置网络接口,特别是设置 IP 地址,是日常管理和维护工作中的重要环节。本文将详细介绍如何临时和永久地修改网卡的 IP 地址、网关以及 DNS 设置。 首先,我们来看临时修改 IP 地址的...

    linux下设置IP地址并激活

    Linux 下设置 IP 地址并激活 Linux 系统下设置 IP 地址是一个非常重要的操作,正确地设置 IP 地址可以确保 Linux 系统的网络连接正常工作。在本文中,我们将详细介绍如何在 Linux 系统下设置 IP 地址并激活。 一、...

    Linux网卡设置

    在 Linux 系统中,可以使用 ifconfig 命令临时修改网卡的 IP 地址。例如,使用以下命令可以将 eth0 网卡的 IP 地址设置为 192.168.100.100: # ifconfig eth0 192.168.100.100 此外,还可以使用 route 命令设置...

    linux配置多个ip

    在Linux操作系统中,配置一块网卡(网络接口卡,NIC)以承载多个IP地址是一项常见的网络管理任务。这主要应用于服务器环境,例如负载均衡、高可用性设置或虚拟主机服务。下面将详细介绍如何在Linux中为一个网卡配置...

    AR8161 LINUX 网卡驱动

    5. **配置网络**:驱动安装成功后,可以使用`ifconfig`或`ip addr add`命令配置网络接口,以及`dhclient`或`dhcpcd`命令获取动态IP地址。 6. **故障排查**:如果遇到问题,可以查看日志文件(如`/var/log/dmesg`)...

    Linux下使用ip netns命令进行网口的隔离和配置ip地址

    ip netns exec fd ip addr add 192.168.1.2/24 dev eth1 4. 有多个网口时,可以将两对网口直连,配置同网段ip,执行ping操作,验证隔离网口ip配置是否成功: ping -I eth2 192.168.1.2 知识点扩展:Linux查看i

    red hat linux 网卡知识,区别于无线网卡部分

    在Linux系统中,网卡被识别为一种特殊的文件系统,通过这些文件系统可以对网卡进行各种操作,如配置IP地址、启动或停止网络接口等。 在Red Hat Linux中,有多种类型的网卡驱动支持不同的硬件设备。例如Intel、...

    linux配置网卡IP地址命令详细介绍及一些常用网络配置命令参考.pdf

    Linux 配置网卡 IP 地址命令详细介绍及一些常用网络配置命令参考。 Linux 操作系统中配置网卡 IP 地址需要手动配置,下面将介绍几种配置方法: 即时生效(重启后失效) 使用 ifconfig 命令可以即时生效,但重启后...

    Linux 网卡配置文件设置

    - 使用`ifconfig`命令可以临时更改接口的IP地址或子网掩码。 - **示例**: ```shell ifconfig eth0 192.168.0.10 ifconfig eth0 192.168.0.10 netmask 255.255.255.252 ``` - **激活接口**: - **命令**: ...

    修改Linux 主机 IP

    本篇文章将详细讲解如何在Linux系统下更改IP地址,包括静态配置和动态配置两种方式,以适应不同场景的需求。 首先,我们关注静态配置IP地址的方法。在大多数Linux发行版中,网络接口的配置文件位于`/etc/sysconfig/...

    不重启网卡,配置本地ip

    "不重启网卡,配置本地IP"这个主题聚焦于如何在不中断网络连接的情况下更改或设定计算机的本地IP地址。这种方法适用于那些需要频繁调整IP地址但又不能容忍短暂网络中断的情况,比如服务器维护或开发环境的快速切换。...

    c语言实现设置ip、网关、子网掩码

    这需要提供一个`struct ip_mreq`结构体,包含多播组的IP地址和本地接口的IP地址。 在`net.c`和`net.h`这两个文件中,我们可以预见到它们包含了实现这些功能的函数声明和定义。`net.h`很可能定义了相关的结构体和...

    Linux命令行修改IP、网关、DNS的方法.pdf

    编辑 /etc/sysconfig/network-scripts/ifcfg-eth0 文件,例如:DEVICE=eth0 ONBOOT=yes TYPE=Ethernet IPADDR=192.168.0.1 NETMASK=255.255.255.0 GATEWAY=192.168.0.254,可以永久修改 IP 地址和网关。 编辑 /etc/...

    linux双网卡绑定

    2. **配置物理网卡**:确保物理网卡不配置IP地址等网络参数,而是直接设置为启用状态。 ```bash vi /etc/sysconfig/network-scripts/ifcfg-eth0 vi /etc/sysconfig/network-scripts/ifcfg-eth1 ``` 示例: ...

    Linux学习之CentOS(二十九)--Linux网卡高级命令、IP别名及多网卡绑定的方法

    例如,给eth0接口添加一个新的IP地址,可以使用`ifconfig eth0:0 192.168.1.2 netmask 255.255.255.0`(对于较新的系统,推荐使用`ip addr add`命令)。 【多网卡绑定(Bonding)】 多网卡绑定是提高网络连接的...

    linux路由配置在Linux操作系统下修改IP、DNS和路由配置.pdf

    Linux系统下进行网络配置是一个重要的操作,涉及到IP地址、DNS、网关等关键网络参数的设置,以及路由的配置。本知识点将详细介绍如何在Linux操作系统下修改IP、DNS和路由配置,并解释相关命令及配置文件的作用。 ##...

Global site tag (gtag.js) - Google Analytics