6363#include <linux/if_tun.h>
6464#include <linux/crc32.h>
6565#include <linux/nsproxy.h>
66+ #include <linux/virtio_net.h>
6667#include <net/net_namespace.h>
6768#include <net/netns/generic.h>
6869
@@ -283,6 +284,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
283284 struct tun_pi pi = { 0 , __constant_htons (ETH_P_IP ) };
284285 struct sk_buff * skb ;
285286 size_t len = count , align = 0 ;
287+ struct virtio_net_hdr gso = { 0 };
286288
287289 if (!(tun -> flags & TUN_NO_PI )) {
288290 if ((len -= sizeof (pi )) > count )
@@ -292,6 +294,17 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
292294 return - EFAULT ;
293295 }
294296
297+ if (tun -> flags & TUN_VNET_HDR ) {
298+ if ((len -= sizeof (gso )) > count )
299+ return - EINVAL ;
300+
301+ if (memcpy_fromiovec ((void * )& gso , iv , sizeof (gso )))
302+ return - EFAULT ;
303+
304+ if (gso .hdr_len > len )
305+ return - EINVAL ;
306+ }
307+
295308 if ((tun -> flags & TUN_TYPE_MASK ) == TUN_TAP_DEV ) {
296309 align = NET_IP_ALIGN ;
297310 if (unlikely (len < ETH_HLEN ))
@@ -311,6 +324,16 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
311324 return - EFAULT ;
312325 }
313326
327+ if (gso .flags & VIRTIO_NET_HDR_F_NEEDS_CSUM ) {
328+ if (!skb_partial_csum_set (skb , gso .csum_start ,
329+ gso .csum_offset )) {
330+ tun -> dev -> stats .rx_frame_errors ++ ;
331+ kfree_skb (skb );
332+ return - EINVAL ;
333+ }
334+ } else if (tun -> flags & TUN_NOCHECKSUM )
335+ skb -> ip_summed = CHECKSUM_UNNECESSARY ;
336+
314337 switch (tun -> flags & TUN_TYPE_MASK ) {
315338 case TUN_TUN_DEV :
316339 if (tun -> flags & TUN_NO_PI ) {
@@ -337,8 +360,35 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
337360 break ;
338361 };
339362
340- if (tun -> flags & TUN_NOCHECKSUM )
341- skb -> ip_summed = CHECKSUM_UNNECESSARY ;
363+ if (gso .gso_type != VIRTIO_NET_HDR_GSO_NONE ) {
364+ pr_debug ("GSO!\n" );
365+ switch (gso .gso_type & ~VIRTIO_NET_HDR_GSO_ECN ) {
366+ case VIRTIO_NET_HDR_GSO_TCPV4 :
367+ skb_shinfo (skb )-> gso_type = SKB_GSO_TCPV4 ;
368+ break ;
369+ case VIRTIO_NET_HDR_GSO_TCPV6 :
370+ skb_shinfo (skb )-> gso_type = SKB_GSO_TCPV6 ;
371+ break ;
372+ default :
373+ tun -> dev -> stats .rx_frame_errors ++ ;
374+ kfree_skb (skb );
375+ return - EINVAL ;
376+ }
377+
378+ if (gso .gso_type & VIRTIO_NET_HDR_GSO_ECN )
379+ skb_shinfo (skb )-> gso_type |= SKB_GSO_TCP_ECN ;
380+
381+ skb_shinfo (skb )-> gso_size = gso .gso_size ;
382+ if (skb_shinfo (skb )-> gso_size == 0 ) {
383+ tun -> dev -> stats .rx_frame_errors ++ ;
384+ kfree_skb (skb );
385+ return - EINVAL ;
386+ }
387+
388+ /* Header must be checked, and gso_segs computed. */
389+ skb_shinfo (skb )-> gso_type |= SKB_GSO_DODGY ;
390+ skb_shinfo (skb )-> gso_segs = 0 ;
391+ }
342392
343393 netif_rx_ni (skb );
344394 tun -> dev -> last_rx = jiffies ;
@@ -384,6 +434,39 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
384434 total += sizeof (pi );
385435 }
386436
437+ if (tun -> flags & TUN_VNET_HDR ) {
438+ struct virtio_net_hdr gso = { 0 }; /* no info leak */
439+ if ((len -= sizeof (gso )) < 0 )
440+ return - EINVAL ;
441+
442+ if (skb_is_gso (skb )) {
443+ struct skb_shared_info * sinfo = skb_shinfo (skb );
444+
445+ /* This is a hint as to how much should be linear. */
446+ gso .hdr_len = skb_headlen (skb );
447+ gso .gso_size = sinfo -> gso_size ;
448+ if (sinfo -> gso_type & SKB_GSO_TCPV4 )
449+ gso .gso_type = VIRTIO_NET_HDR_GSO_TCPV4 ;
450+ else if (sinfo -> gso_type & SKB_GSO_TCPV6 )
451+ gso .gso_type = VIRTIO_NET_HDR_GSO_TCPV6 ;
452+ else
453+ BUG ();
454+ if (sinfo -> gso_type & SKB_GSO_TCP_ECN )
455+ gso .gso_type |= VIRTIO_NET_HDR_GSO_ECN ;
456+ } else
457+ gso .gso_type = VIRTIO_NET_HDR_GSO_NONE ;
458+
459+ if (skb -> ip_summed == CHECKSUM_PARTIAL ) {
460+ gso .flags = VIRTIO_NET_HDR_F_NEEDS_CSUM ;
461+ gso .csum_start = skb -> csum_start - skb_headroom (skb );
462+ gso .csum_offset = skb -> csum_offset ;
463+ } /* else everything is zero */
464+
465+ if (unlikely (memcpy_toiovec (iv , (void * )& gso , sizeof (gso ))))
466+ return - EFAULT ;
467+ total += sizeof (gso );
468+ }
469+
387470 len = min_t (int , skb -> len , len );
388471
389472 skb_copy_datagram_iovec (skb , 0 , iv , len );
@@ -598,6 +681,11 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
598681 else
599682 tun -> flags &= ~TUN_ONE_QUEUE ;
600683
684+ if (ifr -> ifr_flags & IFF_VNET_HDR )
685+ tun -> flags |= TUN_VNET_HDR ;
686+ else
687+ tun -> flags &= ~TUN_VNET_HDR ;
688+
601689 file -> private_data = tun ;
602690 tun -> attached = 1 ;
603691 get_net (dev_net (tun -> dev ));
@@ -684,7 +772,8 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
684772 /* Currently this just means: "what IFF flags are valid?".
685773 * This is needed because we never checked for invalid flags on
686774 * TUNSETIFF. */
687- return put_user (IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE ,
775+ return put_user (IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE |
776+ IFF_VNET_HDR ,
688777 (unsigned int __user * )argp );
689778 }
690779
0 commit comments