diff -urp v7.1/linux/include/net/ip_fib.h linux/include/net/ip_fib.h --- v7.1/linux/include/net/ip_fib.h 2026-08-10 13:56:44.000000000 +0300 +++ linux/include/net/ip_fib.h 2026-08-10 14:04:15.777051288 +0300 @@ -436,6 +436,8 @@ static inline bool fib4_rules_early_flow return true; } +u32 fib_result_table(struct fib_result *res); + #endif /* CONFIG_IP_MULTIPLE_TABLES */ static inline bool fib_dscp_masked_match(dscp_t dscp, const struct flowi4 *fl4) @@ -466,6 +468,8 @@ fib_validate_source_reason(struct sk_buf return SKB_NOT_DROPPED_YET; } +void fib_select_default(const struct flowi4 *flp, struct fib_result *res); + #ifdef CONFIG_IP_ROUTE_CLASSID static inline int fib_num_tclassid_users(struct net *net) { diff -urp v7.1/linux/include/uapi/linux/rtnetlink.h linux/include/uapi/linux/rtnetlink.h --- v7.1/linux/include/uapi/linux/rtnetlink.h 2026-08-10 13:55:14.000000000 +0300 +++ linux/include/uapi/linux/rtnetlink.h 2026-08-10 14:04:15.777526514 +0300 @@ -432,9 +432,11 @@ struct rtnexthop { #define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */ #define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */ #define RTNH_F_TRAP 64 /* Nexthop is trapping packets */ +#define RTNH_F_SUSPECT 128 /* We don't know the real state */ +#define RTNH_F_BADSTATE (RTNH_F_DEAD | RTNH_F_SUSPECT) #define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \ - RTNH_F_OFFLOAD | RTNH_F_TRAP) + RTNH_F_OFFLOAD | RTNH_F_TRAP | RTNH_F_SUSPECT) /* Macros to handle hexthops */ diff -urp v7.1/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c --- v7.1/linux/net/ipv4/fib_frontend.c 2026-08-10 13:57:03.000000000 +0300 +++ linux/net/ipv4/fib_frontend.c 2026-08-10 14:04:15.778034603 +0300 @@ -50,6 +50,8 @@ #ifndef CONFIG_IP_MULTIPLE_TABLES +#define FIB_RES_TABLE(r) (RT_TABLE_MAIN) + static int __net_init fib4_rules_init(struct net *net) { struct fib_table *local_table, *main_table; @@ -74,6 +76,8 @@ fail: } #else +#define FIB_RES_TABLE(r) (fib_result_table(r)) + struct fib_table *fib_new_table(struct net *net, u32 id) { struct fib_table *tb, *alias = NULL; @@ -348,6 +352,9 @@ static int __fib_validate_source(struct { struct net *net = dev_net(dev); enum skb_drop_reason reason; + u32 table; + unsigned char prefixlen; + unsigned char scope; struct flow_keys flkeys; int ret, no_addr; struct fib_result res; @@ -402,15 +409,23 @@ static int __fib_validate_source(struct } if (no_addr) goto last_resort; - if (rpf == 1) - goto e_rpf; + table = FIB_RES_TABLE(&res); + prefixlen = res.prefixlen; + scope = res.scope; fl4.flowi4_oif = dev->ifindex; ret = 0; if (fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE) == 0) { - if (res.type == RTN_UNICAST) + if (res.type == RTN_UNICAST && + ((table == FIB_RES_TABLE(&res) && + res.prefixlen >= prefixlen && res.scope >= scope) || + !rpf)) { ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_HOST; + return ret; + } } + if (rpf == 1) + goto e_rpf; return ret; last_resort: diff -urp v7.1/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c --- v7.1/linux/net/ipv4/fib_rules.c 2026-01-08 20:23:34.000000000 +0200 +++ linux/net/ipv4/fib_rules.c 2026-08-10 14:04:15.778476018 +0300 @@ -81,6 +81,11 @@ unsigned int fib4_rules_seq_read(const s return fib_rules_seq_read(net, AF_INET); } +u32 fib_result_table(struct fib_result *res) +{ + return res->table ? res->table->tb_id : RT_TABLE_UNSPEC; +} + int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res, unsigned int flags) { diff -urp v7.1/linux/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c --- v7.1/linux/net/ipv4/fib_semantics.c 2026-08-10 13:57:03.000000000 +0300 +++ linux/net/ipv4/fib_semantics.c 2026-08-10 14:11:04.674300648 +0300 @@ -579,35 +579,74 @@ errout: static int fib_detect_death(struct fib_info *fi, int order, struct fib_info **last_resort, int *last_idx, - int dflt) + int dflt, int *last_nhsel, + const struct flowi4 *flp) { - const struct fib_nh_common *nhc = fib_info_nhc(fi, 0); + struct fib_nh_common *nhc; struct neighbour *n; - int state = NUD_NONE; + int nhsel; + int state; + int flag, dead = 1; + + /* change_nexthops(fi) { */ + for (nhsel = 0; nhsel < fib_info_num_path(fi); nhsel++) { + nhc = fib_info_nhc(fi, nhsel); + if (flp->flowi4_oif && flp->flowi4_oif != nhc->nhc_oif) + continue; + if (nhc->nhc_flags & RTNH_F_DEAD) + continue; - if (likely(nhc->nhc_gw_family == AF_INET)) - n = neigh_lookup(&arp_tbl, &nhc->nhc_gw.ipv4, nhc->nhc_dev); - else if (IS_ENABLED(CONFIG_IPV6) && nhc->nhc_gw_family == AF_INET6) - n = neigh_lookup(&nd_tbl, &nhc->nhc_gw.ipv6, nhc->nhc_dev); - else - n = NULL; + flag = 0; + if (nhc->nhc_dev->flags & IFF_NOARP) { + dead = 0; + goto setfl; + } - if (n) { - state = READ_ONCE(n->nud_state); - neigh_release(n); - } else { - return 0; - } - if (state == NUD_REACHABLE) - return 0; - if ((state & NUD_VALID) && order != dflt) - return 0; - if ((state & NUD_VALID) || - (*last_idx < 0 && order > dflt && state != NUD_INCOMPLETE)) { - *last_resort = fi; - *last_idx = order; + state = NUD_NONE; + if (!nhc->nhc_gw_family || nhc->nhc_scope != RT_SCOPE_LINK || + (nhc->nhc_gw_family == AF_INET && !nhc->nhc_gw.ipv4)) + n = neigh_lookup(&arp_tbl, &flp->daddr, + nhc->nhc_dev); + else if (likely(nhc->nhc_gw_family == AF_INET)) + n = neigh_lookup(&arp_tbl, &nhc->nhc_gw.ipv4, + nhc->nhc_dev); + else if (IS_ENABLED(CONFIG_IPV6) && + nhc->nhc_gw_family == AF_INET6) + n = neigh_lookup(&nd_tbl, &nhc->nhc_gw.ipv6, + nhc->nhc_dev); + else + n = NULL; + if (n) { + state = READ_ONCE(n->nud_state); + neigh_release(n); + } + if (state == NUD_REACHABLE || + ((state & NUD_VALID) && order != dflt)) { + dead = 0; + goto setfl; + } + if (!(state & NUD_VALID)) + flag = 1; + if (!dead) + goto setfl; + if ((state & NUD_VALID) || + (*last_idx < 0 && order >= dflt)) { + *last_resort = fi; + *last_idx = order; + *last_nhsel = nhsel; + } + +setfl: + read_lock_bh(&fib_nhflags_lock); + if (flag) + nhc->nhc_flags |= RTNH_F_SUSPECT; + else + nhc->nhc_flags &= ~RTNH_F_SUSPECT; + read_unlock_bh(&fib_nhflags_lock); } - return 1; + /* } endfor_nexthops(fi) */ + + return dead; } int fib_nh_common_init(struct net *net, struct fib_nh_common *nhc, @@ -2040,20 +2079,19 @@ int fib_sync_down_dev(struct net_device } /* Must be invoked inside of an RCU protected region. */ -static void fib_select_default(const struct flowi4 *flp, struct fib_result *res) +void fib_select_default(const struct flowi4 *flp, struct fib_result *res) { struct fib_info *fi = NULL, *last_resort = NULL; struct hlist_head *fa_head = res->fa_head; struct fib_table *tb = res->table; u8 slen = 32 - res->prefixlen; - int order = -1, last_idx = -1; + int order = -1, last_idx = -1, last_nhsel = 0; struct fib_alias *fa, *fa1 = NULL; u32 last_prio = res->fi->fib_priority; dscp_t last_dscp = 0; hlist_for_each_entry_rcu(fa, fa_head, fa_list) { struct fib_info *next_fi = fa->fa_info; - struct fib_nh_common *nhc; if (fa->fa_slen != slen) continue; @@ -2076,10 +2114,6 @@ static void fib_select_default(const str fa->fa_type != RTN_UNICAST) continue; - nhc = fib_info_nhc(next_fi, 0); - if (!nhc->nhc_gw_family || nhc->nhc_scope != RT_SCOPE_LINK) - continue; - fib_alias_accessed(fa); if (!fi) { @@ -2087,7 +2121,8 @@ static void fib_select_default(const str break; fa1 = fa; } else if (!fib_detect_death(fi, order, &last_resort, - &last_idx, fa1->fa_default)) { + &last_idx, fa1->fa_default, + &last_nhsel, flp)) { fib_result_assign(res, fi); fa1->fa_default = order; goto out; @@ -2097,20 +2132,32 @@ static void fib_select_default(const str } if (order <= 0 || !fi) { + if (fi && fib_info_num_path(fi) > 1 && + fib_detect_death(fi, order, &last_resort, &last_idx, + fa1->fa_default, &last_nhsel, flp) && + last_resort == fi) { + read_lock_bh(&fib_nhflags_lock); + fi->fib_nh[last_nhsel].fib_nh_flags &= ~RTNH_F_SUSPECT; + read_unlock_bh(&fib_nhflags_lock); + } if (fa1) fa1->fa_default = -1; goto out; } if (!fib_detect_death(fi, order, &last_resort, &last_idx, - fa1->fa_default)) { + fa1->fa_default, &last_nhsel, flp)) { fib_result_assign(res, fi); fa1->fa_default = order; goto out; } - if (last_idx >= 0) + if (last_idx >= 0) { fib_result_assign(res, last_resort); + read_lock_bh(&fib_nhflags_lock); + last_resort->fib_nh[last_nhsel].fib_nh_flags &= ~RTNH_F_SUSPECT; + read_unlock_bh(&fib_nhflags_lock); + } fa1->fa_default = last_idx; out: return; @@ -2283,23 +2330,16 @@ void fib_select_multipath(struct fib_res void fib_select_path(struct net *net, struct fib_result *res, struct flowi4 *fl4, const struct sk_buff *skb) { - if (fl4->flowi4_oif) - goto check_saddr; - + if (res->type == RTN_UNICAST) + fib_select_default(fl4, res); #ifdef CONFIG_IP_ROUTE_MULTIPATH if (fib_info_num_path(res->fi) > 1) { int h = fib_multipath_hash(net, fl4, skb, NULL); fib_select_multipath(res, h, fl4); } - else #endif - if (!res->prefixlen && - res->table->tb_num_default > 1 && - res->type == RTN_UNICAST) - fib_select_default(fl4, res); -check_saddr: if (!fl4->saddr) { struct net_device *l3mdev; diff -urp v7.1/linux/net/ipv4/route.c linux/net/ipv4/route.c --- v7.1/linux/net/ipv4/route.c 2026-08-10 13:56:44.000000000 +0300 +++ linux/net/ipv4/route.c 2026-08-10 14:04:15.779597143 +0300 @@ -2167,9 +2167,11 @@ int fib_multipath_hash(const struct net static enum skb_drop_reason ip_mkroute_input(struct sk_buff *skb, struct fib_result *res, + const struct flowi4 *fl4, struct in_device *in_dev, __be32 daddr, __be32 saddr, dscp_t dscp, struct flow_keys *hkeys) { + fib_select_default(fl4, res); #ifdef CONFIG_IP_ROUTE_MULTIPATH if (res->fi && fib_info_num_path(res->fi) > 1) { int h = fib_multipath_hash(res->fi->fib_net, NULL, skb, hkeys); @@ -2388,7 +2390,7 @@ ip_route_input_slow(struct sk_buff *skb, } make_route: - reason = ip_mkroute_input(skb, res, in_dev, daddr, saddr, dscp, + reason = ip_mkroute_input(skb, res, &fl4, in_dev, daddr, saddr, dscp, flkeys); out: