qrasvasdf
分类: LINUX
2015-01-30 18:56:41
查看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捆绑删除的问题消失了!