* Thomas Graf <[EMAIL PROTECTED]> 2005-11-05 14:46 > Assuming this is a separate bug, I'm not sure if this is the right > way to fix it. I think it would be better to rewrite the preferred > source address of all related local routes and only perform a > remove-and-add on the secondary address being promoted.
I tried it out and although it works it's not clean yet because changing fib_info also changes pref_src for the routes to be deleted which will lead to slightly incorrect notifications later on. I now think that explicitely deleting and re-adding them later on is better as well ;-) Index: linux-2.6/net/ipv4/devinet.c =================================================================== --- linux-2.6.orig/net/ipv4/devinet.c +++ linux-2.6/net/ipv4/devinet.c @@ -230,31 +230,38 @@ int inet_addr_onlink(struct in_device *i return 0; } +static inline int ifa_is_secondary(struct in_ifaddr *primary, + struct in_ifaddr *candiate) +{ + return ((candiate->ifa_flags & IFA_F_SECONDARY) && + primary->ifa_mask == candiate->ifa_mask && + inet_ifa_match(primary->ifa_address, candiate)); +} + static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy) { + int do_promote; struct in_ifaddr *promote = NULL; - struct in_ifaddr *ifa1 = *ifap; + struct in_ifaddr *ifa, *ifa1 = *ifap; ASSERT_RTNL(); /* 1. Deleting primary ifaddr forces deletion all secondaries * unless alias promotion is set **/ + do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { - struct in_ifaddr *ifa; struct in_ifaddr **ifap1 = &ifa1->ifa_next; while ((ifa = *ifap1) != NULL) { - if (!(ifa->ifa_flags & IFA_F_SECONDARY) || - ifa1->ifa_mask != ifa->ifa_mask || - !inet_ifa_match(ifa1->ifa_address, ifa)) { + if (!ifa_is_secondary(ifa1, ifa)) { ifap1 = &ifa->ifa_next; continue; } - if (!IN_DEV_PROMOTE_SECONDARIES(in_dev)) { + if (!do_promote) { *ifap1 = ifa->ifa_next; rtmsg_ifa(RTM_DELADDR, ifa); @@ -271,6 +278,10 @@ static void inet_del_ifa(struct in_devic *ifap = ifa1->ifa_next; + for (ifa = promote; ifa != NULL; ifa = ifa->ifa_next) + if (ifa_is_secondary(ifa1, ifa)) + fib_promote(ifa1->ifa_local, ifa->ifa_local); + /* 3. Announce address deletion */ /* Send message first, then call notifier. @@ -290,7 +301,7 @@ static void inet_del_ifa(struct in_devic inetdev_destroy(in_dev); } - if (promote && IN_DEV_PROMOTE_SECONDARIES(in_dev)) { + if (promote) { /* not sure if we should send a delete notify first? */ promote->ifa_flags &= ~IFA_F_SECONDARY; rtmsg_ifa(RTM_NEWADDR, promote); Index: linux-2.6/include/net/ip_fib.h =================================================================== --- linux-2.6.orig/include/net/ip_fib.h +++ linux-2.6/include/net/ip_fib.h @@ -242,6 +242,7 @@ extern void fib_select_multipath(const s extern int ip_fib_check_default(u32 gw, struct net_device *dev); extern int fib_sync_down(u32 local, struct net_device *dev, int force); extern int fib_sync_up(struct net_device *dev); +extern int fib_promote(u32 old, u32 new); extern int fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm, struct kern_rta *rta, struct rtentry *r); extern u32 __fib_res_prefsrc(struct fib_result *res); Index: linux-2.6/net/ipv4/fib_semantics.c =================================================================== --- linux-2.6.orig/net/ipv4/fib_semantics.c +++ linux-2.6/net/ipv4/fib_semantics.c @@ -1338,3 +1338,24 @@ void fib_select_multipath(const struct f spin_unlock_bh(&fib_multipath_lock); } #endif + +int fib_promote(u32 old, u32 new) +{ + int ret = 0; + + if (old && fib_info_laddrhash) { + unsigned int hash = fib_laddr_hashfn(old); + struct hlist_head *head = &fib_info_laddrhash[hash]; + struct hlist_node *node; + struct fib_info *fi; + + hlist_for_each_entry(fi, node, head, fib_lhash) { + if (fi->fib_prefsrc == old) { + fi->fib_prefsrc = new; + ret++; + } + } + } + + return ret; +} - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html