Chinaunix首页 | 论坛 | 博客
  • 博客访问: 255013
  • 博文数量: 99
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 246
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-03 18:23
个人简介

qrasvasdf

文章分类

全部博文(99)

文章存档

2016年(1)

2015年(36)

2014年(62)

我的朋友

分类: LINUX

2015-01-30 18:56:41

linux支持在一个接口上添加多个虚拟ip
添加:ifconfig eth0:1234 168.0.1.2/24
删除:ifconfig eth0:1234 down
     或 ip addr del 168.0.1.2/24 dev eth0

但是删除的时候发现一个问题:
当删除其中一个虚拟ip时,同接口上的其他同网段的ip都被删除了!!什么原因?

问题初步分析:
原来linux在添加虚拟ip时,也有主次之分。同一网段第一个被添加的接口作为同网段内的组长,后添加的最为附属
当删除组长时候,同一网段的内的所有ip都被删除了
当删除同网段非组长的ip时,就只会删除他自己。
举例:
ifconfig eth0:1 168.0.1.1/24
ifconfig eth0:2 168.0.1.2/24
ifconfig eth0:3 168.0.1.3/24

删除ifconfig eth0:1 down,会发现上面3个ip全没了
删除ifconfig eth0:2 down,就只有eth0:2自己删掉了


问题解决参见:http://blog.chinaunix.net/uid-28856509-id-5036486.html

深入到内核继续分析:

查看linux内核源码net/ipv4/devinet.c可以大致看出,每个网络设备net_device下挂有自己的网络相关信息指针ip_ptr,从下面这段代码可以看出这个指针是in_device类型的结构体。

static __inline__ struct in_device *

__in_dev_get_rtnl(const struct net_device *dev)

{

         return (struct in_device*)dev->ip_ptr;

}

in_device结构体中又挂有自己的网卡地址信息链表ifa_list。每个链表节点中都有ip,掩码,广播地址,网卡名等信息。下面列出简要代码(感兴趣的同事可以对照linux内核源码看)

 

struct net_device

{

...

void  *ip_ptr;    /* IPv4 specific data*/实际就是in_device类型

...

}

 

struct in_device

{

....

struct in_ifaddr         *ifa_list;   /* IP ifaddr chain*/

...

}

 

struct in_ifaddr

{

struct in_ifaddr         *ifa_next;

struct in_device        *ifa_dev;

struct rcu_head                 rcu_head;

__be32                       ifa_local;

__be32                       ifa_address;

__be32                       ifa_mask;

__be32                       ifa_broadcast;

unsigned char           ifa_scope;

unsigned char           ifa_flags; 

unsigned char           ifa_prefixlen;

char                    ifa_label[IFNAMSIZ];

};


使用ifconfig命令创建虚拟ip时,实际是调用的ioctl(fd, SIOCSIFFLAGS, &ifr)

ioctl把网卡up时调用了__inet_insert_ifa函数,把网卡down掉时调用了__inet_del_ifa函数,下面是截取的关键代码:

static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,u32 pid)

{

........

         ifa->ifa_flags &= ~IFA_F_SECONDARY;    /*把要增加的地址节点ifa_flags先置为非次地址,即为主地址*/

         last_primary = &in_dev->ifa_list;

 

         for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;        

              ifap = &ifa1->ifa_next) {

                   if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&

                       ifa->ifa_scope <= ifa1->ifa_scope)

                            last_primary = &ifa1->ifa_next;

                   if (ifa1->ifa_mask == ifa->ifa_mask &&

                       inet_ifa_match(ifa1->ifa_address, ifa)) { 

                            if (ifa1->ifa_local == ifa->ifa_local) {

                                     inet_free_ifa(ifa);

                                     return -EEXIST;

                            }

                            if (ifa1->ifa_scope != ifa->ifa_scope) {

                                     inet_free_ifa(ifa);

                                     return -EINVAL;

                            }

                            ifa->ifa_flags |= IFA_F_SECONDARY;/*从这段代码可以看出,增加网卡时,轮询设备上现有的地址链表节点,看是否存在同网段的ip,如果存在则把新增的节点ifa_flags置为次地址*/

                   }

         }

.........

}

static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,

                             int destroy, struct nlmsghdr *nlh, u32 pid)

{

         struct in_ifaddr *promote = NULL;

         struct in_ifaddr *ifa, *ifa1 = *ifap;

         struct in_ifaddr *last_prim = in_dev->ifa_list;

         struct in_ifaddr *prev_prom = NULL;

         int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);      /*获取系统是否设置了alias promotion选项*/

 

         ASSERT_RTNL();

 

         /* 1. Deleting primary ifaddr forces deletion all secondaries

          * unless alias promotion is set

          **/

         /*从上述内核代码的注释看出,删除网卡时,如果删除主地址ip会导致所有同网段的次地址ip全被删除,除非系统设置了alias promotion选项,而且从下面的代码逻辑看,也确实是这样的*/

         if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {

                   struct in_ifaddr **ifap1 = &ifa1->ifa_next;

 

                   while ((ifa = *ifap1) != NULL) {

                            if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&

                                ifa1->ifa_scope <= ifa->ifa_scope)

                                     last_prim = ifa;

 

                            if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||

                                ifa1->ifa_mask != ifa->ifa_mask ||

                                !inet_ifa_match(ifa1->ifa_address, ifa)) {

                                     ifap1 = &ifa->ifa_next;

                                     prev_prom = ifa;

                                     continue;

                            }

 

                            if (!do_promote) {    /*如果设置了alias promotion选项,会把下一个次地址升级为主地址*/

                                     *ifap1 = ifa->ifa_next;

 

                                     rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);

                                     blocking_notifier_call_chain(&inetaddr_chain,

                                                        NETDEV_DOWN, ifa);

                                     inet_free_ifa(ifa);

                            } else {

                                     promote = ifa;

                                     break;

                            }

                   }

         }

..........

}


结论:
上述内核代码的分析与我们的测试结果是一致的,这就是虚拟设备连带删除的原因。

同时注释也指明了解决问题的方向,找到设置alias promotion的地方,应该就能够解决我们的问题

解决:

上网查了一下资料,设置alias promotion的方法是在shell输入命令:

sysctl -w net.ipv4.conf.all.promote_secondaries=1
经过测试,设置上述参数后,虚拟IP捆绑删除的问题消失了!

阅读(1602) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~