diff options
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r-- | net/ipv6/route.c | 70 |
1 files changed, 37 insertions, 33 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b18e85cd7587..23a20d62daac 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -380,11 +380,8 @@ static void ip6_dst_destroy(struct dst_entry *dst) | |||
380 | in6_dev_put(idev); | 380 | in6_dev_put(idev); |
381 | } | 381 | } |
382 | 382 | ||
383 | rcu_read_lock(); | 383 | from = xchg((__force struct fib6_info **)&rt->from, NULL); |
384 | from = rcu_dereference(rt->from); | ||
385 | rcu_assign_pointer(rt->from, NULL); | ||
386 | fib6_info_release(from); | 384 | fib6_info_release(from); |
387 | rcu_read_unlock(); | ||
388 | } | 385 | } |
389 | 386 | ||
390 | static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, | 387 | static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, |
@@ -1323,9 +1320,7 @@ static void rt6_remove_exception(struct rt6_exception_bucket *bucket, | |||
1323 | /* purge completely the exception to allow releasing the held resources: | 1320 | /* purge completely the exception to allow releasing the held resources: |
1324 | * some [sk] cache may keep the dst around for unlimited time | 1321 | * some [sk] cache may keep the dst around for unlimited time |
1325 | */ | 1322 | */ |
1326 | from = rcu_dereference_protected(rt6_ex->rt6i->from, | 1323 | from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL); |
1327 | lockdep_is_held(&rt6_exception_lock)); | ||
1328 | rcu_assign_pointer(rt6_ex->rt6i->from, NULL); | ||
1329 | fib6_info_release(from); | 1324 | fib6_info_release(from); |
1330 | dst_dev_put(&rt6_ex->rt6i->dst); | 1325 | dst_dev_put(&rt6_ex->rt6i->dst); |
1331 | 1326 | ||
@@ -3495,11 +3490,8 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu | |||
3495 | 3490 | ||
3496 | rcu_read_lock(); | 3491 | rcu_read_lock(); |
3497 | res.f6i = rcu_dereference(rt->from); | 3492 | res.f6i = rcu_dereference(rt->from); |
3498 | /* This fib6_info_hold() is safe here because we hold reference to rt | 3493 | if (!res.f6i) |
3499 | * and rt already holds reference to fib6_info. | 3494 | goto out; |
3500 | */ | ||
3501 | fib6_info_hold(res.f6i); | ||
3502 | rcu_read_unlock(); | ||
3503 | 3495 | ||
3504 | res.nh = &res.f6i->fib6_nh; | 3496 | res.nh = &res.f6i->fib6_nh; |
3505 | res.fib6_flags = res.f6i->fib6_flags; | 3497 | res.fib6_flags = res.f6i->fib6_flags; |
@@ -3514,10 +3506,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu | |||
3514 | 3506 | ||
3515 | nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; | 3507 | nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; |
3516 | 3508 | ||
3517 | /* No need to remove rt from the exception table if rt is | 3509 | /* rt6_insert_exception() will take care of duplicated exceptions */ |
3518 | * a cached route because rt6_insert_exception() will | ||
3519 | * takes care of it | ||
3520 | */ | ||
3521 | if (rt6_insert_exception(nrt, &res)) { | 3510 | if (rt6_insert_exception(nrt, &res)) { |
3522 | dst_release_immediate(&nrt->dst); | 3511 | dst_release_immediate(&nrt->dst); |
3523 | goto out; | 3512 | goto out; |
@@ -3530,7 +3519,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu | |||
3530 | call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); | 3519 | call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); |
3531 | 3520 | ||
3532 | out: | 3521 | out: |
3533 | fib6_info_release(res.f6i); | 3522 | rcu_read_unlock(); |
3534 | neigh_release(neigh); | 3523 | neigh_release(neigh); |
3535 | } | 3524 | } |
3536 | 3525 | ||
@@ -3772,23 +3761,34 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
3772 | 3761 | ||
3773 | static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) | 3762 | static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) |
3774 | { | 3763 | { |
3775 | int type; | ||
3776 | struct dst_entry *dst = skb_dst(skb); | 3764 | struct dst_entry *dst = skb_dst(skb); |
3765 | struct net *net = dev_net(dst->dev); | ||
3766 | struct inet6_dev *idev; | ||
3767 | int type; | ||
3768 | |||
3769 | if (netif_is_l3_master(skb->dev) && | ||
3770 | dst->dev == net->loopback_dev) | ||
3771 | idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); | ||
3772 | else | ||
3773 | idev = ip6_dst_idev(dst); | ||
3774 | |||
3777 | switch (ipstats_mib_noroutes) { | 3775 | switch (ipstats_mib_noroutes) { |
3778 | case IPSTATS_MIB_INNOROUTES: | 3776 | case IPSTATS_MIB_INNOROUTES: |
3779 | type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); | 3777 | type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); |
3780 | if (type == IPV6_ADDR_ANY) { | 3778 | if (type == IPV6_ADDR_ANY) { |
3781 | IP6_INC_STATS(dev_net(dst->dev), | 3779 | IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); |
3782 | __in6_dev_get_safely(skb->dev), | ||
3783 | IPSTATS_MIB_INADDRERRORS); | ||
3784 | break; | 3780 | break; |
3785 | } | 3781 | } |
3786 | /* FALLTHROUGH */ | 3782 | /* FALLTHROUGH */ |
3787 | case IPSTATS_MIB_OUTNOROUTES: | 3783 | case IPSTATS_MIB_OUTNOROUTES: |
3788 | IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), | 3784 | IP6_INC_STATS(net, idev, ipstats_mib_noroutes); |
3789 | ipstats_mib_noroutes); | ||
3790 | break; | 3785 | break; |
3791 | } | 3786 | } |
3787 | |||
3788 | /* Start over by dropping the dst for l3mdev case */ | ||
3789 | if (netif_is_l3_master(skb->dev)) | ||
3790 | skb_dst_drop(skb); | ||
3791 | |||
3792 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); | 3792 | icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); |
3793 | kfree_skb(skb); | 3793 | kfree_skb(skb); |
3794 | return 0; | 3794 | return 0; |
@@ -5056,16 +5056,20 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, | |||
5056 | 5056 | ||
5057 | rcu_read_lock(); | 5057 | rcu_read_lock(); |
5058 | from = rcu_dereference(rt->from); | 5058 | from = rcu_dereference(rt->from); |
5059 | 5059 | if (from) { | |
5060 | if (fibmatch) | 5060 | if (fibmatch) |
5061 | err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif, | 5061 | err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, |
5062 | RTM_NEWROUTE, NETLINK_CB(in_skb).portid, | 5062 | iif, RTM_NEWROUTE, |
5063 | nlh->nlmsg_seq, 0); | 5063 | NETLINK_CB(in_skb).portid, |
5064 | else | 5064 | nlh->nlmsg_seq, 0); |
5065 | err = rt6_fill_node(net, skb, from, dst, &fl6.daddr, | 5065 | else |
5066 | &fl6.saddr, iif, RTM_NEWROUTE, | 5066 | err = rt6_fill_node(net, skb, from, dst, &fl6.daddr, |
5067 | NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, | 5067 | &fl6.saddr, iif, RTM_NEWROUTE, |
5068 | 0); | 5068 | NETLINK_CB(in_skb).portid, |
5069 | nlh->nlmsg_seq, 0); | ||
5070 | } else { | ||
5071 | err = -ENETUNREACH; | ||
5072 | } | ||
5069 | rcu_read_unlock(); | 5073 | rcu_read_unlock(); |
5070 | 5074 | ||
5071 | if (err < 0) { | 5075 | if (err < 0) { |