@@ -278,6 +278,45 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info)
278278 kfree_skb (skb );
279279}
280280
281+ void ipv6_local_rxpmtu (struct sock * sk , struct flowi * fl , u32 mtu )
282+ {
283+ struct ipv6_pinfo * np = inet6_sk (sk );
284+ struct ipv6hdr * iph ;
285+ struct sk_buff * skb ;
286+ struct ip6_mtuinfo * mtu_info ;
287+
288+ if (!np -> rxopt .bits .rxpmtu )
289+ return ;
290+
291+ skb = alloc_skb (sizeof (struct ipv6hdr ), GFP_ATOMIC );
292+ if (!skb )
293+ return ;
294+
295+ skb_put (skb , sizeof (struct ipv6hdr ));
296+ skb_reset_network_header (skb );
297+ iph = ipv6_hdr (skb );
298+ ipv6_addr_copy (& iph -> daddr , & fl -> fl6_dst );
299+
300+ mtu_info = IP6CBMTU (skb );
301+ if (!mtu_info ) {
302+ kfree_skb (skb );
303+ return ;
304+ }
305+
306+ mtu_info -> ip6m_mtu = mtu ;
307+ mtu_info -> ip6m_addr .sin6_family = AF_INET6 ;
308+ mtu_info -> ip6m_addr .sin6_port = 0 ;
309+ mtu_info -> ip6m_addr .sin6_flowinfo = 0 ;
310+ mtu_info -> ip6m_addr .sin6_scope_id = fl -> oif ;
311+ ipv6_addr_copy (& mtu_info -> ip6m_addr .sin6_addr , & ipv6_hdr (skb )-> daddr );
312+
313+ __skb_pull (skb , skb_tail_pointer (skb ) - skb -> data );
314+ skb_reset_transport_header (skb );
315+
316+ skb = xchg (& np -> rxpmtu , skb );
317+ kfree_skb (skb );
318+ }
319+
281320/*
282321 * Handle MSG_ERRQUEUE
283322 */
@@ -381,6 +420,54 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
381420 return err ;
382421}
383422
423+ /*
424+ * Handle IPV6_RECVPATHMTU
425+ */
426+ int ipv6_recv_rxpmtu (struct sock * sk , struct msghdr * msg , int len )
427+ {
428+ struct ipv6_pinfo * np = inet6_sk (sk );
429+ struct sk_buff * skb ;
430+ struct sockaddr_in6 * sin ;
431+ struct ip6_mtuinfo mtu_info ;
432+ int err ;
433+ int copied ;
434+
435+ err = - EAGAIN ;
436+ skb = xchg (& np -> rxpmtu , NULL );
437+ if (skb == NULL )
438+ goto out ;
439+
440+ copied = skb -> len ;
441+ if (copied > len ) {
442+ msg -> msg_flags |= MSG_TRUNC ;
443+ copied = len ;
444+ }
445+ err = skb_copy_datagram_iovec (skb , 0 , msg -> msg_iov , copied );
446+ if (err )
447+ goto out_free_skb ;
448+
449+ sock_recv_timestamp (msg , sk , skb );
450+
451+ memcpy (& mtu_info , IP6CBMTU (skb ), sizeof (mtu_info ));
452+
453+ sin = (struct sockaddr_in6 * )msg -> msg_name ;
454+ if (sin ) {
455+ sin -> sin6_family = AF_INET6 ;
456+ sin -> sin6_flowinfo = 0 ;
457+ sin -> sin6_port = 0 ;
458+ sin -> sin6_scope_id = mtu_info .ip6m_addr .sin6_scope_id ;
459+ ipv6_addr_copy (& sin -> sin6_addr , & mtu_info .ip6m_addr .sin6_addr );
460+ }
461+
462+ put_cmsg (msg , SOL_IPV6 , IPV6_PATHMTU , sizeof (mtu_info ), & mtu_info );
463+
464+ err = copied ;
465+
466+ out_free_skb :
467+ kfree_skb (skb );
468+ out :
469+ return err ;
470+ }
384471
385472
386473int datagram_recv_ctl (struct sock * sk , struct msghdr * msg , struct sk_buff * skb )
0 commit comments