--- v2.2.20/linux/include/net/ip_fib.h Sat Oct 21 12:10:47 2000 +++ linux/include/net/ip_fib.h Fri Dec 14 02:01:03 2001 @@ -214,6 +214,7 @@ extern int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos, struct fib_info *fi); +extern int fib_num_down_nh_devs(struct fib_info *fi); extern int fib_sync_down(u32 local, struct device *dev, int force); extern int fib_sync_up(struct device *dev); extern int fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm, --- v2.2.20/linux/net/ipv4/fib_hash.c Sat Oct 21 12:10:50 2000 +++ linux/net/ipv4/fib_hash.c Fri Dec 14 02:01:03 2001 @@ -686,7 +686,10 @@ while ((f = *fp) != NULL) { struct fib_info *fi = FIB_INFO(f); - if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) { + if (fi && ((f->fn_state&FN_S_ZOMBIE) || + (fi->fib_flags&RTNH_F_DEAD && + (fi->fib_protocol != RTPROT_STATIC || + !fib_num_down_nh_devs(fi))))) { *fp = f->fn_next; synchronize_bh(); --- v2.2.20/linux/net/ipv4/fib_semantics.c Sat Oct 21 12:10:47 2000 +++ linux/net/ipv4/fib_semantics.c Fri Dec 14 02:04:07 2001 @@ -170,6 +170,30 @@ return -1; } +/* + * Return 0 only when we are sure that the preferred source is deleted + * or when all nexthop devices are removed + */ + +int fib_num_down_nh_devs(struct fib_info *fi) +{ +struct in_device *in_dev; +struct device *dev; +int dead = 0; + + change_nexthops(fi) { + if (!(nh->nh_flags&RTNH_F_DEAD)) + return 0; + dev = dev_get_by_index(nh->nh_oif); + if (dev && !(dev->flags&IFF_UP) && + ((in_dev = dev->ip_ptr) != NULL) && + in_dev->ifa_list) + dead ++; + } endfor_nexthops(fi) + /* dead>0: All are marked DEAD but there is one in DOWN state */ + return dead; +} + #ifdef CONFIG_IP_ROUTE_MULTIPATH static u32 fib_get_attr32(struct rtattr *attr, int attrlen, int type) @@ -354,11 +378,25 @@ if (key.scope < RT_SCOPE_LINK) key.scope = RT_SCOPE_LINK; - if ((err = fib_lookup(&key, &res)) != 0) - return err; - nh->nh_scope = res.scope; - nh->nh_oif = FIB_RES_OIF(res); - nh->nh_dev = FIB_RES_DEV(res); + err = fib_lookup(&key, &res); + if (err) { + if (err == -ENETUNREACH && + fi->fib_protocol == RTPROT_STATIC) { + struct device *dev; + + dev = dev_get_by_index(nh->nh_oif); + if (dev == NULL || dev->flags & IFF_UP || + inet_addr_type(nh->nh_gw) == RTN_LOCAL) + return err; + nh->nh_flags |= RTNH_F_DEAD; + nh->nh_scope = RT_SCOPE_LINK; + nh->nh_dev = dev; + } else return err; + } else { + nh->nh_scope = res.scope; + nh->nh_oif = FIB_RES_OIF(res); + nh->nh_dev = FIB_RES_DEV(res); + } } else { struct in_device *in_dev; @@ -368,8 +406,11 @@ in_dev = inetdev_by_index(nh->nh_oif); if (in_dev == NULL) return -ENODEV; - if (!(in_dev->dev->flags&IFF_UP)) - return -ENETDOWN; + if (!(in_dev->dev->flags&IFF_UP)) { + if (fi->fib_protocol != RTPROT_STATIC) + return -ENETDOWN; + nh->nh_flags |= RTNH_F_DEAD; + } nh->nh_dev = in_dev->dev; nh->nh_scope = RT_SCOPE_HOST; } @@ -490,10 +531,16 @@ if (nh->nh_dev == NULL) goto failure; } else { + int dead = 0; change_nexthops(fi) { if ((err = fib_check_nh(r, fi, nh)) != 0) goto failure; + if (nh->nh_flags & RTNH_F_DEAD) + dead ++; } endfor_nexthops(fi) + if (dead >= fi->fib_nhs) { + fi->fib_flags |= RTNH_F_DEAD; + } } if (fi->fib_prefsrc) { @@ -858,20 +905,22 @@ return ret; } -#ifdef CONFIG_IP_ROUTE_MULTIPATH - /* Dead device goes up. We wake up dead nexthops. - It takes sense only on multipath routes. */ int fib_sync_up(struct device *dev) { - int ret = 0; + struct rt_key key; + struct fib_result res; + int ret, rep; +repeat: if (!(dev->flags&IFF_UP)) return 0; + ret = 0; + rep = 0; for_fib_info() { int alive = 0; @@ -880,22 +929,39 @@ alive++; continue; } - if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP)) + if (nh->nh_oif != dev->ifindex || dev->ip_ptr == NULL) continue; - if (nh->nh_dev != dev || dev->ip_ptr == NULL) + if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP)) continue; + if (nh->nh_gw && fi->fib_protocol == RTPROT_STATIC) { + memset(&key, 0, sizeof(key)); + key.dst = nh->nh_gw; + key.oif = nh->nh_oif; + key.scope = nh->nh_scope; + if (fib_lookup(&key, &res) != 0) + continue; + if (res.type != RTN_UNICAST) + continue; + rep = 1; + } alive++; +#ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_power = 0; +#endif nh->nh_flags &= ~RTNH_F_DEAD; } endfor_nexthops(fi) - if (alive == fi->fib_nhs) { + if (alive > 0) { fi->fib_flags &= ~RTNH_F_DEAD; ret++; } } endfor_fib_info(); + if (rep) + goto repeat; return ret; } + +#ifdef CONFIG_IP_ROUTE_MULTIPATH /* The algorithm is suboptimal, but it provides really --- v2.2.20/linux/net/ipv4/fib_frontend.c Sat Oct 21 12:10:47 2000 +++ linux/net/ipv4/fib_frontend.c Fri Dec 14 02:01:03 2001 @@ -543,6 +543,8 @@ switch (event) { case NETDEV_UP: fib_add_ifaddr(ifa); + if (ifa->ifa_dev && ifa->ifa_dev->dev) + fib_sync_up(ifa->ifa_dev->dev); rt_cache_flush(-1); break; case NETDEV_DOWN: @@ -573,9 +575,7 @@ for_ifa(in_dev) { fib_add_ifaddr(ifa); } endfor_ifa(in_dev); -#ifdef CONFIG_IP_ROUTE_MULTIPATH fib_sync_up(dev); -#endif rt_cache_flush(-1); break; case NETDEV_DOWN: