6060static int ip6_finish_output2 (struct net * net , struct sock * sk , struct sk_buff * skb )
6161{
6262 struct dst_entry * dst = skb_dst (skb );
63- struct net_device * dev = dst_dev (dst );
63+ struct net_device * dev = dst_dev_rcu (dst );
6464 struct inet6_dev * idev = ip6_dst_idev (dst );
6565 unsigned int hh_len = LL_RESERVED_SPACE (dev );
6666 const struct in6_addr * daddr , * nexthop ;
@@ -70,15 +70,12 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
7070
7171 /* Be paranoid, rather than too clever. */
7272 if (unlikely (hh_len > skb_headroom (skb )) && dev -> header_ops ) {
73- /* Make sure idev stays alive */
74- rcu_read_lock ();
73+ /* idev stays alive because we hold rcu_read_lock(). */
7574 skb = skb_expand_head (skb , hh_len );
7675 if (!skb ) {
7776 IP6_INC_STATS (net , idev , IPSTATS_MIB_OUTDISCARDS );
78- rcu_read_unlock ();
7977 return - ENOMEM ;
8078 }
81- rcu_read_unlock ();
8279 }
8380
8481 hdr = ipv6_hdr (skb );
@@ -123,23 +120,20 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
123120
124121 IP6_UPD_PO_STATS (net , idev , IPSTATS_MIB_OUT , skb -> len );
125122
126- rcu_read_lock ();
127123 nexthop = rt6_nexthop (dst_rt6_info (dst ), daddr );
128124 neigh = __ipv6_neigh_lookup_noref (dev , nexthop );
129125
130126 if (IS_ERR_OR_NULL (neigh )) {
131127 if (unlikely (!neigh ))
132128 neigh = __neigh_create (& nd_tbl , nexthop , dev , false);
133129 if (IS_ERR (neigh )) {
134- rcu_read_unlock ();
135130 IP6_INC_STATS (net , idev , IPSTATS_MIB_OUTNOROUTES );
136131 kfree_skb_reason (skb , SKB_DROP_REASON_NEIGH_CREATEFAIL );
137132 return - EINVAL ;
138133 }
139134 }
140135 sock_confirm_neigh (skb , neigh );
141136 ret = neigh_output (neigh , skb , false);
142- rcu_read_unlock ();
143137 return ret ;
144138}
145139
@@ -233,22 +227,29 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
233227int ip6_output (struct net * net , struct sock * sk , struct sk_buff * skb )
234228{
235229 struct dst_entry * dst = skb_dst (skb );
236- struct net_device * dev = dst_dev (dst ), * indev = skb -> dev ;
237- struct inet6_dev * idev = ip6_dst_idev (dst );
230+ struct net_device * dev , * indev = skb -> dev ;
231+ struct inet6_dev * idev ;
232+ int ret ;
238233
239234 skb -> protocol = htons (ETH_P_IPV6 );
235+ rcu_read_lock ();
236+ dev = dst_dev_rcu (dst );
237+ idev = ip6_dst_idev (dst );
240238 skb -> dev = dev ;
241239
242240 if (unlikely (!idev || READ_ONCE (idev -> cnf .disable_ipv6 ))) {
243241 IP6_INC_STATS (net , idev , IPSTATS_MIB_OUTDISCARDS );
242+ rcu_read_unlock ();
244243 kfree_skb_reason (skb , SKB_DROP_REASON_IPV6DISABLED );
245244 return 0 ;
246245 }
247246
248- return NF_HOOK_COND (NFPROTO_IPV6 , NF_INET_POST_ROUTING ,
249- net , sk , skb , indev , dev ,
250- ip6_finish_output ,
251- !(IP6CB (skb )-> flags & IP6SKB_REROUTED ));
247+ ret = NF_HOOK_COND (NFPROTO_IPV6 , NF_INET_POST_ROUTING ,
248+ net , sk , skb , indev , dev ,
249+ ip6_finish_output ,
250+ !(IP6CB (skb )-> flags & IP6SKB_REROUTED ));
251+ rcu_read_unlock ();
252+ return ret ;
252253}
253254EXPORT_SYMBOL (ip6_output );
254255
0 commit comments