diff -NurpP linux-3.7-vs2.3.5.1.1/kernel/vserver/network.c linux-3.7-vs2.3.5.1.2/kernel/vserver/network.c --- linux-3.7-vs2.3.5.1.1/kernel/vserver/network.c 2012-12-13 13:48:35.000000000 +0000 +++ linux-3.7-vs2.3.5.1.2/kernel/vserver/network.c 2012-12-13 18:10:29.000000000 +0000 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -592,20 +593,43 @@ int vc_net_migrate(struct nx_info *nxi, } +static inline +struct nx_addr_v4 *__find_v4_addr(struct nx_info *nxi, + __be32 ip, __be32 ip2, __be32 mask, uint16_t type, uint16_t flags, + struct nx_addr_v4 **prev) +{ + struct nx_addr_v4 *nxa = &nxi->v4; + + for (; nxa; nxa = nxa->next) { + if ((nxa->ip[0].s_addr == ip) && + (nxa->ip[1].s_addr == ip2) && + (nxa->mask.s_addr == mask) && + (nxa->type == type) && + (nxa->flags == flags)) + return nxa; + + /* save previous entry */ + if (prev) + *prev = nxa; + } + return NULL; +} int do_add_v4_addr(struct nx_info *nxi, __be32 ip, __be32 ip2, __be32 mask, uint16_t type, uint16_t flags) { - struct nx_addr_v4 *nxa = &nxi->v4; + struct nx_addr_v4 *nxa = NULL; struct nx_addr_v4 *new = __alloc_nx_addr_v4(); + int ret = -EEXIST; if (IS_ERR(new)) return PTR_ERR(new); spin_lock(&nxi->addr_lock); + if (__find_v4_addr(nxi, ip, ip2, mask, type, flags, &nxa)) + goto out_unlock; + if (NX_IPV4(nxi)) { - /* locate last entry */ - for (; nxa->next; nxa = nxa->next); nxa->next = new; nxa = new; new = NULL; @@ -619,25 +643,46 @@ int do_add_v4_addr(struct nx_info *nxi, nxa->mask.s_addr = mask; nxa->type = type; nxa->flags = flags; + ret = 0; +out_unlock: spin_unlock(&nxi->addr_lock); if (new) __dealloc_nx_addr_v4(new); - return 0; + return ret; } int do_remove_v4_addr(struct nx_info *nxi, __be32 ip, __be32 ip2, __be32 mask, uint16_t type, uint16_t flags) { - struct nx_addr_v4 *nxa = &nxi->v4; + struct nx_addr_v4 *nxa = NULL; struct nx_addr_v4 *old = NULL; int ret = 0; spin_lock(&nxi->addr_lock); switch (type) { -/* case NXA_TYPE_ADDR: - break; */ + case NXA_TYPE_ADDR: + old = __find_v4_addr(nxi, ip, ip2, mask, type, flags, &nxa); + if (old) { + if (nxa) { + nxa->next = old->next; + old->next = NULL; + } else { + if (old->next) { + nxa = old; + old = old->next; + *nxa = *old; + old->next = NULL; + } else { + memset(old, 0, sizeof(*old)); + old = NULL; + } + } + } else + ret = -ESRCH; + break; case NXA_TYPE_ANY: + nxa = &nxi->v4; old = xchg(&nxa->next, NULL); memset(nxa, 0, sizeof(*nxa)); break; @@ -788,20 +833,46 @@ int vc_net_rem_ipv4(struct nx_info *nxi, #ifdef CONFIG_IPV6 +static inline +struct nx_addr_v6 *__find_v6_addr(struct nx_info *nxi, + struct in6_addr *ip, struct in6_addr *mask, + uint32_t prefix, uint16_t type, uint16_t flags, + struct nx_addr_v6 **prev) +{ + struct nx_addr_v6 *nxa = &nxi->v6; + + for (; nxa; nxa = nxa->next) { + if (ipv6_addr_equal(&nxa->ip, ip) && + ipv6_addr_equal(&nxa->mask, mask) && + (nxa->prefix == prefix) && + (nxa->type == type) && + (nxa->flags == flags)) + return nxa; + + /* save previous entry */ + if (prev) + *prev = nxa; + } + return NULL; +} + + int do_add_v6_addr(struct nx_info *nxi, struct in6_addr *ip, struct in6_addr *mask, uint32_t prefix, uint16_t type, uint16_t flags) { - struct nx_addr_v6 *nxa = &nxi->v6; + struct nx_addr_v6 *nxa = NULL; struct nx_addr_v6 *new = __alloc_nx_addr_v6(); + int ret = -EEXIST; if (IS_ERR(new)) return PTR_ERR(new); spin_lock(&nxi->addr_lock); + if (__find_v6_addr(nxi, ip, mask, prefix, type, flags, &nxa)) + goto out_unlock; + if (NX_IPV6(nxi)) { - /* locate last entry */ - for (; nxa->next; nxa = nxa->next); nxa->next = new; nxa = new; new = NULL; @@ -812,26 +883,47 @@ int do_add_v6_addr(struct nx_info *nxi, nxa->prefix = prefix; nxa->type = type; nxa->flags = flags; + ret = 0; +out_unlock: spin_unlock(&nxi->addr_lock); if (new) __dealloc_nx_addr_v6(new); - return 0; + return ret; } int do_remove_v6_addr(struct nx_info *nxi, struct in6_addr *ip, struct in6_addr *mask, uint32_t prefix, uint16_t type, uint16_t flags) { - struct nx_addr_v6 *nxa = &nxi->v6; + struct nx_addr_v6 *nxa = NULL; struct nx_addr_v6 *old = NULL; int ret = 0; spin_lock(&nxi->addr_lock); switch (type) { -/* case NXA_TYPE_ADDR: - break; */ + case NXA_TYPE_ADDR: + old = __find_v6_addr(nxi, ip, mask, prefix, type, flags, &nxa); + if (old) { + if (nxa) { + nxa->next = old->next; + old->next = NULL; + } else { + if (old->next) { + nxa = old; + old = old->next; + *nxa = *old; + old->next = NULL; + } else { + memset(old, 0, sizeof(*old)); + old = NULL; + } + } + } else + ret = -ESRCH; + break; case NXA_TYPE_ANY: + nxa = &nxi->v6; old = xchg(&nxa->next, NULL); memset(nxa, 0, sizeof(*nxa)); break; @@ -872,9 +964,14 @@ int vc_net_remove_ipv6(struct nx_info *n return -EFAULT; switch (vc_data.type) { + case NXA_TYPE_ADDR: + memset(&vc_data.mask, ~0, sizeof(vc_data.mask)); + /* fallthrough */ + case NXA_TYPE_MASK: + return do_remove_v6_addr(nxi, &vc_data.ip, &vc_data.mask, + vc_data.prefix, vc_data.type, vc_data.flags); case NXA_TYPE_ANY: - do_remove_v6_addr(nxi, NULL, NULL, 0, vc_data.type, 0); - break; + return do_remove_v6_addr(nxi, NULL, NULL, 0, vc_data.type, 0); default: return -EINVAL; }