diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/ipv4/fib_semantics.c | 34 | ||||
| -rw-r--r-- | net/ipv4/sysctl_net_ipv4.c | 11 |
2 files changed, 40 insertions, 5 deletions
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index d97268e8ff10..ab64d9f2eef9 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c | |||
| @@ -1559,21 +1559,45 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags) | |||
| 1559 | } | 1559 | } |
| 1560 | 1560 | ||
| 1561 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 1561 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
| 1562 | static bool fib_good_nh(const struct fib_nh *nh) | ||
| 1563 | { | ||
| 1564 | int state = NUD_REACHABLE; | ||
| 1565 | |||
| 1566 | if (nh->nh_scope == RT_SCOPE_LINK) { | ||
| 1567 | struct neighbour *n; | ||
| 1568 | |||
| 1569 | rcu_read_lock_bh(); | ||
| 1570 | |||
| 1571 | n = __ipv4_neigh_lookup_noref(nh->nh_dev, nh->nh_gw); | ||
| 1572 | if (n) | ||
| 1573 | state = n->nud_state; | ||
| 1574 | |||
| 1575 | rcu_read_unlock_bh(); | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | return !!(state & NUD_VALID); | ||
| 1579 | } | ||
| 1562 | 1580 | ||
| 1563 | void fib_select_multipath(struct fib_result *res, int hash) | 1581 | void fib_select_multipath(struct fib_result *res, int hash) |
| 1564 | { | 1582 | { |
| 1565 | struct fib_info *fi = res->fi; | 1583 | struct fib_info *fi = res->fi; |
| 1584 | struct net *net = fi->fib_net; | ||
| 1585 | bool first = false; | ||
| 1566 | 1586 | ||
| 1567 | for_nexthops(fi) { | 1587 | for_nexthops(fi) { |
| 1568 | if (hash > atomic_read(&nh->nh_upper_bound)) | 1588 | if (hash > atomic_read(&nh->nh_upper_bound)) |
| 1569 | continue; | 1589 | continue; |
| 1570 | 1590 | ||
| 1571 | res->nh_sel = nhsel; | 1591 | if (!net->ipv4.sysctl_fib_multipath_use_neigh || |
| 1572 | return; | 1592 | fib_good_nh(nh)) { |
| 1593 | res->nh_sel = nhsel; | ||
| 1594 | return; | ||
| 1595 | } | ||
| 1596 | if (!first) { | ||
| 1597 | res->nh_sel = nhsel; | ||
| 1598 | first = true; | ||
| 1599 | } | ||
| 1573 | } endfor_nexthops(fi); | 1600 | } endfor_nexthops(fi); |
| 1574 | |||
| 1575 | /* Race condition: route has just become dead. */ | ||
| 1576 | res->nh_sel = 0; | ||
| 1577 | } | 1601 | } |
| 1578 | #endif | 1602 | #endif |
| 1579 | 1603 | ||
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 1e1fe6086dd9..bb0419582b8d 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c | |||
| @@ -960,6 +960,17 @@ static struct ctl_table ipv4_net_table[] = { | |||
| 960 | .mode = 0644, | 960 | .mode = 0644, |
| 961 | .proc_handler = proc_dointvec, | 961 | .proc_handler = proc_dointvec, |
| 962 | }, | 962 | }, |
| 963 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | ||
| 964 | { | ||
| 965 | .procname = "fib_multipath_use_neigh", | ||
| 966 | .data = &init_net.ipv4.sysctl_fib_multipath_use_neigh, | ||
| 967 | .maxlen = sizeof(int), | ||
| 968 | .mode = 0644, | ||
| 969 | .proc_handler = proc_dointvec_minmax, | ||
| 970 | .extra1 = &zero, | ||
| 971 | .extra2 = &one, | ||
| 972 | }, | ||
| 973 | #endif | ||
| 963 | { } | 974 | { } |
| 964 | }; | 975 | }; |
| 965 | 976 | ||
