aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulian Anastasov <ja@ssi.bg>2015-10-30 04:23:33 -0400
committerDavid S. Miller <davem@davemloft.net>2015-11-01 16:57:39 -0500
commit4f823defdd5b106a5e89745ee8b163c71855de1e (patch)
tree0e5c931525020e31361e2af3426f9f2d78953f00
parent5dbebbb44a6ad94aab2cd1a46f7676f255403f64 (diff)
ipv4: fix to not remove local route on link down
When fib_netdev_event calls fib_disable_ip on NETDEV_DOWN event we should not delete the local routes if the local address is still present. The confusion comes from the fact that both fib_netdev_event and fib_inetaddr_event use the NETDEV_DOWN constant. Fix it by returning back the variable 'force'. Steps to reproduce: modprobe dummy ifconfig dummy0 192.168.168.1 up ifconfig dummy0 down ip route list table local | grep dummy | grep host local 192.168.168.1 dev dummy0 proto kernel scope host src 192.168.168.1 Fixes: 8a3d03166f19 ("net: track link-status of ipv4 nexthops") Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ip_fib.h2
-rw-r--r--net/ipv4/fib_frontend.c13
-rw-r--r--net/ipv4/fib_semantics.c11
3 files changed, 16 insertions, 10 deletions
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 727d6e9a9685..965fa5b1a274 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -317,7 +317,7 @@ void fib_flush_external(struct net *net);
317 317
318/* Exported by fib_semantics.c */ 318/* Exported by fib_semantics.c */
319int ip_fib_check_default(__be32 gw, struct net_device *dev); 319int ip_fib_check_default(__be32 gw, struct net_device *dev);
320int fib_sync_down_dev(struct net_device *dev, unsigned long event); 320int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force);
321int fib_sync_down_addr(struct net *net, __be32 local); 321int fib_sync_down_addr(struct net *net, __be32 local);
322int fib_sync_up(struct net_device *dev, unsigned int nh_flags); 322int fib_sync_up(struct net_device *dev, unsigned int nh_flags);
323void fib_select_multipath(struct fib_result *res); 323void fib_select_multipath(struct fib_result *res);
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 690bcbc59f26..457b2cd75b85 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -1110,9 +1110,10 @@ static void nl_fib_lookup_exit(struct net *net)
1110 net->ipv4.fibnl = NULL; 1110 net->ipv4.fibnl = NULL;
1111} 1111}
1112 1112
1113static void fib_disable_ip(struct net_device *dev, unsigned long event) 1113static void fib_disable_ip(struct net_device *dev, unsigned long event,
1114 bool force)
1114{ 1115{
1115 if (fib_sync_down_dev(dev, event)) 1116 if (fib_sync_down_dev(dev, event, force))
1116 fib_flush(dev_net(dev)); 1117 fib_flush(dev_net(dev));
1117 rt_cache_flush(dev_net(dev)); 1118 rt_cache_flush(dev_net(dev));
1118 arp_ifdown(dev); 1119 arp_ifdown(dev);
@@ -1140,7 +1141,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
1140 /* Last address was deleted from this interface. 1141 /* Last address was deleted from this interface.
1141 * Disable IP. 1142 * Disable IP.
1142 */ 1143 */
1143 fib_disable_ip(dev, event); 1144 fib_disable_ip(dev, event, true);
1144 } else { 1145 } else {
1145 rt_cache_flush(dev_net(dev)); 1146 rt_cache_flush(dev_net(dev));
1146 } 1147 }
@@ -1157,7 +1158,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
1157 unsigned int flags; 1158 unsigned int flags;
1158 1159
1159 if (event == NETDEV_UNREGISTER) { 1160 if (event == NETDEV_UNREGISTER) {
1160 fib_disable_ip(dev, event); 1161 fib_disable_ip(dev, event, true);
1161 rt_flush_dev(dev); 1162 rt_flush_dev(dev);
1162 return NOTIFY_DONE; 1163 return NOTIFY_DONE;
1163 } 1164 }
@@ -1178,14 +1179,14 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
1178 rt_cache_flush(net); 1179 rt_cache_flush(net);
1179 break; 1180 break;
1180 case NETDEV_DOWN: 1181 case NETDEV_DOWN:
1181 fib_disable_ip(dev, event); 1182 fib_disable_ip(dev, event, false);
1182 break; 1183 break;
1183 case NETDEV_CHANGE: 1184 case NETDEV_CHANGE:
1184 flags = dev_get_flags(dev); 1185 flags = dev_get_flags(dev);
1185 if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 1186 if (flags & (IFF_RUNNING | IFF_LOWER_UP))
1186 fib_sync_up(dev, RTNH_F_LINKDOWN); 1187 fib_sync_up(dev, RTNH_F_LINKDOWN);
1187 else 1188 else
1188 fib_sync_down_dev(dev, event); 1189 fib_sync_down_dev(dev, event, false);
1189 /* fall through */ 1190 /* fall through */
1190 case NETDEV_CHANGEMTU: 1191 case NETDEV_CHANGEMTU:
1191 rt_cache_flush(net); 1192 rt_cache_flush(net);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 064bd3caaa4f..2aa5b5e7da75 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1281,7 +1281,13 @@ int fib_sync_down_addr(struct net *net, __be32 local)
1281 return ret; 1281 return ret;
1282} 1282}
1283 1283
1284int fib_sync_down_dev(struct net_device *dev, unsigned long event) 1284/* Event force Flags Description
1285 * NETDEV_CHANGE 0 LINKDOWN Carrier OFF, not for scope host
1286 * NETDEV_DOWN 0 LINKDOWN|DEAD Link down, not for scope host
1287 * NETDEV_DOWN 1 LINKDOWN|DEAD Last address removed
1288 * NETDEV_UNREGISTER 1 LINKDOWN|DEAD Device removed
1289 */
1290int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force)
1285{ 1291{
1286 int ret = 0; 1292 int ret = 0;
1287 int scope = RT_SCOPE_NOWHERE; 1293 int scope = RT_SCOPE_NOWHERE;
@@ -1290,8 +1296,7 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event)
1290 struct hlist_head *head = &fib_info_devhash[hash]; 1296 struct hlist_head *head = &fib_info_devhash[hash];
1291 struct fib_nh *nh; 1297 struct fib_nh *nh;
1292 1298
1293 if (event == NETDEV_UNREGISTER || 1299 if (force)
1294 event == NETDEV_DOWN)
1295 scope = -1; 1300 scope = -1;
1296 1301
1297 hlist_for_each_entry(nh, head, nh_hash) { 1302 hlist_for_each_entry(nh, head, nh_hash) {