aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/devinet.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r--net/ipv4/devinet.c40
1 files changed, 30 insertions, 10 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 4ec4b2ca6ab1..04a6fe3e95a2 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -234,7 +234,10 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
234 int destroy) 234 int destroy)
235{ 235{
236 struct in_ifaddr *promote = NULL; 236 struct in_ifaddr *promote = NULL;
237 struct in_ifaddr *ifa1 = *ifap; 237 struct in_ifaddr *ifa, *ifa1 = *ifap;
238 struct in_ifaddr *last_prim = in_dev->ifa_list;
239 struct in_ifaddr *prev_prom = NULL;
240 int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
238 241
239 ASSERT_RTNL(); 242 ASSERT_RTNL();
240 243
@@ -243,18 +246,22 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
243 **/ 246 **/
244 247
245 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 248 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
246 struct in_ifaddr *ifa;
247 struct in_ifaddr **ifap1 = &ifa1->ifa_next; 249 struct in_ifaddr **ifap1 = &ifa1->ifa_next;
248 250
249 while ((ifa = *ifap1) != NULL) { 251 while ((ifa = *ifap1) != NULL) {
252 if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
253 ifa1->ifa_scope <= ifa->ifa_scope)
254 last_prim = ifa;
255
250 if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 256 if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
251 ifa1->ifa_mask != ifa->ifa_mask || 257 ifa1->ifa_mask != ifa->ifa_mask ||
252 !inet_ifa_match(ifa1->ifa_address, ifa)) { 258 !inet_ifa_match(ifa1->ifa_address, ifa)) {
253 ifap1 = &ifa->ifa_next; 259 ifap1 = &ifa->ifa_next;
260 prev_prom = ifa;
254 continue; 261 continue;
255 } 262 }
256 263
257 if (!IN_DEV_PROMOTE_SECONDARIES(in_dev)) { 264 if (!do_promote) {
258 *ifap1 = ifa->ifa_next; 265 *ifap1 = ifa->ifa_next;
259 266
260 rtmsg_ifa(RTM_DELADDR, ifa); 267 rtmsg_ifa(RTM_DELADDR, ifa);
@@ -283,18 +290,31 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
283 */ 290 */
284 rtmsg_ifa(RTM_DELADDR, ifa1); 291 rtmsg_ifa(RTM_DELADDR, ifa1);
285 notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 292 notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
286 if (destroy) {
287 inet_free_ifa(ifa1);
288 293
289 if (!in_dev->ifa_list) 294 if (promote) {
290 inetdev_destroy(in_dev); 295
291 } 296 if (prev_prom) {
297 prev_prom->ifa_next = promote->ifa_next;
298 promote->ifa_next = last_prim->ifa_next;
299 last_prim->ifa_next = promote;
300 }
292 301
293 if (promote && IN_DEV_PROMOTE_SECONDARIES(in_dev)) {
294 /* not sure if we should send a delete notify first? */
295 promote->ifa_flags &= ~IFA_F_SECONDARY; 302 promote->ifa_flags &= ~IFA_F_SECONDARY;
296 rtmsg_ifa(RTM_NEWADDR, promote); 303 rtmsg_ifa(RTM_NEWADDR, promote);
297 notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); 304 notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote);
305 for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) {
306 if (ifa1->ifa_mask != ifa->ifa_mask ||
307 !inet_ifa_match(ifa1->ifa_address, ifa))
308 continue;
309 fib_add_ifaddr(ifa);
310 }
311
312 }
313 if (destroy) {
314 inet_free_ifa(ifa1);
315
316 if (!in_dev->ifa_list)
317 inetdev_destroy(in_dev);
298 } 318 }
299} 319}
300 320