|
@@ -266,7 +266,7 @@ static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi)
|
|
|
#ifdef CONFIG_IP_ROUTE_CLASSID
|
|
|
nh->nh_tclassid != onh->nh_tclassid ||
|
|
|
#endif
|
|
|
- ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_F_DEAD))
|
|
|
+ ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_COMPARE_MASK))
|
|
|
return -1;
|
|
|
onh++;
|
|
|
} endfor_nexthops(fi);
|
|
@@ -318,7 +318,7 @@ static struct fib_info *fib_find_info(const struct fib_info *nfi)
|
|
|
nfi->fib_type == fi->fib_type &&
|
|
|
memcmp(nfi->fib_metrics, fi->fib_metrics,
|
|
|
sizeof(u32) * RTAX_MAX) == 0 &&
|
|
|
- ((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 &&
|
|
|
+ !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) &&
|
|
|
(nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
|
|
|
return fi;
|
|
|
}
|
|
@@ -604,6 +604,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
|
|
|
return -ENODEV;
|
|
|
if (!(dev->flags & IFF_UP))
|
|
|
return -ENETDOWN;
|
|
|
+ if (!netif_carrier_ok(dev))
|
|
|
+ nh->nh_flags |= RTNH_F_LINKDOWN;
|
|
|
nh->nh_dev = dev;
|
|
|
dev_hold(dev);
|
|
|
nh->nh_scope = RT_SCOPE_LINK;
|
|
@@ -621,7 +623,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
|
|
|
/* It is not necessary, but requires a bit of thinking */
|
|
|
if (fl4.flowi4_scope < RT_SCOPE_LINK)
|
|
|
fl4.flowi4_scope = RT_SCOPE_LINK;
|
|
|
- err = fib_lookup(net, &fl4, &res);
|
|
|
+ err = fib_lookup(net, &fl4, &res,
|
|
|
+ FIB_LOOKUP_IGNORE_LINKSTATE);
|
|
|
if (err) {
|
|
|
rcu_read_unlock();
|
|
|
return err;
|
|
@@ -636,6 +639,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
|
|
|
if (!dev)
|
|
|
goto out;
|
|
|
dev_hold(dev);
|
|
|
+ if (!netif_carrier_ok(dev))
|
|
|
+ nh->nh_flags |= RTNH_F_LINKDOWN;
|
|
|
err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN;
|
|
|
} else {
|
|
|
struct in_device *in_dev;
|
|
@@ -654,6 +659,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
|
|
|
nh->nh_dev = in_dev->dev;
|
|
|
dev_hold(nh->nh_dev);
|
|
|
nh->nh_scope = RT_SCOPE_HOST;
|
|
|
+ if (!netif_carrier_ok(nh->nh_dev))
|
|
|
+ nh->nh_flags |= RTNH_F_LINKDOWN;
|
|
|
err = 0;
|
|
|
}
|
|
|
out:
|
|
@@ -920,11 +927,17 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
|
|
|
if (!nh->nh_dev)
|
|
|
goto failure;
|
|
|
} else {
|
|
|
+ int linkdown = 0;
|
|
|
+
|
|
|
change_nexthops(fi) {
|
|
|
err = fib_check_nh(cfg, fi, nexthop_nh);
|
|
|
if (err != 0)
|
|
|
goto failure;
|
|
|
+ if (nexthop_nh->nh_flags & RTNH_F_LINKDOWN)
|
|
|
+ linkdown++;
|
|
|
} endfor_nexthops(fi)
|
|
|
+ if (linkdown == fi->fib_nhs)
|
|
|
+ fi->fib_flags |= RTNH_F_LINKDOWN;
|
|
|
}
|
|
|
|
|
|
if (fi->fib_prefsrc) {
|
|
@@ -1023,12 +1036,20 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
|
|
|
nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc))
|
|
|
goto nla_put_failure;
|
|
|
if (fi->fib_nhs == 1) {
|
|
|
+ struct in_device *in_dev;
|
|
|
+
|
|
|
if (fi->fib_nh->nh_gw &&
|
|
|
nla_put_in_addr(skb, RTA_GATEWAY, fi->fib_nh->nh_gw))
|
|
|
goto nla_put_failure;
|
|
|
if (fi->fib_nh->nh_oif &&
|
|
|
nla_put_u32(skb, RTA_OIF, fi->fib_nh->nh_oif))
|
|
|
goto nla_put_failure;
|
|
|
+ if (fi->fib_nh->nh_flags & RTNH_F_LINKDOWN) {
|
|
|
+ in_dev = __in_dev_get_rcu(fi->fib_nh->nh_dev);
|
|
|
+ if (in_dev &&
|
|
|
+ IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev))
|
|
|
+ rtm->rtm_flags |= RTNH_F_DEAD;
|
|
|
+ }
|
|
|
#ifdef CONFIG_IP_ROUTE_CLASSID
|
|
|
if (fi->fib_nh[0].nh_tclassid &&
|
|
|
nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid))
|
|
@@ -1045,11 +1066,19 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
for_nexthops(fi) {
|
|
|
+ struct in_device *in_dev;
|
|
|
+
|
|
|
rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
|
|
|
if (!rtnh)
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
rtnh->rtnh_flags = nh->nh_flags & 0xFF;
|
|
|
+ if (nh->nh_flags & RTNH_F_LINKDOWN) {
|
|
|
+ in_dev = __in_dev_get_rcu(nh->nh_dev);
|
|
|
+ if (in_dev &&
|
|
|
+ IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev))
|
|
|
+ rtnh->rtnh_flags |= RTNH_F_DEAD;
|
|
|
+ }
|
|
|
rtnh->rtnh_hops = nh->nh_weight - 1;
|
|
|
rtnh->rtnh_ifindex = nh->nh_oif;
|
|
|
|
|
@@ -1103,7 +1132,7 @@ int fib_sync_down_addr(struct net *net, __be32 local)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-int fib_sync_down_dev(struct net_device *dev, int force)
|
|
|
+int fib_sync_down_dev(struct net_device *dev, unsigned long event)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
int scope = RT_SCOPE_NOWHERE;
|
|
@@ -1112,7 +1141,8 @@ int fib_sync_down_dev(struct net_device *dev, int force)
|
|
|
struct hlist_head *head = &fib_info_devhash[hash];
|
|
|
struct fib_nh *nh;
|
|
|
|
|
|
- if (force)
|
|
|
+ if (event == NETDEV_UNREGISTER ||
|
|
|
+ event == NETDEV_DOWN)
|
|
|
scope = -1;
|
|
|
|
|
|
hlist_for_each_entry(nh, head, nh_hash) {
|
|
@@ -1129,7 +1159,15 @@ int fib_sync_down_dev(struct net_device *dev, int force)
|
|
|
dead++;
|
|
|
else if (nexthop_nh->nh_dev == dev &&
|
|
|
nexthop_nh->nh_scope != scope) {
|
|
|
- nexthop_nh->nh_flags |= RTNH_F_DEAD;
|
|
|
+ switch (event) {
|
|
|
+ case NETDEV_DOWN:
|
|
|
+ case NETDEV_UNREGISTER:
|
|
|
+ nexthop_nh->nh_flags |= RTNH_F_DEAD;
|
|
|
+ /* fall through */
|
|
|
+ case NETDEV_CHANGE:
|
|
|
+ nexthop_nh->nh_flags |= RTNH_F_LINKDOWN;
|
|
|
+ break;
|
|
|
+ }
|
|
|
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
|
|
spin_lock_bh(&fib_multipath_lock);
|
|
|
fi->fib_power -= nexthop_nh->nh_power;
|
|
@@ -1139,14 +1177,23 @@ int fib_sync_down_dev(struct net_device *dev, int force)
|
|
|
dead++;
|
|
|
}
|
|
|
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
|
|
- if (force > 1 && nexthop_nh->nh_dev == dev) {
|
|
|
+ if (event == NETDEV_UNREGISTER &&
|
|
|
+ nexthop_nh->nh_dev == dev) {
|
|
|
dead = fi->fib_nhs;
|
|
|
break;
|
|
|
}
|
|
|
#endif
|
|
|
} endfor_nexthops(fi)
|
|
|
if (dead == fi->fib_nhs) {
|
|
|
- fi->fib_flags |= RTNH_F_DEAD;
|
|
|
+ switch (event) {
|
|
|
+ case NETDEV_DOWN:
|
|
|
+ case NETDEV_UNREGISTER:
|
|
|
+ fi->fib_flags |= RTNH_F_DEAD;
|
|
|
+ /* fall through */
|
|
|
+ case NETDEV_CHANGE:
|
|
|
+ fi->fib_flags |= RTNH_F_LINKDOWN;
|
|
|
+ break;
|
|
|
+ }
|
|
|
ret++;
|
|
|
}
|
|
|
}
|
|
@@ -1210,13 +1257,11 @@ out:
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
-#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 net_device *dev)
|
|
|
+int fib_sync_up(struct net_device *dev, unsigned int nh_flags)
|
|
|
{
|
|
|
struct fib_info *prev_fi;
|
|
|
unsigned int hash;
|
|
@@ -1243,7 +1288,7 @@ int fib_sync_up(struct net_device *dev)
|
|
|
prev_fi = fi;
|
|
|
alive = 0;
|
|
|
change_nexthops(fi) {
|
|
|
- if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) {
|
|
|
+ if (!(nexthop_nh->nh_flags & nh_flags)) {
|
|
|
alive++;
|
|
|
continue;
|
|
|
}
|
|
@@ -1254,14 +1299,18 @@ int fib_sync_up(struct net_device *dev)
|
|
|
!__in_dev_get_rtnl(dev))
|
|
|
continue;
|
|
|
alive++;
|
|
|
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
|
|
spin_lock_bh(&fib_multipath_lock);
|
|
|
nexthop_nh->nh_power = 0;
|
|
|
- nexthop_nh->nh_flags &= ~RTNH_F_DEAD;
|
|
|
+ nexthop_nh->nh_flags &= ~nh_flags;
|
|
|
spin_unlock_bh(&fib_multipath_lock);
|
|
|
+#else
|
|
|
+ nexthop_nh->nh_flags &= ~nh_flags;
|
|
|
+#endif
|
|
|
} endfor_nexthops(fi)
|
|
|
|
|
|
if (alive > 0) {
|
|
|
- fi->fib_flags &= ~RTNH_F_DEAD;
|
|
|
+ fi->fib_flags &= ~nh_flags;
|
|
|
ret++;
|
|
|
}
|
|
|
}
|
|
@@ -1269,6 +1318,8 @@ int fib_sync_up(struct net_device *dev)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
|
|
+
|
|
|
/*
|
|
|
* The algorithm is suboptimal, but it provides really
|
|
|
* fair weighted route distribution.
|
|
@@ -1276,16 +1327,22 @@ int fib_sync_up(struct net_device *dev)
|
|
|
void fib_select_multipath(struct fib_result *res)
|
|
|
{
|
|
|
struct fib_info *fi = res->fi;
|
|
|
+ struct in_device *in_dev;
|
|
|
int w;
|
|
|
|
|
|
spin_lock_bh(&fib_multipath_lock);
|
|
|
if (fi->fib_power <= 0) {
|
|
|
int power = 0;
|
|
|
change_nexthops(fi) {
|
|
|
- if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) {
|
|
|
- power += nexthop_nh->nh_weight;
|
|
|
- nexthop_nh->nh_power = nexthop_nh->nh_weight;
|
|
|
- }
|
|
|
+ in_dev = __in_dev_get_rcu(nexthop_nh->nh_dev);
|
|
|
+ if (nexthop_nh->nh_flags & RTNH_F_DEAD)
|
|
|
+ continue;
|
|
|
+ if (in_dev &&
|
|
|
+ IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) &&
|
|
|
+ nexthop_nh->nh_flags & RTNH_F_LINKDOWN)
|
|
|
+ continue;
|
|
|
+ power += nexthop_nh->nh_weight;
|
|
|
+ nexthop_nh->nh_power = nexthop_nh->nh_weight;
|
|
|
} endfor_nexthops(fi);
|
|
|
fi->fib_power = power;
|
|
|
if (power <= 0) {
|