[IPV4]: Fix secondary IP addresses after promotion

This patch fixes the problem with promoting aliases when:
a) a single primary and > 1 secondary addresses
b) multiple primary addresses each with at least one secondary address

Based on earlier efforts from Brian Pomerantz <bapper@piratehaven.org>,
Patrick McHardy <kaber@trash.net> and Thomas Graf <tgraf@suug.ch>

Signed-off-by: Jamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Jamal Hadi Salim and committed by David S. Miller 0ff60a45 c27bd492

+34 -11
+3
include/net/route.h
··· 126 extern void ip_rt_get_source(u8 *src, struct rtable *rt); 127 extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); 128 129 static inline void ip_rt_put(struct rtable * rt) 130 { 131 if (rt)
··· 126 extern void ip_rt_get_source(u8 *src, struct rtable *rt); 127 extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); 128 129 + struct in_ifaddr; 130 + extern void fib_add_ifaddr(struct in_ifaddr *); 131 + 132 static inline void ip_rt_put(struct rtable * rt) 133 { 134 if (rt)
+30 -10
net/ipv4/devinet.c
··· 234 int destroy) 235 { 236 struct in_ifaddr *promote = NULL; 237 - struct in_ifaddr *ifa1 = *ifap; 238 239 ASSERT_RTNL(); 240 ··· 246 **/ 247 248 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 249 - struct in_ifaddr *ifa; 250 struct in_ifaddr **ifap1 = &ifa1->ifa_next; 251 252 while ((ifa = *ifap1) != NULL) { 253 if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 254 ifa1->ifa_mask != ifa->ifa_mask || 255 !inet_ifa_match(ifa1->ifa_address, ifa)) { 256 ifap1 = &ifa->ifa_next; 257 continue; 258 } 259 260 - if (!IN_DEV_PROMOTE_SECONDARIES(in_dev)) { 261 *ifap1 = ifa->ifa_next; 262 263 rtmsg_ifa(RTM_DELADDR, ifa); ··· 290 */ 291 rtmsg_ifa(RTM_DELADDR, ifa1); 292 notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 293 if (destroy) { 294 inet_free_ifa(ifa1); 295 296 if (!in_dev->ifa_list) 297 inetdev_destroy(in_dev); 298 - } 299 - 300 - if (promote && IN_DEV_PROMOTE_SECONDARIES(in_dev)) { 301 - /* not sure if we should send a delete notify first? */ 302 - promote->ifa_flags &= ~IFA_F_SECONDARY; 303 - rtmsg_ifa(RTM_NEWADDR, promote); 304 - notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); 305 } 306 } 307
··· 234 int destroy) 235 { 236 struct in_ifaddr *promote = NULL; 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); 241 242 ASSERT_RTNL(); 243 ··· 243 **/ 244 245 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 246 struct in_ifaddr **ifap1 = &ifa1->ifa_next; 247 248 while ((ifa = *ifap1) != NULL) { 249 + if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 250 + ifa1->ifa_scope <= ifa->ifa_scope) 251 + last_prim = ifa; 252 + 253 if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 254 ifa1->ifa_mask != ifa->ifa_mask || 255 !inet_ifa_match(ifa1->ifa_address, ifa)) { 256 ifap1 = &ifa->ifa_next; 257 + prev_prom = ifa; 258 continue; 259 } 260 261 + if (!do_promote) { 262 *ifap1 = ifa->ifa_next; 263 264 rtmsg_ifa(RTM_DELADDR, ifa); ··· 283 */ 284 rtmsg_ifa(RTM_DELADDR, ifa1); 285 notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 286 + 287 + if (promote) { 288 + 289 + if (prev_prom) { 290 + prev_prom->ifa_next = promote->ifa_next; 291 + promote->ifa_next = last_prim->ifa_next; 292 + last_prim->ifa_next = promote; 293 + } 294 + 295 + promote->ifa_flags &= ~IFA_F_SECONDARY; 296 + rtmsg_ifa(RTM_NEWADDR, promote); 297 + notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); 298 + for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { 299 + if (ifa1->ifa_mask != ifa->ifa_mask || 300 + !inet_ifa_match(ifa1->ifa_address, ifa)) 301 + continue; 302 + fib_add_ifaddr(ifa); 303 + } 304 + 305 + } 306 if (destroy) { 307 inet_free_ifa(ifa1); 308 309 if (!in_dev->ifa_list) 310 inetdev_destroy(in_dev); 311 } 312 } 313
+1 -1
net/ipv4/fib_frontend.c
··· 407 tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL); 408 } 409 410 - static void fib_add_ifaddr(struct in_ifaddr *ifa) 411 { 412 struct in_device *in_dev = ifa->ifa_dev; 413 struct net_device *dev = in_dev->dev;
··· 407 tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL); 408 } 409 410 + void fib_add_ifaddr(struct in_ifaddr *ifa) 411 { 412 struct in_device *in_dev = ifa->ifa_dev; 413 struct net_device *dev = in_dev->dev;