diff options
| -rw-r--r-- | drivers/net/vrf.c | 5 | ||||
| -rw-r--r-- | include/net/ip6_route.h | 15 | ||||
| -rw-r--r-- | net/ipv6/fib6_rules.c | 12 | ||||
| -rw-r--r-- | net/ipv6/ip6_fib.c | 5 | ||||
| -rw-r--r-- | net/ipv6/route.c | 112 | ||||
| -rw-r--r-- | net/l3mdev/l3mdev.c | 7 |
6 files changed, 95 insertions, 61 deletions
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 11b9525dff27..69ef9cce5858 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c | |||
| @@ -1072,12 +1072,14 @@ static struct sk_buff *vrf_l3_rcv(struct net_device *vrf_dev, | |||
| 1072 | #if IS_ENABLED(CONFIG_IPV6) | 1072 | #if IS_ENABLED(CONFIG_IPV6) |
| 1073 | /* send to link-local or multicast address via interface enslaved to | 1073 | /* send to link-local or multicast address via interface enslaved to |
| 1074 | * VRF device. Force lookup to VRF table without changing flow struct | 1074 | * VRF device. Force lookup to VRF table without changing flow struct |
| 1075 | * Note: Caller to this function must hold rcu_read_lock() and no refcnt | ||
| 1076 | * is taken on the dst by this function. | ||
| 1075 | */ | 1077 | */ |
| 1076 | static struct dst_entry *vrf_link_scope_lookup(const struct net_device *dev, | 1078 | static struct dst_entry *vrf_link_scope_lookup(const struct net_device *dev, |
| 1077 | struct flowi6 *fl6) | 1079 | struct flowi6 *fl6) |
| 1078 | { | 1080 | { |
| 1079 | struct net *net = dev_net(dev); | 1081 | struct net *net = dev_net(dev); |
| 1080 | int flags = RT6_LOOKUP_F_IFACE; | 1082 | int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_DST_NOREF; |
| 1081 | struct dst_entry *dst = NULL; | 1083 | struct dst_entry *dst = NULL; |
| 1082 | struct rt6_info *rt; | 1084 | struct rt6_info *rt; |
| 1083 | 1085 | ||
| @@ -1087,7 +1089,6 @@ static struct dst_entry *vrf_link_scope_lookup(const struct net_device *dev, | |||
| 1087 | */ | 1089 | */ |
| 1088 | if (fl6->flowi6_oif == dev->ifindex) { | 1090 | if (fl6->flowi6_oif == dev->ifindex) { |
| 1089 | dst = &net->ipv6.ip6_null_entry->dst; | 1091 | dst = &net->ipv6.ip6_null_entry->dst; |
| 1090 | dst_hold(dst); | ||
| 1091 | return dst; | 1092 | return dst; |
| 1092 | } | 1093 | } |
| 1093 | 1094 | ||
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 7375a165fd98..89ad7917b98d 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h | |||
| @@ -36,6 +36,7 @@ struct route_info { | |||
| 36 | #define RT6_LOOKUP_F_SRCPREF_PUBLIC 0x00000010 | 36 | #define RT6_LOOKUP_F_SRCPREF_PUBLIC 0x00000010 |
| 37 | #define RT6_LOOKUP_F_SRCPREF_COA 0x00000020 | 37 | #define RT6_LOOKUP_F_SRCPREF_COA 0x00000020 |
| 38 | #define RT6_LOOKUP_F_IGNORE_LINKSTATE 0x00000040 | 38 | #define RT6_LOOKUP_F_IGNORE_LINKSTATE 0x00000040 |
| 39 | #define RT6_LOOKUP_F_DST_NOREF 0x00000080 | ||
| 39 | 40 | ||
| 40 | /* We do not (yet ?) support IPv6 jumbograms (RFC 2675) | 41 | /* We do not (yet ?) support IPv6 jumbograms (RFC 2675) |
| 41 | * Unlike IPv4, hdr->seg_len doesn't include the IPv6 header | 42 | * Unlike IPv4, hdr->seg_len doesn't include the IPv6 header |
| @@ -83,6 +84,10 @@ struct dst_entry *ip6_route_input_lookup(struct net *net, | |||
| 83 | struct flowi6 *fl6, | 84 | struct flowi6 *fl6, |
| 84 | const struct sk_buff *skb, int flags); | 85 | const struct sk_buff *skb, int flags); |
| 85 | 86 | ||
| 87 | struct dst_entry *ip6_route_output_flags_noref(struct net *net, | ||
| 88 | const struct sock *sk, | ||
| 89 | struct flowi6 *fl6, int flags); | ||
| 90 | |||
| 86 | struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, | 91 | struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, |
| 87 | struct flowi6 *fl6, int flags); | 92 | struct flowi6 *fl6, int flags); |
| 88 | 93 | ||
| @@ -93,6 +98,16 @@ static inline struct dst_entry *ip6_route_output(struct net *net, | |||
| 93 | return ip6_route_output_flags(net, sk, fl6, 0); | 98 | return ip6_route_output_flags(net, sk, fl6, 0); |
| 94 | } | 99 | } |
| 95 | 100 | ||
| 101 | /* Only conditionally release dst if flags indicates | ||
| 102 | * !RT6_LOOKUP_F_DST_NOREF or dst is in uncached_list. | ||
| 103 | */ | ||
| 104 | static inline void ip6_rt_put_flags(struct rt6_info *rt, int flags) | ||
| 105 | { | ||
| 106 | if (!(flags & RT6_LOOKUP_F_DST_NOREF) || | ||
| 107 | !list_empty(&rt->rt6i_uncached)) | ||
| 108 | ip6_rt_put(rt); | ||
| 109 | } | ||
| 110 | |||
| 96 | struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, | 111 | struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, |
| 97 | const struct sk_buff *skb, int flags); | 112 | const struct sk_buff *skb, int flags); |
| 98 | struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, | 113 | struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, |
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index bcfae13409b5..d22b6c140f23 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c | |||
| @@ -113,14 +113,15 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, | |||
| 113 | rt = lookup(net, net->ipv6.fib6_local_tbl, fl6, skb, flags); | 113 | rt = lookup(net, net->ipv6.fib6_local_tbl, fl6, skb, flags); |
| 114 | if (rt != net->ipv6.ip6_null_entry && rt->dst.error != -EAGAIN) | 114 | if (rt != net->ipv6.ip6_null_entry && rt->dst.error != -EAGAIN) |
| 115 | return &rt->dst; | 115 | return &rt->dst; |
| 116 | ip6_rt_put(rt); | 116 | ip6_rt_put_flags(rt, flags); |
| 117 | rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags); | 117 | rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags); |
| 118 | if (rt->dst.error != -EAGAIN) | 118 | if (rt->dst.error != -EAGAIN) |
| 119 | return &rt->dst; | 119 | return &rt->dst; |
| 120 | ip6_rt_put(rt); | 120 | ip6_rt_put_flags(rt, flags); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | dst_hold(&net->ipv6.ip6_null_entry->dst); | 123 | if (!(flags & RT6_LOOKUP_F_DST_NOREF)) |
| 124 | dst_hold(&net->ipv6.ip6_null_entry->dst); | ||
| 124 | return &net->ipv6.ip6_null_entry->dst; | 125 | return &net->ipv6.ip6_null_entry->dst; |
| 125 | } | 126 | } |
| 126 | 127 | ||
| @@ -237,13 +238,14 @@ static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp, | |||
| 237 | goto out; | 238 | goto out; |
| 238 | } | 239 | } |
| 239 | again: | 240 | again: |
| 240 | ip6_rt_put(rt); | 241 | ip6_rt_put_flags(rt, flags); |
| 241 | err = -EAGAIN; | 242 | err = -EAGAIN; |
| 242 | rt = NULL; | 243 | rt = NULL; |
| 243 | goto out; | 244 | goto out; |
| 244 | 245 | ||
| 245 | discard_pkt: | 246 | discard_pkt: |
| 246 | dst_hold(&rt->dst); | 247 | if (!(flags & RT6_LOOKUP_F_DST_NOREF)) |
| 248 | dst_hold(&rt->dst); | ||
| 247 | out: | 249 | out: |
| 248 | res->rt6 = rt; | 250 | res->rt6 = rt; |
| 249 | return err; | 251 | return err; |
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 1d16a01eccf5..5b1c9b5b9247 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c | |||
| @@ -316,9 +316,10 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, | |||
| 316 | 316 | ||
| 317 | rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags); | 317 | rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags); |
| 318 | if (rt->dst.error == -EAGAIN) { | 318 | if (rt->dst.error == -EAGAIN) { |
| 319 | ip6_rt_put(rt); | 319 | ip6_rt_put_flags(rt, flags); |
| 320 | rt = net->ipv6.ip6_null_entry; | 320 | rt = net->ipv6.ip6_null_entry; |
| 321 | dst_hold(&rt->dst); | 321 | if (!(flags | RT6_LOOKUP_F_DST_NOREF)) |
| 322 | dst_hold(&rt->dst); | ||
| 322 | } | 323 | } |
| 323 | 324 | ||
| 324 | return &rt->dst; | 325 | return &rt->dst; |
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4c5142a30808..3975ae8e2440 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
| @@ -1391,9 +1391,6 @@ static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res) | |||
| 1391 | 1391 | ||
| 1392 | pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu); | 1392 | pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu); |
| 1393 | 1393 | ||
| 1394 | if (pcpu_rt) | ||
| 1395 | ip6_hold_safe(NULL, &pcpu_rt); | ||
| 1396 | |||
| 1397 | return pcpu_rt; | 1394 | return pcpu_rt; |
| 1398 | } | 1395 | } |
| 1399 | 1396 | ||
| @@ -1403,12 +1400,9 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net, | |||
| 1403 | struct rt6_info *pcpu_rt, *prev, **p; | 1400 | struct rt6_info *pcpu_rt, *prev, **p; |
| 1404 | 1401 | ||
| 1405 | pcpu_rt = ip6_rt_pcpu_alloc(res); | 1402 | pcpu_rt = ip6_rt_pcpu_alloc(res); |
| 1406 | if (!pcpu_rt) { | 1403 | if (!pcpu_rt) |
| 1407 | dst_hold(&net->ipv6.ip6_null_entry->dst); | 1404 | return NULL; |
| 1408 | return net->ipv6.ip6_null_entry; | ||
| 1409 | } | ||
| 1410 | 1405 | ||
| 1411 | dst_hold(&pcpu_rt->dst); | ||
| 1412 | p = this_cpu_ptr(res->nh->rt6i_pcpu); | 1406 | p = this_cpu_ptr(res->nh->rt6i_pcpu); |
| 1413 | prev = cmpxchg(p, NULL, pcpu_rt); | 1407 | prev = cmpxchg(p, NULL, pcpu_rt); |
| 1414 | BUG_ON(prev); | 1408 | BUG_ON(prev); |
| @@ -2189,9 +2183,12 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, | |||
| 2189 | const struct sk_buff *skb, int flags) | 2183 | const struct sk_buff *skb, int flags) |
| 2190 | { | 2184 | { |
| 2191 | struct fib6_result res = {}; | 2185 | struct fib6_result res = {}; |
| 2192 | struct rt6_info *rt; | 2186 | struct rt6_info *rt = NULL; |
| 2193 | int strict = 0; | 2187 | int strict = 0; |
| 2194 | 2188 | ||
| 2189 | WARN_ON_ONCE((flags & RT6_LOOKUP_F_DST_NOREF) && | ||
| 2190 | !rcu_read_lock_held()); | ||
| 2191 | |||
| 2195 | strict |= flags & RT6_LOOKUP_F_IFACE; | 2192 | strict |= flags & RT6_LOOKUP_F_IFACE; |
| 2196 | strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; | 2193 | strict |= flags & RT6_LOOKUP_F_IGNORE_LINKSTATE; |
| 2197 | if (net->ipv6.devconf_all->forwarding == 0) | 2194 | if (net->ipv6.devconf_all->forwarding == 0) |
| @@ -2200,23 +2197,15 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, | |||
| 2200 | rcu_read_lock(); | 2197 | rcu_read_lock(); |
| 2201 | 2198 | ||
| 2202 | fib6_table_lookup(net, table, oif, fl6, &res, strict); | 2199 | fib6_table_lookup(net, table, oif, fl6, &res, strict); |
| 2203 | if (res.f6i == net->ipv6.fib6_null_entry) { | 2200 | if (res.f6i == net->ipv6.fib6_null_entry) |
| 2204 | rt = net->ipv6.ip6_null_entry; | 2201 | goto out; |
| 2205 | rcu_read_unlock(); | ||
| 2206 | dst_hold(&rt->dst); | ||
| 2207 | return rt; | ||
| 2208 | } | ||
| 2209 | 2202 | ||
| 2210 | fib6_select_path(net, &res, fl6, oif, false, skb, strict); | 2203 | fib6_select_path(net, &res, fl6, oif, false, skb, strict); |
| 2211 | 2204 | ||
| 2212 | /*Search through exception table */ | 2205 | /*Search through exception table */ |
| 2213 | rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr); | 2206 | rt = rt6_find_cached_rt(&res, &fl6->daddr, &fl6->saddr); |
| 2214 | if (rt) { | 2207 | if (rt) { |
| 2215 | if (ip6_hold_safe(net, &rt)) | 2208 | goto out; |
| 2216 | dst_use_noref(&rt->dst, jiffies); | ||
| 2217 | |||
| 2218 | rcu_read_unlock(); | ||
| 2219 | return rt; | ||
| 2220 | } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && | 2209 | } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && |
| 2221 | !res.nh->fib_nh_gw_family)) { | 2210 | !res.nh->fib_nh_gw_family)) { |
| 2222 | /* Create a RTF_CACHE clone which will not be | 2211 | /* Create a RTF_CACHE clone which will not be |
| @@ -2224,40 +2213,38 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, | |||
| 2224 | * the daddr in the skb during the neighbor look-up is different | 2213 | * the daddr in the skb during the neighbor look-up is different |
| 2225 | * from the fl6->daddr used to look-up route here. | 2214 | * from the fl6->daddr used to look-up route here. |
| 2226 | */ | 2215 | */ |
| 2227 | struct rt6_info *uncached_rt; | 2216 | rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL); |
| 2228 | |||
| 2229 | uncached_rt = ip6_rt_cache_alloc(&res, &fl6->daddr, NULL); | ||
| 2230 | 2217 | ||
| 2231 | rcu_read_unlock(); | 2218 | if (rt) { |
| 2232 | 2219 | /* 1 refcnt is taken during ip6_rt_cache_alloc(). | |
| 2233 | if (uncached_rt) { | 2220 | * As rt6_uncached_list_add() does not consume refcnt, |
| 2234 | /* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc() | 2221 | * this refcnt is always returned to the caller even |
| 2235 | * No need for another dst_hold() | 2222 | * if caller sets RT6_LOOKUP_F_DST_NOREF flag. |
| 2236 | */ | 2223 | */ |
| 2237 | rt6_uncached_list_add(uncached_rt); | 2224 | rt6_uncached_list_add(rt); |
| 2238 | atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); | 2225 | atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); |
| 2239 | } else { | 2226 | rcu_read_unlock(); |
| 2240 | uncached_rt = net->ipv6.ip6_null_entry; | ||
| 2241 | dst_hold(&uncached_rt->dst); | ||
| 2242 | } | ||
| 2243 | 2227 | ||
| 2244 | return uncached_rt; | 2228 | return rt; |
| 2229 | } | ||
| 2245 | } else { | 2230 | } else { |
| 2246 | /* Get a percpu copy */ | 2231 | /* Get a percpu copy */ |
| 2247 | |||
| 2248 | struct rt6_info *pcpu_rt; | ||
| 2249 | |||
| 2250 | local_bh_disable(); | 2232 | local_bh_disable(); |
| 2251 | pcpu_rt = rt6_get_pcpu_route(&res); | 2233 | rt = rt6_get_pcpu_route(&res); |
| 2252 | 2234 | ||
| 2253 | if (!pcpu_rt) | 2235 | if (!rt) |
| 2254 | pcpu_rt = rt6_make_pcpu_route(net, &res); | 2236 | rt = rt6_make_pcpu_route(net, &res); |
| 2255 | 2237 | ||
| 2256 | local_bh_enable(); | 2238 | local_bh_enable(); |
| 2257 | rcu_read_unlock(); | ||
| 2258 | |||
| 2259 | return pcpu_rt; | ||
| 2260 | } | 2239 | } |
| 2240 | out: | ||
| 2241 | if (!rt) | ||
| 2242 | rt = net->ipv6.ip6_null_entry; | ||
| 2243 | if (!(flags & RT6_LOOKUP_F_DST_NOREF)) | ||
| 2244 | ip6_hold_safe(net, &rt); | ||
| 2245 | rcu_read_unlock(); | ||
| 2246 | |||
| 2247 | return rt; | ||
| 2261 | } | 2248 | } |
| 2262 | EXPORT_SYMBOL_GPL(ip6_pol_route); | 2249 | EXPORT_SYMBOL_GPL(ip6_pol_route); |
| 2263 | 2250 | ||
| @@ -2388,11 +2375,12 @@ u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6, | |||
| 2388 | return mhash >> 1; | 2375 | return mhash >> 1; |
| 2389 | } | 2376 | } |
| 2390 | 2377 | ||
| 2378 | /* Called with rcu held */ | ||
| 2391 | void ip6_route_input(struct sk_buff *skb) | 2379 | void ip6_route_input(struct sk_buff *skb) |
| 2392 | { | 2380 | { |
| 2393 | const struct ipv6hdr *iph = ipv6_hdr(skb); | 2381 | const struct ipv6hdr *iph = ipv6_hdr(skb); |
| 2394 | struct net *net = dev_net(skb->dev); | 2382 | struct net *net = dev_net(skb->dev); |
| 2395 | int flags = RT6_LOOKUP_F_HAS_SADDR; | 2383 | int flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_DST_NOREF; |
| 2396 | struct ip_tunnel_info *tun_info; | 2384 | struct ip_tunnel_info *tun_info; |
| 2397 | struct flowi6 fl6 = { | 2385 | struct flowi6 fl6 = { |
| 2398 | .flowi6_iif = skb->dev->ifindex, | 2386 | .flowi6_iif = skb->dev->ifindex, |
| @@ -2414,8 +2402,8 @@ void ip6_route_input(struct sk_buff *skb) | |||
| 2414 | if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) | 2402 | if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) |
| 2415 | fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys); | 2403 | fl6.mp_hash = rt6_multipath_hash(net, &fl6, skb, flkeys); |
| 2416 | skb_dst_drop(skb); | 2404 | skb_dst_drop(skb); |
| 2417 | skb_dst_set(skb, | 2405 | skb_dst_set_noref(skb, ip6_route_input_lookup(net, skb->dev, |
| 2418 | ip6_route_input_lookup(net, skb->dev, &fl6, skb, flags)); | 2406 | &fl6, skb, flags)); |
| 2419 | } | 2407 | } |
| 2420 | 2408 | ||
| 2421 | static struct rt6_info *ip6_pol_route_output(struct net *net, | 2409 | static struct rt6_info *ip6_pol_route_output(struct net *net, |
| @@ -2427,8 +2415,9 @@ static struct rt6_info *ip6_pol_route_output(struct net *net, | |||
| 2427 | return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags); | 2415 | return ip6_pol_route(net, table, fl6->flowi6_oif, fl6, skb, flags); |
| 2428 | } | 2416 | } |
| 2429 | 2417 | ||
| 2430 | struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, | 2418 | struct dst_entry *ip6_route_output_flags_noref(struct net *net, |
| 2431 | struct flowi6 *fl6, int flags) | 2419 | const struct sock *sk, |
| 2420 | struct flowi6 *fl6, int flags) | ||
| 2432 | { | 2421 | { |
| 2433 | bool any_src; | 2422 | bool any_src; |
| 2434 | 2423 | ||
| @@ -2436,6 +2425,7 @@ struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, | |||
| 2436 | (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) { | 2425 | (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) { |
| 2437 | struct dst_entry *dst; | 2426 | struct dst_entry *dst; |
| 2438 | 2427 | ||
| 2428 | /* This function does not take refcnt on the dst */ | ||
| 2439 | dst = l3mdev_link_scope_lookup(net, fl6); | 2429 | dst = l3mdev_link_scope_lookup(net, fl6); |
| 2440 | if (dst) | 2430 | if (dst) |
| 2441 | return dst; | 2431 | return dst; |
| @@ -2443,6 +2433,7 @@ struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, | |||
| 2443 | 2433 | ||
| 2444 | fl6->flowi6_iif = LOOPBACK_IFINDEX; | 2434 | fl6->flowi6_iif = LOOPBACK_IFINDEX; |
| 2445 | 2435 | ||
| 2436 | flags |= RT6_LOOKUP_F_DST_NOREF; | ||
| 2446 | any_src = ipv6_addr_any(&fl6->saddr); | 2437 | any_src = ipv6_addr_any(&fl6->saddr); |
| 2447 | if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || | 2438 | if ((sk && sk->sk_bound_dev_if) || rt6_need_strict(&fl6->daddr) || |
| 2448 | (fl6->flowi6_oif && any_src)) | 2439 | (fl6->flowi6_oif && any_src)) |
| @@ -2455,6 +2446,28 @@ struct dst_entry *ip6_route_output_flags(struct net *net, const struct sock *sk, | |||
| 2455 | 2446 | ||
| 2456 | return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output); | 2447 | return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output); |
| 2457 | } | 2448 | } |
| 2449 | EXPORT_SYMBOL_GPL(ip6_route_output_flags_noref); | ||
| 2450 | |||
| 2451 | struct dst_entry *ip6_route_output_flags(struct net *net, | ||
| 2452 | const struct sock *sk, | ||
| 2453 | struct flowi6 *fl6, | ||
| 2454 | int flags) | ||
| 2455 | { | ||
| 2456 | struct dst_entry *dst; | ||
| 2457 | struct rt6_info *rt6; | ||
| 2458 | |||
| 2459 | rcu_read_lock(); | ||
| 2460 | dst = ip6_route_output_flags_noref(net, sk, fl6, flags); | ||
| 2461 | rt6 = (struct rt6_info *)dst; | ||
| 2462 | /* For dst cached in uncached_list, refcnt is already taken. */ | ||
| 2463 | if (list_empty(&rt6->rt6i_uncached) && !dst_hold_safe(dst)) { | ||
| 2464 | dst = &net->ipv6.ip6_null_entry->dst; | ||
| 2465 | dst_hold(dst); | ||
| 2466 | } | ||
| 2467 | rcu_read_unlock(); | ||
| 2468 | |||
| 2469 | return dst; | ||
| 2470 | } | ||
| 2458 | EXPORT_SYMBOL_GPL(ip6_route_output_flags); | 2471 | EXPORT_SYMBOL_GPL(ip6_route_output_flags); |
| 2459 | 2472 | ||
| 2460 | struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) | 2473 | struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) |
| @@ -6029,6 +6042,7 @@ static int __net_init ip6_route_net_init(struct net *net) | |||
| 6029 | net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; | 6042 | net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops; |
| 6030 | dst_init_metrics(&net->ipv6.ip6_null_entry->dst, | 6043 | dst_init_metrics(&net->ipv6.ip6_null_entry->dst, |
| 6031 | ip6_template_metrics, true); | 6044 | ip6_template_metrics, true); |
| 6045 | INIT_LIST_HEAD(&net->ipv6.ip6_null_entry->rt6i_uncached); | ||
| 6032 | 6046 | ||
| 6033 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | 6047 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES |
| 6034 | net->ipv6.fib6_has_custom_rules = false; | 6048 | net->ipv6.fib6_has_custom_rules = false; |
| @@ -6040,6 +6054,7 @@ static int __net_init ip6_route_net_init(struct net *net) | |||
| 6040 | net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; | 6054 | net->ipv6.ip6_prohibit_entry->dst.ops = &net->ipv6.ip6_dst_ops; |
| 6041 | dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, | 6055 | dst_init_metrics(&net->ipv6.ip6_prohibit_entry->dst, |
| 6042 | ip6_template_metrics, true); | 6056 | ip6_template_metrics, true); |
| 6057 | INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached); | ||
| 6043 | 6058 | ||
| 6044 | net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, | 6059 | net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, |
| 6045 | sizeof(*net->ipv6.ip6_blk_hole_entry), | 6060 | sizeof(*net->ipv6.ip6_blk_hole_entry), |
| @@ -6049,6 +6064,7 @@ static int __net_init ip6_route_net_init(struct net *net) | |||
| 6049 | net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; | 6064 | net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; |
| 6050 | dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, | 6065 | dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, |
| 6051 | ip6_template_metrics, true); | 6066 | ip6_template_metrics, true); |
| 6067 | INIT_LIST_HEAD(&net->ipv6.ip6_blk_hole_entry->rt6i_uncached); | ||
| 6052 | #endif | 6068 | #endif |
| 6053 | 6069 | ||
| 6054 | net->ipv6.sysctl.flush_delay = 0; | 6070 | net->ipv6.sysctl.flush_delay = 0; |
diff --git a/net/l3mdev/l3mdev.c b/net/l3mdev/l3mdev.c index cfc9fcb97465..f35899d45a9a 100644 --- a/net/l3mdev/l3mdev.c +++ b/net/l3mdev/l3mdev.c | |||
| @@ -118,6 +118,8 @@ EXPORT_SYMBOL_GPL(l3mdev_fib_table_by_index); | |||
| 118 | * local and multicast addresses | 118 | * local and multicast addresses |
| 119 | * @net: network namespace for device index lookup | 119 | * @net: network namespace for device index lookup |
| 120 | * @fl6: IPv6 flow struct for lookup | 120 | * @fl6: IPv6 flow struct for lookup |
| 121 | * This function does not hold refcnt on the returned dst. | ||
| 122 | * Caller must hold rcu_read_lock(). | ||
| 121 | */ | 123 | */ |
| 122 | 124 | ||
| 123 | struct dst_entry *l3mdev_link_scope_lookup(struct net *net, | 125 | struct dst_entry *l3mdev_link_scope_lookup(struct net *net, |
| @@ -126,9 +128,8 @@ struct dst_entry *l3mdev_link_scope_lookup(struct net *net, | |||
| 126 | struct dst_entry *dst = NULL; | 128 | struct dst_entry *dst = NULL; |
| 127 | struct net_device *dev; | 129 | struct net_device *dev; |
| 128 | 130 | ||
| 131 | WARN_ON_ONCE(!rcu_read_lock_held()); | ||
| 129 | if (fl6->flowi6_oif) { | 132 | if (fl6->flowi6_oif) { |
| 130 | rcu_read_lock(); | ||
| 131 | |||
| 132 | dev = dev_get_by_index_rcu(net, fl6->flowi6_oif); | 133 | dev = dev_get_by_index_rcu(net, fl6->flowi6_oif); |
| 133 | if (dev && netif_is_l3_slave(dev)) | 134 | if (dev && netif_is_l3_slave(dev)) |
| 134 | dev = netdev_master_upper_dev_get_rcu(dev); | 135 | dev = netdev_master_upper_dev_get_rcu(dev); |
| @@ -136,8 +137,6 @@ struct dst_entry *l3mdev_link_scope_lookup(struct net *net, | |||
| 136 | if (dev && netif_is_l3_master(dev) && | 137 | if (dev && netif_is_l3_master(dev) && |
| 137 | dev->l3mdev_ops->l3mdev_link_scope_lookup) | 138 | dev->l3mdev_ops->l3mdev_link_scope_lookup) |
| 138 | dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6); | 139 | dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6); |
| 139 | |||
| 140 | rcu_read_unlock(); | ||
| 141 | } | 140 | } |
| 142 | 141 | ||
| 143 | return dst; | 142 | return dst; |
