diff options
author | Julian Anastasov <ja@ssi.bg> | 2015-10-30 04:23:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-11-01 16:57:39 -0500 |
commit | 4f823defdd5b106a5e89745ee8b163c71855de1e (patch) | |
tree | 0e5c931525020e31361e2af3426f9f2d78953f00 | |
parent | 5dbebbb44a6ad94aab2cd1a46f7676f255403f64 (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.h | 2 | ||||
-rw-r--r-- | net/ipv4/fib_frontend.c | 13 | ||||
-rw-r--r-- | net/ipv4/fib_semantics.c | 11 |
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 */ |
319 | int ip_fib_check_default(__be32 gw, struct net_device *dev); | 319 | int ip_fib_check_default(__be32 gw, struct net_device *dev); |
320 | int fib_sync_down_dev(struct net_device *dev, unsigned long event); | 320 | int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force); |
321 | int fib_sync_down_addr(struct net *net, __be32 local); | 321 | int fib_sync_down_addr(struct net *net, __be32 local); |
322 | int fib_sync_up(struct net_device *dev, unsigned int nh_flags); | 322 | int fib_sync_up(struct net_device *dev, unsigned int nh_flags); |
323 | void fib_select_multipath(struct fib_result *res); | 323 | void 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 | ||
1113 | static void fib_disable_ip(struct net_device *dev, unsigned long event) | 1113 | static 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 | ||
1284 | int 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 | */ | ||
1290 | int 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) { |