diff options
-rw-r--r-- | include/linux/inetdevice.h | 3 | ||||
-rw-r--r-- | include/net/fib_rules.h | 3 | ||||
-rw-r--r-- | include/net/ip_fib.h | 20 | ||||
-rw-r--r-- | include/uapi/linux/ip.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/rtnetlink.h | 3 | ||||
-rw-r--r-- | net/ipv4/devinet.c | 2 | ||||
-rw-r--r-- | net/ipv4/fib_frontend.c | 29 | ||||
-rw-r--r-- | net/ipv4/fib_rules.c | 5 | ||||
-rw-r--r-- | net/ipv4/fib_semantics.c | 93 | ||||
-rw-r--r-- | net/ipv4/fib_trie.c | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_rpfilter.c | 2 | ||||
-rw-r--r-- | net/ipv4/route.c | 10 |
12 files changed, 130 insertions, 47 deletions
diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 0a21fbefdfbe..a4328cea376a 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h | |||
@@ -120,6 +120,9 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) | |||
120 | || (!IN_DEV_FORWARD(in_dev) && \ | 120 | || (!IN_DEV_FORWARD(in_dev) && \ |
121 | IN_DEV_ORCONF((in_dev), ACCEPT_REDIRECTS))) | 121 | IN_DEV_ORCONF((in_dev), ACCEPT_REDIRECTS))) |
122 | 122 | ||
123 | #define IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) \ | ||
124 | IN_DEV_CONF_GET((in_dev), IGNORE_ROUTES_WITH_LINKDOWN) | ||
125 | |||
123 | #define IN_DEV_ARPFILTER(in_dev) IN_DEV_ORCONF((in_dev), ARPFILTER) | 126 | #define IN_DEV_ARPFILTER(in_dev) IN_DEV_ORCONF((in_dev), ARPFILTER) |
124 | #define IN_DEV_ARP_ACCEPT(in_dev) IN_DEV_ORCONF((in_dev), ARP_ACCEPT) | 127 | #define IN_DEV_ARP_ACCEPT(in_dev) IN_DEV_ORCONF((in_dev), ARP_ACCEPT) |
125 | #define IN_DEV_ARP_ANNOUNCE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_ANNOUNCE) | 128 | #define IN_DEV_ARP_ANNOUNCE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_ANNOUNCE) |
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 6d67383a5114..903a55efbffe 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h | |||
@@ -36,7 +36,8 @@ struct fib_lookup_arg { | |||
36 | void *result; | 36 | void *result; |
37 | struct fib_rule *rule; | 37 | struct fib_rule *rule; |
38 | int flags; | 38 | int flags; |
39 | #define FIB_LOOKUP_NOREF 1 | 39 | #define FIB_LOOKUP_NOREF 1 |
40 | #define FIB_LOOKUP_IGNORE_LINKSTATE 2 | ||
40 | }; | 41 | }; |
41 | 42 | ||
42 | struct fib_rules_ops { | 43 | struct fib_rules_ops { |
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 54271ed0ed45..49c142bdf01e 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h | |||
@@ -226,7 +226,7 @@ static inline struct fib_table *fib_new_table(struct net *net, u32 id) | |||
226 | } | 226 | } |
227 | 227 | ||
228 | static inline int fib_lookup(struct net *net, const struct flowi4 *flp, | 228 | static inline int fib_lookup(struct net *net, const struct flowi4 *flp, |
229 | struct fib_result *res) | 229 | struct fib_result *res, unsigned int flags) |
230 | { | 230 | { |
231 | struct fib_table *tb; | 231 | struct fib_table *tb; |
232 | int err = -ENETUNREACH; | 232 | int err = -ENETUNREACH; |
@@ -234,7 +234,7 @@ static inline int fib_lookup(struct net *net, const struct flowi4 *flp, | |||
234 | rcu_read_lock(); | 234 | rcu_read_lock(); |
235 | 235 | ||
236 | tb = fib_get_table(net, RT_TABLE_MAIN); | 236 | tb = fib_get_table(net, RT_TABLE_MAIN); |
237 | if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) | 237 | if (tb && !fib_table_lookup(tb, flp, res, flags | FIB_LOOKUP_NOREF)) |
238 | err = 0; | 238 | err = 0; |
239 | 239 | ||
240 | rcu_read_unlock(); | 240 | rcu_read_unlock(); |
@@ -249,16 +249,18 @@ void __net_exit fib4_rules_exit(struct net *net); | |||
249 | struct fib_table *fib_new_table(struct net *net, u32 id); | 249 | struct fib_table *fib_new_table(struct net *net, u32 id); |
250 | struct fib_table *fib_get_table(struct net *net, u32 id); | 250 | struct fib_table *fib_get_table(struct net *net, u32 id); |
251 | 251 | ||
252 | int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res); | 252 | int __fib_lookup(struct net *net, struct flowi4 *flp, |
253 | struct fib_result *res, unsigned int flags); | ||
253 | 254 | ||
254 | static inline int fib_lookup(struct net *net, struct flowi4 *flp, | 255 | static inline int fib_lookup(struct net *net, struct flowi4 *flp, |
255 | struct fib_result *res) | 256 | struct fib_result *res, unsigned int flags) |
256 | { | 257 | { |
257 | struct fib_table *tb; | 258 | struct fib_table *tb; |
258 | int err; | 259 | int err; |
259 | 260 | ||
261 | flags |= FIB_LOOKUP_NOREF; | ||
260 | if (net->ipv4.fib_has_custom_rules) | 262 | if (net->ipv4.fib_has_custom_rules) |
261 | return __fib_lookup(net, flp, res); | 263 | return __fib_lookup(net, flp, res, flags); |
262 | 264 | ||
263 | rcu_read_lock(); | 265 | rcu_read_lock(); |
264 | 266 | ||
@@ -266,11 +268,11 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, | |||
266 | 268 | ||
267 | for (err = 0; !err; err = -ENETUNREACH) { | 269 | for (err = 0; !err; err = -ENETUNREACH) { |
268 | tb = rcu_dereference_rtnl(net->ipv4.fib_main); | 270 | tb = rcu_dereference_rtnl(net->ipv4.fib_main); |
269 | if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) | 271 | if (tb && !fib_table_lookup(tb, flp, res, flags)) |
270 | break; | 272 | break; |
271 | 273 | ||
272 | tb = rcu_dereference_rtnl(net->ipv4.fib_default); | 274 | tb = rcu_dereference_rtnl(net->ipv4.fib_default); |
273 | if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF)) | 275 | if (tb && !fib_table_lookup(tb, flp, res, flags)) |
274 | break; | 276 | break; |
275 | } | 277 | } |
276 | 278 | ||
@@ -305,9 +307,9 @@ void fib_flush_external(struct net *net); | |||
305 | 307 | ||
306 | /* Exported by fib_semantics.c */ | 308 | /* Exported by fib_semantics.c */ |
307 | int ip_fib_check_default(__be32 gw, struct net_device *dev); | 309 | int ip_fib_check_default(__be32 gw, struct net_device *dev); |
308 | int fib_sync_down_dev(struct net_device *dev, int force); | 310 | int fib_sync_down_dev(struct net_device *dev, unsigned long event); |
309 | int fib_sync_down_addr(struct net *net, __be32 local); | 311 | int fib_sync_down_addr(struct net *net, __be32 local); |
310 | int fib_sync_up(struct net_device *dev); | 312 | int fib_sync_up(struct net_device *dev, unsigned int nh_flags); |
311 | void fib_select_multipath(struct fib_result *res); | 313 | void fib_select_multipath(struct fib_result *res); |
312 | 314 | ||
313 | /* Exported by fib_trie.c */ | 315 | /* Exported by fib_trie.c */ |
diff --git a/include/uapi/linux/ip.h b/include/uapi/linux/ip.h index 411959405ab6..08f894d2ddbd 100644 --- a/include/uapi/linux/ip.h +++ b/include/uapi/linux/ip.h | |||
@@ -164,6 +164,7 @@ enum | |||
164 | IPV4_DEVCONF_ROUTE_LOCALNET, | 164 | IPV4_DEVCONF_ROUTE_LOCALNET, |
165 | IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, | 165 | IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, |
166 | IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, | 166 | IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, |
167 | IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, | ||
167 | __IPV4_DEVCONF_MAX | 168 | __IPV4_DEVCONF_MAX |
168 | }; | 169 | }; |
169 | 170 | ||
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 17fb02f488da..fdd8f07f1d34 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h | |||
@@ -338,6 +338,9 @@ struct rtnexthop { | |||
338 | #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ | 338 | #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ |
339 | #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ | 339 | #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ |
340 | #define RTNH_F_OFFLOAD 8 /* offloaded route */ | 340 | #define RTNH_F_OFFLOAD 8 /* offloaded route */ |
341 | #define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ | ||
342 | |||
343 | #define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN) | ||
341 | 344 | ||
342 | /* Macros to handle hexthops */ | 345 | /* Macros to handle hexthops */ |
343 | 346 | ||
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 419d23c53ec7..7498716e8f54 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -2169,6 +2169,8 @@ static struct devinet_sysctl_table { | |||
2169 | "igmpv2_unsolicited_report_interval"), | 2169 | "igmpv2_unsolicited_report_interval"), |
2170 | DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, | 2170 | DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, |
2171 | "igmpv3_unsolicited_report_interval"), | 2171 | "igmpv3_unsolicited_report_interval"), |
2172 | DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN, | ||
2173 | "ignore_routes_with_linkdown"), | ||
2172 | 2174 | ||
2173 | DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), | 2175 | DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), |
2174 | DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), | 2176 | DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), |
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 872494e6e6eb..6bbc54940eb4 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c | |||
@@ -280,7 +280,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) | |||
280 | fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); | 280 | fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); |
281 | fl4.flowi4_scope = scope; | 281 | fl4.flowi4_scope = scope; |
282 | fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; | 282 | fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; |
283 | if (!fib_lookup(net, &fl4, &res)) | 283 | if (!fib_lookup(net, &fl4, &res, 0)) |
284 | return FIB_RES_PREFSRC(net, res); | 284 | return FIB_RES_PREFSRC(net, res); |
285 | } else { | 285 | } else { |
286 | scope = RT_SCOPE_LINK; | 286 | scope = RT_SCOPE_LINK; |
@@ -319,7 +319,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, | |||
319 | fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0; | 319 | fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0; |
320 | 320 | ||
321 | net = dev_net(dev); | 321 | net = dev_net(dev); |
322 | if (fib_lookup(net, &fl4, &res)) | 322 | if (fib_lookup(net, &fl4, &res, 0)) |
323 | goto last_resort; | 323 | goto last_resort; |
324 | if (res.type != RTN_UNICAST && | 324 | if (res.type != RTN_UNICAST && |
325 | (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev))) | 325 | (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev))) |
@@ -354,7 +354,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, | |||
354 | fl4.flowi4_oif = dev->ifindex; | 354 | fl4.flowi4_oif = dev->ifindex; |
355 | 355 | ||
356 | ret = 0; | 356 | ret = 0; |
357 | if (fib_lookup(net, &fl4, &res) == 0) { | 357 | if (fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE) == 0) { |
358 | if (res.type == RTN_UNICAST) | 358 | if (res.type == RTN_UNICAST) |
359 | ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; | 359 | ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; |
360 | } | 360 | } |
@@ -1063,9 +1063,9 @@ static void nl_fib_lookup_exit(struct net *net) | |||
1063 | net->ipv4.fibnl = NULL; | 1063 | net->ipv4.fibnl = NULL; |
1064 | } | 1064 | } |
1065 | 1065 | ||
1066 | static void fib_disable_ip(struct net_device *dev, int force) | 1066 | static void fib_disable_ip(struct net_device *dev, unsigned long event) |
1067 | { | 1067 | { |
1068 | if (fib_sync_down_dev(dev, force)) | 1068 | if (fib_sync_down_dev(dev, event)) |
1069 | fib_flush(dev_net(dev)); | 1069 | fib_flush(dev_net(dev)); |
1070 | rt_cache_flush(dev_net(dev)); | 1070 | rt_cache_flush(dev_net(dev)); |
1071 | arp_ifdown(dev); | 1071 | arp_ifdown(dev); |
@@ -1081,7 +1081,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, | |||
1081 | case NETDEV_UP: | 1081 | case NETDEV_UP: |
1082 | fib_add_ifaddr(ifa); | 1082 | fib_add_ifaddr(ifa); |
1083 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 1083 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
1084 | fib_sync_up(dev); | 1084 | fib_sync_up(dev, RTNH_F_DEAD); |
1085 | #endif | 1085 | #endif |
1086 | atomic_inc(&net->ipv4.dev_addr_genid); | 1086 | atomic_inc(&net->ipv4.dev_addr_genid); |
1087 | rt_cache_flush(dev_net(dev)); | 1087 | rt_cache_flush(dev_net(dev)); |
@@ -1093,7 +1093,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, | |||
1093 | /* Last address was deleted from this interface. | 1093 | /* Last address was deleted from this interface. |
1094 | * Disable IP. | 1094 | * Disable IP. |
1095 | */ | 1095 | */ |
1096 | fib_disable_ip(dev, 1); | 1096 | fib_disable_ip(dev, event); |
1097 | } else { | 1097 | } else { |
1098 | rt_cache_flush(dev_net(dev)); | 1098 | rt_cache_flush(dev_net(dev)); |
1099 | } | 1099 | } |
@@ -1107,9 +1107,10 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo | |||
1107 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 1107 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
1108 | struct in_device *in_dev; | 1108 | struct in_device *in_dev; |
1109 | struct net *net = dev_net(dev); | 1109 | struct net *net = dev_net(dev); |
1110 | unsigned int flags; | ||
1110 | 1111 | ||
1111 | if (event == NETDEV_UNREGISTER) { | 1112 | if (event == NETDEV_UNREGISTER) { |
1112 | fib_disable_ip(dev, 2); | 1113 | fib_disable_ip(dev, event); |
1113 | rt_flush_dev(dev); | 1114 | rt_flush_dev(dev); |
1114 | return NOTIFY_DONE; | 1115 | return NOTIFY_DONE; |
1115 | } | 1116 | } |
@@ -1124,16 +1125,22 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo | |||
1124 | fib_add_ifaddr(ifa); | 1125 | fib_add_ifaddr(ifa); |
1125 | } endfor_ifa(in_dev); | 1126 | } endfor_ifa(in_dev); |
1126 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 1127 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
1127 | fib_sync_up(dev); | 1128 | fib_sync_up(dev, RTNH_F_DEAD); |
1128 | #endif | 1129 | #endif |
1129 | atomic_inc(&net->ipv4.dev_addr_genid); | 1130 | atomic_inc(&net->ipv4.dev_addr_genid); |
1130 | rt_cache_flush(net); | 1131 | rt_cache_flush(net); |
1131 | break; | 1132 | break; |
1132 | case NETDEV_DOWN: | 1133 | case NETDEV_DOWN: |
1133 | fib_disable_ip(dev, 0); | 1134 | fib_disable_ip(dev, event); |
1134 | break; | 1135 | break; |
1135 | case NETDEV_CHANGEMTU: | ||
1136 | case NETDEV_CHANGE: | 1136 | case NETDEV_CHANGE: |
1137 | flags = dev_get_flags(dev); | ||
1138 | if (flags & (IFF_RUNNING | IFF_LOWER_UP)) | ||
1139 | fib_sync_up(dev, RTNH_F_LINKDOWN); | ||
1140 | else | ||
1141 | fib_sync_down_dev(dev, event); | ||
1142 | /* fall through */ | ||
1143 | case NETDEV_CHANGEMTU: | ||
1137 | rt_cache_flush(net); | 1144 | rt_cache_flush(net); |
1138 | break; | 1145 | break; |
1139 | } | 1146 | } |
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 56151982f74e..18123d50f576 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c | |||
@@ -47,11 +47,12 @@ struct fib4_rule { | |||
47 | #endif | 47 | #endif |
48 | }; | 48 | }; |
49 | 49 | ||
50 | int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) | 50 | int __fib_lookup(struct net *net, struct flowi4 *flp, |
51 | struct fib_result *res, unsigned int flags) | ||
51 | { | 52 | { |
52 | struct fib_lookup_arg arg = { | 53 | struct fib_lookup_arg arg = { |
53 | .result = res, | 54 | .result = res, |
54 | .flags = FIB_LOOKUP_NOREF, | 55 | .flags = flags, |
55 | }; | 56 | }; |
56 | int err; | 57 | int err; |
57 | 58 | ||
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 28ec3c1823bf..3bfccd83551c 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c | |||
@@ -266,7 +266,7 @@ static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) | |||
266 | #ifdef CONFIG_IP_ROUTE_CLASSID | 266 | #ifdef CONFIG_IP_ROUTE_CLASSID |
267 | nh->nh_tclassid != onh->nh_tclassid || | 267 | nh->nh_tclassid != onh->nh_tclassid || |
268 | #endif | 268 | #endif |
269 | ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_F_DEAD)) | 269 | ((nh->nh_flags ^ onh->nh_flags) & ~RTNH_COMPARE_MASK)) |
270 | return -1; | 270 | return -1; |
271 | onh++; | 271 | onh++; |
272 | } endfor_nexthops(fi); | 272 | } endfor_nexthops(fi); |
@@ -318,7 +318,7 @@ static struct fib_info *fib_find_info(const struct fib_info *nfi) | |||
318 | nfi->fib_type == fi->fib_type && | 318 | nfi->fib_type == fi->fib_type && |
319 | memcmp(nfi->fib_metrics, fi->fib_metrics, | 319 | memcmp(nfi->fib_metrics, fi->fib_metrics, |
320 | sizeof(u32) * RTAX_MAX) == 0 && | 320 | sizeof(u32) * RTAX_MAX) == 0 && |
321 | ((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 && | 321 | !((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_COMPARE_MASK) && |
322 | (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) | 322 | (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) |
323 | return fi; | 323 | return fi; |
324 | } | 324 | } |
@@ -604,6 +604,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, | |||
604 | return -ENODEV; | 604 | return -ENODEV; |
605 | if (!(dev->flags & IFF_UP)) | 605 | if (!(dev->flags & IFF_UP)) |
606 | return -ENETDOWN; | 606 | return -ENETDOWN; |
607 | if (!netif_carrier_ok(dev)) | ||
608 | nh->nh_flags |= RTNH_F_LINKDOWN; | ||
607 | nh->nh_dev = dev; | 609 | nh->nh_dev = dev; |
608 | dev_hold(dev); | 610 | dev_hold(dev); |
609 | nh->nh_scope = RT_SCOPE_LINK; | 611 | nh->nh_scope = RT_SCOPE_LINK; |
@@ -621,7 +623,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, | |||
621 | /* It is not necessary, but requires a bit of thinking */ | 623 | /* It is not necessary, but requires a bit of thinking */ |
622 | if (fl4.flowi4_scope < RT_SCOPE_LINK) | 624 | if (fl4.flowi4_scope < RT_SCOPE_LINK) |
623 | fl4.flowi4_scope = RT_SCOPE_LINK; | 625 | fl4.flowi4_scope = RT_SCOPE_LINK; |
624 | err = fib_lookup(net, &fl4, &res); | 626 | err = fib_lookup(net, &fl4, &res, |
627 | FIB_LOOKUP_IGNORE_LINKSTATE); | ||
625 | if (err) { | 628 | if (err) { |
626 | rcu_read_unlock(); | 629 | rcu_read_unlock(); |
627 | return err; | 630 | return err; |
@@ -636,6 +639,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, | |||
636 | if (!dev) | 639 | if (!dev) |
637 | goto out; | 640 | goto out; |
638 | dev_hold(dev); | 641 | dev_hold(dev); |
642 | if (!netif_carrier_ok(dev)) | ||
643 | nh->nh_flags |= RTNH_F_LINKDOWN; | ||
639 | err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; | 644 | err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; |
640 | } else { | 645 | } else { |
641 | struct in_device *in_dev; | 646 | struct in_device *in_dev; |
@@ -654,6 +659,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, | |||
654 | nh->nh_dev = in_dev->dev; | 659 | nh->nh_dev = in_dev->dev; |
655 | dev_hold(nh->nh_dev); | 660 | dev_hold(nh->nh_dev); |
656 | nh->nh_scope = RT_SCOPE_HOST; | 661 | nh->nh_scope = RT_SCOPE_HOST; |
662 | if (!netif_carrier_ok(nh->nh_dev)) | ||
663 | nh->nh_flags |= RTNH_F_LINKDOWN; | ||
657 | err = 0; | 664 | err = 0; |
658 | } | 665 | } |
659 | out: | 666 | out: |
@@ -920,11 +927,17 @@ struct fib_info *fib_create_info(struct fib_config *cfg) | |||
920 | if (!nh->nh_dev) | 927 | if (!nh->nh_dev) |
921 | goto failure; | 928 | goto failure; |
922 | } else { | 929 | } else { |
930 | int linkdown = 0; | ||
931 | |||
923 | change_nexthops(fi) { | 932 | change_nexthops(fi) { |
924 | err = fib_check_nh(cfg, fi, nexthop_nh); | 933 | err = fib_check_nh(cfg, fi, nexthop_nh); |
925 | if (err != 0) | 934 | if (err != 0) |
926 | goto failure; | 935 | goto failure; |
936 | if (nexthop_nh->nh_flags & RTNH_F_LINKDOWN) | ||
937 | linkdown++; | ||
927 | } endfor_nexthops(fi) | 938 | } endfor_nexthops(fi) |
939 | if (linkdown == fi->fib_nhs) | ||
940 | fi->fib_flags |= RTNH_F_LINKDOWN; | ||
928 | } | 941 | } |
929 | 942 | ||
930 | if (fi->fib_prefsrc) { | 943 | if (fi->fib_prefsrc) { |
@@ -1023,12 +1036,20 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, | |||
1023 | nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc)) | 1036 | nla_put_in_addr(skb, RTA_PREFSRC, fi->fib_prefsrc)) |
1024 | goto nla_put_failure; | 1037 | goto nla_put_failure; |
1025 | if (fi->fib_nhs == 1) { | 1038 | if (fi->fib_nhs == 1) { |
1039 | struct in_device *in_dev; | ||
1040 | |||
1026 | if (fi->fib_nh->nh_gw && | 1041 | if (fi->fib_nh->nh_gw && |
1027 | nla_put_in_addr(skb, RTA_GATEWAY, fi->fib_nh->nh_gw)) | 1042 | nla_put_in_addr(skb, RTA_GATEWAY, fi->fib_nh->nh_gw)) |
1028 | goto nla_put_failure; | 1043 | goto nla_put_failure; |
1029 | if (fi->fib_nh->nh_oif && | 1044 | if (fi->fib_nh->nh_oif && |
1030 | nla_put_u32(skb, RTA_OIF, fi->fib_nh->nh_oif)) | 1045 | nla_put_u32(skb, RTA_OIF, fi->fib_nh->nh_oif)) |
1031 | goto nla_put_failure; | 1046 | goto nla_put_failure; |
1047 | if (fi->fib_nh->nh_flags & RTNH_F_LINKDOWN) { | ||
1048 | in_dev = __in_dev_get_rcu(fi->fib_nh->nh_dev); | ||
1049 | if (in_dev && | ||
1050 | IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev)) | ||
1051 | rtm->rtm_flags |= RTNH_F_DEAD; | ||
1052 | } | ||
1032 | #ifdef CONFIG_IP_ROUTE_CLASSID | 1053 | #ifdef CONFIG_IP_ROUTE_CLASSID |
1033 | if (fi->fib_nh[0].nh_tclassid && | 1054 | if (fi->fib_nh[0].nh_tclassid && |
1034 | nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid)) | 1055 | 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, | |||
1045 | goto nla_put_failure; | 1066 | goto nla_put_failure; |
1046 | 1067 | ||
1047 | for_nexthops(fi) { | 1068 | for_nexthops(fi) { |
1069 | struct in_device *in_dev; | ||
1070 | |||
1048 | rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); | 1071 | rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); |
1049 | if (!rtnh) | 1072 | if (!rtnh) |
1050 | goto nla_put_failure; | 1073 | goto nla_put_failure; |
1051 | 1074 | ||
1052 | rtnh->rtnh_flags = nh->nh_flags & 0xFF; | 1075 | rtnh->rtnh_flags = nh->nh_flags & 0xFF; |
1076 | if (nh->nh_flags & RTNH_F_LINKDOWN) { | ||
1077 | in_dev = __in_dev_get_rcu(nh->nh_dev); | ||
1078 | if (in_dev && | ||
1079 | IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev)) | ||
1080 | rtnh->rtnh_flags |= RTNH_F_DEAD; | ||
1081 | } | ||
1053 | rtnh->rtnh_hops = nh->nh_weight - 1; | 1082 | rtnh->rtnh_hops = nh->nh_weight - 1; |
1054 | rtnh->rtnh_ifindex = nh->nh_oif; | 1083 | rtnh->rtnh_ifindex = nh->nh_oif; |
1055 | 1084 | ||
@@ -1103,7 +1132,7 @@ int fib_sync_down_addr(struct net *net, __be32 local) | |||
1103 | return ret; | 1132 | return ret; |
1104 | } | 1133 | } |
1105 | 1134 | ||
1106 | int fib_sync_down_dev(struct net_device *dev, int force) | 1135 | int fib_sync_down_dev(struct net_device *dev, unsigned long event) |
1107 | { | 1136 | { |
1108 | int ret = 0; | 1137 | int ret = 0; |
1109 | int scope = RT_SCOPE_NOWHERE; | 1138 | int scope = RT_SCOPE_NOWHERE; |
@@ -1112,7 +1141,8 @@ int fib_sync_down_dev(struct net_device *dev, int force) | |||
1112 | struct hlist_head *head = &fib_info_devhash[hash]; | 1141 | struct hlist_head *head = &fib_info_devhash[hash]; |
1113 | struct fib_nh *nh; | 1142 | struct fib_nh *nh; |
1114 | 1143 | ||
1115 | if (force) | 1144 | if (event == NETDEV_UNREGISTER || |
1145 | event == NETDEV_DOWN) | ||
1116 | scope = -1; | 1146 | scope = -1; |
1117 | 1147 | ||
1118 | hlist_for_each_entry(nh, head, nh_hash) { | 1148 | hlist_for_each_entry(nh, head, nh_hash) { |
@@ -1129,7 +1159,15 @@ int fib_sync_down_dev(struct net_device *dev, int force) | |||
1129 | dead++; | 1159 | dead++; |
1130 | else if (nexthop_nh->nh_dev == dev && | 1160 | else if (nexthop_nh->nh_dev == dev && |
1131 | nexthop_nh->nh_scope != scope) { | 1161 | nexthop_nh->nh_scope != scope) { |
1132 | nexthop_nh->nh_flags |= RTNH_F_DEAD; | 1162 | switch (event) { |
1163 | case NETDEV_DOWN: | ||
1164 | case NETDEV_UNREGISTER: | ||
1165 | nexthop_nh->nh_flags |= RTNH_F_DEAD; | ||
1166 | /* fall through */ | ||
1167 | case NETDEV_CHANGE: | ||
1168 | nexthop_nh->nh_flags |= RTNH_F_LINKDOWN; | ||
1169 | break; | ||
1170 | } | ||
1133 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 1171 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
1134 | spin_lock_bh(&fib_multipath_lock); | 1172 | spin_lock_bh(&fib_multipath_lock); |
1135 | fi->fib_power -= nexthop_nh->nh_power; | 1173 | fi->fib_power -= nexthop_nh->nh_power; |
@@ -1139,14 +1177,23 @@ int fib_sync_down_dev(struct net_device *dev, int force) | |||
1139 | dead++; | 1177 | dead++; |
1140 | } | 1178 | } |
1141 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 1179 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
1142 | if (force > 1 && nexthop_nh->nh_dev == dev) { | 1180 | if (event == NETDEV_UNREGISTER && |
1181 | nexthop_nh->nh_dev == dev) { | ||
1143 | dead = fi->fib_nhs; | 1182 | dead = fi->fib_nhs; |
1144 | break; | 1183 | break; |
1145 | } | 1184 | } |
1146 | #endif | 1185 | #endif |
1147 | } endfor_nexthops(fi) | 1186 | } endfor_nexthops(fi) |
1148 | if (dead == fi->fib_nhs) { | 1187 | if (dead == fi->fib_nhs) { |
1149 | fi->fib_flags |= RTNH_F_DEAD; | 1188 | switch (event) { |
1189 | case NETDEV_DOWN: | ||
1190 | case NETDEV_UNREGISTER: | ||
1191 | fi->fib_flags |= RTNH_F_DEAD; | ||
1192 | /* fall through */ | ||
1193 | case NETDEV_CHANGE: | ||
1194 | fi->fib_flags |= RTNH_F_LINKDOWN; | ||
1195 | break; | ||
1196 | } | ||
1150 | ret++; | 1197 | ret++; |
1151 | } | 1198 | } |
1152 | } | 1199 | } |
@@ -1210,13 +1257,11 @@ out: | |||
1210 | return; | 1257 | return; |
1211 | } | 1258 | } |
1212 | 1259 | ||
1213 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | ||
1214 | |||
1215 | /* | 1260 | /* |
1216 | * Dead device goes up. We wake up dead nexthops. | 1261 | * Dead device goes up. We wake up dead nexthops. |
1217 | * It takes sense only on multipath routes. | 1262 | * It takes sense only on multipath routes. |
1218 | */ | 1263 | */ |
1219 | int fib_sync_up(struct net_device *dev) | 1264 | int fib_sync_up(struct net_device *dev, unsigned int nh_flags) |
1220 | { | 1265 | { |
1221 | struct fib_info *prev_fi; | 1266 | struct fib_info *prev_fi; |
1222 | unsigned int hash; | 1267 | unsigned int hash; |
@@ -1243,7 +1288,7 @@ int fib_sync_up(struct net_device *dev) | |||
1243 | prev_fi = fi; | 1288 | prev_fi = fi; |
1244 | alive = 0; | 1289 | alive = 0; |
1245 | change_nexthops(fi) { | 1290 | change_nexthops(fi) { |
1246 | if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) { | 1291 | if (!(nexthop_nh->nh_flags & nh_flags)) { |
1247 | alive++; | 1292 | alive++; |
1248 | continue; | 1293 | continue; |
1249 | } | 1294 | } |
@@ -1254,14 +1299,18 @@ int fib_sync_up(struct net_device *dev) | |||
1254 | !__in_dev_get_rtnl(dev)) | 1299 | !__in_dev_get_rtnl(dev)) |
1255 | continue; | 1300 | continue; |
1256 | alive++; | 1301 | alive++; |
1302 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | ||
1257 | spin_lock_bh(&fib_multipath_lock); | 1303 | spin_lock_bh(&fib_multipath_lock); |
1258 | nexthop_nh->nh_power = 0; | 1304 | nexthop_nh->nh_power = 0; |
1259 | nexthop_nh->nh_flags &= ~RTNH_F_DEAD; | 1305 | nexthop_nh->nh_flags &= ~nh_flags; |
1260 | spin_unlock_bh(&fib_multipath_lock); | 1306 | spin_unlock_bh(&fib_multipath_lock); |
1307 | #else | ||
1308 | nexthop_nh->nh_flags &= ~nh_flags; | ||
1309 | #endif | ||
1261 | } endfor_nexthops(fi) | 1310 | } endfor_nexthops(fi) |
1262 | 1311 | ||
1263 | if (alive > 0) { | 1312 | if (alive > 0) { |
1264 | fi->fib_flags &= ~RTNH_F_DEAD; | 1313 | fi->fib_flags &= ~nh_flags; |
1265 | ret++; | 1314 | ret++; |
1266 | } | 1315 | } |
1267 | } | 1316 | } |
@@ -1269,6 +1318,8 @@ int fib_sync_up(struct net_device *dev) | |||
1269 | return ret; | 1318 | return ret; |
1270 | } | 1319 | } |
1271 | 1320 | ||
1321 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | ||
1322 | |||
1272 | /* | 1323 | /* |
1273 | * The algorithm is suboptimal, but it provides really | 1324 | * The algorithm is suboptimal, but it provides really |
1274 | * fair weighted route distribution. | 1325 | * fair weighted route distribution. |
@@ -1276,16 +1327,22 @@ int fib_sync_up(struct net_device *dev) | |||
1276 | void fib_select_multipath(struct fib_result *res) | 1327 | void fib_select_multipath(struct fib_result *res) |
1277 | { | 1328 | { |
1278 | struct fib_info *fi = res->fi; | 1329 | struct fib_info *fi = res->fi; |
1330 | struct in_device *in_dev; | ||
1279 | int w; | 1331 | int w; |
1280 | 1332 | ||
1281 | spin_lock_bh(&fib_multipath_lock); | 1333 | spin_lock_bh(&fib_multipath_lock); |
1282 | if (fi->fib_power <= 0) { | 1334 | if (fi->fib_power <= 0) { |
1283 | int power = 0; | 1335 | int power = 0; |
1284 | change_nexthops(fi) { | 1336 | change_nexthops(fi) { |
1285 | if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) { | 1337 | in_dev = __in_dev_get_rcu(nexthop_nh->nh_dev); |
1286 | power += nexthop_nh->nh_weight; | 1338 | if (nexthop_nh->nh_flags & RTNH_F_DEAD) |
1287 | nexthop_nh->nh_power = nexthop_nh->nh_weight; | 1339 | continue; |
1288 | } | 1340 | if (in_dev && |
1341 | IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && | ||
1342 | nexthop_nh->nh_flags & RTNH_F_LINKDOWN) | ||
1343 | continue; | ||
1344 | power += nexthop_nh->nh_weight; | ||
1345 | nexthop_nh->nh_power = nexthop_nh->nh_weight; | ||
1289 | } endfor_nexthops(fi); | 1346 | } endfor_nexthops(fi); |
1290 | fi->fib_power = power; | 1347 | fi->fib_power = power; |
1291 | if (power <= 0) { | 1348 | if (power <= 0) { |
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 6c666a9f1bd5..15d32612e3c6 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c | |||
@@ -1412,9 +1412,15 @@ found: | |||
1412 | continue; | 1412 | continue; |
1413 | for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { | 1413 | for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { |
1414 | const struct fib_nh *nh = &fi->fib_nh[nhsel]; | 1414 | const struct fib_nh *nh = &fi->fib_nh[nhsel]; |
1415 | struct in_device *in_dev = __in_dev_get_rcu(nh->nh_dev); | ||
1415 | 1416 | ||
1416 | if (nh->nh_flags & RTNH_F_DEAD) | 1417 | if (nh->nh_flags & RTNH_F_DEAD) |
1417 | continue; | 1418 | continue; |
1419 | if (in_dev && | ||
1420 | IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && | ||
1421 | nh->nh_flags & RTNH_F_LINKDOWN && | ||
1422 | !(fib_flags & FIB_LOOKUP_IGNORE_LINKSTATE)) | ||
1423 | continue; | ||
1418 | if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif) | 1424 | if (flp->flowi4_oif && flp->flowi4_oif != nh->nh_oif) |
1419 | continue; | 1425 | continue; |
1420 | 1426 | ||
diff --git a/net/ipv4/netfilter/ipt_rpfilter.c b/net/ipv4/netfilter/ipt_rpfilter.c index 4bfaedf9b34e..8618fd150c96 100644 --- a/net/ipv4/netfilter/ipt_rpfilter.c +++ b/net/ipv4/netfilter/ipt_rpfilter.c | |||
@@ -40,7 +40,7 @@ static bool rpfilter_lookup_reverse(struct flowi4 *fl4, | |||
40 | struct net *net = dev_net(dev); | 40 | struct net *net = dev_net(dev); |
41 | int ret __maybe_unused; | 41 | int ret __maybe_unused; |
42 | 42 | ||
43 | if (fib_lookup(net, fl4, &res)) | 43 | if (fib_lookup(net, fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) |
44 | return false; | 44 | return false; |
45 | 45 | ||
46 | if (res.type != RTN_UNICAST) { | 46 | if (res.type != RTN_UNICAST) { |
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index f6055984c307..d0362a2de3d3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -747,7 +747,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow | |||
747 | if (!(n->nud_state & NUD_VALID)) { | 747 | if (!(n->nud_state & NUD_VALID)) { |
748 | neigh_event_send(n, NULL); | 748 | neigh_event_send(n, NULL); |
749 | } else { | 749 | } else { |
750 | if (fib_lookup(net, fl4, &res) == 0) { | 750 | if (fib_lookup(net, fl4, &res, 0) == 0) { |
751 | struct fib_nh *nh = &FIB_RES_NH(res); | 751 | struct fib_nh *nh = &FIB_RES_NH(res); |
752 | 752 | ||
753 | update_or_create_fnhe(nh, fl4->daddr, new_gw, | 753 | update_or_create_fnhe(nh, fl4->daddr, new_gw, |
@@ -975,7 +975,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu) | |||
975 | return; | 975 | return; |
976 | 976 | ||
977 | rcu_read_lock(); | 977 | rcu_read_lock(); |
978 | if (fib_lookup(dev_net(dst->dev), fl4, &res) == 0) { | 978 | if (fib_lookup(dev_net(dst->dev), fl4, &res, 0) == 0) { |
979 | struct fib_nh *nh = &FIB_RES_NH(res); | 979 | struct fib_nh *nh = &FIB_RES_NH(res); |
980 | 980 | ||
981 | update_or_create_fnhe(nh, fl4->daddr, 0, mtu, | 981 | update_or_create_fnhe(nh, fl4->daddr, 0, mtu, |
@@ -1186,7 +1186,7 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt) | |||
1186 | fl4.flowi4_mark = skb->mark; | 1186 | fl4.flowi4_mark = skb->mark; |
1187 | 1187 | ||
1188 | rcu_read_lock(); | 1188 | rcu_read_lock(); |
1189 | if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0) | 1189 | if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res, 0) == 0) |
1190 | src = FIB_RES_PREFSRC(dev_net(rt->dst.dev), res); | 1190 | src = FIB_RES_PREFSRC(dev_net(rt->dst.dev), res); |
1191 | else | 1191 | else |
1192 | src = inet_select_addr(rt->dst.dev, | 1192 | src = inet_select_addr(rt->dst.dev, |
@@ -1716,7 +1716,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
1716 | fl4.flowi4_scope = RT_SCOPE_UNIVERSE; | 1716 | fl4.flowi4_scope = RT_SCOPE_UNIVERSE; |
1717 | fl4.daddr = daddr; | 1717 | fl4.daddr = daddr; |
1718 | fl4.saddr = saddr; | 1718 | fl4.saddr = saddr; |
1719 | err = fib_lookup(net, &fl4, &res); | 1719 | err = fib_lookup(net, &fl4, &res, 0); |
1720 | if (err != 0) { | 1720 | if (err != 0) { |
1721 | if (!IN_DEV_FORWARD(in_dev)) | 1721 | if (!IN_DEV_FORWARD(in_dev)) |
1722 | err = -EHOSTUNREACH; | 1722 | err = -EHOSTUNREACH; |
@@ -2123,7 +2123,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4) | |||
2123 | goto make_route; | 2123 | goto make_route; |
2124 | } | 2124 | } |
2125 | 2125 | ||
2126 | if (fib_lookup(net, fl4, &res)) { | 2126 | if (fib_lookup(net, fl4, &res, 0)) { |
2127 | res.fi = NULL; | 2127 | res.fi = NULL; |
2128 | res.table = NULL; | 2128 | res.table = NULL; |
2129 | if (fl4->flowi4_oif) { | 2129 | if (fl4->flowi4_oif) { |