diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3bb991fd2b51..a314955e6994 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -611,6 +611,46 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
611 | return err; | 611 | return err; |
612 | } | 612 | } |
613 | 613 | ||
614 | /* This is like a cut-down ethtool ops, except done via tun fd so no | ||
615 | * privs required. */ | ||
616 | static int set_offload(struct net_device *dev, unsigned long arg) | ||
617 | { | ||
618 | unsigned int old_features, features; | ||
619 | |||
620 | old_features = dev->features; | ||
621 | /* Unset features, set them as we chew on the arg. */ | ||
622 | features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST | ||
623 | |NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6)); | ||
624 | |||
625 | if (arg & TUN_F_CSUM) { | ||
626 | features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; | ||
627 | arg &= ~TUN_F_CSUM; | ||
628 | |||
629 | if (arg & (TUN_F_TSO4|TUN_F_TSO6)) { | ||
630 | if (arg & TUN_F_TSO_ECN) { | ||
631 | features |= NETIF_F_TSO_ECN; | ||
632 | arg &= ~TUN_F_TSO_ECN; | ||
633 | } | ||
634 | if (arg & TUN_F_TSO4) | ||
635 | features |= NETIF_F_TSO; | ||
636 | if (arg & TUN_F_TSO6) | ||
637 | features |= NETIF_F_TSO6; | ||
638 | arg &= ~(TUN_F_TSO4|TUN_F_TSO6); | ||
639 | } | ||
640 | } | ||
641 | |||
642 | /* This gives the user a way to test for new features in future by | ||
643 | * trying to set them. */ | ||
644 | if (arg) | ||
645 | return -EINVAL; | ||
646 | |||
647 | dev->features = features; | ||
648 | if (old_features != dev->features) | ||
649 | netdev_features_change(dev); | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
614 | static int tun_chr_ioctl(struct inode *inode, struct file *file, | 654 | static int tun_chr_ioctl(struct inode *inode, struct file *file, |
615 | unsigned int cmd, unsigned long arg) | 655 | unsigned int cmd, unsigned long arg) |
616 | { | 656 | { |
@@ -715,6 +755,15 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, | |||
715 | break; | 755 | break; |
716 | #endif | 756 | #endif |
717 | 757 | ||
758 | case TUNSETOFFLOAD: | ||
759 | { | ||
760 | int ret; | ||
761 | rtnl_lock(); | ||
762 | ret = set_offload(tun->dev, arg); | ||
763 | rtnl_unlock(); | ||
764 | return ret; | ||
765 | } | ||
766 | |||
718 | case SIOCGIFFLAGS: | 767 | case SIOCGIFFLAGS: |
719 | ifr.ifr_flags = tun->if_flags; | 768 | ifr.ifr_flags = tun->if_flags; |
720 | if (copy_to_user( argp, &ifr, sizeof ifr)) | 769 | if (copy_to_user( argp, &ifr, sizeof ifr)) |